简体   繁体   English

当我更改C#COM dll的签名时,为什么必须从x86到msil再回到x86

[英]Why do I have to go from x86 to msil back to x86 when I change signature of C# COM dll

I am trying to have a C# library expose it's classes/methods to a native C++ application via COM. 我正在尝试让C#库通过COM向本机C ++应用程序公开其类/方法。 I am using registration free com (ie with manifest files) so as to not have to register the COM library with Windows. 我正在使用免费注册的com(即清单文件),以便不必在Windows上注册COM库。 I am running in to a problem where if I modify the C# library and do things like add a new class or change the name of a class then when that class is instantiated in the C++ application it throws a EETypeLoadException which my understanding means that the COM library doesn't match what the C++ app thinks it should look like. 我遇到一个问题,如果我修改C#库并执行诸如添加新类或更改类的名称之类的操作,则当在C ++应用程序中实例化该类时,它将引发EETypeLoadException,据我的理解,这意味着COM库与C ++应用程序认为的外观不匹配。 My projects are set to automatically generate the type library and manifest file for the C# library every time you build so that the C++ app will get the most recent version. 我的项目设置为在每次构建时自动为C#库生成类型库和清单文件,以便C ++应用程序获得最新版本。 To fix this error I have to modify the manifest file for the C++ app to say that the C# dll targets msil, build, then flip it back to x86 and build again. 要解决此错误,我必须修改C ++应用程序的清单文件,说C#dll以msil为目标,进行构建,然后将其翻转回x86并再次进行构建。 At that point the C++ program and C# program are in sync and throw no errors. 此时,C ++程序和C#程序已同步,并且不会引发任何错误。 I tried wiping out the output directory and obj directory to get rid of any possible cached files but this doesn't work, only toggling the manifest file back and forth. 我试图清除输出目录和obj目录,以除去所有可能的缓存文件,但这不起作用,只能来回切换清单文件。

Here is how I have my solution setup: 这是我的解决方案设置方法:

Create a new solution and add a C# Class Library (.Net Framework) called ExampleLib to it. 创建一个新的解决方案,并向其添加一个名为ExampleLib的C#类库(.Net Framework)。

Add an interface that contains an arbitrary method: 添加一个包含任意方法的接口:

namespace ExampleLib
{
    public interface ITestClass
    {
        int Add(int a, int b);
    }
}

Add a class with a guid attribute that uses that interface and implement the required method. 添加具有guid属性的类,该类使用该接口并实现所需的方法。

using System.Runtime.InteropServices;

namespace ExampleLib
{
    [Guid("5054A38A-946A-4BB2-854B-E1A31633DD77")]
    public class TestClass : ITestClass
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
}

Go in to the project properties. 进入项目属性。 In the Application section click Assembly Information and check the Make assembly COM-Visible box. 在“应用程序”部分中,单击“程序集信息”,然后选中“使程序集成为COM可见”框。 In the Build section set the platform target to x86 and change the Output Path to: 在“构建”部分中,将平台目标设置为x86,并将“输出路径”更改为:

..\Debug\

In the Build Events section add the following to the Post-build event: 在“构建事件”部分中,将以下内容添加到“构建后事件”中:

"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.2 Tools\tlbexp.exe" "$(TargetPath)" /out:"$(TargetDir)$(TargetName).tlb"
"C:\Program Files (x86)\Windows Kits\10\bin\10.0.18362.0\x86\mt.exe" -managedassemblyname:"$(TargetPath)" -out:"$(TargetName).manifest" -nodependency

If you have a different version of the Windows SDK installed or to a different location you will need to alter the above commands to match the locations for tblexp.exe and mt.exe. 如果您安装了Windows SDK的不同版本或位于其他位置,则需要更改以上命令以匹配tblexp.exe和mt.exe的位置。

Add a C++ Console app to the project called ExampleClient. 将C ++控制台应用程序添加到名为ExampleClient的项目中。

In the Source Files section add a stdafx.cpp file with the following code (this is boilerplate): 在“源文件”部分中,添加带有以下代码的stdafx.cpp文件(这是样板):

#include "stdafx.h"

In the ExampleClient.cpp file replace to default code from the template with the following: 在ExampleClient.cpp文件中,使用以下内容替换为模板中的默认代码:

#include "stdafx.h"
#include "atlbase.h"
#include <conio.h>

#ifdef DEBUG
#import "..\Debug\ExampleLib.tlb" raw_interfaces_only
#else
#import "..\Release\ExampleLib.tlb" raw_interfaces_only
#endif

using namespace ExampleLib;

int main()
{
    HRESULT coInitResult = CoInitialize(0);
    try
    {
        ITestClassPtr pTest(__uuidof(TestClass));
    }
    catch (_com_error _com_err)
    {
        wprintf(L"\n %s", _com_err.ErrorMessage());
        auto _ = _getch();
    }
    CoUninitialize();
    return 0;
}

