问题描述
.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
解决方案十五:
大神求指导,务必联系我!