• ベストアンサー

最適化オプションと LoadLibrary(あるいはCoInitialize関連?)などを組み合わせると…

コンパイラはVisual C++ 2008 Express Editionです。 以下のようなコードを書くと #include <windows.h> #include <stdio.h> #include <tchar.h> #include <commctrl.h> #pragma comment ( lib, "comctl32.lib") HRESULT InitCOM_Ole32(){ //初期化 HMODULE hDLL( LoadLibrary(_T("Ole32.dll")) ); if (!hDLL) return E_FAIL; typedef HRESULT (*Func)( LPVOID ); Func func = (Func)GetProcAddress(hDLL,"CoInitialize"); HRESULT hr( func( 0 ) ); FreeLibrary( hDLL ); return hr; } void UnInitCOM_Ole32(){ //後始末 HMODULE hDLL( LoadLibrary(_T("Ole32.dll")) ); if (!hDLL) return; void (*func)() = (void (*)())GetProcAddress(hDLL,"CoUninitialize"); func(); FreeLibrary( hDLL ); } int main(){ InitCOM_Ole32(); InitCommonControls(); UnInitCOM_Ole32(); getchar(); return 0; } 最適化オプションが無効なら問題ないのですが、最適化オプションを付けると getchar(); 直後に落ちてしまいます。 また、別のプロジェクトでも、最適化オプションなしなら正常に動くのですが、最適化オプションを付けると、そちらは初期化関数サイドを抜ける瞬間には落ちてしまいます。 アセンブリ出力を見ても、関数名とかはそのまま書かれている分に感じるので、どこがまずいのかよく分かりません。 これらはどのように対処すればいいのでしょうか? あるいは、書き方自体が間違っている(未定義動作になる)部分があるのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • jgk
  • ベストアンサー率75% (104/138)
回答No.2

WindowsAPIの呼び出し規約はstdcallで、VC++のデフォルトの呼び出し規約はcdeclです。 ですので、stdcallを指定しなければ、stdcallの関数をcdeclで呼んでしまいおかしなことになります。 ですので、 typedef HRESULT (__stdcall * Func)( LPVOID ); みたいにする必要があります。 WindowsAPIならこっちの方が良いかも知れません。 typedef HRESULT (WINAPI * Func)( LPVOID );

LongSecret
質問者

お礼

なるほど! そういえば数回無意識に触れていたようですが、呼び出し規約という概念自体、あまりよく知らなかったです。 これを機に調べてみて納得しました。 また、jgkさんのおっしゃる通りWindowsAPI用に呼び出し規約を指定することで、どちらのプロジェクトも問題なく動作するようになりました。 ただ、質問文のコードのLoadLibrary、FreeLibraryの使い方が、この場合は問題ないのかあるのかはやはり気になるので、最大で数日程度締め切りを待たせてください。

LongSecret
質問者

補足

あ、あまりにgoodjobすぎて書き忘れてましたw ありがとうございます♪

その他の回答 (1)

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.1

CoInitializeとCoUninitializeを別の インスタンスで実行して大丈夫なんですか? 普通に考えると気持ち悪いですね。 何かMSのドキュメントで保証されているので しょうか? 同じインスタンスで実行するようにhDLLを 大域変数として、後処理でのみFreeLibraryを 実行するように修正してみたらどうなりますか?

LongSecret
質問者

お礼

ありがとうございます。 分かりやすくグローバルな位置(とかただの名前空間内)に持ってきて、初期化サイドから FreeLibrary を 後始末サイドから LoadLibrary を消して「最適化オプション付き」でやってみると、今度は「初期化関数を抜けるときに」確実に落ちるようになりました。 最適化オプションなしならやはりhDLLが同じ変数であってもなんら問題はありません。 LoadLibraryについて http://msdn.microsoft.com/ja-jp/library/cc429241.aspx HMODULEというのは文字通りモジュールハンドルなのでDLLを必要に応じてロードしたり解放したり、というのは意味的にも問題ないと思っていましたが、逆に(COMの初期化、後始末が関係するという点で)上記質問文のコードの場合は問題がある、という記述はあるのでしょうか? いちおう質問文でのコードではアドレスが一致するか調べてみましたが TCHAR c[100]; _stprintf_s(c,100,_T("%p"),hDLL); _tprintf(c); 別のインスタンスで実行して、同じアドレスが得られました。 やはり最適化でどっかが浸食されてるんじゃないかな…

