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

葬爱の丫 2014-07-29 10:03:01
.net互操作一直没有接触过,最近有一个C#程序必须要使用C++DLL类成员函数.....头都木了,各种狂查,基本对Pinvoke有一些了解。但第一次看到PInvoke声明DLL函数的写法“static extern” 时,就有疑问:这个static,难道意味着C++DLL函数都必须是全局的?想调用C++public类成员函数是行不通的?于是写了个测试程序,发现PInvoke貌似调不了C++类成员函数..........但由于本人没有此方面任何经验,无法做结论,所以请教各位:PInvoke技术是不是不可以用于调用C++类成员函数?

如何确定不行的话,要使用什么办法才可以呢?网上查到说可以用托管C++封装一层?如何封装呢?我查询“使用托管C++封装非托管资源”,基本没什么好的文章啊。

如果各位有什么好文章,讲用托管C++封装的....也可以推荐一下,我去拜读。

谢谢。
...全文
878 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
zhoujinjinyangcui 2015-07-24
  • 打赏
  • 举报
回复
大神求指导,务必联系我!
zhoujinjinyangcui 2015-07-24
  • 打赏
  • 举报
回复
想联系,博主右右yy,遇到了相同的问题。qq==1010112108
葬爱の丫 2014-07-31
  • 打赏
  • 举报
回复
谁能解答8楼的问题?
Saleayas 2014-07-30
  • 打赏
  • 举报
回复
如果没有虚函数,那么把相关的vtable 去掉。
Saleayas 2014-07-30
  • 打赏
  • 举报
