1

GetOpenFileName 共通ダイアログを使用してユーザーにファイルを選択させる C アプリケーションがあります。Windows2008R2 でクラッシュが発生しています。アプリケーションに DEP 例外を設定すると、クラッシュが停止することがわかりました。

しかし、何が間違っているのか、そもそもクラッシュを止めるために何ができるのかわかりません。コードを以下に配置しました。

typedef struct {
    OPENFILENAME    ofn;
    COUNT       nInternal;
    COUNT       nExternal;
    char        szDirName[_MAX_DIR];
    char        szFile[_MAX_PATH];
    char        szFileTitle[_MAX_PATH];
    char        szFilter[128];
} OPENFILENAMEINFO;

typedef OPENFILENAMEINFO FAR *LPOPENFILENAMEINFO;


LPOPENFILENAMEINFO RequestFileNameEx(HWND hDlg, LPSTR lpExt, BOOL bSave, LPSTR lpInit)
{

    LPOPENFILENAMEINFO lpFileNameInfo;
    int i;
    DWORD   dwError;
    DWORD   dwSize;
    LPSTR   lpDir;
    LPSTR   lpDrive;

    lpFileNameInfo = (LPOPENFILENAMEINFO)mballc(1,sizeof(OPENFILENAMEINFO));
    strcpy(lpFileNameInfo->szFilter,lpExt);

    for (i=0; lpFileNameInfo->szFilter[i] != '\0'; i++) {
        if (lpFileNameInfo->szFilter[i] == '|')
            lpFileNameInfo->szFilter[i] = '\0';
    }

    memset(&lpFileNameInfo->ofn, 0, sizeof(OPENFILENAME));

    lpFileNameInfo->ofn.lStructSize = sizeof(OPENFILENAME);
    lpFileNameInfo->ofn.hwndOwner       = hDlg;
    lpFileNameInfo->ofn.lpstrFilter = lpFileNameInfo->szFilter;
    lpFileNameInfo->ofn.nFilterIndex    = 1;
    lpFileNameInfo->ofn.lpstrFile       = lpFileNameInfo->szFile;
    lpFileNameInfo->ofn.nMaxFile        = sizeof(lpFileNameInfo->szFile);
    lpFileNameInfo->ofn.lpstrFileTitle  = lpFileNameInfo->szFileTitle;
    lpFileNameInfo->ofn.nMaxFileTitle   = sizeof(lpFileNameInfo->szFileTitle);

    lpFileNameInfo->ofn.lpstrInitialDir = _getcwd(lpFileNameInfo->szDirName, _MAX_DIR);

    if (bSave) {
        lpFileNameInfo->ofn.Flags           = OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR;
        dwError = GetSaveFileName(&lpFileNameInfo->ofn);
    } else {
        lpFileNameInfo->ofn.Flags           = OFN_SHOWHELP | OFN_PATHMUSTEXIST | (bDir==FALSE?OFN_FILEMUSTEXIST:0) | OFN_NOCHANGEDIR;
        dwError = GetOpenFileName(&lpFileNameInfo->ofn);
    }

    if (!dwError) {
        dwError = CommDlgExtendedError();
        if (dwError)
            ResourceHandleError(GETOPENFAIL, dwError);
        mbfree(lpFileNameInfo);
        return(NULL);
    }


    return(lpFileNameInfo);
}

クラッシュ ダンプのスタック トレースは次のようになります

0023:73E61FFF (0x080D7974 0x080D7970 0x078B62F0 0x00000000) msxml6.dll
0023:73E68165 (0x080D7970 0x080D78F0 0x078B62F0 0x080D78F0) msxml6.dll, DllCanUnloadNow()+22084 byte(s)
0023:73E67D08 (0x078B62F0 0x080D7970 0x00000000 0x080D78F0) msxml6.dll, DllCanUnloadNow()+20967 byte(s)
0023:73E6827A (0x080D78F0 0x080D7970 0x080D7950 0x59E489BA) msxml6.dll, DllCanUnloadNow()+22361 byte(s)
0023:73E68241 (0x080D7970 0x080D7950 0x59E489BA 0x00000000) msxml6.dll, DllCanUnloadNow()+22304 byte(s)
0023:73E69DDF (0x00000000 0x080D7950 0x00000000 0x0762FAE0) msxml6.dll, DllCanUnloadNow()+29374 byte(s)
0023:73E6BF9F (0x080D7970 0x080D7950 0x71932915 0x078B5E90) msxml6.dll, DllGetClassObject()+5125 byte(s)
0023:73E6BF83 (0x73E81B38 0x080D39C0 0x080D39C0 0x080D3980) msxml6.dll, DllGetClassObject()+5097 byte(s)
0023:73E6C318 (0x71932881 0x06148CB8 0x06148CB8 0x00000000) msxml6.dll, DllGetClassObject()+6014 byte(s)
0023:73E6CD18 (0x720B35A0 0x0762FBD8 0x06148CB8 0x0762FD68) msxml6.dll, DllGetClassObject()+8574 byte(s)
0023:73E78671 (0x720B35A0 0x0762FBD8 0x0762FD68 0x00000000) msxml6.dll, DllGetClassObject()+56023 byte(s)
0023:73E6AAE5 (0x73E6AC28 0x00000000 0x720B35A0 0x0762FBD8) msxml6.dll, DllCanUnloadNow()+32708 byte(s)
0023:74B0A0E1 (0x00000000 0x00000000 0x00000000 0x00000001) ole32.dll, CoCreateInstanceEx()+0915 byte(s)
0023:74B09FA1 (0x720B3614 0x00000000 0x00000017 0x00000000) ole32.dll, CoCreateInstanceEx()+0595 byte(s)
0023:74B09E25 (0x720B3614 0x00000000 0x00000017 0x00000000) ole32.dll, CoCreateInstanceEx()+0215 byte(s)
0023:74B09D86 (0x720B3614 0x00000000 0x00000017 0x00000000) ole32.dll, CoCreateInstanceEx()+0056 byte(s)
0023:74B09D3F (0x720B3614 0x00000000 0x00000017 0x720B35A0) ole32.dll, CoCreateInstance()+0052 byte(s)
0023:720B352B (0x0553A7C0 0x00000000 0x0070E2DC 0x0070E288) FunDisc.dll
0023:720B9470 (0x0553A7C0 0x00000000 0x00000001 0x00000001) FunDisc.dll, DllGetClassObject()+21871 byte(s)
0023:720C3B69 (0x00000001 0x0070E288 0x8007000E 0x00000000) FunDisc.dll, DllUnregisterServer()+20504 byte(s)
0023:720B75AA (0x73751590 0x00000000 0x00000001 0x00000000) FunDisc.dll, DllGetClassObject()+13993 byte(s)
0023:720B1CE9 (0x73751590 0x00000000 0x00000001 0x055874F8) FunDisc.dll
0023:720B1C39 (0x00709310 0x73751590 0x00000000 0x00000001) FunDisc.dll
0023:73752F84 (0x055E2F28 0x00709310 0x73751590 0x00000000) NetworkItemFactory.dll
0023:737530A5 (0x055E2F28 0x0762FF88 0x763643C0 0x055E2F28) NetworkItemFactory.dll
0023:73753144 (0x055E2F28 0x00000000 0x00000000 0x03EDFB9C) NetworkItemFactory.dll
0023:763643C0 (0x03EDFB9C 0x0762FFD4 0x77029EF2 0x03EDFB9C) SHLWAPI.dll, IUnknown_QueryService()+0346 byte(s)
0023:74C9339A (0x03EDFB9C 0x13BB74FB 0x00000000 0x00000000) kernel32.dll, BaseThreadInitThunk()+0018 byte(s)
0023:77029EF2 (0x763642ED 0x03EDFB9C 0xFFFFFFFF 0x770B736F) ntdll.dll, RtlInitializeExceptionChain()+0099 byte(s)
0023:77029EC5 (0x00000000 0x00000000 0x00000000 0x00000000) ntdll.dll, RtlInitializeExceptionChain()+0054 byte(s)
4

