1)问题的来源:
ISAutomationRunner程序中,主体功能(调用vbs)完成以后。由于这个软件需要在批处理中被调用,处于方便性考虑需要加入控制台输出功能。因为程序主体来自codeguru上别人的程序,而且是GUI的,无法直接进行控制台输出。首先试了试改成支持MFC的console程序的,结果报错,粗略分析了下是由于源码中有几个地方用到了窗口句柄。不得以采用了为GUI程序添加控制台输出的方法:
-------------------------------------
AttachConsole(-1); // 将当前程序附着到父进程上
freopen("CONIN$", "r+t", stdin); // 重定向 STDIN
freopen("CONOUT$", "w+t", stdout); // 重定向STDOUT
-------------------------------------
其中AttachConsole需要用新的SDK,因而需要设置_WIN32_WINNT=0x0500,这么一设置后AttachConsole使用正常了。
编译,运行,出现文件选择框,确定。晕,出现异常了。
IDE中调试一番,发现是程序中使用的CTextFile类(Johan Rosengren所写的一个很实用的类)中调用的CFileDialog析构时出现异常了,源码如下
-------------------------------------
BOOL CTextFile::GetFilename( BOOL save, CString& filename )
/* ============================================================
Function : CTextFile::GetFilename
Description : The function will display a standard file
dialog. If the instance is created with an
extension, the extension will be used to
filter files.
Return : BOOL - TRUE if a file was
selected
Parameters : BOOL save - TRUE if the file
should be saved.
CString& filename - Placeholder for the
selected filename
============================================================*/
{
CString filter;
CString extension = GetExtension();
if( extension.GetLength() )
filter = extension + _T( "-files (*." + extension + ")|*." ) + extension + _T( "|All Files (*.*)|*.*||" );
BOOL result = FALSE;
CFileDialog dlg( !save, extension, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, filter );
if( dlg.DoModal() == IDOK )
{
filename = dlg.GetPathName();
result = TRUE;
}
return result;
}
--------------------------------------
2)问题的原因:
首先注释了新入的AttachConsole函数,问题依旧。自己郁闷的折腾了好一番,没法只好暂时搁置了。刚才又想起来,于是开始google,一搜“ CFileDialog 析构 异常 ”,乖乖-总算让我碰上一个MFC的bug了。
问题的产生原因在
http://codeguru.earthweb.net/forum/printthread.php?t=320297&pp=50
有所解释,我认为大体可以理解为:
VC6 自带的SDK和 MFC42中的定义,再后来的SDK中更新了,从而导致在析构的时候多卸载了内容
3)问题的解决:
a)换回老SDK,手动加载AttachConsole
b)修改CTextFile源码,由在栈上分配CFileDialog改为在堆上分配
根据我的情况,我选择了a方法,手动加载AttachConsole的源码如下
--------------------------------------
//为了在不用PLATFORM SDK的情况下加载AttachConsole
BOOL myAttachConsole(DWORD dwProcessId)
{
typedef BOOL (WINAPI* _AttachConsole)(DWORD dwProcessId);
HINSTANCE hinstance = LoadLibrary(_T("kernel32.dll"));
if (hinstance == NULL)
{
return FALSE;
}
_AttachConsole AttachConsole = NULL;
AttachConsole = (_AttachConsole)GetProcAddress(hinstance , "AttachConsole");
if (AttachConsole == NULL)
{
return FALSE;
}
AttachConsole(dwProcessId);
return TRUE;
}
--------------------------------------
4)感慨:
写程序就是这样的充满意外。。。