C#想方设法调用C++类成员函数!

问题描述

.net互操作一直没有接触过,最近有一个C#程序必须要使用C++DLL类成员函数.....头都木了,各种狂查,基本对Pinvoke有一些了解。但第一次看到PInvoke声明DLL函数的写法“staticextern”时,就有疑问:这个static,难道意味着C++DLL函数都必须是全局的?想调用C++public类成员函数是行不通的?于是写了个测试程序,发现PInvoke貌似调不了C++类成员函数..........但由于本人没有此方面任何经验,无法做结论,所以请教各位:PInvoke技术是不是不可以用于调用C++类成员函数?如何确定不行的话,要使用什么办法才可以呢?网上查到说可以用托管C++封装一层?如何封装呢?我查询“使用托管C++封装非托管资源”,基本没什么好的文章啊。如果各位有什么好文章,讲用托管C++封装的....也可以推荐一下,我去拜读。谢谢。

解决方案

解决方案二:

解决方案三:
理论上说PInvoke也可以访问成员函数,因为成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。但是因为C#操作C++对象比较麻烦,这样不推荐,你可以用C++/CLI(也就是C++.NET)作为代理去访问原生C++类,再包装出来给C#用。或者用原生C++编写dll,将成员函数转换为一般的函数。
解决方案四:
该回复于2014-07-30 00:19:45被版主删除
解决方案五:
托管C++就是C++,因此调用unmanagedC++类库没有问题。然后在暴露managed接口,这样C#就可以调用了。用C做中间层一样的道理,C调用C++库,在暴露纯C的接口,给platform用。
解决方案六:
NativeC++classCNative{public:intNativeFunction(inta){}}

C++/CLIpublicrefclassCNativeWrapper{private:CNative*m_pNative;public:CNativeWrapper(){m_pNative=newCNative;}intManagedMethod(inta){returnm_pNative->NativeFunction(a);}~CNativeWrapper(){deletem_pNative;}protected:!CNativeWrapper(){deletem_pNative;}}

C#classProgram{intMain(){varnativeWrapper=newCNativeWrapper();nativeWrapper.ManagedMethod();return0;}}

解决方案七:
感谢楼上各位的帮助!现在我去试试用托管c++封装,另外2楼说:“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。”我也去试试!
解决方案八:
刚才去试了下2楼的想法,2楼所说“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上”,这事儿办不到....声明一个指向c++的某个类的指针,这怎么可能在C#中做到呢?C++中的struct倒可以在C#中重写个struct,再作为参数声明。C++中的class不可能在C#中重写啊........另外,刚才我建了个C++DLL工程,导出了一个全局函数。,在C#中用PInvoke调用,怎么会提示:“未将引用设置到对象的实例”?我这个C++全局函数不需要实例化任何对象啊,怎么会出这样的错呢?网上某些人说C++导出函数必须声明为extern"c",我认为这是错误的,externc只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。所以各位使用过pinvoke的,C++导出函数并没有必要写externc,对吧?那么我这个异常究竟是为何引起的呢......
解决方案九:
“未将引用设置到对象的实例”?问题找到了.....我dllimport时DLL名字写错了.......!另外关于2楼,我发现只要我的成员函数定义中,没有操作数据成员的话(例如给某成员赋值),就可以在C#中被成功调用。也就是C++中那个隐含的this参数,自动被C#去掉了....C#这么智能啊。当然,如果此类成员函数中有对成员变量赋值的操作,那么自然会出错,因为C#中根本没有实例化过此C++类的实例。那么看来对于这种类成员函数,只能用托管C++封装了,PInvoke无能为力。剩下的问题就是:①:2楼所说“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上”,这事儿办不到....声明一个指向c++的某个类的指针,这怎么可能在C#中做到呢?C++中的struct倒可以在C#中重写个struct,再作为参数声明。C++中的class不可能在C#中重写啊........②:网上某些人说C++导出函数必须声明为extern"c",我认为这是错误的,externc只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。对吧?
解决方案十:
给你一个例程。classCFORCS_APICCForCS{public:CCForCS(void);CCForCS(intnValue);~CCForCS(void);public:voidPrint(LPCWSTRpszMessage);intGetValue(void);protected:virtualintVFunctionBefore(LPCWSTRpszMessage,int*pnValue);virtualvoidVFunctionAfter(LPCWSTRpszMessage,intnValue);private:int_nValue;};

