dos,
你的最后两句话离答案已经很近了。其实使用WinDBG或者VS观察一下汇编代码,答案就有了。
下面是没有MessageBox时,OnBnClickedException函数的代码:
TestThrowDll!CTestThrowDllDlg::OnBnClickedException:00401460 6a01 push 100401462 e837000000 call TestThrowDll!ThrowExceptionFun (0040149e)00401467 c3 ret
而下面是有MessageBox时的代码:
TestThrowDll!CTestThrowDllDlg::OnBnClickedException:00401460 55 push ebp00401461 8bec mov ebp,esp00401463 6aff push 0FFFFFFFFh00401465 68b0224000 push offset TestThrowDll!_allmul+0x40 (004022b0)0040146a 64a100000000 mov eax,dword ptr fs:[00000000h]00401470 50 push eax00401471 83ec08 sub esp,800401474 53 push ebx00401475 56 push esi00401476 57 push edi00401477 a120504000 mov eax,dword ptr [TestThrowDll!__security_cookie (00405020)]0040147c 33c5 xor eax,ebp0040147e 50 push eax0040147f 8d45f4 lea eax,[ebp-0Ch]00401482 64a300000000 mov dword ptr fs:[00000000h],eax00401488 8965f0 mov dword ptr [ebp-10h],esp0040148b 894dec mov dword ptr [ebp-14h],ecx0040148e 6a00 push 000401490 6a00 push 000401492 68a0354000 push offset TestThrowDll!`string' (004035a0)00401497 c745fc00000000 mov dword ptr [ebp-4],00040149e e8e5010000 call TestThrowDll!CWnd::MessageBoxA (00401688)004014a3 6a01 push 1004014a5 e864000000 call TestThrowDll!ThrowExceptionFun (0040150e)004014aa 8b4df4 mov ecx,dword ptr [ebp-0Ch]004014ad 64890d00000000 mov dword ptr fs:[0],ecx004014b4 59 pop ecx004014b5 5f pop edi004014b6 5e pop esi004014b7 5b pop ebx004014b8 8be5 mov esp,ebp004014ba 5d pop ebp004014bb c3 ret004014bc 8b4dec mov ecx,dword ptr [ebp-14h]004014bf 6a00 push 0004014c1 6a00 push 0004014c3 68b0354000 push offset TestThrowDll!`string' (004035b0)004014c8 e8bb010000 call TestThrowDll!CWnd::MessageBoxA (00401688)004014cd b8aa144000 mov eax,offset TestThrowDll!CTestThrowDllDlg::OnBnClickedException+0x4a (004014aa)004014d2 c3 ret
也就是说,当OnBnClickedException方法中的Try{}catch块中只调用了一个ThrowExceptionFun函数时,编译器将异常处理代码优化掉了。
优化掉的原因如你在EMAIL中说的,“默认的exe的release方式编译选项里是/EHsc,按MSDN上的说明,就是"仅捕获 C++ 异常并通知编译器假定 extern C 函数从未引发 C++ 异常"。而DLL导出函数恰好是用extern C声明了。”既然try{}catch中只有一段肯定不抛出异常的函数调用,那么这个异常处理就“没有用”了。从上面的汇编清单可以看到,裁掉后的确节约了很多空间(EXE的大小)和时间(运行时少执行很多准备异常捕捉的代码)。
加上了MessageBox调用后,因为MessageBox被认为是可能抛出异常,所以异常保护代码就在了。这也是为什么把MessageBox加在Throw前面还是后面都可以的原因。
找到了根源后,只要在OnBnClickedException方法前使用一下编译器指令禁止优化:
#pragma optimize( "", off )
那么即使没有MessageBox调用,那么也会捕捉到异常了。