DLL内でのグローバル変数

[戻る]
hirocco 2011/02/24(木) 14:24:42
DLL内で定義されたグローバル変数があるとします
そして,そのDLLを利用するDLLが2つ以上あるとします
そして,そのDLLを利用する2つ以上あるDLLを1つのアプリケーション上で
利用するとします

そのときの根元のDLL内で定義されたグローバル変数は同じ値として使うことができるの
でしょうか?
実際動くことは動くのですけど,それは安全といえるのでしょうか?

いろいろすみません
よろしくお願いします

仲澤@失業者 2011/02/24(木) 15:19:59
Win32に限定します。
まず実行ファイルは「プロセス」として起動されます。
プロセスには、それ専用の「仮想アドレス」を持った「メモリー空間」が
割り当てられます。これらはPCに実装されている物理メモリの物理アドレス
とは直接の関係はありません(あるけど、任意のポインタが物理アドレスのどこに
あるかは、意識する必要がないことを意味します。もちろんポインタの値
(アドレス)は仮想的(=なんちゃって)なものです)。

従って、EXEは他にだれもいないメモリー空間に、単独で実行されている
という状態だといえます。これはGetModuleHandl( NULL)の結果が
いかなるEXEであっても常に0x00400000をとることで、確認できます。
この値はEXEのインスタンスハンドルとして利用されています。

そのEXEがDLLをロードするということは、自分のメモリー空間にマッピング
されることを意味します。

さて、何らかのOSのシステム固定値がそのDLL内にあったとき、
対応する関数でそれを取得するときに、呼び出しもとに関係なく
同じ値が戻してもらえます。user32.dllやkernel32.dllに実装されている
関数は、それをやってくれまよね。

もちろん、これらのシステムDLLも例外ではなく
「このEXEのメモリー空間にマッピング」されているわけです。

仲澤@失業者 2011/02/24(木) 15:30:15
きれちゃったorz。
んで、さっきのは、リンクした場合の話し、
では、LoadLibraly()でロードしたものはどうなるのかというと、
そのDLLの参照カウントがアップします。カウントアップは重要な
ことで、これにより再マップされることになります。従って、
最初のLoadLibraly()でロードされたDLLと、次のLoadLibraly()で
ロードされたものは別の位置(アドレス)にマップされているはずです。
その内部のデータは言わずもがなですよね。

hirocco 2011/02/24(木) 15:39:44
libでのリンク時は
DLL内のグローバル変数は同一のEXE内では共通だけど
別のEXEでは別のものとなるということで,

LoadLibraly()でロードされると同一のEXE内でも
グローバル変数は別物となってしまうということですか?

えっと読み違えてますか?

tetrapod 2011/02/24(木) 16:58:28
まず、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")) と、パス明示指定しない場合に、まれにはまる。

hirocco 2011/02/24(木) 17:20:20
仲澤@失業者さん,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配列は
影響しませんでした

なるほどな結果です

hirocco 2011/02/24(木) 17:36:56
複数の異なるアプリケーション間でのDLL内の内容を一致させる場合というのは
簡単にはいかないものなんでしょうか?

レアなケースなので簡単じゃなければ,簡単にあきらめますけど...


あ,でも,知りたかった事は学べましたので解決にします

どうもありがとうございました

hirocco 2011/02/24(木) 17:37:54
[[解決]]
解決にしますって言っておいてチェックしてないかったです...

subaru 2011/02/24(木) 17:59:49
>複数の異なるアプリケーション間での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あたりを使う必要もあったかと。

hirocco 2011/02/24(木) 19:16:26
客先で客先の人がちょこっとだけ変更したりできるように
して欲しい的な要求もたまにあって,どうしたものかと,
インタプリタ的なものを実装させてそこをイジッテもらうか
なんてのも考えたんですけど
メンドイし...
そんなにお金貰ってないので,別exeを呼び出しますって,
必要だったらそのexeと同名のexeを作ってくれればいいですから
なんて事にしたこともありまして,
で,このDLLを使ってください
そうすれば必要なデータは共有できるますから
なんて使い方できたらいいですね,という発想からでした
客先の人が作るって言っても,きっと浅い知識で偏った先入観どっぷりの
オカシナ事をするんだろうねぇと思うので(あ,毒出てます?)
できるだけ簡単に安全にできるといいですねって思いました

