OCX AfxOleRegisterTypeLib 失败,出现错误 0x80040200

OCX AfxOleRegisterTypeLib fails with error 0x80040200

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

的调用

是的,我看到了 SO 文章,其中指出 "absence of a tlb file near the dll"。其他搜索状态为 运行 从管理命令提示符。

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

TlbExp : error TX0000 : Could not load file or assembly 'file:///C:\pathto.ocx' or one of its dependencies. The module was expected to contain an assembly manifest.

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 清单需要更多的东西来帮助 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(又名 Dependency Walker 64-bit)并且没有缺少组件 DLL。它很好地找到了所有这些,RegSvr32.exe.

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

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

想法?

进一步测试的笔记

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

  2. (2019 年 9 月 21 日,星期六)新的测试项目没有附带清单文件,TlbExp.exe 失败并出现与我的真实项目相同的错误消息。我去添加新项目,看到 "Package Manifest"。该清单文件虽然仍然产生相同的 TlbExp.exe 错误,但看起来与上面的应用程序清单文件有很大不同。我创建了另一个新的 MFC ActiveX Control 项目并从上面的清单中添加了只是更改名称并看到该项目拒绝构建抛出 1) 错误 c1010001 属性 "level" 的值在不同的清单片段中不相等。和 2) LNK1327 在 运行 宁 mt.exe 期间发生故障。这告诉我原来的 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. 这个 article 有一个有趣的方法,即创建一个 C++ DLL,然后调用 LoadLibrary(dll),然后 GetProcAddress(module, "DllRegisterServer") 看哪个失败。好吧,在我的例子中,这两个函数都成功了。这意味着作者错过了另一个失败分支并且这两个 API 调用并不是 RegSvr32.exe 所做的唯一事情。

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

答案是主CPP文件中的GUID

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

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

在主注册函数中使用的 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
{
...
}

我有 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 的源代码。它是位于 VCSamples-master.

的 Microsoft GitHub 来源的一部分

将 Link 指向 RegSvr32.exehere

直接Link下载zip:here

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

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

这里我一头雾水,直到我想到要获取 AfxOleRegisterTypeLib 的源代码,因为那是失败的地方。我想看看那个东西到底做了什么,以及源文件 ctlreg.cpp.

第 113 行的内容

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

代码如下:

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 行确实在 non-code 行,但实际失败是显而易见的。我知道我没有在 ASSERT_POINTER 上失败,因为错误消息不同,这意味着我在

上失败了
        ASSERT(IsEqualGUID(tlid, tlidActual));

我详细查看了代码以及入口参数。我决定将此功能内容复制并粘贴到我的 OCX 中的真实注册代码中,以便在调试时获得更多可见性。我想查看值。

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

我统一了ID,重新构建,现在RegSvr32.exe不再抱怨了。是啊,既然拿到了code,那就只好注册了。它不修改注册表与我所说的是不同的故事和问题,但这是另一个问题。 RegSvr32.exe 现在注册无投诉。

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

太棒了。所有此类情况下的问题是,与 DLL/OCX 在注册代码中所具有的相比,IDL 具有不同的 GUID。它是如何改变的,我没有调查但能够通过从项目生成的 *.idl 文件中读取新的 GUID 来解决这个问题。