Also in the Source Files section add ExampleClient.exe.manifest file with the following: 另外,在“源文件”部分中,添加带有以下内容的ExampleClient.exe.manifest文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32"
                    name="ExampleClient"
                    version="1.0.0.0">
  </assemblyIdentity>

  <dependency>
    <dependentAssembly>
      <assemblyIdentity name="ExampleLib" version="1.0.0.0" processorArchitecture="x86"/>
    </dependentAssembly>
  </dependency>
</assembly>

In the Header Files section add a stdafx.h file (boilerplate): 在“头文件”部分中,添加一个stdafx.h文件(样板):

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

Also in the Header Files section add a targetver.h file (boilerplate): 另外,在“头文件”部分中,添加targetver.h文件(样板):

#pragma once
#include <SDKDDKVer.h>

Right click on the C++ project and unload the project then edit the ExampleClient.vcxproj file. 右键单击C ++项目并卸载该项目,然后编辑ExampleClient.vcxproj文件。 There are many changes to this file compared to what it currently is out of the box including precompiled header changes, not embedding the manifest, additional include directories, and copying the manifest to the output directory. 与当前可用的文件相比,此文件有很多更改,包括预编译的标头更改,不嵌入清单,其他包含目录以及将清单复制到输出目录。 To make reproducing simplier I will just include the entire file which should work fine 为了简化复制,我将包括整个文件,该文件应该可以正常工作

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup Label="ProjectConfigurations">
    <ProjectConfiguration Include="Debug|Win32">
      <Configuration>Debug</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|Win32">
      <Configuration>Release</Configuration>
      <Platform>Win32</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Debug|x64">
      <Configuration>Debug</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
    <ProjectConfiguration Include="Release|x64">
      <Configuration>Release</Configuration>
      <Platform>x64</Platform>
    </ProjectConfiguration>
  </ItemGroup>
  <PropertyGroup Label="Globals">
    <VCProjectVersion>16.0</VCProjectVersion>
    <ProjectGuid>{32A23FFD-3FD3-4191-8799-3A8DD34DCB94}</ProjectGuid>
    <Keyword>Win32Proj</Keyword>
    <RootNamespace>ExampleClient</RootNamespace>
    <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>true</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
    <ConfigurationType>Application</ConfigurationType>
    <UseDebugLibraries>false</UseDebugLibraries>
    <PlatformToolset>v142</PlatformToolset>
    <WholeProgramOptimization>true</WholeProgramOptimization>
    <CharacterSet>Unicode</CharacterSet>
  </PropertyGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
  <ImportGroup Label="ExtensionSettings">
  </ImportGroup>
  <ImportGroup Label="Shared">
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
  </ImportGroup>
  <PropertyGroup Label="UserMacros" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <LinkIncremental>true</LinkIncremental>
    <EmbedManifest>false</EmbedManifest>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <LinkIncremental>true</LinkIncremental>
    <GenerateManifest>false</GenerateManifest>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <LinkIncremental>false</LinkIncremental>
    <EmbedManifest>false</EmbedManifest>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <LinkIncremental>false</LinkIncremental>
  </PropertyGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>..\$(Configuration)</AdditionalIncludeDirectories>
      <AdditionalUsingDirectories>
      </AdditionalUsingDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
    </Link>
    <PostBuildEvent>
      <Command>xcopy $(ProjectName).exe.manifest ..\$(Configuration)\ /Y</Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
    <ClCompile>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <WarningLevel>Level3</WarningLevel>
      <Optimization>Disabled</Optimization>
      <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
      <AdditionalIncludeDirectories>..\$(Configuration)</AdditionalIncludeDirectories>
      <AdditionalUsingDirectories>
      </AdditionalUsingDirectories>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
    </Link>
    <PostBuildEvent>
      <Command>
      </Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
    </Link>
    <PostBuildEvent>
      <Command>xcopy $(ProjectName).exe.manifest ..\$(Configuration)\ /Y</Command>
    </PostBuildEvent>
  </ItemDefinitionGroup>
  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
    <ClCompile>
      <WarningLevel>Level3</WarningLevel>
      <PrecompiledHeader>Use</PrecompiledHeader>
      <Optimization>MaxSpeed</Optimization>
      <FunctionLevelLinking>true</FunctionLevelLinking>
      <IntrinsicFunctions>true</IntrinsicFunctions>
      <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
    </ClCompile>
    <Link>
      <SubSystem>Console</SubSystem>
      <EnableCOMDATFolding>true</EnableCOMDATFolding>
      <OptimizeReferences>true</OptimizeReferences>
    </Link>
  </ItemDefinitionGroup>
  <ItemGroup>
    <ClInclude Include="stdafx.h" />
    <ClInclude Include="targetver.h" />
  </ItemGroup>
  <ItemGroup>
    <ClCompile Include="ExampleClient.cpp" />
    <ClCompile Include="stdafx.cpp">
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
    </ClCompile>
  </ItemGroup>
  <ItemGroup>
    <Manifest Include="ExampleClient.exe.manifest">
      <SubType>Designer</SubType>
    </Manifest>
  </ItemGroup>
  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
  <ImportGroup Label="ExtensionTargets">
  </ImportGroup>
