C++ OCX AfxOleRegisterTypeLib失败,错误为0x80040200

C++ OCX AfxOleRegisterTypeLib失败,错误为0x80040200,c++,visual-studio,mfc,activex,regsvr32,C++,Visual Studio,Mfc,Activex,Regsvr32,我将OCX库从VS2010/Win7升级到VS2019/Win10。但是,当我尝试从提升的命令提示符使用RegSvr32.exe时,我收到错误0x0040200。我做了一些调试,错误的调用是调用AfxOleRegisterTypeLib 是的,我看过一篇SO文章,其中指出“dll附近缺少tlb文件”。要从管理命令提示符运行的其他搜索状态 我在OCX控制装置附近没有TLB。如果尝试使用tlbexp.exe创建一个,则会出现以下错误: TlbExp:错误TX0000:无法加载文件或程序集的file

我将OCX库从VS2010/Win7升级到VS2019/Win10。但是,当我尝试从提升的命令提示符使用
RegSvr32.exe
时,我收到错误0x0040200。我做了一些调试,错误的调用是调用
AfxOleRegisterTypeLib

是的,我看过一篇SO文章,其中指出“dll附近缺少tlb文件”。要从管理命令提示符运行的其他搜索状态

我在OCX控制装置附近没有TLB。如果尝试使用
tlbexp.exe
创建一个,则会出现以下错误:

TlbExp:错误TX0000:无法加载文件或程序集的file:///C:\“pathto.ocx”或其依赖项之一。模块应包含程序集清单

TlbExp命令行(用于以管理员身份运行所有cmd.exe):

我确实使用了
dependends64
(也称为
Dependency Walker 64位
),并且没有缺少组件DLL。它可以很好地找到它们,就像RegSvr32.exe一样

将OCX文件放在C:\Windows\System32中没有帮助

