首页 > 解决方案 > OCX AfxOleRegisterTypeLib 失败并出现错误 0x80040200

问题描述

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

RegSvr32.exe OCX 错误 0x80040200

是的,我看到了这篇SO 文章,其中指出“dll 附近没有 tlb 文件”。其他搜索状态从管理命令提示符运行。

我在 OCX 控件附近没有 TLB。如果我尝试使用创建一个tlbexp.exe,我会收到以下错误:

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

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

"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.2 Tools\x64\tlbexp.exe" /VERBOSE "<path to OCX file>" /out:"<path to .tlb output file>"

我下载Resource Tuner了,它很好地显示了清单。清单没有任何 TLB 信息。

OCX 文件的清单

我在想,也许 OCX 清单需要更多的东西来帮助TlbExp获取它想要的信息,只是一个想法。

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="1.0.0.0" name="DriveOps.ocx"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
      </requestedPrivileges>
    </security>
  </trustInfo>
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
      <!-- Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
      <!-- Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
      <!-- Windows 10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
  </compatibility>
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
    </windowsSettings>
  </application>
</assembly>

我确实使用过Depends64(aka Dependency Walker 64-bit) 并且没有缺少组件 DLL。它可以很好地找到所有这些,RegSvr32.exe 也是如此。

64位OCX的Dependency Walker 64输出(调试版)

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

对于任何查看 DLL 的人来说,这些相同的 DLL 在 Win7 机器上运行良好。这是有关非 Windows DLL 的更多信息

这是代码:

// 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;
}

Intel Code Composer Studio 2011文件在目录C:\Windows\System32中,就像在 Win7 盒子上一样。

对于它的价值,TlbExp在Win7盒子上也失败了,只是它注册了,这可能是UI可以添加控件的原因。我记得我曾经在Win7项目上更换过OCX,VS2010自动创建了TLB并在前面加上前缀Ax,但那是几年前的事了,所以我的记忆可能不是最准确的。尝试将 OCX 添加到 UI(.Net WinForms)失败得很惨,只是说无法添加。

OCX 确实使用最新的平台工具集 ( Visual Studio 2019 (v142))。

OCX 属性页

对开发新手的评论regasm.exe是针对 .Net Assemblies 的。RegSvr32.exe是 for ActiveX Controls (OCX/DLL),这就是我所拥有的。RegSvr32 用于动态加载的模块,因此是 DllRegister 入口点。

想法?

进一步测试的笔记

  1. (星期六 9/21/2019)升级时,我创建了一个空的 C++ DLL 项目,然后添加了所有文件,通过旧项目设置将目标扩展名更改为 OCX,并在合理时将它们与新项目文件对齐保持不变。我想做一个测试,看看一个全新的 OCX 项目会发生什么。我看到VS2019中有一个项目类型的“MFC ActiveX控件”这样的东西。我创建了它并看到我得到了不同的基础文件,但更重要的是 RegSvr32.exe 有效。这意味着错误是初始项目文件,所以我需要导入一个干净的项目或逐个导入,如果可能的话,看看哪里出了问题。

  2. (2019 年 9 月 21 日星期六)新的测试项目没有附带清单文件,并且TlbExp.exe失败并出现与我的真实项目相同的错误消息。我去添加新项目并看到“包裹清单”。该清单文件虽然仍然产生相同的 TlbExp.exe 错误,但看起来与上面的应用程序清单文件完全不同。我创建了另一个新MFC ActiveX Control项目并从上面添加了清单,只是更改了名称,然后看到该项目拒绝构建抛出 1) 错误 c1010001 不同清单片段中属性“级别”的值不相等。2) 运行 mt.exe 时出现 LNK1327 故障。这告诉我原来的 Win7 项目和我的 Win10 项目文件可能有一些错误,否则 VS 应该把这些错误抛给我。TlbExp.exe失败。也许清单中的某些属性是必需的。我只是离开了默认值。

包清单(这是我第一次看到其中一个。我总是看到 app.manifest 那种。)

