C#动态调用非托管dll的问题

ExetremElegance 2013-03-04 04:22:28
非常棘手的问题,项目要求能动态加载C++写的DLL,于是我用了委托的方法
C++的接口是这样的
#ifndef RANDOMTEST_H
#define RANDOMTEST_H
#define FREQUENCY 1
#define EXPORT_API __declspec(dllexport)
_declspec(dllexport) int RandomTest(int testid, int m, int bitlen,BitSequence *epsilon,int testserial,double *pvalue);
#endif

就是要用RandomTest这个函数,那个BitSequence他用
typedef unsigned char	BitSequence;
说白了就是一个8bit的无符号整形的指针,pvalue是double型的指针,但关键是这两个指针指向的内容会在dll中进行修改(C#申请空间--〉传指针到dll中--〉dll修改--〉C#再读出指针指向的内容)

动态加载过程我用的是一个包装好的类
public static class DLLWrapper
{
///<summary>
/// API LoadLibrary
///</summary>
[DllImport("Kernel32")]
public static extern int LoadLibrary(String funcname);

///<summary>
/// API GetProcAddress
///</summary>
[DllImport("Kernel32")]
public static extern int GetProcAddress(int handle, String funcname);

///<summary>
/// API FreeLibrary
///</summary>
[DllImport("Kernel32")]
public static extern int FreeLibrary(int handle);

///<summary>
///通过非托管函数名转换为对应的委托
///</summary>
///<param name="dllModule">通过LoadLibrary获得的DLL句柄</param>
///<param name="functionName">非托管函数名</param>
///<param name="t">对应的委托类型</param>
///<returns>委托实例,可强制转换为适当的委托类型</returns>
public static Delegate GetFunctionAddress(int dllModule, string functionName, Type t)
{
int address = GetProcAddress(dllModule, functionName);
if (address == 0)
return null;
else
return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
}

///<summary>
///将表示函数地址的IntPtr实例转换成对应的委托
///</summary>
public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
{
if (address == IntPtr.Zero)
return null;
else
return Marshal.GetDelegateForFunctionPointer(address, t);
}

///<summary>
///将表示函数地址的int转换成对应的委托
///</summary>
public static Delegate GetDelegateFromIntPtr(int address, Type t)
{
if (address == 0)
return null;
else
return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
}
}


然后在需要用到dll的窗体里申请委托
delegate int COUNT(int a, int b, int c, IntPtr d, int e, IntPtr f);

在执行dll中算法的按钮事件里写

string funname = "RandomTest";
int launchresult;
int hModule = DLLWrapper.LoadLibrary(dllfilepath);
if (hModule == 0)
{
MessageBox.Show("组件加载失败");
}
else
{
COUNT launchdll = (COUNT)DLLWrapper.GetFunctionAddress(hModule, funname, typeof(COUNT));
if (launchdll == null)
{
DLLWrapper.FreeLibrary(hModule);
MessageBox.Show("组件运行失败");
}
else
{
int a0 = 1;
int b0 = 1;
int c0 = 1;
//byte d0 = 0xF1;
int e0 = 1;
double f0 = 0.5;
IntPtr ptrbyte = Marshal.AllocHGlobal(sizeof(byte));//申请byte非托管内存
Marshal.WriteByte(ptrbyte, 0xF1);
IntPtr ptrdouble = Marshal.AllocHGlobal(sizeof(double));//申请double非托管内存
launchresult = launchdll(a0, b0, c0, ptrbyte, e0, ptrdouble);
}
}

结果执行抛异常“对 PInvoke 函数“EvaluationProject!EvaluationProject.EvaluationMainForm+COUNT::Invoke”的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签名是否匹配。”
求大神帮忙解决。。。。。感激不尽
...全文
470 8 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
搬砖的码农 2013-06-11
  • 打赏
  • 举报
回复
引用 5 楼 gomoku 的回复:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int COUNT(int a, int b, int c, ref byte d, int e, ref double f);
用UnmanagedFunctionPointer特性修饰你的函数声明。UnmanagedFunctionPointer可见http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.unmanagedfunctionpointerattribute%28v=vs.80%29.aspx
帮大忙了,要的就是[UnmanagedFunctionPointer(CallingConvention.Cdecl)],此处省略1W表示感谢字
ExetremElegance 2013-03-05
  • 打赏
  • 举报
回复
引用 5 楼 gomoku 的回复:
引用 4 楼 t_Razer 的回复:因为要能动态加载制定的dll,所以没法用dllimport,那cdecl属性该怎样指定呢? C# code?12[UnmanagedFunctionPointer(CallingConvention.Cdecl)]delegate int COUNT(int a, int b, int c, ref byte d, int e,……
厉害,可以用。可是现在有个问题,这个内存用完之后是在C#中释放呢还是得再DLL里单独写释放的内容?
ExetremElegance 2013-03-05
  • 打赏
  • 举报
回复
引用 5 楼 gomoku 的回复:
引用 4 楼 t_Razer 的回复:因为要能动态加载制定的dll,所以没法用dllimport,那cdecl属性该怎样指定呢? C# code?12[UnmanagedFunctionPointer(CallingConvention.Cdecl)]delegate int COUNT(int a, int b, int c, ref byte d, int e,……
我试试
gomoku 2013-03-05
  • 打赏
  • 举报
回复
引用 4 楼 t_Razer 的回复:
因为要能动态加载制定的dll,所以没法用dllimport,那cdecl属性该怎样指定呢?

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int COUNT(int a, int b, int c, ref byte d, int e, ref double f);
用UnmanagedFunctionPointer特性修饰你的函数声明。UnmanagedFunctionPointer可见http://msdn.microsoft.com/zh-cn/library/system.runtime.interopservices.unmanagedfunctionpointerattribute%28v=vs.80%29.aspx
ExetremElegance 2013-03-04
  • 打赏
  • 举报
回复
因为要能动态加载制定的dll,所以没法用dllimport,那cdecl属性该怎样指定呢?
ExetremElegance 2013-03-04
  • 打赏
  • 举报
回复
引用 1 楼 bdmh 的回复:
第一,结构体等数据结构要匹配, 第二,调用约定要正确,c默认是cdecl,你调用时显示的指定一下,dllimport中可以指定
谢谢版主大大这么快回复 但是那个DLL是个统计计算类的算法,里面没有结构体,他的全部功能就是这个:
int RandomTest(int testid, int m, int n,BitSequence *epsilon,int testserial,double *pvalue)
{
	int ret;
	switch (testid)
	{
	case 1:
		ret=Frequency(n,epsilon,testserial,pvalue);
		break;

	default:
		ret=-1;
		break;

	
	}

	return ret;
}
相关的Frequency函数就是这个功能
int Frequency(int n,BitSequence *epsilon,int testserial,double *pvalue)
{
	n=100;
	*pvalue=0.05;
	*epsilon=0xff;
	return 56;
}
是不是我调用DLL时指针没声明好?
ExetremElegance 2013-03-04
  • 打赏
  • 举报
回复
补充一下,执行到最后那个launchedll函数才出错,前面的加载dll和卸载dll都是正常的
bdmh 2013-03-04
  • 打赏
  • 举报
回复
第一,结构体等数据结构要匹配, 第二,调用约定要正确,c默认是cdecl,你调用时显示的指定一下,dllimport中可以指定

111,098

社区成员

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

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

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