トップ回答者
DLLを使った、多プロセス間でのメモリ共有(変数データ)の練習プログラム

質問
-
VS 2008 でDLL生成前ソースファイル内で、定義された変数データ(LONG)を、多プロセスからアクセスし、
各プロセスが、変数データ(LONG)をインクリメントして、表示するダイアログボックスのプログラムを書いています。
DLL生成前ソースファイル sharedll.cpp の一部は、↓で
#include <windows.h> // ----------------------------------------------------------------------------------- #pragma data_seg( "foo" ) LONG instanceCounter = 0; #pragma data_seg() // 省略
追加したDEFファイル sharedll.def は、↓です。
LIBRARY "sharedll" SECTIONS foo READ WRITE SHARED
DLL 生成プロジェクト名は、「"shareVarDll"」だったので、リビルドすると
Release フォルダに shareVarDll.dll と shareVarDll.lib が生成されました。
次に、
DLL内の変数にアクセスするダイアログプログラム shareVar.exe を下の手順で作成しました。
プロジェクト名「"shareVar"」
1. shareVarDll.lib をプロジェクトに[追加] - [既存の項目...] で追加。
2. 実行プログラムファイル pgm.cpp は、下記です。
# include <windows.h> # include "resource.h" //---------------------------------------------------------------------------- // DLL エクスポート関数プロトタイプ宣言 // WORD __declspec(dllimport) __stdcall getInstanse( void ); //---------------------------------------------------------------------------- // Dialogboxプロシージャ プロトタイプ宣言 // BOOL CALLBACK dlgProc( HWND , UINT , WPARAM , LPARAM ); // --------------------------------------------------------------------------- // WinMain // int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hDlg; MSG msg; // ダイアログボックスの生成 hDlg = CreateDialog( hInstance, L"IDD_DIALOG1", HWND_DESKTOP, (DLGPROC)dlgProc ); if ( hDlg == 0 ) { return FALSE; } while ( GetMessage( &msg, NULL, 0, 0 ) != 0 ) { if ( !IsDialogMessage( hDlg, &msg ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } } return msg.wParam; } //---------------------------------------------------------------------------- // Dialogboxプロシージャ // BOOL CALLBACK dlgProc( HWND hDlg , UINT uMessage , WPARAM wParam , LPARAM lParam ) { switch ( uMessage ) { case WM_SHOWWINDOW: SetDlgItemInt( hDlg, IDC_EDIT1 , getInstanse() , FALSE ); break; case WM_COMMAND: switch ( wParam ) { case IDC_CLOSE: PostMessage( hDlg, (UINT)WM_DESTROY , (UINT)0, (LONG)0 ); break; } break; case WM_DESTROY: PostQuitMessage( 0 ); return TRUE; } return FALSE; // メッセージを処理しない場合はFALSEを返す }
3. [構成マネージャ]で構成を"Release"にして、リビルド。
shareVar.exe が作成された。
そして、shareVar.exe と shareVarDll.dll を同じフォルダに置いて、shareVar.exe を実行してみました。(ダブルクリック)
すると、
「コンポーネントが見つかりません」エラー、エラー内容は、↓でした。
"sharedll.dll が見つからなかったため、このアプリケーションを開始できませんでした。アプリケーションをいんすとーるし直すとこの問題は解決される場合があります。"
なぜ "shareVarDll.dll" でなく "sharedll.dll" を見つけようとするのでしょう?
また、
DLLを生成するしなおす際に、
プロジェクトの プロパティ ページで、
[構成プロパティ] - [リンカ] - [全般] の「出力ファイル」を "$(OutDir)\sharedll.dll" に指定し[適用]し、
ビルドすると。"sharedll.dll" と "sharedll.lib" が生成させましたので、
呼び出し側プロジェクト「"shareVar"」に
sharedll.lib を[追加] - [既存の項目...] して、
"Debug"構成でビルドし直した、shareVar.exe を、"sharedll.dll" と同じフォルダでデバッグしたら、
pgm.cpp の
hDlg = CreateDialog( hInstance, L"IDD_DIALOG1", HWND_DESKTOP, (DLGPROC)dlgProc );
の返り値が
"0x00000000" で、プログラムを終了するはめになります。
分からないことは、大きく2つあり、
1. なぜ、実行プログラム"shareVar.exe" は、"shareVarDll.dll" でなく、"sharedll.dll" を見つけようとするのか?
2. なぜ、CreateDialog( hInstance, L"IDD_DIALOG1", HWND_DESKTOP, (DLGPROC)dlgProc ); に失敗するのか?
です。
長文を最後まで読んで頂き、とてもありがたく思います。
どんなことでも教えて頂けたらうれしいです。よろしくお願いします。。
回答
-
>1.
>LIBRARY "sharedll"
これが原因で発生した現象だと考えられます。
「出力ファイル」で指定されているファイル名と異なる場合、以下Warningが発生します。
Warning 1 warning LNK4070: /OUT:sharedll.dll directive in .EXP differs from output filename 「出力ファイル」 ; ignoring directive
結果、exeではLIBRARYで指定されたモジュール名が使用され、
DLLのBuildでは「出力ファイル」で指定されているファイル名で出力され、整合性が取れなくなります。
(dumpbin /imports shareVar.exeで確認できますよ)
WarningはErrorではないのでBuildは続行されますが、取り除かないと実行時にErrorになるWarningもあります。
原則Warningはすべて取り除くべきしょう。
>2.
GetLastErrorで何故失敗するか確認してください。
おそらく、L"IDD_DIALOG1" Dialog Resourceが見つからないのではないでしょうか。
>多プロセスからアクセスし、各プロセスが、変数データ(LONG)をインクリメントして
排他制御を行わないと、意図しない結果になることがあります。
InterlockedIncrementによる制御を検討したほうが良いでしょう。- 編集済み kozz 2010年1月12日 1:27 記載ミス
- 回答としてマーク fedoracche 2010年1月12日 2:52
すべての返信
-
>1.
>LIBRARY "sharedll"
これが原因で発生した現象だと考えられます。
「出力ファイル」で指定されているファイル名と異なる場合、以下Warningが発生します。
Warning 1 warning LNK4070: /OUT:sharedll.dll directive in .EXP differs from output filename 「出力ファイル」 ; ignoring directive
結果、exeではLIBRARYで指定されたモジュール名が使用され、
DLLのBuildでは「出力ファイル」で指定されているファイル名で出力され、整合性が取れなくなります。
(dumpbin /imports shareVar.exeで確認できますよ)
WarningはErrorではないのでBuildは続行されますが、取り除かないと実行時にErrorになるWarningもあります。
原則Warningはすべて取り除くべきしょう。
>2.
GetLastErrorで何故失敗するか確認してください。
おそらく、L"IDD_DIALOG1" Dialog Resourceが見つからないのではないでしょうか。
>多プロセスからアクセスし、各プロセスが、変数データ(LONG)をインクリメントして
排他制御を行わないと、意図しない結果になることがあります。
InterlockedIncrementによる制御を検討したほうが良いでしょう。- 編集済み kozz 2010年1月12日 1:27 記載ミス
- 回答としてマーク fedoracche 2010年1月12日 2:52
-
2. なぜ、CreateDialog( hInstance, L"IDD_DIALOG1", HWND_DESKTOP, (DLGPROC)dlgProc ); に失敗するのか
これについては、まずは GetLastError() を呼んで拡張エラー情報を得るのが最初のステップかと。
GetLastError 関数
http://msdn.microsoft.com/ja-jp/library/cc428944.aspx
CreateDialog() の日本語のヘルプだと、
CreateDialog 関数
http://msdn.microsoft.com/ja-jp/library/cc410690.aspx
戻り値の説明に
関数が失敗すると、NULL が返ります。拡張エラー情報を取得するには、 関数を使います。
などと何とも意味不明なことが書かれていますが(関数って何?)、
英語版のヘルプを見ると、
CreateDialog Function ()
http://msdn.microsoft.com/en-us/library/ms645434(VS.85).aspx
もう少しましな説明が書かれています。
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
This function typically fails for one of the following reasons:
- an invalid parameter value
- the system class was registered by a different module
- The WH_CBT hook is installed and returns a failure code
- if one of the controls in the dialog template is not registered, or its window window procedure fails WM_CREATE or WM_NCCREATE
-
ご回答ありがとうございます。
pgm.cpp の WinMain に↓の GetLastError()処理を追加しまして、デバッグ実行しました。
HWND hDlg;
結果、kozzさんの言うとおりメッセージボックスに「指定されたリソース名がイメージ ファイルに見つかりません。」と現れました。
MSG msg;
LPVOID lpMsgBuf; //追加
SetLastError(NO_ERROR); //追加
// ダイアログボックスの生成
hDlg = CreateDialog( hInstance, L"IDD_DIALOG1", HWND_DESKTOP, (DLGPROC)dlgProc );
if ( hDlg == 0 )
{
FormatMessage( //エラー表示文字列作成
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
(LPTSTR) &lpMsgBuf,
0,
NULL
);
MessageBox( NULL, (LPTSTR)lpMsgBuf, NULL, MB_OK );
LocalFree( lpMsgBuf );
return FALSE;
}
CreateDialog() の第2引数を、
hDlg = CreateDialog( hInstance, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP, (DLGPROC)dlgProc );
に変更したら、ここの部分はデバッグが通過しましたが、
次のコード
while ( GetMessage( &msg, NULL, 0, 0 ) != 0 )
の最初で、デバッグが先に進まないようです。[F5]を押しても反応はありません。
{
if ( !IsDialogMessage( hDlg, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
因みに、
resource.h は↓です。
#define IDD_DIALOG1 101
shareVar.rc の一部は↓です。
#define IDC_CLOSE 1001
#define IDC_EDIT1 1002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
/////////////////////////////////////////////////////////////////////////////
ダイアログボックスが表示されないのは、
//
// Dialog
//
IDD_DIALOG1 DIALOGEX 0, 0, 210, 153
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "メモリのシェア"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "閉じる",IDC_CLOSE,79,123,50,14
LTEXT "インスタンス数",IDC_STATIC,29,47,44,8
EDITTEXT IDC_EDIT1,122,44,40,14,ES_AUTOHSCROLL
END
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE ←がなかったからでした。
これでやっと解決することができました。
感謝いたします。