<?xml version="1.0" encoding="utf-8"?>
<!-- TODO: Make sure to set the Package attributes -->
<Package xmlns="urn:Microsoft.WindowsPhone/PackageSchema.v8.00"
  Owner=""
  OwnerType="OEM"
  Platform=""
  Component=""
  SubComponent="Package"
  ReleaseType="Test" >

  <Components>
    <Driver InfSource="$(_RELEASEDIR)$(TARGETNAME).inf">
      <Reference Source="$(_RELEASEDIR)$(TARGETNAME)$(TARGETEXT)" />
      <Files>
        <!-- For kernel mode drivers, $(DRIVER_DEST) evaluates to "drivers" by default -->
        <!-- For user mode drivers, $(DRIVER_DEST) evaluates to "drivers\umdf" by default -->
        <File Source="$(_RELEASEDIR)$(TARGETNAME)$(TARGETEXT)" DestinationDir="$(runtime.system32)\$(DRIVER_DEST)" />
      </Files>
    </Driver>

  </Components>
</Package>
  1. 本文一个有趣的方法,即创建一个 C++ DLL,然后调用LoadLibrary(dll),然后GetProcAddress(module, "DllRegisterServer")查看哪个失败。好吧,就我而言,这两个功能都成功了。这意味着作者错过了另一个失败的分支,而且这两个 API 调用并不是唯一的RegSvr32.exe

标签: c++visual-studiomfcactivexregsvr32

解决方案


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

答案GUID在主 CPP 文件中:

(我正在做我的研究,因为我找不到任何人解释它是如何RegSvr32.exe工作的以及它做了什么。我想分享一下,希望它可以帮助别人。)

const GUID CDECL _tlid = { 0xFE5C7D88,0xD53C,0x4977,{0xBA,0x56,0x4B,0xF3,0x02,0x0A,0x5D,0x8A} };

在主注册函数中使用的STDAPI DllRegisterServer(void)必须与GUIDIDL 中的当前匹配:

[uuid(FE5C7D88-D53C-4977-BA56-4BF3020A5D8A), version(1.0),
helpfile("DriveOps.hlp"),
helpstring("DriveOps ActiveX Control module"),
control]
library DriveOpsLib
{
...
}

我有 2 个不同的值,因此失败了。

这是我用来发现问题的方法和研究,但首先我将说明注册功能,因为这又是关键。

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))

我已经RegSvr32.exe在互联网上找到了源代码。它是GitHub位于VCSamples-master.

直接链接RegSvr32.exe这里

下载 zip 的直接链接:这里

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

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

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

我仍在考虑评论procmon和注册表问题,但我认为代码会告诉我是哪一个。我花了一些时间研究,但我找到了代码。我喜欢微软共享代码。他们的错误消息没有帮助,但能够真正看到他们正在尝试做的事情完全有帮助。

这是代码:

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_POINTER,因为该错误消息不同,这意味着我失败了:

        ASSERT(IsEqualGUID(tlid, tlidActual));

我详细查看了代码以及入口参数。我决定将这个函数内容复制并粘贴到我的 OCX 中的真实注册码中,以便在调试时获得进一步的可见性。我想看看价值观。

果然,我看到了 2 个不同GUID的值,一个来自顶部,一个是 my _tlid,另一个是从实例句柄返回的。我拿出我方便的TextPad文本编辑器,虽然Visual Studio有一个Find in Files,但 TextPad 更容易使用。这导致了整个解决方案中的另一个实例,即在DriveOps.idl. 直到那一刻之前的那个文件对我来说毫无意义,但突然我发现GUID这里就是RegSvr32.exe从实例句柄中拉出的那个。

我统一了ID,重建了,现在RegSvr32.exe不再抱怨了。是的,既然我得到了代码,它别无选择,只能注册。它不修改注册表与我所知的情况和问题不同,但这是另一个问题。RegSvr32.exe现在注册没有投诉。

(是的,我仍然有tlbimp.exe, aximp.exe, 并将我的 OCX 项目添加到我的WinForms项目问题中,但我得到了这个东西舔并在这个过程中学到了一些东西。我猜行号的差异可能是微软在标题中所做的一些更改,无论哪种方式, 功能看起来是一样的。)


推荐阅读