subaruさん,どうもありがとうございます
参考にしてみます

ロマ 2011/02/25(金) 06:04:06
解決済みですが

> グローバル変数
これって、import/exportされた変数という意味でつかってますか、
それとも、単なる外部リンケージの変数のことですか。

単なる外部リンケージならば、
C言語の場合だと、ヘッダにextern無しで、int MyGlobal;とか書くと、
このヘッダをincludeしたexe,dllごとにMyGlobalが作られると思うのですが。

あと、
> いかなるEXEであっても常に0x00400000をとることで、確認できます。
XPのnotepad.exeは0x01000000になっています。

tetrapod 2011/02/25(金) 10:00:56
この話題の主体は [DLL を使う側] だと思っていたが、
勝手に [DLL を実装する側] に話すりかえちゃだめだよ。

DLL を実装する側からみれば 単なる外部結合の変数は1個だけになる。
DLL を使う側としては、その DLL を複数回ロードしたらどうなるのか?
って、話題としてはぜんぜん違う内容。

ロマ 2011/02/25(金) 11:38:35
tetrapodさん、感謝します
テーマを完全に読み違えてました。

仲澤@失業者 2011/02/25(金) 13:11:26
複数のEXEから、DLL内の変数の値を共用するには、関数と同じく
「変数をエクスポート」します。
が、単にお客にいじらせる、かつ、固定値であるという条件なら
*.iniを含む外部ファイルにしときますけど。自分の場合は。

>XPのnotepad.exeは0x01000000になっています。
おおっそですかっ。
GetModuleHandl("notepad.exe");とかの値じゃないですよねぇ。
自分のメモリ空間にマップされたnotepad.exeじゃ
ぜんぜん意味違いますし(笑い)。

subaru 2011/02/25(金) 14:37:19
>>XPのnotepad.exeは0x01000000になっています。
>おおっそですかっ。
>GetModuleHandl("notepad.exe");とかの値じゃないですよねぇ。
一応ベースアドレスはコンパイラの設定でも変更できるので
必ず0x00400000というわけでもなさそうです。
Process Hackerというツールで確認してみると
確かにnotepad.exeは0x01000000になってます。

Ban 2011/02/26(土) 12:48:25
> 必ず0x00400000というわけでもなさそうです。

むかーしは本当に実行毎に割り当てられてましたしね。

仲澤@失業者 2011/02/26(土) 17:30:11
>むかーしは本当に実行毎に割り当てられてましたしね。

16bitの頃は仮想メモリー機能がなかったので、アロケーションした
メモリーのポインタか、ロックしたときのハンドルかどっちだったの
だろうかと、気になりますです(vv;)。

仲澤@失業者 2011/02/26(土) 17:39:37
ごめん。
厳密に言うと「16bitの頃」じゃなくて、
「OSが仮想メモリに対応する前」ですね。
たぶんWindows95からは「対応」のはず(自信なし)。

Ban 2011/02/27(日) 09:58:37
そういう観点で見たことはなかったですが
Win3.1とかは同じアプリを起動すると別の番号がついたりするという。

ちょっと探してみたらこれっぽいですね。
http://support.microsoft.com/kb/103644/en-us
戻るのはハンドル、ただし値としてはベースアドレス、だと。
ありがちな実装といえばありがちですね>アドレスをそのままハンドルとして使う。

仲澤@失業者 2011/02/28(月) 15:06:40
>ちょっと探してみたらこれっぽいですね。
>http://support.microsoft.com/kb/103644/en-us
>戻るのはハンドル、ただし値としてはベースアドレス、だと。

この部分は16bitのことか32bitのことかややあいまいですね。
32bitの方かもしれません。

古いスレッドには追加発言できません。