C#调用C++DLL返回值是个抽象类指针,如何调用其中的函数(不对外发布)?

caxton8310 2014-10-08 05:28:52
C#调用C++DLL返回值是个抽象类指针,如何调用其中的函数(不对外发布)?
...全文
370 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
caxton8310 2014-10-15
  • 打赏
  • 举报
回复
[c#实现代码如下:] [StructLayout(LayoutKind.Sequential)] public class ITest { [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate int QueryInterfaceHandler(ref Guid riid, out IntPtr ppvObject); public QueryInterfaceHandler QueryInterface = new QueryInterfaceHandler(QueryInterfaceFunc); [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint AddRefHandler(IntPtr @this); public AddRefHandler AddRef ; [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint ReleaseHandler(IntPtr @this); public ReleaseHandler Release; [UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate void SetInfoHandler(ref IntPtr pInfo); public SetInfoHandler SetInfo= newSetInfoHandler(SetInfoFunc); 第一次传入c++是可以调用到addref/Release等函数,出来后,再调用发现对应的vtable都是野指针看着像是释放了。 求高人指点?
caxton8310 2014-10-14
  • 打赏
  • 举报
回复
引用 12 楼 Saleayas 的回复:
__thiscall 
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
__stdcall
[UnmanagedFunctionPointer(CallingConvention.StdCall)] //或者删除这句,因为缺省就是 StdCall。
//而 C 语言的导出函数呼叫约定缺省也应该是 __stdcall 。
我是看你的代码里面没有 __stdcall 约定才明确写上的,在C/C++ 里面定义导出最好使用 __stdcall. 这样在 C# 里面就是缺省的呼叫约定了。 至于 IUnknown 不需要在 C# 里面导出,因为 C# 的 AddRef 和 Release 是由GC 决定的。 你需要自己处理,使用 IDisposed 接口。 至于 QI 在 C# 里面是不存在的,她对应于类型转换。 当你需要 QI 除 IUnknown 之外的接口,你都需要有对应的C# 接口,然后,就可以he 样例中 API 一样,QI 到指定的接口,然后转换为对应的 C# 的接口实例。 当你的接口继承 IUnknwon 时,需要注意 INativeCSForC2 接口布局,有三个 IUnknown 的接口方法委托在前面。
谢谢 Saleayas 的细心回答,在你的指导下 c#里调用c++的接口貌似功能函数方面可以调用了 ,引用计数的问题,后面我再 研究研究。 现在有个新的问题 ,例如: 【c++】 声明接口,不实现: ITest :IUnknown { void SetInfo(void** pInfo); } IBase { void SetITest(ITest* pITest); } [c#] 需要实现ITest, 我该如何定义呢?
宝_爸 2014-10-13
  • 打赏
  • 举报
回复
这个应该没法实现吧。。。 用CLI封装C++,然后暴露托管接口,这样是不是好点。
caxton8310 2014-10-13
  • 打赏
  • 举报
回复
引用 7 楼 Saleayas 的回复:
简单说,就是顶一个和 C 虚表一致的 [StructLayout(LayoutKind.Sequential)] 特性的接口。 这样,就可以把虚表中的方法转换到 托管内存中。 在定义个C# 的类来包装这个,并暴露一个一致的、C#模式的接口。(如果内部使用,就可以不要包装,直接调用)。 在使用的时候,使用 C 提供的 API 来获取接口,解析这个接口,获取虚表。 p->lpVtbl 就是虚表的位置。 在 C# 中,她其实就是其 0 偏移的引用。 实现 C# 接口,就是找到该方法对应的 虚表中的方法委托,然后呼叫之。 如果是 IUnknown 接口,需要自己实现更加复杂的 IUnknown 的三个方法。 我记得好像不久前,也有人问,怎么处理一个接口的问题。你可以看看。
非常感谢你,一般的interface用你的方法是可以,还有两点请教下: 1)virtual int _stdcall Login()--------------会出错; 2)Iunknow 3个方法我是定义在NativeVTable里还是interface ICSForC22里。 盼回复。
Saleayas 2014-10-13
  • 打赏
  • 举报
回复
__thiscall 
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
__stdcall
[UnmanagedFunctionPointer(CallingConvention.StdCall)] //或者删除这句,因为缺省就是 StdCall。
//而 C 语言的导出函数呼叫约定缺省也应该是 __stdcall 。
我是看你的代码里面没有 __stdcall 约定才明确写上的,在C/C++ 里面定义导出最好使用 __stdcall. 这样在 C# 里面就是缺省的呼叫约定了。 至于 IUnknown 不需要在 C# 里面导出,因为 C# 的 AddRef 和 Release 是由GC 决定的。 你需要自己处理,使用 IDisposed 接口。 至于 QI 在 C# 里面是不存在的,她对应于类型转换。 当你需要 QI 除 IUnknown 之外的接口,你都需要有对应的C# 接口,然后,就可以he 样例中 API 一样,QI 到指定的接口,然后转换为对应的 C# 的接口实例。 当你的接口继承 IUnknwon 时,需要注意 INativeCSForC2 接口布局,有三个 IUnknown 的接口方法委托在前面。
bigbaldy 2014-10-13
  • 打赏
  • 举报
回复

((Action)Marshal.GetDelegateForFunctionPointer((IntPtr)(**(int**)a), typeof(Action)))();
caxton8310 2014-10-13
  • 打赏
  • 举报
回复
可以实现的,现在就是Iunknown的三个方法不太好搞。
Saleayas 2014-10-11
  • 打赏
  • 举报
