C#动态调用非托管dll(转)
尝试着在C#下调用以前Delphi写的一些DLL,基本实现动态调用,传入回调函数,及调用带结构数组指针作为参数的函数.
虽然DllImport可以方便的静态调用DLL的函数,但在.net2.0中新增加了一个Marshal.GetDelegateForFunctionPointer 方法,可以将非托管函数指针转换为委托。 有了这个方法就可以用三个Windows API函数即:Loadlibrary,GetProcAddress和Freelibrary来实现动态调用DLL了.下面是实现DLL动态调用的静态类
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Data;
namespace Test
{
/// <summary>
/// DLL加载类
/// </summary>
internal class DynamicLoadDll
{
[DllImport("Kernel32")]
public static extern int GetProcAddress(int handle, String funcname);
[DllImport("Kernel32")]
public static extern int LoadLibrary(String funcname);
[DllImport("Kernel32")]
public static extern int FreeLibrary(int handle);
public static Delegate GetAddress(int dllModule, string functionname, Type t)
{
int addr = GetProcAddress(dllModule, functionname);
if (addr == 0)
return null;
else
return Marshal.GetDelegateForFunctionPointer(new IntPtr(addr), t);
}
}
Public class referDll
{
private int m_hDLL = 0; //DLL句柄
private delegate int ShowForm(IntPtr aHandle);
private ShowForm m_ShowForm;
private const string DLLNAEM = "XXX.dll";
m_hDLL = DllLoader.LoadLibrary(DLLNAEM);
if (m_hDLL == 0)
{ MessageBox.Show("加载失败!")
return;
}
m_ShowForm = (ShowForm) DllLoader.GetAddress(m_hPacsview, "ShowForm", typeof (ShowForm));
//使用ShowForm
if (m_ShowForm != null)
m_ShowForm(iHandle);
//卸载DLL
DllLoader.FreeLibrary(m_hDLL);
}
}
接着说说如何调用DLL中带结构数组指针作为参数的函数.在原来Delphi中定义如下:
//一个结构定义如下
TStudyRec = Record
UID : Array[0..127] of Char;
end;
TCharArray=Array[0..49] of TStudyRec;
//在DLL中有如下函数 其中AStudys为TCharArray的指针
function Open(AStudys: Pointer): HRESULT; StdCall;
要在C#里正常调用,首先要定义出一个相同的结构
private struct StudyRec
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] public string UID; //名称
}
接着声明一个委托
private delegate int Open(IntPtr aStudys);
从DLL得到委托实例
private Open m_Open = (Open) DllLoader.GetAddress(m_hDLL, "Open", typeof (Open)); //m_hDLL为DLL指针
到此为止终于在C#里定义出相同的结果及函数了,下面就是要调用了,因为结构数组是要给非托管的DLL使用的,因此最关键的一点是要用Marshal.AllocHGlobal来分配好非托管内存,把结构数组放到内存中去,再把内存指针当作参数调用就OK啦
StudyRec[] arrStudyRec = new StudyRec[50];
int isize = Marshal.SizeOf(typeof (StudyRec));
IntPtr parrStudyRec = Marshal.AllocHGlobal(Marshal.SizeOf(isize*50));
int run = (int) parrStudyRec;
for (int i = 0; i < 50; i++)
{
arrStudyRec[i].UID = i.tostring();//这里只是模拟,所以直接把i当作UID了
Marshal.StructureToPtr(arrStudyRec[i], (IntPtr) run, false);
run += isize;
}
m_Open(parrStudyRec);
转自:http://zhouweigang01.blog.163.com/blog/static/934090720095493459311/