DLL内で定義されたグローバル変数があるとします
そして,そのDLLを利用するDLLが2つ以上あるとします
そして,そのDLLを利用する2つ以上あるDLLを1つのアプリケーション上で
利用するとします
そのときの根元のDLL内で定義されたグローバル変数は同じ値として使うことができるの
でしょうか?
実際動くことは動くのですけど,それは安全といえるのでしょうか?
いろいろすみません
よろしくお願いします
Win32に限定します。
まず実行ファイルは「プロセス」として起動されます。
プロセスには、それ専用の「仮想アドレス」を持った「メモリー空間」が
割り当てられます。これらはPCに実装されている物理メモリの物理アドレス
とは直接の関係はありません(あるけど、任意のポインタが物理アドレスのどこに
あるかは、意識する必要がないことを意味します。もちろんポインタの値
(アドレス)は仮想的(=なんちゃって)なものです)。
従って、EXEは他にだれもいないメモリー空間に、単独で実行されている
という状態だといえます。これはGetModuleHandl( NULL)の結果が
いかなるEXEであっても常に0x00400000をとることで、確認できます。
この値はEXEのインスタンスハンドルとして利用されています。
そのEXEがDLLをロードするということは、自分のメモリー空間にマッピング
されることを意味します。
さて、何らかのOSのシステム固定値がそのDLL内にあったとき、
対応する関数でそれを取得するときに、呼び出しもとに関係なく
同じ値が戻してもらえます。user32.dllやkernel32.dllに実装されている
関数は、それをやってくれまよね。
もちろん、これらのシステムDLLも例外ではなく
「このEXEのメモリー空間にマッピング」されているわけです。
きれちゃったorz。
んで、さっきのは、リンクした場合の話し、
では、LoadLibraly()でロードしたものはどうなるのかというと、
そのDLLの参照カウントがアップします。カウントアップは重要な
ことで、これにより再マップされることになります。従って、
最初のLoadLibraly()でロードされたDLLと、次のLoadLibraly()で
ロードされたものは別の位置(アドレス)にマップされているはずです。
その内部のデータは言わずもがなですよね。
libでのリンク時は
DLL内のグローバル変数は同一のEXE内では共通だけど
別のEXEでは別のものとなるということで,
LoadLibraly()でロードされると同一のEXE内でも
グローバル変数は別物となってしまうということですか?
えっと読み違えてますか?
まず、1つのプロセス内に話を限るものとする。
LoadLibrary() で明示ロードしようが
import_library 経由で暗黙ロードしようが
・同一 DLL は当該プロセスのメモリ空間中に1回だけロードされる。
・ロードのたびに参照カウンタが上昇する。
・アンロードのたびに参照カウンタが減少する。
・参照カウンタが0になったらメモリ空間から除去される
LoadLibrary http://msdn.microsoft.com/ja-jp/library/cc429241.aspx
FreeLibrary http://msdn.microsoft.com/ja-jp/library/cc429103.aspx
つまり、同一 DLL であればコードもデータも共有される。
foo.exe --+-- foo.dll
+-- bar.dll -- foo.dll
+-- baz.dll -- foo.dll
のように foo.dll が使われるとき、その foo.dll がすべて同一のものであれば
コード領域、データ領域はとも、1個づつしかない=共有される。
1個目の foo.dll は、実は c:\workspace\foodll\debug\foo.dll
2個目の foo.dll は、実は c:\workspace\foodll\release\foo.dll
とかなっていると、これは別ものなのでコードもデータも共有されない。
LoadLibrary(_T(foo.dll)) と、パス明示指定しない場合に、まれにはまる。
仲澤@失業者さん,tetrapodさん
ありがとうございます
実際に動かしてみました
・DLL(1)内でグローバルでchar配列(260バイト分)を定義してみました
・そのDLL(1)を2つの異なったのDLL(2)(3)内でリンクさせました
・2つの異なったのDLL(2)(3)内でそれぞれリンクした元々のDLL(1)内で
グローバル定義されているchar配列にアクセス(設定/取得)できるようにしました
・そして2つのDLL(2)(3)を1つのアプリケーション上にリンク/ロードさせてみました
・ボタンを押すとDLL(1)内のグローバルなchar配列に適当な文字列を代入させます
・DLL(2)(3)で取得したDLL(1)内のグローバルなchar配列の内容はタイトルバーに
常時ONで表示させます
同一アプリケーション上からはそれぞれのグローバルのchar配列は
どちらのDLL(2)or(3)から変更させても同じ値に変わりました
そのアプリケーションを2重起動させてみました
プロセスが変わるようで2重に起動したそれぞれのアプリでのグローバルのchar配列は
影響しませんでした
なるほどな結果です
複数の異なるアプリケーション間でのDLL内の内容を一致させる場合というのは
簡単にはいかないものなんでしょうか?
レアなケースなので簡単じゃなければ,簡単にあきらめますけど...
あ,でも,知りたかった事は学べましたので解決にします
どうもありがとうございました
解決にしますって言っておいてチェックしてないかったです...
>複数の異なるアプリケーション間でのDLL内の内容を一致させる場合というのは
>簡単にはいかないものなんでしょうか?
DLL内のグローバル変数をアプリケーション間で共有することは可能です。
http://support.microsoft.com/kb/89817/ja
http://msdn.microsoft.com/ja-jp/library/h90dkhs0%28v=vs.90%29.aspx
ただし共有のグローバル変数がポインタの場合は
VirtualAllocEx,ReadProcessMemory,WriteProcessMemoryあたりを使う必要もあったかと。
客先で客先の人がちょこっとだけ変更したりできるように
して欲しい的な要求もたまにあって,どうしたものかと,
インタプリタ的なものを実装させてそこをイジッテもらうか
なんてのも考えたんですけど
メンドイし...
そんなにお金貰ってないので,別exeを呼び出しますって,
必要だったらそのexeと同名のexeを作ってくれればいいですから
なんて事にしたこともありまして,
で,このDLLを使ってください
そうすれば必要なデータは共有できるますから
なんて使い方できたらいいですね,という発想からでした
客先の人が作るって言っても,きっと浅い知識で偏った先入観どっぷりの
オカシナ事をするんだろうねぇと思うので(あ,毒出てます?)
できるだけ簡単に安全にできるといいですねって思いました
subaruさん,どうもありがとうございます
参考にしてみます
解決済みですが
> グローバル変数
これって、import/exportされた変数という意味でつかってますか、
それとも、単なる外部リンケージの変数のことですか。
単なる外部リンケージならば、
C言語の場合だと、ヘッダにextern無しで、int MyGlobal;とか書くと、
このヘッダをincludeしたexe,dllごとにMyGlobalが作られると思うのですが。
あと、
> いかなるEXEであっても常に0x00400000をとることで、確認できます。
XPのnotepad.exeは0x01000000になっています。
この話題の主体は [DLL を使う側] だと思っていたが、
勝手に [DLL を実装する側] に話すりかえちゃだめだよ。
DLL を実装する側からみれば 単なる外部結合の変数は1個だけになる。
DLL を使う側としては、その DLL を複数回ロードしたらどうなるのか?
って、話題としてはぜんぜん違う内容。
tetrapodさん、感謝します
テーマを完全に読み違えてました。
複数のEXEから、DLL内の変数の値を共用するには、関数と同じく
「変数をエクスポート」します。
が、単にお客にいじらせる、かつ、固定値であるという条件なら
*.iniを含む外部ファイルにしときますけど。自分の場合は。
>XPのnotepad.exeは0x01000000になっています。
おおっそですかっ。
GetModuleHandl(notepad.exe);とかの値じゃないですよねぇ。
自分のメモリ空間にマップされたnotepad.exeじゃ
ぜんぜん意味違いますし(笑い)。
>>XPのnotepad.exeは0x01000000になっています。
>おおっそですかっ。
>GetModuleHandl(notepad.exe);とかの値じゃないですよねぇ。
一応ベースアドレスはコンパイラの設定でも変更できるので
必ず0x00400000というわけでもなさそうです。
Process Hackerというツールで確認してみると
確かにnotepad.exeは0x01000000になってます。