回复
简单说,就是顶一个和 C 虚表一致的 [StructLayout(LayoutKind.Sequential)] 特性的接口。 这样,就可以把虚表中的方法转换到 托管内存中。 在定义个C# 的类来包装这个,并暴露一个一致的、C#模式的接口。(如果内部使用,就可以不要包装,直接调用)。 在使用的时候,使用 C 提供的 API 来获取接口,解析这个接口,获取虚表。 p->lpVtbl 就是虚表的位置。 在 C# 中,她其实就是其 0 偏移的引用。 实现 C# 接口,就是找到该方法对应的 虚表中的方法委托,然后呼叫之。 如果是 IUnknown 接口,需要自己实现更加复杂的 IUnknown 的三个方法。 我记得好像不久前,也有人问,怎么处理一个接口的问题。你可以看看。
Saleayas 2014-10-11
  • 打赏
  • 举报
回复
给你一个样例。 C++ 接口
struct ICForCS2
{
	virtual int Login(const char* user_name, const char* password, int login_type, LoginInfo *plogin_info, const char* end_point_url, const char* local_ip = NULL) = 0;
	virtual int GetUdiskInfo2(s_udiskinfo *a, int max) = 0;


};
C++ 的获取接口的 API。
CFORCS_API void GetICForCS2(ICForCS2 **p);
C# 定义对应的接口
	[StructLayout(LayoutKind.Sequential)]
	struct INativeCSForC2
	{
		[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
		public delegate int _LoginHandler(IntPtr @this, [In, MarshalAs(UnmanagedType.LPStr)] string user_name, [In, MarshalAs(UnmanagedType.LPStr)] string password, [In, MarshalAs(UnmanagedType.I4)]int login_type, [In, Out] ref LoginInfo plogin_info, [In, MarshalAs(UnmanagedType.LPStr)] string end_point_url, [In, MarshalAs(UnmanagedType.LPStr)] string local_ip);
		public _LoginHandler Login;

		[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
		public delegate int _GetUdiskInfoHandler([In] IntPtr @this, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] s_udiskinfo[] info, int max);
		public _GetUdiskInfoHandler GetUdiskInfo;
	}
C# 定义这个接口。
	interface ICSForC22
	{
		int Login([In, MarshalAs(UnmanagedType.LPStr)] string user_name, [In, MarshalAs(UnmanagedType.LPStr)] string password, [In, MarshalAs(UnmanagedType.I4)]int login_type, [In, Out] ref LoginInfo plogin_info, [In, MarshalAs(UnmanagedType.LPStr)] string end_point_url, [In, MarshalAs(UnmanagedType.LPStr)] string local_ip);
		int GetUdiskInfo(s_udiskinfo[] info);
	}
添加一个包装类。
	class NativeCSForC2 : ICSForC22
	{
		public NativeVTable<INativeCSForC2> _native;
		int ICSForC22.Login(string user_name, string password, int login_type, ref LoginInfo login_info, string end_point_url, string local_ip)
		{
			return _native._nativeInterface.Login(_native._ptrInterface, user_name, password, login_type, ref login_info, end_point_url, local_ip);
		}

		int ICSForC22.GetUdiskInfo(s_udiskinfo[] info)
		{
			return _native._nativeInterface.GetUdiskInfo(_native._ptrInterface, info, info.Length);
		}

	}
C# 定义 API 函数对应的方法。
	
[DllImport(....)]
	private static extern void _GetICForCS2([Out] out IntPtr ptrInterface);
		public static NativeCSForC2 GetICForCS2()
		{
			IntPtr ptrInterface;
			_GetICForCS2(out ptrInterface);
			return new NativeCSForC2
			{
				_native = new NativeVTable<INativeCSForC2>(ptrInterface),
			};
		}
如果使用很多的话,定义一个 模板包装
	class NativeVTable< I > : IDisposable
	{
		public IntPtr _ptrInterface;
		public I _nativeInterface;
		private Action< IntPtr > _release = null;
		public NativeVTable(IntPtr ptrInterface)
		{
			System.Diagnostics.Debug.Assert(ptrInterface != IntPtr.Zero);
			_ptrInterface = ptrInterface;
			_nativeInterface = (I)Marshal.PtrToStructure(Marshal.ReadIntPtr(ptrInterface, 0), typeof(I));
		}
		public NativeVTable(IntPtr ptrInterface, Action< IntPtr > release) : this(ptrInterface)
		{
			_release = release;
		}
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

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

		~NativeVTable()
		{
			Dispose(false);
		}

	}



最后使用:
	var vv = (ICSForC22)NativeImport.GetICForCS2();
caxton8310 2014-10-11
  • 打赏
  • 举报
回复
引用 1 楼 kcxnvcs5 的回复:
反射方式调用
麻烦给点伪代码示例下. [c++] Interface test1 { void a(); void b(char*a); }; Interface test { void GetTest1(test1**pptest); void b(char*a); }; dll 对外发布唯一接口: void Get(test** a); 请问c#部分如何实现,给点伪代码展示下如何访问 test1 的 a()函数, 谢谢了!
caxton8310 2014-10-09
  • 打赏
  • 举报
回复
举例 : dll 对外发布唯一接口类似于 void Get(IInterface ** ppInter); IInterface是抽象接口,继承 IUnknown,dll 内部有类继承自IInterface,并实现了功能。 问题 : c#如何获取ppInter? 如何访问IInterface声明的成员函数?(声明了哪些函数外部是知道的)
邋遢的土豆 2014-10-08
  • 打赏
  • 举报
回复
c#调用c++dll 一般情况下能遇到的问题大概是一下两点 1. 数据类型转换问题 2. 指针或地址参数传送问题 你问的问题是什么意思,不太明白
bdmh 2014-10-08
  • 打赏
  • 举报
回复
不对外是什么意思,可以访问public的方法,受保护的不行
kcxnvcs5 2014-10-08
  • 打赏
  • 举报
回复
反射方式调用

110,567

社区成员

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

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

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