自己写了个DLL连接数据库.当调用这个DLL的EXE退出的时候,就出现Crash.Crash的地方是:
//c:\program files\microsoft visual studio 8\vc\include\comip.h
void _Release() throw()
    {
        if (m_pInterface != NULL) {
            m_pInterface->Release(); //Crash
        }
    }如果我把DLL中的代码放在Application中,即不用DLL,就没有问题.我现在分离出了最小的代码集.可能还是有点长.
DLL的入口是一个CDataCenter类,它的静态方法调用一个实现了单态模式的类CADOManager,CADOManager类中包含一个成员变量_ConnectionPtr       m_pCon;连接和关闭数据库就是调用m_pCon的方法.///////////////DataAccess.DLL//class CADOManager
//ADOManager.h
class CADOManager
{
friend CADOManager* TheADOManager();
public:
CADOManager(void);
public:
~CADOManager(void);
BOOL Inital();
BOOL ExInital();
private:
_ConnectionPtr       m_pCon;
static auto_ptr<CADOManager> m_instance;
};
CADOManager* TheADOManager();
//ADOManager.cpp
auto_ptr<CADOManager> CADOManager::m_instance;
CADOManager::CADOManager(void)
{
}CADOManager::~CADOManager(void)
{
}BOOL CADOManager::Inital()
{
m_pCon.CreateInstance(__uuidof(Connection));
m_pCon->Open(_bstr_t(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\sss.xls;Extended Properties=\"Excel 8.0;HDR=Yes\";")),_bstr_t(_T("")),_bstr_t(_T("")),adModeUnknown);
         return TRUE;
}
BOOL CADOManager::ExInital()
{
m_pCon->Close();
         return TRUE;
}
CADOManager* TheADOManager()
{
if(0 == CADOManager::m_instance.get())
{
CADOManager::m_instance.reset(new CADOManager);
}
return CADOManager::m_instance.get();
}//class CDataCenter
//DataCenter.h
class __declspec(dllexport)  CDataCenter
{
public:
static BOOL Inital();
static BOOL ExInital();
};//DataCenter.cpp
BOOL CDataCenter::Inital()
{
TheADOManager()->Inital();
return TRUE;
}
BOOL CDataCenter::ExInital()
{
TheADOManager()->ExInital();
return TRUE;
}////////////////////////Appliaction
//Code:
CDataCenter::Inital();
CDataCenter::ExInital();