对于任何查看DLL的人来说,这些DLL在Win7机器上都可以正常工作。以下是有关非Windows DLL的更多信息

  • PlxApi720_x64.dll:PLX v7.2 API(Broadcom PLX芯片是一个PCIe交换机(想想USB/网络交换机,只是带PCIe通道)
  • LSI DirectAccess.dll:LSI API是一个自包含的dll,允许软件与LSI HBA RAID适配器通信
  • Ipp*.dll:
    Ipp
    前缀是英特尔code Composer Studio重新分发(x64)文件所使用的dll,这里是2011版,是一个旧版本,需要更新为最新和最大版本,更不用说现在的免费API。这些文件都在
    System32
    文件夹中
代码如下:

// DllRegisterServer - Adds entries to the system registry
STDAPI DllRegisterServer(void)
{
    AFX_MANAGE_STATE(_afxModuleAddrThis);

    if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
        return ResultFromScode(SELFREG_E_TYPELIB);           // <- failure line, through debugging

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

    return NOERROR;
}
BOOL AFXAPI AfxOleRegisterTypeLib(HINSTANCE hInstance, REFGUID tlid,
    LPCTSTR pszFileName, LPCTSTR pszHelpDir)
{
    USES_CONVERSION;

    BOOL bSuccess = FALSE;
    CString strPathName;
    TCHAR *szPathName = strPathName.GetBuffer(_MAX_PATH);
    ::GetModuleFileName(hInstance, szPathName, _MAX_PATH);
    strPathName.ReleaseBuffer();
    LPTYPELIB ptlib = NULL;

    // If a filename was specified, replace final component of path with it.
    if (pszFileName != NULL)
    {
        int iBackslash = strPathName.ReverseFind('\\');
        if (iBackslash != -1)
            strPathName = strPathName.Left(iBackslash+1);
        strPathName += pszFileName;
    }

    if (SUCCEEDED(LoadTypeLib(T2COLE(strPathName), &ptlib)))
    {
        ASSERT_POINTER(ptlib, ITypeLib);

        LPTLIBATTR pAttr;
        GUID tlidActual = GUID_NULL;

        if (SUCCEEDED(ptlib->GetLibAttr(&pAttr)))
        {
            ASSERT_POINTER(pAttr, TLIBATTR);
            tlidActual = pAttr->guid;
            ptlib->ReleaseTLibAttr(pAttr);
        }

        // Check that the guid of the loaded type library matches
        // the tlid parameter.
        ASSERT(IsEqualGUID(tlid, tlidActual));

        if (IsEqualGUID(tlid, tlidActual))
        {
            // Register the type library.
            if (SUCCEEDED(RegisterTypeLib(ptlib, T2OLE((LPTSTR)(LPCTSTR)strPathName), T2OLE((LPTSTR)pszHelpDir))))
                bSuccess = TRUE;
        }

        RELEASE(ptlib);
    }
    else
    {
        TRACE1("Warning: Could not load type library from %s\n", (LPCTSTR)strPathName);
    }

    return bSuccess;
}
  • 这有一个有趣的方法,即创建一个C++ DLL,然后调用<代码> LoadLibrary(DLL)< /C> >之后,代码> GeCopToAdter(模块,“DllRegisterServer”)查看哪一个失败。在我的例子中,两个函数都成功了。这意味着作者错过了另一个失败分支,这两个API调用并不是
    RegSvr32.exe
    所做的唯一事情

  • 虽然我还没有走到尽头,因为我在OCX上遇到了
    aximp.exe
    /
    tlbimp.exe
    问题,但我发现了阻止我注册ActiveX控件的问题,这就是这里的问题

    答案是主CPP文件中的
    GUID

    (我正在进行研究,因为我找不到任何人解释
    RegSvr32.exe如何工作以及它的作用。我想与大家分享,希望它能帮助其他人。)

    在主注册函数STDAPI DllRegisterServer(void)
    中使用的必须与IDL中存在的
    GUID
    匹配:

    [uuid(FE5C7D88-D53C-4977-BA56-4BF3020A5D8A), version(1.0),
    helpfile("DriveOps.hlp"),
    helpstring("DriveOps ActiveX Control module"),
    control]
    library DriveOpsLib
    {
    ...
    }
    
    我有两个不同的值,因此失败了

    这是我用来发现问题的方法和研究,但首先我要说明注册函数,因为这也是关键

    STDAPI DllRegisterServer(void)
    {
        AFX_MANAGE_STATE(_afxModuleAddrThis);
    
        HINSTANCE hiTypeLib = AfxGetInstanceHandle();
    
        if (!AfxOleRegisterTypeLib(hiTypeLib, _tlid))
            return ResultFromScode(SELFREG_E_TYPELIB);
    
        if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
            return ResultFromScode(SELFREG_E_CLASS);
    
        return NOERROR;
    }
    
    正如我相信问题中提到的那样,失败就在这条线上

    if (!AfxOleRegisterTypeLib(hiTypeLib, _tlid))
    
    我已经在internet上找到了
    RegSvr32.exe
    的源代码。它是位于
    VCSamples master
    中的Microsoft
    GitHub
    源代码的一部分

    直接链接到
    RegSvr32.exe

    下载zip的直接链接:

    代码是一条死胡同,因为它告诉了我什么应该是显而易见的,即调用DLL的
    DllRegisterServer
    入口点来完成所有工作的实用程序。我应该知道这一点,但是,我必须看到它才有意义

    使用
    procmon.exe
    ,没有任何意义,对注册表的各种调用就像阅读外语一样,没有任何帮助

    在这里我画了一个空白,直到我想到获取
    AfxOleRegisterTypeLib
    的源代码,因为这是失败的。我想看看这东西到底做了什么,以及源文件
    ctreg.cpp
    的第113行是什么

    我仍然在思考
    procmon
    的评论和一个注册表问题,但我想代码会告诉我是哪一个。我花了一些研究,但我发现了。我喜欢Microsoft共享代码。他们的错误消息没有帮助,但能够真正看到他们试图做什么完全有帮助

    代码如下:

    // DllRegisterServer - Adds entries to the system registry
    STDAPI DllRegisterServer(void)
    {
        AFX_MANAGE_STATE(_afxModuleAddrThis);
    
        if (!AfxOleRegisterTypeLib(AfxGetInstanceHandle(), _tlid))
            return ResultFromScode(SELFREG_E_TYPELIB);           // <- failure line, through debugging
    
        if (!COleObjectFactoryEx::UpdateRegistryAll(TRUE))
            return ResultFromScode(SELFREG_E_CLASS);
    
        return NOERROR;
    }
    
    BOOL AFXAPI AfxOleRegisterTypeLib(HINSTANCE hInstance, REFGUID tlid,
        LPCTSTR pszFileName, LPCTSTR pszHelpDir)
    {
        USES_CONVERSION;
    
        BOOL bSuccess = FALSE;
        CString strPathName;
        TCHAR *szPathName = strPathName.GetBuffer(_MAX_PATH);
        ::GetModuleFileName(hInstance, szPathName, _MAX_PATH);
        strPathName.ReleaseBuffer();
        LPTYPELIB ptlib = NULL;
    
        // If a filename was specified, replace final component of path with it.
        if (pszFileName != NULL)
        {
            int iBackslash = strPathName.ReverseFind('\\');
            if (iBackslash != -1)
                strPathName = strPathName.Left(iBackslash+1);
            strPathName += pszFileName;
        }
    
        if (SUCCEEDED(LoadTypeLib(T2COLE(strPathName), &ptlib)))
        {
            ASSERT_POINTER(ptlib, ITypeLib);
    
            LPTLIBATTR pAttr;
            GUID tlidActual = GUID_NULL;
    
            if (SUCCEEDED(ptlib->GetLibAttr(&pAttr)))
            {
                ASSERT_POINTER(pAttr, TLIBATTR);
                tlidActual = pAttr->guid;
                ptlib->ReleaseTLibAttr(pAttr);
            }
    
            // Check that the guid of the loaded type library matches
            // the tlid parameter.
            ASSERT(IsEqualGUID(tlid, tlidActual));
    
            if (IsEqualGUID(tlid, tlidActual))
            {
                // Register the type library.
                if (SUCCEEDED(RegisterTypeLib(ptlib, T2OLE((LPTSTR)(LPCTSTR)strPathName), T2OLE((LPTSTR)pszHelpDir))))
                    bSuccess = TRUE;
            }
    
            RELEASE(ptlib);
        }
        else
        {
            TRACE1("Warning: Could not load type library from %s\n", (LPCTSTR)strPathName);
        }
    
        return bSuccess;
    }
    
    我一直收到一个
    ASSERT
    ,因此尽管第113行确实在非代码行上,但实际的失败是显而易见的。我知道我没有在
    ASSERT\u指针上失败,因为错误消息不同,这意味着我在以下位置失败:

            ASSERT(IsEqualGUID(tlid, tlidActual));
    
    我详细查看了代码和输入参数。我决定将此函数内容复制并粘贴到OCX中的真实注册代码中,以在调试时获得进一步的可见性。我想查看值

    果然,我看到了两个不同的
    GUID
    值,一个来自顶部,一个是我的
    \u tlid
    ,另一个是从实例句柄返回的。我拿出了一个方便的
    TextPad
    文本编辑器,尽管
    Visual Studio
    有一个
    在文件中查找
    ,但TextPad非常容易使用。这导致了t中的另一个实例整个解决方案,即在
    DriveOps.idl
    中。在那之前,该文件对我来说毫无意义,但突然我发现
    GUID
    就是从实例句柄中提取的
    RegSvr32.exe

    我统一了ID,重建了,现在不再抱怨了。是的,因为我得到了代码,它别无选择,只能注册。它没有修改注册表与我所能说的是不同的故事和问题,但这是另一个问题。
    RegSvr32.exe现在注册而没有抱怨

    (是的,我仍然有
    tlbimp.exe
    aximp.exe
    ,并将我的OCX项目添加到我的
    WinFormsBOOL AFXAPI AfxOleRegisterTypeLib(HINSTANCE hInstance, REFGUID tlid,
        LPCTSTR pszFileName, LPCTSTR pszHelpDir)
    {
        USES_CONVERSION;
    
        BOOL bSuccess = FALSE;
        CString strPathName;
        TCHAR *szPathName = strPathName.GetBuffer(_MAX_PATH);
        ::GetModuleFileName(hInstance, szPathName, _MAX_PATH);
        strPathName.ReleaseBuffer();
        LPTYPELIB ptlib = NULL;
    
        // If a filename was specified, replace final component of path with it.
        if (pszFileName != NULL)
        {
            int iBackslash = strPathName.ReverseFind('\\');
            if (iBackslash != -1)
                strPathName = strPathName.Left(iBackslash+1);
            strPathName += pszFileName;
        }
    
        if (SUCCEEDED(LoadTypeLib(T2COLE(strPathName), &ptlib)))
        {
            ASSERT_POINTER(ptlib, ITypeLib);
    
            LPTLIBATTR pAttr;
            GUID tlidActual = GUID_NULL;
    
            if (SUCCEEDED(ptlib->GetLibAttr(&pAttr)))
            {
                ASSERT_POINTER(pAttr, TLIBATTR);
                tlidActual = pAttr->guid;
                ptlib->ReleaseTLibAttr(pAttr);
            }
    
            // Check that the guid of the loaded type library matches
            // the tlid parameter.
            ASSERT(IsEqualGUID(tlid, tlidActual));
    
            if (IsEqualGUID(tlid, tlidActual))
            {
                // Register the type library.
                if (SUCCEEDED(RegisterTypeLib(ptlib, T2OLE((LPTSTR)(LPCTSTR)strPathName), T2OLE((LPTSTR)pszHelpDir))))
                    bSuccess = TRUE;
            }
    
            RELEASE(ptlib);
        }
        else
        {
            TRACE1("Warning: Could not load type library from %s\n", (LPCTSTR)strPathName);
        }
    
        return bSuccess;
    }
    
            ASSERT(IsEqualGUID(tlid, tlidActual));