解决方案十一:
classCVTable:IDisposable{IntPtr_vtable;publicCVTable(paramsDelegate[]methods){_vtable=Marshal.AllocHGlobal(methods.Length*Marshal.SizeOf(typeof(IntPtr)));for(inti=0;i<methods.Length;++i){Marshal.WriteIntPtr(_vtable,i*Marshal.SizeOf(typeof(IntPtr)),Marshal.GetFunctionPointerForDelegate(methods[i]));}}publicvoidDispose(){Dispose(true);GC.SuppressFinalize(this);}privatebooldisposed=false;protectedvirtualvoidDispose(booldisposing){if(!this.disposed){if(_vtable!=IntPtr.Zero){Marshal.FreeHGlobal(_vtable);_vtable=IntPtr.Zero;}disposed=true;}}~CVTable(){Dispose(false);}publicIntPtrCreateVTable(IntPtr@this){IntPtrold=Marshal.ReadIntPtr(@this,0*Marshal.SizeOf(typeof(IntPtr)));Marshal.WriteIntPtr(@this,0*Marshal.SizeOf(typeof(IntPtr)),_vtable);returnold;}publicvoidReleaseVTable(IntPtr@this,IntPtrvtable){Marshal.WriteIntPtr(@this,0*Marshal.SizeOf(typeof(IntPtr)),vtable);}}partialclassCSForC:IDisposable{publicvoidDispose(){Dispose(true);GC.SuppressFinalize(this);}privatebooldisposed=false;protectedvirtualvoidDispose(booldisposing){if(!this.disposed){_Dispose();disposed=true;}}~CSForC(){Dispose(false);}}[StructLayout(LayoutKind.Sequential)]publicstruct_CForCS{publicIntPtr_vtable;publicint_nValue;}partialclassCSForC{privateIntPtr_basecpp;privateconststringdllname="..\..\..\Debug\CForCS.dll";[DllImport(dllname,EntryPoint="??0CCForCS@@QAE@XZ",CallingConvention=CallingConvention.ThisCall)]privatestaticexternvoid_Constructor([In]IntPtr@this);[DllImport(dllname,EntryPoint="??0CCForCS@@QAE@H@Z",CallingConvention=CallingConvention.ThisCall)]privatestaticexternvoid_Constructor([In]IntPtr@this,[In,MarshalAs(UnmanagedType.I4)]intvalue);[DllImport(dllname,EntryPoint="??1CCForCS@@QAE@XZ",CallingConvention=CallingConvention.ThisCall)]privatestaticexternvoid_Destructor([In]IntPtr@this);[DllImport(dllname,EntryPoint="?Print@CCForCS@@QAEXPB_W@Z",CallingConvention=CallingConvention.ThisCall)]privatestaticexternvoid_Print([In]IntPtr@this,[In,MarshalAs(UnmanagedType.LPWStr)]stringmessage);[DllImport(dllname,EntryPoint="?GetValue@CCForCS@@QAEHXZ",CallingConvention=CallingConvention.ThisCall)][return:MarshalAs(UnmanagedType.I4)]privatestaticexternint_GetValue([In]IntPtr@this);publicCSForC(){_basecpp=Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));_Constructor(_basecpp);CreateVtable();}publicCSForC(intvalue){_basecpp=Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));_Constructor(_basecpp,value);CreateVtable();}privatevoid_Dispose(){if(_basecpp!=IntPtr.Zero){ReleaseVtable();_Destructor(_basecpp);Marshal.FreeCoTaskMem(_basecpp);_basecpp=IntPtr.Zero;}}publicvoidPrint(stringmessage){_Print(_basecpp,message);}publicintGetValue(){return_GetValue(_basecpp);}IntPtr_basecppvtable;CVTablevtable;privatevoidCreateVtable(){vtable=newCVTable(new_VFunctionBeforeHandler(VFunctionBefore),new_VFunctionAfterHandler(VFunctionAfter));_basecppvtable=vtable.CreateVTable(_basecpp);}privatevoidReleaseVtable(){if(_basecppvtable!=IntPtr.Zero){vtable.ReleaseVTable(_basecpp,_basecppvtable);((IDisposable)vtable).Dispose();}}[DllImport(dllname,EntryPoint="?VFunctionBefore@CCForCS@@MAEHPB_WPAH@Z",CallingConvention=CallingConvention.ThisCall)][return:MarshalAs(UnmanagedType.I4)]privatestaticexternint_VFunctionBefore([In]IntPtr@this,[In,MarshalAs(UnmanagedType.LPWStr)]stringmessage,[Out,MarshalAs(UnmanagedType.I4)]outintvalue);[DllImport(dllname,EntryPoint="?VFunctionAfter@CCForCS@@MAEXPB_WH@Z",CallingConvention=CallingConvention.ThisCall)]privatestaticexternvoid_VFunctionAfter([In]IntPtr@this,[In,MarshalAs(UnmanagedType.LPWStr)]stringmessage,[In,MarshalAs(UnmanagedType.I4)]intvalue);privatedelegateint_VFunctionBeforeHandler([In,MarshalAs(UnmanagedType.LPWStr)]stringmessage,[Out,MarshalAs(UnmanagedType.I4)]outintvalue);privatedelegatevoid_VFunctionAfterHandler([In,MarshalAs(UnmanagedType.LPWStr)]stringmessage,[In,MarshalAs(UnmanagedType.I4)]intvalue);protectedvirtualintVFunctionBefore(stringmessage,outintvalue){intn=_VFunctionBefore(_basecpp,message,outvalue);value=100;returnn;}protectedvirtualvoidVFunctionAfter(stringmessage,intvalue){System.Diagnostics.Debug.WriteLine(string.Format("{0},{1}.",message,value));}}

