如何从C/C++调用.NET程序集?

28

假设我在使用C++和C#编写一个应用程序。我想将底层部分用C++编写,高级逻辑部分用C#编写。那么如何从我的C++程序中加载一个.NET程序集,开始调用方法并访问我的C#类的属性呢?

9个回答

13

你应该真正了解一下C++/CLI。它能使像这样的任务变得非常轻松。

否则,你就需要在C#代码周围生成COM包装器,并让你的C++应用程序调用这些COM包装器。


如果你是C#开发者,学习C++/CLI也不算太难。这绝对是正确的选择。++点赞 - Jason Olson
听起来像需要重构 C# 代码?是这样吗? - Cenoc
我相信你可以在需要的地方使用本地代码,然后将其制作成一个类库,以便在任何 .net 应用程序中使用。不过我可能错了。 - Christopher Tarquini

13
[Guid("123565C4-C5FA-4512-A560-1D47F9FDFA20")]
public interface IConfig
{
    [DispId(1)]
    string Destination{ get; }

    [DispId(2)]
    void Unserialize();

    [DispId(3)]
    void Serialize();
}

[ComVisible(true)]
[Guid("12AC8095-BD27-4de8-A30B-991940666927")]
[ClassInterface(ClassInterfaceType.None)]
public sealed class Config : IConfig
{
    public Config()
    {
    }

    public string Destination
    {
        get { return ""; }
    }

    public void Serialize()
    {
    }

    public void Unserialize()
    {
    }
}

接下来,您需要对程序集进行regasm操作。Regasm将添加必要的注册表项,以使您的.NET组件可以被视为COM组件。之后,您可以像调用其他任何COM组件一样在C++中调用您的.NET组件。


1
以下是一份教程,同时也展示了C++部分:如何从本地Visual C++代码中调用托管DLL在Visual Studio.NET或Visual Studio 2005中 - Oleg Grishko
我不会使用COM来做那件事,这很笨拙且不方便。我会在C++代码中导出一个函数,在.NET中使用GetProcAddress()来查找此入口点并简单地调用它。你甚至可以使用委托从C++代码实现回调到.NET。(https://code.msdn.microsoft.com/windowsapps/Enumerate-top-level-9aa9d7c1) - Elmue
1
@Elmue 从C++代码中导出函数是为了在C#中调用C++代码,但问题是如何从C++中调用C#代码。如果C# DLL甚至还没有加载,回调也无济于事。 - BlueMonkMN
在我上面发布的链接中,一个本地的C代码(在Windows API中)通过委托回调到一个C#函数。EnumWindows()是一个C Api,它需要一个本地的C回调。这个例子展示了一个技巧,如何让这个C函数调用C#代码。DLL是否被加载或者没有被加载,是另一个可以在其他地方解决的话题。 - Elmue

4
我一定会为此调查C++/CLI,并避免使用COM和所有可能产生的注册问题。
使用C++的动机是什么?如果只是为了风格,那么您可能会发现可以用C++/CLI编写所有内容。如果是性能,那么在托管C++和非托管代码之间来回调用相对简单。但这永远不会是透明的。您不能首先将托管指针传递给非托管代码,而不固定它,以便垃圾收集器不会移动它,当然非托管代码也不会知道您的托管类型。但托管(C ++)代码可以知道您的非托管类型。
还有一件事需要注意的是,包括非托管代码的C++/CLI程序集将是架构特定的。您需要分别为x86和x64 (和IA64)构建不同的版本。

1
如果您的进程中既有托管代码又有非托管代码,您可以创建一个带有虚函数的 C++ 类。使用混合模式 C++/CLI 实现该类。将实现注入到您的 C++ 代码中,以便从您的低级 C++ 代码调用(高级)实现。

如果这个答案附带一些源代码或示例,那就更好了。 - DARKGuy

0

您可以将.NET组件包装在COM组件中 - 使用.NET工具非常容易 - 然后通过COM调用它。


0
如果低级部分是用C++编写的,通常你需要从C#代码中调用它,并传入所需的值。这应该按照你可能习惯的标准方式工作。你需要阅读有关封送(marshalling)的相关资料。
你可以查看博客以获取一些具体细节。

0

按照正常方式创建您的.NET程序集,但一定要使用ClassInterface(ClassInterfaceType.AutoDual)标记类,并确保使用SetAssemblyAtribute将程序集信息设置为ComVisible(true)。

然后,使用REGASM创建COM包装器:

regasm mydll.dll /tlb:mydll.tbl /codebase f:_code\ClassLibraryForCom

请务必使用/codebase指令--如果您不打算给程序集命名,则这是必需的。

rp


Mason Bendixen警告不要使用ClassInterfaceType.AutoDual。http://blogs.msdn.com/mbend/archive/2007/04/17/classinterfacetype-none-is-my-recommended-option-over-autodispatch-autodual.aspx - cwick

0

由于C#可以导入C++标准导出,因此在C#应用程序中加载C++ dll可能比从C++使用COM更容易。

请参阅System.Runtime.InteropServices.DllImport的文档。

此外,以下是您可以在托管代码和非托管代码之间执行的Interop类型的完整列表:

http://blogs.msdn.com/deeptanshuv/archive/2005/06/26/432870.aspx

简而言之:
(a) 使用COM-Interop (b) 使用导入/引用(显式方法调用)
(c) IJW和MC++应用程序:MC++和IJW应用程序可以自由地相互调用。
(d) 托管。这很少见,但CLR可以被非托管应用程序托管,这意味着运行时会调用一堆托管回调。

0
我找到了一个关于嵌入Mono的链接: http://www.mono-project.com/Embedding_Mono 它提供了一个看起来非常直观的接口,可以与程序集进行交互。这可能是一个很有吸引力的选择,特别是如果你想要跨平台。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接