</Project>

Now set the debugger to target x86, set your startup project to ExampleClient, and run the app. 现在,将调试器设置为x86目标,将启动项目设置为ExampleClient,然后运行该应用程序。 You can step through it and you will notice it runs without errors. 您可以逐步执行它,并且会发现它运行无误。 Working as intended. 按预期工作。

Now go in to TestClass.cs and change the class name to something like TestClass2 (doesnt matter what). 现在进入TestClass.cs并将类名更改为类似TestClass2的名称(无关紧要)。 And also go in to the ExampleClient.cpp and change the reference to TestClass to TestClass2. 还要进入ExampleClient.cpp并将对TestClass的引用更改为TestClass2。 Rebuild the solution. 重建解决方案。 When you step through the code you will get an error when trying to instantiate TestClass2. 当您单步执行代码时,尝试实例化TestClass2时将收到错误消息。 To fix it go to the ExampleClient.exe.manifest file and change the processorArchitecture to msil. 要解决此问题,请转到ExampleClient.exe.manifest文件,然后将processorArchitecture更改为msil。 Rebuild. 重建。 Then change processorArchitecture back to x86. 然后将processorArchitecture更改回x86。 Rebuild. 重建。 Now the app will work again. 现在,该应用将再次运行。

You should be able to make changes to the C# library and as long as you change the C++ app to reflect the changes it should work. 您应该能够对C#库进行更改,并且只要更改C ++应用程序即可反映出它应该起作用的更改。 You shouldn't have to toggle processorArchitecture back and forth. 您不必来回切换processorArchitecture。 There must be something getting cached somewhere but I can't figure out where. 必须在某处缓存了一些东西,但我不知道在哪里。

On Windows 10, the OS seems to cache manifest loading. 在Windows 10上,操作系统似乎缓存清单加载。 For an experiment I would try an experiment of "touch"-ing your exe and exe manifest files. 对于实验,我将尝试“触摸”您的exe和exe清单文件的实验。 If you don't have some kind of touch, this command line will work: 如果您没有任何接触,此命令行将起作用:

powershell (ls $1).LastWriteTime = Get-Date

where $1 is the name of the file. 其中$ 1是文件名。 I have a doskey macro defined as 我有一个doskey宏定义为

touch=powershell (ls $1).LastWriteTime = Get-Date

You might try just touching the exe or the manifest. 您可以尝试仅触摸exe或清单。 On Windows 7, I used to be able to log off or restart the computer to clear the manifest cache, but Windows 10 seems to be harder to clear. 在Windows 7上,我曾经能够注销或重新启动计算机以清除清单缓存,但Windows 10似乎更难清除。 So I use touch so that Windows ignores the manifest cache and loads from the new files. 因此,我使用touch来使Windows忽略清单缓存并从新文件加载。

(When I say "manifest cache", that's just a description of my deductions of what I think Windows is doing under the hood) (当我说“清单缓存”时,这只是我对Windows幕后工作的推论的描述)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C#(MSIL)转换为原生X86代码? - C# (MSIL) into native X86 code? 在 C# 中进行按位运算时,除了 x64 或 x86 之外,我还需要考虑 CPU 吗? - When doing bitwise operations in C#, do I ever have to consider the CPU besides x64 or x86? 为什么PCL项目中的DLL是x86程序集? - Why is the DLL from a PCL project an x86 assembly? 为什么在C#项目目录中使用x86文件夹? - Why x86 folder for in c# project directory? 在x86中引用C ++ / CLI DLL的C#项目进行编译时,MSBuild无法生成 - MSBuild fails to build when compiling C# project referencing C++/CLI DLL in x86 VisualStudio C#x64,为什么AddReference选项,.NET选项卡指向x86 DLL而不是x64? - VisualStudio C# x64, why AddReference option, .NET tab points to x86 DLL instead of x64? 在C#中,如何以编程方式了解操作系统是x64还是x86 - In C#, how can I know programmatically if the Operating system is x64 or x86 C# 中的 x86/x64 CPUID - x86/x64 CPUID in C# C# 支持 x64 和 x86 - C# support both x64 and x86 x64 Appdomain中的x86程序集-x86 exe可以工作,x86 dll失败 - x86 Assembly in x64 Appdomain - x86 exe works, x86 dll fails
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM