2012-05-30 173 views
1

我们有一个C应用程序,它使用GetOpenFileName公共对话框让用户选择一个文件。我们一直在Windows2008R2上崩溃。我想通了,如果我们把一个DEP例外,在我们的应用程序崩溃停止C应用程序使用GetOpenFileName崩溃。 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) 
+3

查看崩溃时的堆栈跟踪。这应该确定谁没有将代码标记为可执行文件。它可能是一些shell扩展。 –

+0

[不要使用像'strcpy'](http://stackoverflow.com/questions/610238/c-strcpy-evil)。 MSVC甚至会发出警告...... – DCoder

+0

我上面输入了堆栈转储。它没有告诉我很多。这是标准的windows 2008r2。我已经看到网上张贴现在显示msoffice有同样的问题。 –

回答

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服务器在尚未使用时未初始化引起的。但是我们的(或者至少是我的)代码除了一件事情之外也是合法合法的。我从未在代码中调用过“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,问题就消失了,现在所有东西都可以正常工作,请仔细观察并将其应用到您的代码中,如果还有其他原因导致此异常,请告诉我们:-)

谢谢您的阅读。

对于我自己来说,这不是解决方案,而是一个暗示在哪里看。我在我的多线程应用程序中使用了CoInitialize,它在内部使用COINIT_APARTMENTTHREADED调用CoInitializeEx。 我现在将呼叫从CoInitialize(NULL)更改为CoInitializeEx(NULL,COINIT_MULTITHREADED),我的问题似乎消失了。

+0

有趣。这听起来不像我们的问题。碰撞总是在GetOpenFileName函数中发生。通常绘制对话框。我们也不使用COM,所以我们从来没有调用过CoInitialize。我对该问题的解决方案是通过在注册表中添加异常来禁用DEP,或者使用C++/CLR的.net版本的fileopen对话框 –