1 に答える 1

1

私は同じ問題を抱えていて、Synastryで可能な解決策を見つけました

http://social.msdn.microsoft.com/Forums/en-US/vcmfcatl/thread/5037519a-78e2-42f4-94cd-bbe88e0f16d6/

この問題に苦しんでいる私たちは皆、関数 'CoUninitialize' にコール スタックを持っています。この関数は、COM のワーカー スレッドが終了したときに自動的に呼び出されます。このワーカー スレッドは、ユーザーが直接作成するのではなく、COM ライブラリを使用する関数が呼び出されると作成されます。wagscallion と私は、この関数を一般的に「GetOpenFileName」と呼んでいました。GSansoucie もいくつかの COM 自動化関数を呼び出していると思います。

ワーカー スレッドを終了して 'CoUninitialize' と呼ばれることは、COM ライブラリの正当で正常な動作です。それは理由ではありません。私たちが遭遇した例外は、COM サーバーがまだ使用されている間に初期化を解除したことが原因です。しかし、私たちの (または少なくとも私の) コードも合法であり、1 つのことを除いて適切です。コードで「CoInitialize」または「CoInitializeEx」関数を呼び出したことはありません。

COM ライブラリには内部カウント (参照カウンターと同様) があり、CoInitialize または CoInitializeEx を呼び出すことによってインクリメントされ、CoUninitialize を呼び出すことによってデクリメントされます。しかし、私は初期化関数を呼び出しませんでした。私はそれを呼び出しませんでしたが、GetOpenFileName 関数は、そのワーカー スレッドの GetOpenFileName の実装でそれを呼び出します。関数が戻った後、ワーカー スレッドは別の COM ジョブをしばらく待ちます。これが、GetOpenFileName 関数が返されたときにすぐに例外が発生しない理由です。しかし、ワーカー スレッドは終了することを決定し、CoUninitialize を呼び出すと、COM ライブラリ サーバーの内部カウントが 0 になり、CoUninitialize がメモリからすべてのリソースを解放します。

しかし、GetOpenFileName 関数が戻った後、一部のリソースはメモリに残っているはずです (これについてはわかりませんが、この仮定が正しくない場合、例外に遭遇することはありません)。それらが解放されないようにするには、プログラムの初期化で CoInitialize または CoInitializeEx (MSDN では後者を推奨) を呼び出す必要があります。また、プログラムが終了する前に CoUninitialize を呼び出す必要があります。

要するに、プログラムの開始時に「CoInitialize」または「CoInitializeEx」を呼び出し、プログラムの最後に「CoUninitialize」を呼び出す必要があります。しかし、MSDN は、GetOpenFileName や COM ライブラリを使用するその他の関数について、これについて説明していません。:-(

私の場合、initializer と uninitializer を呼び出すことで問題はなくなり、すべてがうまく機能するようになりました。見て、コードに適用してください。この例外を引き起こす別の理由がある場合は、こちらもお知らせください。:-)

読んでくれてありがとう。

私自身にとって、これは解決策ではなく、どこを見るべきかのヒントでした。COINIT_APARTMENTTHREADED で CoInitializeEx を内部的に呼び出すマルチスレッド アプリケーションで CoInitialize を使用しました。呼び出しを CoInitialize(NULL) から CoInitializeEx(NULL, COINIT_MULTITHREADED) に変更したところ、問題は解決したようです。

于 2013-04-08T08:16:44.547 に答える