事实上这一直是个非常无解的问题。最好的办法是将C++类库写成COM。

可是有时候往往不能这个做。那就仅仅有两种办法:转成C函数形式或者Manage C++封装。

下文就介绍了这两种方法。

原帖:http://www.codeproject.com/KB/cs/marshalCPPclass.aspx

Introduction

I recently needed to marshal some legacy C++ classes into a C# project on which I was working. Microsoft provides well documented means to marshal C-functions, and to marshal COM components, but they left out a mechanism to marshal C++ classes. This article documents the discoveries I made and the eventual solution I came up with.

Audience

This article assumes the reader is knowledgeable in C# and .NET and is already familiar with PInvoke and marshaling.

Article

I had existing (unmanaged) C++ DLLs which needed to be used with a managed C# project I was working on. Although I had access to the source code of the DLLs, one of the requirements was that the C++ source code and DLLs could not be dramatically altered. This was due to many reasons, including backwards compatibility with existing projects, the code having already been QA'ed, and project deadlines, so converting the original DLLs to be managed C++ DLLs, or converting the classes within the original DLLs to be COM components was out.

Upon first investigating this issue, I was hoping to be able to declare a definition for a C# version of the class and marshal the object back and forth between the managed and unmanaged memory spaces, similar to how a structure is marshaled back and forth. Unfortunately, this is not possible; in my research I discovered that unmanaged C++ classes can't be marshaled and that the best approach is to either create bridge/wrapper C-functions for the public methods of the class and marshal the functions, or to create a bridge DLL in managed C++.

Solution A: Create Bridge Functions in C and Use PInvoke

Suppose we have the following unmanaged C++ class:

Collapse

class EXAMPLEUNMANAGEDDLL_API CUnmanagedTestClass
{
public:
CUnmanagedTestClass();
virtual ~CUnmanagedTestClass();
void PassInt(int nValue);
void PassString(char* pchValue);
char* ReturnString();
};

Running dumpbin on the DLL containing the class yields the following results:

(Click for larger view. We'll cover the results from dumpbin in a moment).

Since the instantiation of a C++ class object is just a pointer, we can use C#'s IntPtr data type to pass unmanaged C++ objects back and forth, but C-functions need to be added to the unmanaged DLL in order to create and dispose instantiations of the class:

Collapse

// C++:

extern "C" EXAMPLEUNMANAGEDDLL_API CUnmanagedTestClass* CreateTestClass()
{
return new CUnmanagedTestClass();
} extern "C" EXAMPLEUNMANAGEDDLL_API void DisposeTestClass(
CUnmanagedTestClass* pObject)
{
if(pObject != NULL)
{
delete pObject;
pObject = NULL;
}
}

Collapse

// C#:

[DllImport("ExampleUnmanagedDLL.dll")]
static public extern IntPtr CreateTestClass(); [DllImport("ExampleUnmanagedDLL.dll")]
static public extern void DisposeTestClass(IntPtr pTestClassObject); IntPtr pTestClass = CreateTestClass();
DisposeTestClass(pTestClass);
pTestClass = IntPtr.Zero;
// Always NULL out deleted objects in order to prevent a dirty pointer

This allows us to pass the object back and forth, but how do we call the methods of our class? There are two approaches to accessing the methods. The first approach is to use PInvoke and to use CallingConvention.ThisCall. If you go back to the output from dumpbin, you will see the mangled name for the PassInt() method is "?PassInt@CUnmanagedTestClass@@QAEXH@Z". Using CallingConvention.ThisCall, the PInvoke definition of PassInt() is:

Collapse

[DllImport("ExampleUnmanagedDLL.dll",
EntryPoint="?PassInt@CUnmanagedTestClass@@QAEXH@Z",
CallingConvention=CallingConvention.ThisCall)]
static public extern void PassInt(IntPtr pClassObject, int nValue);

The second approach is to create C-functions which act as a bridge for each public method within the DLL...

Collapse

// C++:

extern "C" EXAMPLEUNMANAGEDDLL_API void CallPassInt(
CUnmanagedTestClass* pObject, int nValue)
{
if(pObject != NULL)
{
pObject->PassInt(nValue);
}
}
.
.
.

...and marshal each of new C-functions in C#...

Collapse

// C#:

[DllImport("ExampleUnmanagedDLL.dll")]
static public extern void CallPassInt(IntPtr pTestClassObject, int nValue);
.
.
.

I chose to go with the second approach; the name mangling the compiler does means that the first approach is susceptible to breaking if a different compiler is used to compile the C++ DLL (newer version, different vendor, etc...), or if additional methods are added to the class. There is a little extra work involved with the second approach, but I feel the extra work is rewarded by having better maintainable code and code which is less likely to break in the future.

At this point I should point out that I added the bridge functions to the original DLL and recompiled the DLL, but what if the DLL in question is a third party DLL and you don't have access to the sources so you can't recompile the DLL (you only have the rights to redistribute it)? In this scenario I suggest either:

  1. Creating a new DLL in unmanaged C and place the bridge functions within the new DLL.
  2. Create a managed C++ DLL and have it act as the bridge between the C# code and the unmanaged C++ classes (see Solution B further on).

At this point, the C# code to call our C++ class looks like:

Collapse

// C#:

IntPtr pTestClass = CreateTestClass();
CallPassInt(pTestClass, 42);
DisposeTestClass(pTestClass);
pTestClass = IntPtr.Zero;

This is fine as it is, but this isn't very Object-Oriented. Suppose you aren't the only one working on the project? Will other clients of your code remember to dispose the C++ class object via DisposeTestClass()? Will they correctly use an IntPtr created from CreatetestClassDLL()and not some other IntPtr? The next step is to wrap our C# code and PInvoke definitions into a class.

During my investigation, I came across the following newsgroup posting...

http://groups.google.com/group/microsoft.public.dotnet.framework.interop/ browse_thread/thread/d4022eb907736cdd/0e74fa0d34947251?lnk=gst&q= C%2B%2B+class&rnum=6&hl=en#0e74fa0d34947251

...and I decided to mirror this approach and create a class in C# called CSUnmanagedTestClass:

Collapse

// C#:

public class CSUnmanagedTestClass : IDisposable
{
#region PInvokes
[DllImport("TestClassDLL.dll")]
static private extern IntPtr CreateTestClass(); [DllImport("TestClassDLL.dll")]
static private extern void DisposeTestClass(IntPtr pTestClassObject); [DllImport("TestClassDLL.dll")]
static private extern void CallPassInt(IntPtr pTestClassObject, int nValue);
.
.
.
#endregion PInvokes #region Members
private IntPtr m_pNativeObject;
// Variable to hold the C++ class's this pointer #endregion Members public CSUnmanagedTestClass()
{
// We have to Create an instance of this class through an exported // function this.m_pNativeObject = CreateTestClass();
} public void Dispose()
{
Dispose(true);
} protected virtual void Dispose(bool bDisposing)
{
if(this.m_pNativeObject != IntPtr.Zero)
{
// Call the DLL Export to dispose this class DisposeTestClass(this.m_pNativeObject);
this.m_pNativeObject = IntPtr.Zero;
} if(bDisposing)
{
// No need to call the finalizer since we've now cleaned // up the unmanaged memory GC.SuppressFinalize(this);
}
} // This finalizer is called when Garbage collection occurs, but only if // the IDisposable.Dispose method wasn't already called. ~CSUnmanagedTestClass()
{
Dispose(false);
} #region Wrapper methods
public void PassInt(int nValue)
{
CallPassInt(this.m_pNativeObject, nValue);
}
.
.
.
#endregion Wrapper methods
}

Now, the C# client of this code simply does:

Collapse

// C#:

CSUnmanagedTestClass testClass = new CSUnmanagedTestClass();
testClass.PassInt(42);
testClass.Dispose();

Solution B: Create a Bridge DLL in Managed C++

Another option is to leave the original DLL untouched and create a new DLL in managed C++ to act as a bridge between the managed C# code and the unmanaged C++ classes in the unmanaged DLL. Using the CUnmanagedTestClass within the managed DLL wasn't difficult, and PInvoke definitions weren't required, but the managed C++ syntax and classes which needed to be used was a bit vexing:

Collapse

// MCPP:

// Forward declariation

class CUnmanagedTestClass;

public ref class CExampleMCppBridge
{
public:
CExampleMCppBridge();
virtual ~CExampleMCppBridge();
void PassInt(int nValue);
void PassString(String^ strValue);
String^ ReturnString(); private:
CUnmanagedTestClass* m_pUnmanagedTestClass;
}; CExampleMCppBridge::CExampleMCppBridge()
: m_pUnmanagedTestClass(NULL)
{
this->m_pUnmanagedTestClass = new CUnmanagedTestClass();
} CExampleMCppBridge::~CExampleMCppBridge()
{
delete this->m_pUnmanagedTestClass;
this->m_pUnmanagedTestClass = NULL;
} void CExampleMCppBridge::PassInt(int nValue)
{
this->m_pUnmanagedTestClass->PassInt(nValue);
}
.
.
.

Collapse

// C#:

CExampleMCppBridge example = new CExampleMCppBridge();
example.PassInt(42);
example.Dispose();

(and I have to admit, I'm not very fluent in MCPP)

Pros/Cons

Both approach A and approach B have their own pros and cons. Are you unfamiliar with MCPP? Go with approach A and create C-functions to wrap the public methods of the class and use PInvoke. Can't modify the original DLL and don't want to create PInvode definitions? Create bridge classes in a new MCPP DLL as demonstrated in approach B.

Conclusion

In this article I have presented the reader with a number of different approaches and solutions to the problem of marshaling an unmanaged C++ class to C#. For the sake of brevity I have only included the CallPassInt() examples in this article, however the...

Collapse

CallPassString()
CallReturnString()

...are in the source code accompanying this article.

.net 调用C++类库的更多相关文章

  1. java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊

    java 调用 C# 类库搞定,三步即可,可以调用任何类及方法,很简单,非常爽啊 java 调用 C# 类库搞定,可以调用任何类及方法,很简单,非常爽啊 总体分三步走: 一.准备一个 C# 类库 (d ...

  2. Asp.Net MVC ajax调用 .net 类库问题

    如果你还在为 ajax 调用 .net 类库还束手无策的话,相信这篇博客将帮助你解决这个世纪问题! 因为Visual Studio 内置了asp.net mvc ,不过当你添加asp.net mvc项 ...

  3. 利用C#的反射机制动态调用DLL类库

    最近由于业务要求,需要动态调用DLL类库,所以研究了一下,感觉还好也不太难,今天就把自己理解的写了一个小例子(已经通过VS2005跑通),供大家一起研究和探讨,有理解不当的地方还请高手们多多指正,谢谢 ...

  4. 在SQL Server中使用CLR调用.NET类库中的方法 (转载)

    在SQL Server中调用 .NET 类库的方法要分为下面几步来实现: 在.NET中新建一个类库项目,并在这个项目中添加一个类文件,并把要被SQL Server调用的方法定义为公有的,静态的方法. ...

  5. asp.net调用opencv类库,实现图像处理显示

    asp.net调用opencv类库,实现图像处理显示     ​      原理上来说,通过dll的调用,无论是asp.net还是winform都可以调用opencv及其类库.但是在实现的过程还是有许 ...

  6. java 调用 C# 类库 实战视频, 非常简单, 通过 云寻觅 javacallcsharp 生成器 一步即可!

    java 调用 C# 类库 实战视频, 非常简单, 通过 云寻觅 javacallcsharp 生成器 一步即可! 通过 云寻觅 javacallcsharp 生成器 自动生成java jni类库,  ...

  7. php通过JavaBridge调用Java类库和不带包的自定义java类成功 但是调用带包的自定义Java类报错,该怎么解决

    php通过JavaBridge调用Java类库和不带包的自定义java类成功 但是调用带包的自定义Java类报错,Class.forName("com.mysql.jdbc.Driver&q ...

  8. C#调用C++类库的几种方式

    1.  直接调用C++类库中的公共方法 使用DllImport特性对方法进行调用,比如一个C++类库SampleCppWrapper.dll中的公共方法: extern "C" _ ...

  9. C++调用C#类库函数

    最近做一个信息化三维仿真项目,基于第三方提供的虚拟引擎通过VC++2008做二次开发,其中涉及到与C#客户端的融合以及数据交互的问题, 主要是VC++需要调用C#客户端提供的类库内的接口获取C#客户端 ...

  10. 学习使用Delphi for android 调用Java类库

    http://blog.csdn.net/laorenshen/article/details/41148253 学习使用Delphi for android 调用Java类库 2014-11-15 ...

随机推荐

  1. HighChart 实现从后台取数据来实时更新柱状和折线组图

    前段时间公司让弄图表,给我说有HighCharts这个js插件,于是上网上搜,由于本人是写后端的,对于JavaScript和jQuery不是很熟悉,虽然找到了模板,但是还是不明白,所以一点一点的改,但 ...

  2. PHP 关于回调的用法

    class aClass { public static function directWrite($message) { echo 'this is a static function from a ...

  3. leetcode刷题总结一

    大四狗找工作,要刷题了,leetcode上面题目比较适合面试算法类题目,也不纯粹为了蒙题,锻炼一下面试类型的思维 Single Number: 有N个数,其中只有一个数出现了一次,其他都是两次,找出那 ...

  4. 150个JS特效脚本

    收集了其它一些不太方便归类的JS特效,共150个,供君查阅. 1. simplyScroll simplyScroll这个jQuery插件能够让任意一组元素产生滚动动画效果,可以是自动.手动滚动,水平 ...

  5. Linux-head,tail用法

    linux ---tail命令 linux中tail命令---用于查看文件内容 最基本的是cat.more和less. 1. 如果你只想看文件的前5行,可以使用head命令,如: head -5 /e ...

  6. Hadoop 1.1.2 Eclipse 插件使用——异常解决

    permission denied user 1.修改配置文件在conf/hdfs-site.xml文件中添加如下内容: <property> <name>dfs.permis ...

  7. 第三百二十八天 how can I 坚持

    今天电脑快把我搞疯了,一天得死机快十次,不知道怎么回事,最后升级了win10,感觉就是比较好. 哎,成了这个样子,当初为什么又让我抽中了那个签,搞不懂啊,这都是为啥. 我哪里错了,还是冥冥中自有天意, ...

  8. Umbraco部署到IIS中权限问题(back office没有权限新建template)

    在开发项目中,发现把基于Umbraco平台开发的网站部署到服务器的IIS之后,访问该网站的back office 在back office中增加一个template时,发送错误,提示 Access t ...

  9. Light oj 1005 - Rooks (找规律)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1005 纸上画一下,找了一下规律,Ank*Cnk. //#pragma comm ...

  10. POJ3321 Apple Tree (树状数组)

    Apple Tree Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 16180   Accepted: 4836 Descr ...