classProgram{staticvoidMain(string[]args){varv=newCSForC(123456789);v.Print("CSMessage");System.Diagnostics.Debug.WriteLine(string.Format("GetValue()={0}",v.GetValue()));((IDisposable)v).Dispose();}}

解决方案十二:
如果没有虚函数,那么把相关的vtable去掉。
解决方案十三:
谁能解答8楼的问题?
解决方案十四:
想联系,博主右右yy,遇到了相同的问题。qq==1010112108
解决方案十五:
大神求指导,务必联系我!

时间: 2024-09-17 03:12:23

C#想方设法调用C++类成员函数!的相关文章

c++,关于类成员函数作为线程的入口函数

问题描述 c++,关于类成员函数作为线程的入口函数 class Map {public: Bird *pB; Pig *pP; ..........}class Grav {public: Map *pM; ...... void runBird(Bird &b); void runPig(Pig &p); void run(Map &m);}void Grav::run(Map &m) { thread t[2]; t[0] = thread(&Grav::run

直接调用类成员函数地址

一.成员函数指针的用法 在C++中,成员函数的指针是个比较特殊的东西.对普通的函数指针来说,可以视为一个地址,在需要的时候可以任意转换并直接调用.但对成员函数来说,常规类型转换是通不过编译的,调用的时候也必须采用特殊的语法.C++专门为成员指针准备了三个运算符: "::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数.比如: class tt { public: void foo(int x){ printf("\

【C/C++学院】(8)全局函数和类成员函数转化/友元/操作符重载

1.全局函数和类成员函数转化     全局函数和成员函数的相互转化:只需要修改一个指向本类的this指针: #include <iostream> using namespace std; class Test { public: Test(int a, int b) { this->a = a; this->b = b; } //成员函数 Test &Gadd2(Test &t2) { this->a = this->a + t2.a; this-&g

c++-【析构函数写法】如何使用析构函数释放类成员函数申请的堆内存

问题描述 [析构函数写法]如何使用析构函数释放类成员函数申请的堆内存 标题:[析构函数写法]如何使用析构函数释放类成员函数申请的堆内存 环境:win7 64位/AMD CPU/C++ GCC4.7.2/Codeblocks 详细描述:如下所示"代码块1",每次在调用encrypt()函数时均会申请一次内存,如何在析构函数中一次性销毁所有产生的内存,"代码块2"的方法不符合要求 扩展:如果上述问题无较好答案,是否有其他方法可以避免在类函数中使用new,但也能达到目的

c#-调用别的类的函数的问题

问题描述 调用别的类的函数的问题 private void menuSave_Click(object sender, EventArgs e) { if(_album.FileName == null) { menuSaveAs_Click(sender, e); } else { _album.Save(); _bAlbumChanged = false; } } 这里的_album是一个PhotoAlbum类的变量,PhotoAlbum里面定义了Save函数,但是编译的时候提示PhotoA

编程-为什么派生类的指针指向基类对象 还可以调用派生类的函数?

问题描述 为什么派生类的指针指向基类对象 还可以调用派生类的函数? 想不明白啊,高手帮帮忙吧 Derived1 * hh = static_cast(new Base); hh->derivedfunction(); // 这里竟然是可以的 为什么呢 hh指向的是一个基类对象啊?? 而且即使derivedfunction里面用到派生类的私有成员也是可以的, 这到底是为什么啊??? 解决方案 虽然语法上让你通过了,static_cast是强制类型转换,所以语法没办法阻止你错误的做法,但是实际运行结

类成员函数指针区别于用法

通常的函数指针大家已经非常熟悉了.但我们今天讨论一下类成员函数指针的用法. 今天我们来看一下成员函数指针,加入我们想要声明一个 void CTest::Show()成员函数指针类型,那么我们一般的做法是: typedef void(CTest::*pShow)(); 从上面可以看出一些和一般函数指针类型不同的地方.让我们把他和一般的函数声明比较一下.下面是一般的函数声明: typedef void(*pShow)(); 我们可以看出成员函数指针和一般函数指针的不同,那就是成员函数指针声明时加上类

C++ 如何获取类成员函数地址?

C语言中可以用函数地址直接调用函数:     void print ()       {          printf ("function print");       }       typdef void (*fun)();       fun f = print;       f(); C++中类非静态成员函数必须通过实例去调用,C++中类成员函数调用:     class test       {       public:       void print ()     

把C++类成员函数集成到lua

       有时我们会把C++类导入到lua,这样方便在lua里面对C++类进行操作,这在游戏编程里面经常使用,本文只是简单介绍一种实现.        1.       lua里面面向对象的实现               在lua里面,我们可以这样操作表,如下:          Account = {balance = 0}        function Account:withdraw (v)               self.balance = self.balan