関連するQ&A

  • DLL関数を使ったプログラム

     DLL関数を使ったプログラムを動かしたんですが、ERRORが解決できず 困っています。DLLを作成したのはいいのですが、それをLoadLibraryで読み込もうとしても指定したプロシージャが見つからないというエラーになるようです。使用している環境はVisual Studio.NET2003です。  以下はDLLのソースです。 // plug.c // #include <windows.h> #include <stdio.h> __declspec(dllexport) void CALLBACK TestFunc() {    printf("DLLのTestFunc()関数が実行されました。\n"); } これをビルドするとplug.dllとplug.expとplug.libが作成されました。  そしてこれを使用したプログラムが // stab.c // #include <windows.h> #include <stdio.h> typedef void (*TestFunc)(void); void main() {     HMODULE hModule;     DWORD error;     TestFunc funcPointer;     hModule = LoadLibrary(TEXT("plug"));     error = GetLastError();//error値が127     funcPointer = (TestFunc)GetProcAddress(hModule,TEXT("TestFunc"));     funcPointer();     FreeLibrary(hModule);     getchar(); } LoadLibrary関数を使用してアプリのメモリ空間にDLLを読み込もうとしているんですが、ここでハンドルが正確に渡されていないみたいなんです。どうしてこうなるのか分かりません。わかる方いらっしゃったらよろしくお願いします。

  • DLLの関数呼び出しで引数があるとフリーズしてしまう。

    はじめまして、C言語勉強中の初心者です。 現在、DLLに定義されている関数を呼び出すことを試していますが、うまくいかないので質問させて頂きました。 DLLには2つの関数が定義されています。  1.void Hello()  2.void HelloEx(char *pval); 1の関数を呼び出す場合は異常なく終了するのですが、2の関数を呼び出すと、フリーズしてしまいます。 フリーズする原因が分からないので、教えて頂ければと思います。 以下にソースを掲載します。 因みにコンパイラはBCC5.5.1を使用しています。 ***************************************************** DLL(Hello.c) [bcc32 -WD Hello.c] ***************************************************** #include <windows.h> #include <stdio.h> __declspec(dllexport) void CALLBACK Hello(void) { printf("Hello!\n"); } __declspec(dllexport) void CALLBACK HelloEx(char *pVal) { printf("Hello!%s\n", pVal); } ***************************************************** EXE(HelloTest.c)[bcc32 -L HelloTest.c] ***************************************************** #include <windows.h> #include <stdio.h> typedef void (*Hello)(void); typedef void (*HelloEx)(char*); int main(void) { HMODULE hMod; Hello func; HelloEx funcEx; hMod = LoadLibrary( "Hello.dll" ); if(!hMod) return FALSE; func = (Hello)GetProcAddress( hMod, "Hello"); if(!func) return FALSE; funcEx = (HelloEx)GetProcAddress( hMod, "HelloEx"); if(!funcEx) return FALSE; func(); funcEx("World"); FreeLibrary(hMod); return 0; } *********************************************** 以上

  • LoadLibraryの戻り値(HMODULE)について

    ファイル名の異なる2つのDLLを下記のソースの様 にLoadLibraryしたところA,Bとも同じアドレスが 戻って来てしまいます。これではFreeLibraryにて 開放しようとした時に、おかしな事になってしまう と思うのですが、なぜ同じアドレスが戻ってくるのか 分りません。 あと、A,Bをグローバル変数として宣言 したときに、既に宣言済みですと怒られます。これも よく分りません。何方か理由が分る方ご教授願います。 void CXXX_Dlg::ButtonClick() { // dllファイルロード HMODULE A = ::LoadLibrary("A.DLL"); HMODULE B = ::LoadLibrary("B.DLL"); } 環境:Win2K, VC++.Net 2003 MFC です。 DLLファイルはプロジェクトと同ディレクトリに 入っています。

  • GetProcAddressとUnicode、Ansiについて

    先日似たような部分で疑問が生じましたが GetProcAddressについて、実践的な部分をもうちょっと詳しく知りたくなりました。 Unicode版、Ansi版での、ロードする関数名が 末尾にWかAがついてることが多いと思うのですが GetProcAddressには文字列を指定しなければならないため なるべく手間を避けるとしても #ifdef _UNICODE #define _FWA(x) #x"W" #define _tfuncwa(x) L#x L"W" #else #define _FWA(x) #x"A" #define _tfuncwa(x) #x"A" #endif こういう風にプリプロセッサに対してなんらかの配慮をして置いたうえで HMODULE hDLL( LoadLibrary(_T("Shlwapi.dll")) ); typedef HRESULT (__stdcall *Func)( LPCTSTR ); Func func = (Func)GetProcAddress(hDLL,_FWA(PathFileExists)); とかやるようにするか、若しくはDllから動的に引っ張ってくる関数の周りで使う文字列は UnicodeかAnsiかに決め打ちして、必要が生じるなら変換、といった配慮のいずれかは必ず必要になるのでしょうか?

  • 【C++】関数ポインタの代入

    C++の関数ポインタについて質問です。 下記のように関数ポインタを宣言し、3通りの代入を行ってみました。 (3)のように関数名の頭に&を付けた場合と(2)のように&を付けなかった場合で 全く動きが同じになってしまうのですが、何故なのでしょうか? ------------------------------------------ #include "stdafx.h" #include <iostream> using namespace std; void Func1(){ cout<<"Func1が呼ばれました。"<<endl; return; } int main() { //(1) void (*fp1_1)(); fp1_1 = Func1; fp1_1(); //(2) void (*fp1_2)()=Func1; fp1_2(); //(3) void (*fp1_3)()=&Func1; fp1_3(); getchar(); return 0; }

  • bcc32 GetProcAddress( )

    mydll.cpp と mymain.cpp でDLL内の関数を使うテストを しようとしたところです。 GetProcAddress( ) が失敗します。 どこを直せばいいか教えてください。 ///////// mydll.cpp #include <windows.h> extern "C" __declspec(dllexport) int MyFunc(int, int); int MyFunc(int a, int b){return( a + b );} ///////// mymain.cpp #include <windows.h> typedef int (*FUNCTYPE)(int, int); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){  HMODULE hModule;  FUNCTYPE ft;  int i;  switch(msg){  case WM_RBUTTONDOWN:   hModule = LoadLibrary("mydll.dll");   ft = (FUNCTYPE)GetProcAddress(hModule, "MyFunc");   if(ft == NULL){    //GetLastError();   }   i = (*ft)(7, 8);  break;  case WM_DESTROY:   PostQuitMessage(0);  break;  default:   return DefWindowProc(hWnd, msg, wParam, lParam);  }  return 0; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int){  ・・・ } ///////// コンパイル C:\bcc32 -WD mydll C:\bcc32 -W mymain mydll.obj ///////// 実行 LoadLibrary( ) は NULL 以外を返しました。 DLL_PROCESS_ATTACH は確認できました。 GetProcAddress( ) は NULL を返しました。

  • VC++フォームアプリケーションにDLLをリンク2

    2日前に、同じタイトルで投稿した者です。 http://okwave.jp/qa/q7326569.html その節は素晴らしい回答を頂きありがとうございました。 それを実現するように、作成して頂いたプログラムの修正に取り組んでいるのですが、うまく行きません。 どなたかお力をお貸し下さいm(>o<)m 前回の回答で、指摘されたように、LoadLibrary/GetProcAddress/FreeLibrary を用いて 作成して頂いたプログラムを修正しました。 具体的には、ボタンを押すとSecretFunc関数を呼び出すコードForm1.cppを以下のように修正しました。ビルドしても、エラーは出ません。 次に、このプログラムのReleaseフォルダー内のDLL_sample.dllを 前回、私が投稿したソースコード(http://okwave.jp/qa/q7326569.html)で作成したDLL_sample.dllと置き換えました。 そして、プログラムを起動(WinForm_sample.exe)すると、エラーは発生しないのですが、SecretFunc関数が見つけられず、プログラムの"No DLL"表示の方を通ります。 その原因が分かりません。 プログラムは全て、VC++2010ExpressEditionで作成しています。 もし、お分かりの方がいらっしゃいましたら、ご教授のほどよろしくお願いします。 //修正したForm1.cppのコード #include "stdafx.h" #include "Form1.h" #include "DLL_sample.h" #include <Windows.h> typedef UINT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT); namespace WinForm_sample { System::Void Form1::btnCalc_Click(System::Object^ sender, System::EventArgs^ e) { System::Double a, b; if ( System::Double::TryParse(txtA->Text, a) && System::Double::TryParse(txtB->Text, b) ) { HINSTANCE hDLL; // Handle to DLL LPFNDLLFUNC1 lpfnDllFunc1; // Function pointer //DWORD dwParam1; //UINT uParam2, uReturnVal; hDLL = LoadLibrary(TEXT("DLL_sample.dll")); if (hDLL != NULL) { lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL,"SecretFunc"); if (!lpfnDllFunc1) { // call the function double c = SecretFunc(a,b); txtResult->Text = c.ToString(); // handle the error FreeLibrary(hDLL); //return SOME_ERROR_CODE; } else { // Initializes the variables to pass to the MessageBox.Show method. txtResult->Text = L"No DLL"; } } else { txtResult->Text = L"不正な入力"; } } } }

  • CXXXApp::InitInstance内でDLLの呼び出し

    CXXXApp::InitInstance内でDLLの呼び出しをしています。 呼び出すDLLは、MFCの共有DLLで作成しました。 DLLの内容は、単純にダイアログを表示しているだけです。 そこで、問題が発生しています。 DLLを呼び出すと、アクセスバイオレイションで落ちてしまいます。 どうも、ダイアログの親ウインドウが無いのが問題のようです。どうすれば解決するでしょうか? Exe--------------- BOOL CXXXXApp::InitInstance() { HINSTANCE hDll = NULL; int (__stdcall *lpdlg)( ); hDll = ::LoadLibrary("test.dll"); (FARPROC&)lpdlg = ::GetProcAddress(hDll, "dlgtest"); (*lpdlg)( ); ::FreeLibrary(hDll); } DLL内--------------------- AFX_MANAGE_STATE(AfxGetStaticModuleState()); AfxSetResourceHandle(AfxGetResourceHandle()); CTestDlg dlg; dlg.DoModal();

  • 漢字の読みを表示するプログラム

    漢字の読みを表示するプログラム C/C++で漢字の読みを取得したいです。 kakasiを利用してみましたが、以下のようなエラーになってしまいます。 エラー内容:実行時エラーで、「○○.exeは動作を停止しました。」となります。 OS:Vistaです。 環境変数はマニュアル通りに設定しました。 ソースはネット上のサンプルを参考に書きましたが、 LoadLibrary・GetProcAddressなどの関数の意味がmsdnを見ても解りませんでした。 (環境変数に設定しない場合の書き方なのかもしれないと思い、消してみましたが、 そうすると多数のコンパイルエラーになってしまいます。) まずは動かしてみたいのです。よろしくお願いいたします。 以下エラーが出るソースです。 #include <stdio.h> #include <stdlib.h> #include <windows.h> #include "libkakasi.h" void main(void){ HMODULE hKakasiDLL = LoadLibrary("C:\\kakasi\\bin"); int (__cdecl *kakasi_getopt_argv)(int, char**) = (int (__cdecl *)(int, char**))GetProcAddress( hKakasiDLL, "kakasi_getopt_argv" ); char* (__cdecl *kakasi_do)(const char*) = (char* (__cdecl *)(const char*))GetProcAddress( hKakasiDLL, "kakasi_do" ); int (__cdecl *kakasi_free)(char *p) = (int (__cdecl *)(char *p))GetProcAddress( hKakasiDLL, "kakasi_free" ); putenv( "ITAIJIDICTPATH=C:\\kakasi\\share\\kakasi\\itaijidict"); putenv( "KANWADICTPATH=C:\\kakasi\\share\\kakasi\\kanwadict"); char* argv[] = {"kakasi", "-JH","-kH"}; kakasi_getopt_argv(3,argv); char* hiragana = kakasi_do("漢字"); printf("%s\n", hiragana); kakasi_free(hiragana); FreeLibrary(hKakasiDLL); }

  • DLLの明示的な読み込みの方法

    開発環境:WindowsXP sp2 + VisualStudio2005 MSDNやその他のWebページで、DLLの明示的なロードについて勉強しています。 試しに以下のようなコードを書いてみたのですが、コンパイルは通りますが、実行時にDLLが読み込まれているのにもかかわらず、関数のアドレスが取得できません。 ---DLLのコード--- #include <iostream> using namespace std; __declspec(dllexport) void printMessage() { cout<<"printMessage関数が呼ばれた"<<endl; } ---終わり--- コンパイルしたDLLのみをDLLを使用した実行形式がコンパイル後に収納されるフォルダに移動して、以下のコードを書いてコンパイルしました。 ---EXEのコード--- #include <windows.h> #include <iostream> typedef void(*TFUNC)(); int main(void) { HINSTANCE hInstDLL; TFUNC DllFunction; //DLLの読み込み hInstDLL=LoadLibrary(L"dll.dll"); if(hInstDLL==NULL) { std::cout<<"ERROR"<<std::endl; return 0; } //printMessage関数のアドレスを取得 DllFunction=(TFUNC)GetProcAddress(hInstDLL,"printMessage"); //この辺りがおかしいかも知れない //関数の使用 if(DllFunction==NULL) { std::cout<<"ERROR"<<std::endl; FreeLibrary(hInstDLL); return 0; } DllFunction(); //DLLの解放 if(!FreeLibrary(hInstDLL)) return 0; return 0; } ---終わり--- DLLは見つかる物の、どうも上手くいかないようです......原因は何なのでしょうか。

専門家に質問してみよう