解决方案 »

  1.   

    检查一下你的CoUninitialize, 是不是在Release之前就被Call了。用Smart Pointer的时候一定要小心全局变量,你这里 auto_ptr<CADOManager> CADOManager::m_instance; 要在CoUninitialize之前 (或者OleUninitialize)把所有的Release都弄干净,否则你的COM Apartment一丢,再Release就Crash了。
      

  2.   

    你使用的是ADO智能指针,不用手动的Release();它自己会释放的。
      

  3.   

    另外,给你的代码套上
    try
    {
    ...
    }
    catch(_com_error e)
    {
    printf("%s\n", (char*)e.Description());
    return;
    }
    把错误抛出来看看。
      

  4.   

    问一下楼上的,catch(...)
    是不是可以捕获所有的exception,包括com的
      

  5.   

    千万不要用catch(...)! 这是很多公司最后花无数多钱请专家的一个重要原因。C++的catch(...)会抓住Structure Exception, 这样就把一些比如Access Denied等致命的错误都抓住了,还有比如Heap Corruption,最后出错的时候已经无法跟踪解决了。这个catch(...)是 C++ 里设计很糟糕的一个东西。楼主程序之所以Crash就是因为用了智能指针,因为是全局变量,所以在Main 函数结束以后Release才被自动叫到。这个时候通常COM 已经都结构了(CoUninitialze被叫过了),所以会立即Crash。这个根本不是 COM 的 exception, _com_error根本抓不到。这个问题我已经见过无数次了,在ADO里面尤其容易见到。智能指针是双刃剑,很容易出问题。很多公司不提倡使用,尤其是_com_ptr_结构的,含Exception在里面,非常不好。
      

  6.   

    千万别用catch(...)???
    那用什么???
    这个世道还有什么千万别用.
    是不是千万别用goto啊.
    不是千万别用,是自己不会用.
    虽然catch(...)会抓住所有异常,但你就不能在这之前抓你知道的异常吗??
    智能指针是双刃剑,可用剑的是人.剑本身是不会伤人的.
    因为一个东西有可能造成危害就不使用???
    因噎废食.
      

  7.   

    catch(...)和Goto是两回事情,catch(...)能抓你知道的异常??微软Compiler因为实现中会抓住Structure Exception 被骂得狗血喷头,不得不在最新版中修改。catch(...)是万恶之源已经是业界公认,John Robbins的《Debugging Applications>》中有带实例的专门论述,我就不罗嗦了。不是说智能指针不能用,而是得明白它的好处和坏处。_com_ptr_t类的smart pointer是当时 MFC支持COM弄出来的不伦不类的东西。后来已经不推荐使用,改为ATL的CComPtr。因为ADO类的传统程序使用没有办法,否则是能不用就不用,这也是共识了。这和因噎废食两回事情,有更好更便宜的东西,你非得还用你的286??
      

  8.   

    不知道是你语文不好还是我表达不好.
    虽然catch(...)会抓住所有异常,但你就不能在这之前抓你知道的异常吗??
    我的意思是在catch(...)语句前用其他catch语句.
    John Robbins说的就是对的???如果catch(...)是万恶之源为什么现在还没把它从C++里面去除???根据需要使用就是对的.人才是万恶之源.
      

  9.   

    你只能抓住你所知道的异常, catch(...)的问题就是他抓住的是你不知道的异常,你如果连异常都不知道,如何处理它? 被catch(...)抓住的东西不敢说100%,也差不多就是Bug. 你能给我举一个用catch(...)的正常用法?John Robbins不一定就是对的,我提那本书是想让大家看看里面的实际例子.看看用catch(...)的潜在后果。C++ 的Exception不是语言原来最基本的部分,和Template一样都是后加的,C++ 作为OO最主要的问题是Class不是都从一个Object继承出来的,所以不能有后来的C#和Java那样的有个Exception的Base类。没办法弄了个尴尬的 catch(...), 本身语言规范就含糊不清。于是各种编译器自行其道,到微软,为了和以前微软的Excetion Handling 兼容(__try, __except, __finally),连Structured Exception都抓到(...)里面。(包含Access Violation, Heap Corruption, int 3,除0,等程序应该立即中止的错误),彻底把潘多拉的盒子打开。现在很多程序扫描程序扫到这个就当成Bug。C++ 也在发展,现在的managed C++ 已经没这个东西了。根据需要是对的,问题是你能给我举个正确的需要吗?
      

  10.   

    晕死.
    catch(...)的目的就是让你抓住未知和未捕捉的异常.
    如何使用是你自己的问题和catch(...)有什么关系???
    有时候抓住异常并不是要处理异常本身,而是让程序正常的退出.或者让程序继续运行.
    程序异常中止的对话框对程序员和调试人员也许有用.但对用户来说.没有任何作用.程序员写的程序是给用户用的,不是自己用的.
    Access Violation, Heap Corruption, int 3,除0
    这些错误对局部是致命的.但如果这只是程序一个不重要的环节或者可以恢复的环节.那你也要中止程序???
    我认为程序应该最大可能的从错误中恢复而不是有错误就挂掉.想法不一样.没必要再说下去了.
      

  11.   

    两位的说法都对我们都有保留不同意见的权利
    我目前的程序,就需要catch(...) 因为我也不知道哪里异常了,但是程序要发布给用户,总不能让程序老崩溃,所以就catch(...)。然后自己慢慢找bug,等找到了,再加catch(xxx e)
      

  12.   

    好像跑题了.我不能不用智能指针的. 我要用ADO啊.
      

  13.   

    跑题了,倒带,你用智能指针没有问题了,就是得保证你的Release在CoUninitialize之前就可以了。
      

  14.   

    COM库初始化我不是用的Coinitialize,而是用的AfxOleInit,它相对应的释放函数不用用户自己调用,MFC就做了.这样我的静态变量不知道怎么控制生命周期了.请高手指教.
      

  15.   

    问题解决了.多谢大家的帮助.
    从中确实学到了很多知识.
    我在BOOL CADOManager::ExInital()中增加了一行代码:m_pCon->Release().只要外部程序在退出之前调用了CADOManager::ExInital(),就会释放掉智能指针m_pCon.
    BOOL CADOManager::ExInital()
    {
             m_pCon->Close();
             m_pCon->Release(); //显示调用释放函数,避免在CoUninitialize之后释放.
             return TRUE;
    }
      

  16.   

    很高兴你的问题解决了,我很少用MFC做COM,总说不到点子上。不过原理我想你已经明白了。这就是我说的用智能指针需要注意的地方。