回复
	class CVTable : IDisposable
	{
		IntPtr _vtable;
		public CVTable(params Delegate[] methods)
		{
			_vtable = Marshal.AllocHGlobal(methods.Length * Marshal.SizeOf(typeof(IntPtr)));
			for (int i = 0; i < methods.Length; ++i)
			{
				Marshal.WriteIntPtr(_vtable, i * Marshal.SizeOf(typeof(IntPtr)), Marshal.GetFunctionPointerForDelegate(methods[i]));
			}
		}
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private bool disposed = false;
		protected virtual void Dispose(bool disposing)
		{
			if (!this.disposed)
			{
				if (_vtable != IntPtr.Zero)
				{
					Marshal.FreeHGlobal(_vtable);
					_vtable = IntPtr.Zero;
				}
				disposed = true;
			}
		}

		~CVTable()
		{
			Dispose(false);
		}

		public IntPtr CreateVTable(IntPtr @this)
		{
			IntPtr old = Marshal.ReadIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)));
			Marshal.WriteIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)), _vtable);
			return old;
		}
		public void ReleaseVTable(IntPtr @this, IntPtr vtable)
		{
			Marshal.WriteIntPtr(@this, 0 * Marshal.SizeOf(typeof(IntPtr)), vtable);
		}
	}
	partial class CSForC : IDisposable
	{
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		private bool disposed = false;
		protected virtual void Dispose(bool disposing)
		{
			if (!this.disposed)
			{
				_Dispose();
				disposed = true;
			}
		}

		~CSForC()
		{
			Dispose(false);
		}
	}

	[StructLayout(LayoutKind.Sequential)]
	public struct _CForCS
	{
		public IntPtr _vtable;
		public int _nValue;
	}

	partial class CSForC
	{
		private IntPtr _basecpp;
		private const string dllname = "..\\..\\..\\Debug\\CForCS.dll";
		[DllImport(dllname, EntryPoint = "??0CCForCS@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]
		private static extern void _Constructor([In] IntPtr @this);
		[DllImport(dllname, EntryPoint = "??0CCForCS@@QAE@H@Z", CallingConvention = CallingConvention.ThisCall)]
		private static extern void _Constructor([In] IntPtr @this, [In, MarshalAs(UnmanagedType.I4)] int value);
		[DllImport(dllname, EntryPoint = "??1CCForCS@@QAE@XZ", CallingConvention = CallingConvention.ThisCall)]
		private static extern void _Destructor([In] IntPtr @this);

		[DllImport(dllname, EntryPoint = "?Print@CCForCS@@QAEXPB_W@Z", CallingConvention = CallingConvention.ThisCall)]
		private static extern void _Print([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message);
		[DllImport(dllname, EntryPoint = "?GetValue@CCForCS@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)]
		[return: MarshalAs(UnmanagedType.I4)]
		private static extern int _GetValue([In] IntPtr @this);

		public CSForC()
		{
			_basecpp = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));
			_Constructor(_basecpp);
			CreateVtable();
		}

		public CSForC(int value)
		{
			_basecpp = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(_CForCS)));
			_Constructor(_basecpp, value);
			CreateVtable();
		}

		private void _Dispose()
		{
			if (_basecpp != IntPtr.Zero)
			{
				ReleaseVtable();
				_Destructor(_basecpp);
				Marshal.FreeCoTaskMem(_basecpp);
				_basecpp = IntPtr.Zero;
			}
		}

		public void Print(string message)
		{
			_Print(_basecpp, message);
		}
		public int GetValue()
		{
			return _GetValue(_basecpp);
		}

		IntPtr _basecppvtable;
		CVTable vtable;
		private void CreateVtable()
		{
			vtable = new CVTable(new _VFunctionBeforeHandler(VFunctionBefore), new _VFunctionAfterHandler(VFunctionAfter));
			_basecppvtable = vtable.CreateVTable(_basecpp);
		}
		private void ReleaseVtable()
		{
			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)]
		private static extern int _VFunctionBefore([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message, [Out, MarshalAs(UnmanagedType.I4)] out int value);
		[DllImport(dllname, EntryPoint = "?VFunctionAfter@CCForCS@@MAEXPB_WH@Z", CallingConvention = CallingConvention.ThisCall)]
		private static extern void _VFunctionAfter([In] IntPtr @this, [In, MarshalAs(UnmanagedType.LPWStr)]string message, [In, MarshalAs(UnmanagedType.I4)]int value);
		private delegate int _VFunctionBeforeHandler([In, MarshalAs(UnmanagedType.LPWStr)]string message, [Out, MarshalAs(UnmanagedType.I4)]out int value);
		private delegate void _VFunctionAfterHandler([In, MarshalAs(UnmanagedType.LPWStr)]string message, [In, MarshalAs(UnmanagedType.I4)]int value);
		protected virtual int VFunctionBefore(string message, out int value)
		{
			int n = _VFunctionBefore(_basecpp, message, out value);
			value = 100;
			return n;
		}
		protected virtual void VFunctionAfter(string message, int value)
		{
			System.Diagnostics.Debug.WriteLine(string.Format("{0}, {1}.", message, value));
		}
	}
	class Program
	{
		static void Main(string[] args)
		{
			var v = new CSForC(123456789);
			v.Print("CSMessage ");
			System.Diagnostics.Debug.WriteLine(string.Format("GetValue() = {0}", v.GetValue()));

			((IDisposable)v).Dispose();
		}
	}
Saleayas 2014-07-30
  • 打赏
  • 举报
回复
给你一个例程。
class CFORCS_API CCForCS
{
public:
	CCForCS(void);
	CCForCS(int nValue);
	~CCForCS(void);
public:
	void Print(LPCWSTR pszMessage);
	int GetValue(void);
protected:
	virtual int VFunctionBefore(LPCWSTR pszMessage, int *pnValue);
	virtual void VFunctionAfter(LPCWSTR pszMessage, int nValue);
private:
	int _nValue;
};
葬爱の丫 2014-07-30
  • 打赏
  • 举报
回复
“未将引用设置到对象的实例”?问题找到了.....我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",我认为这是错误的,extern c只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。 对吧?
葬爱の丫 2014-07-30
  • 打赏
  • 举报
回复
刚才去试了下2楼的想法,2楼所说“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上”,这事儿办不到....声明一个指向c++的某个类的指针,这怎么可能在C#中做到呢?C++中的struct倒可以在C#中重写个struct,再作为参数声明。C++中的class不可能在C#中重写啊........ 另外,刚才我建了个C++DLL工程,导出了一个全局函数。,在C#中用PInvoke调用,怎么会提示:“未将引用设置到对象的实例”?我这个C++全局函数不需要实例化任何对象啊,怎么会出这样的错呢? 网上某些人说C++导出函数必须声明为extern "c",我认为这是错误的,extern c只是告诉链接器“我这个函数在编译时是按C编译的”,编译后生成的目标文件中的函数符号是C编译器编译的结果。其实只是和C++编译器生成的函数符号不一样而已。不管按哪个编译,我们只要在PInvoke时正确将入口点指明为此函数符号即可。所以不管C编译器或C++编译器规则都无所谓,入口点指明正确,就能在链接时正确找到此函数位置。 所以各位使用过pinvoke的,C++导出函数并没有必要写extern c,对吧?那么我这个异常究竟是为何引起的呢......
葬爱の丫 2014-07-30
  • 打赏
  • 举报
回复
感谢楼上各位的帮助!现在我去试试用托管c++封装, 另外2楼说:“成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。”我也去试试!
GoingNaive 2014-07-30
  • 打赏
  • 举报
回复
Native C++

class CNative
{
public:
  int NativeFunction(int a) {}
}
C++/CLI

public ref class CNativeWrapper
{
private:
  CNative *m_pNative;
public:
  CNativeWrapper()  { m_pNative = new CNative; }
  int ManagedMethod(int a) { return m_pNative->NativeFunction(a); }
  ~CNativeWrapper() { delete m_pNative; }
protected:
  !CNativeWrapper() { delete m_pNative; }
}
C#

class Program
{
  int Main()
  {
    var nativeWrapper = new CNativeWrapper();
    nativeWrapper.ManagedMethod();
    return 0;
  }
}
宝_爸 2014-07-30
  • 打赏
  • 举报
回复
托管C++就是C++,因此调用unmanaged C++类库没有问题。然后在暴露managed接口,这样C#就可以调用了。 用C做中间层一样的道理,C调用C++库,在暴露纯C的接口,给platform用。
threenewbee 2014-07-29
  • 打赏
  • 举报
回复
理论上说PInvoke也可以访问成员函数,因为成员函数只不过包含一个隐型的this指针参数。你声明原型的时候加上,并且用cdelc方式调用即可。但是因为C#操作C++对象比较麻烦,这样不推荐,你可以用C++/CLI(也就是C++.NET)作为代理去访问原生C++类,再包装出来给C#用。或者用原生C++编写dll,将成员函数转换为一般的函数。

110,537

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