我如何从C#调用C ++ / CLI?
我有一个用C ++实现的类,负责程序的算术计算,以及使用WPF的接口。 我用C#处理input,但是如何使用我的C ++类?
我已经看到了一些关于使一个托pipe的C ++包装类与它交互的意见,但我不知道从哪里开始。 我也不知道如何将其与所有其他代码一起编译。 我无法真正find关于这个的教程,并且在托pipeC ++上显示的东西并不是真的有帮助。
有什么可以帮助我的吗? 这对我来说似乎不合理。
编辑尝试m3rLinEz解决scheme,但它给我一个BadImageFormatException,我想这是因为该DLL不会生成。 我做了所有的事情,不知道发生了什么事。 有任何想法吗?
你看看C ++ / CLI吗?
让我举个简短的例子。 这里是来自Visual C ++ – > CLR – >类库项目的源文件。 它基本上获得Windows用户名并将其返回。
请注意,为了获得这个编译,你必须进入项目设置,并标记“附加依赖”为“inheritance父母”,因为我们正在使用这些Windows库(kernel32.lib,user32.lib,..)
// CSCPP.h #pragma once #include "windows.h" using namespace System; namespace CSCPP { public ref class Class1 { // TODO: Add your methods for this class here. public: String^ GetText(){ WCHAR acUserName[100]; DWORD nUserName = sizeof(acUserName); if (GetUserName(acUserName, &nUserName)) { String^ name = gcnew String(acUserName); return String::Format("Hello {0} !", name); }else{ return gcnew String("Error!"); } } }; }
现在创build一个新的C#项目并添加对我们第一个C ++ / CLI类库项目的引用。 然后调用实例方法。
namespace CSTester { class Program { static void Main(string[] args) { CSCPP.Class1 instance = new CSCPP.Class1(); Console.WriteLine(instance.GetText()); } } }
这在我的机器上得到了以下结果:
你好m3rlinez!
C ++ / CLI基本上是C ++标准的托pipe扩展。 它允许您在C ++ / CLI项目中使用CLR类和数据types,并将其公开给托pipe语言。 你可以使用这个为你的旧C ++库创build一个托pipe包装器。 有一些奇怪的语法,例如String^
来定义CLRstring的引用types。 我发现“快速C ++ / CLI – 在不到10分钟的时间内学习C ++ / CLI”在这里很有用。
至less有三种方法可以在同一个进程中调用托pipe的非托pipe代码:
- C ++ / CLI
- 平台调用
- 把你的C ++包装在一个COM对象中
在工作中,我们使用C ++ / CLI来实现这一点,它似乎工作。
我将创build一个标准(非COM /托pipe)dynamic链接库, 如上所述 ,然后使用C#代码中的DllImport属性 (平台调用)来访问导出的函数。
这篇文章的重点:
请注意此代码中方法声明中的__declspec(dllexport)修饰符。 这些修饰符使该方法可以由DLL导出,以便其他应用程序可以使用它们。 有关更多信息,请参阅dllexport,dllimport。
这是一个实际的COM互操作包装器的轻量级替代方法,避免了诸如注册等问题(该DLL可以简单地放置在应用程序目录中)。
另一种select是它只是工作 (IJW)。 如果您已经pipe理C ++代码并需要从其他.NET语言访问此代码,则这可能是更好的select。 但是这只是一个select,如果你能/很高兴将非托pipeC ++转换为托pipeC ++。
我会远离P / Invoke,因为它比IJW(It Just Works)相当慢。 后者允许你无缝地交织托pipe的和非托pipe的c ++。 你所要做的就是创build一个托pipe的c ++程序集,编写一个从c#可见的托pipe类,然后调用非托pipe代码。
呃…好的。 我的印象是,P / Invoke调用比较慢,而且他们不是。 但是,通过对编组的显式控制,可以使您的C ++ / CLI版本在许多情况下执行得更好。
这里是微软有关这两种机制的文章:
http://msdn.microsoft.com/en-us/library/ms235282.aspx
IJW的优势
- 没有必要为程序使用的非托pipeAPI编写DLLImport属性声明。 只需包含头文件并链接到导入库。
- IJW机制稍微快一些(例如,IJW存根不需要检查是否需要固定或复制数据项,因为这是由开发人员明确的)。
- 它清楚地说明了性能问题。 在这种情况下,您将从Unicodestring转换为ANSIstring,并且您有相应的内存分配和释放。 在这种情况下,使用IJW编写代码的开发人员会意识到,调用_putws和使用PtrToStringChars会更好地提高性能。
- 如果使用相同的数据调用许多非托pipeAPI,封送一次并传递封送后的副本比每次重新编组效率高得多。
还有美学的优点:
- C#代码看起来像C#代码,没有任何interop'y怪异。
-
您不必定义
DLLImport
属性,您不必定义任何数据结构(也可以使用p / invoke特定属性),如下所示:[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Ansi)] public struct DevMode {[MarshalAs(UnmanagedType.ByValTStr,SizeConst = 32)] public string dmDeviceName; }
- 您不必将所有参数原始types转换为它们的.NET对应物( 本页面上的表列出了托pipetypes映射到非托pipetypes的方式)。
- 你可以使用C ++ / CLI,这是非常有趣的学习和真正的抛光。 自从2003年以来,这已经有了很长的路要走,现在已经是一个全function的.NET语言了。 微软的文档是相当不错的,所有的IJW信息。
- 在C ++ / CLI中做C ++互操作感觉非常自然,而不是C#。 这是完全主观的,但我宁愿在C ++中做
Marshal.PtrToString(ptr)
string编组。 - 如果暴露一个API,你可能想把所有P / Invoke的东西包装到另外一个层次,所以你不必处理P / Invoke的丑陋。 这样你就可以在所有的编组和C#层的开销。 使用C ++ / CLI编组和互操作抽象是在一个地方,你可以select你需要多less编组。
恕我直言,如果您在Windows SDK中调用一个奇怪的函数,请使用P / Invoke。 如果您将一个中等复杂的C ++ API公开给托pipe的世界,绝对是C ++ / CLI。