5

我正在开发一个包含多个 C++ COM Dll 的遗留项目。每次在调试配置上构建解决方案时,构建过程都会为每个 COM 项目提供错误:

Error   10  error MSB3073: The command "
        regsvr32 /s /c "D:\*****removed intentionally****_d.dll"
        echo regsvr32 exec. time > ".\Debug\regsvr32.trg"
        echo Server registration done!
      :VCEnd
    " exited with code -1073741819. 

我最近加入了这个项目,所以当我询问它时,我被告知每个人都忽略这些错误,因为解决方案在第二次构建时成功构建。

我决定深入挖掘,似乎 COM 注册本身成功(这解释了为什么第二次构建没有重新尝试注册),但是对 regsvr32 的调用返回错误代码(0xC0000005)。这就是构建失败的原因。

我还尝试从自定义构建步骤中删除注册,而是选择从链接器属性表“Register Output=YES”中注册,并得到相同的错误。

我尝试使用其中一个 dll 调试 regsvr32 并发现以下内容:

  1. 起初我无法在 DllRegisterServer 中设置断点,然后我尝试调试 regsvr32 的 X86 版本(来自 Windows\SysWOW64),然后才能在 DllRegisterServer 上中断。有人可以解释一下吗?

  2. DllRegisterServer 成功并返回 S_OK。

  3. DllRegisterServer 返回后,抛出异常,异常和堆栈跟踪如下:

regsvr32.exe 中 0x759849c1 处的第一次机会异常:0xC0000005:访问冲突读取位置 0x005bf028。

oleaut32.dll!_MemFree@4()  + 0x25 bytes 
oleaut32.dll!OLE_TYPEMGR::Release()  + 0x1b138 bytes    
oleaut32.dll!ReleaseAppData()  + 0x317 bytes    
oleaut32.dll!_OACleanup@0()  + 0x5 bytes    
ole32.dll!wCoUninitialize(COleTls & Tls, int fHostThread)  Line 2830    C++
ole32.dll!CoUninitialize()  Line 2620   C++
regsvr32.exe!_wWinMain@16()  + 0xa77 bytes  
regsvr32.exe!__initterm_e()  + 0x1b1 bytes  
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes   
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes    

那么您对此有何看法?我是否应该像团队其他成员一样忽略它(产品在开发和生产中运行良好)?

您知道为什么即使注册成功我也会遇到访问冲突吗?注册 COM dll 后是否还有其他进程/逻辑运行?还有其他方向吗?

这是处理注册的类的代码:

   CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_C3DT2D, CC3DT2D)
END_OBJECT_MAP()

class CMy3DT2DApp : public CWinApp
{
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMy3DT2DApp)
    public:
    virtual BOOL InitInstance();
    virtual int ExitInstance();
    //}}AFX_VIRTUAL

    //{{AFX_MSG(CMy3DT2DApp)
        // NOTE - the ClassWizard will add and remove member functions here.
        //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMy3DT2DApp, CWinApp)
    //{{AFX_MSG_MAP(CMy3DT2DApp)
        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CMy3DT2DApp theApp;

BOOL CMy3DT2DApp::InitInstance()
{
    _Module.Init(ObjectMap, m_hInstance, &LIBID_MY3DT2DLib);
    return CWinApp::InitInstance();
}

int CMy3DT2DApp::ExitInstance()
{
    _Module.Term();
    return CWinApp::ExitInstance();
}

/////////////////////////////////////////////////////////////////////////////
// Used to determine whether the DLL can be unloaded by OLE

STDAPI DllCanUnloadNow(void)
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    return (AfxDllCanUnloadNow()==S_OK && _Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// Returns a class factory to create an object of the requested type

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    return _Module.GetClassObject(rclsid, riid, ppv);
}

/////////////////////////////////////////////////////////////////////////////
// DllRegisterServer - Adds entries to the system registry

STDAPI DllRegisterServer(void)
{
    // registers object, typelib and all interfaces in typelib
    return _Module.RegisterServer(TRUE);
}

/////////////////////////////////////////////////////////////////////////////
// DllUnregisterServer - Removes entries from the system registry

STDAPI DllUnregisterServer(void)
{
    return _Module.UnregisterServer(TRUE);
}
4

2 回答 2

4

我认为内存损坏是通过混合 ATL 和 MFC 代码进行初始化/终止来触发的。

如果你使用VS2010生成一个“MFC ActiveX控件”,你会注意到:

1CMy3DT2DApp应该来自,而COleControlModule不是来自CWinApp

2 and方法应该调用InitInstanceandExitInstanceCOleControlModule::InitInstanceCOleControlModule::ExitInstance

3 DllRegisterServer 应该是:

AFX_MANAGE_STATE(_afxModuleAddrThis);

if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
    return ResultFromScode(SELFREG_E_TYPELIB);

if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
    return ResultFromScode(SELFREG_E_CLASS);

return NOERROR;

4个类似的实现DllUnregisterServer(使用AfxOleUnregisterTypeLib

5没有实现DllCanUnloadNow

一种解决方案可能是禁止对 CComModule _Module 的所有引用并使用常规 MFC 代码。

另一种解决方案是让 DLL 成为常规 ATL Dll。

您应该使用 VS2010 向导向您展示这两种代码库。(创建 ATL Dll 后,使用“添加类”菜单选项,然后选择“ATL 控件”

解决方案很大程度上取决于在剩余代码中使用 MFC 和/或 ATL 函数。

建议:使 DLL 成为完整的 MFC,抑制“atl 模块”,然后使用向现有 mfc 应用程序添加 ATL 支持来添加对 ATL 的支持

于 2013-11-18T11:38:47.527 回答
4

显然,罪魁祸首是一个名为Visual Leak Detector的工具,它在包含到源文件时会附加自身。

我们从一个新的 ATL 项目开始(就像 manuell 建议的那样)发现了它,并一次将功能向后添加。当我们添加一些链接到另一个包含 vld.h 的 dll 的代码时,注册错误就会返回。

所以我们删除了所有对 VLD 的引用,现在我们很高兴地注册,即使是原始发布的代码。

我们甚至发现了这个小美女......

感谢大家的帮助!

于 2013-11-18T16:42:38.680 回答