在合作开发时,C#时常需要调用C++DLL,当传递参数时时常遇到问题,尤其是传递和返回字符串是,现总结一下,分享给大家
C#调用C++一般分为两种方式
静态调用和动态调用,两种方式各有利弊,下文会一一论述。
【一】C++中公布方法供DotNet调用
extern “C”
{
void WINAPI Test1(LPCTSTR str)
{
AfxMessageBox(str);
}
void WINAPI Test2(LPTSTR str)
{
lstrcpy(str,_T(“我是测试的cadgj.com”));
}
}
在.def文件中增加两行
Test1
Test2
这样方法Test1和Test2就分别被公布出来了,可以由外部调用。
【二】C#调用
(1)静态方法
通过DllImport实现
如需调用上面公开的方法Test1,只需要在相应的类中申明
[DllImport(“InvokeDll.dll”,EntryPoint=”Test1″,CharSet=CharSet.Unicode)]
public static extern void Test1(string str);
就可以直接调用C++的方法Test1。
利:使用非常方便,代码简洁。
弊:DllImport导入的DLL的路径必须是已知的,必须部署在exe文件同目录下。或者部署在system32目录下,如果是其它路径则必须使用全路径,比较不灵活。
(2)动态方法
通过Kernel32.dll中提供的方法LoadLibrary,FreeLibrary加载和释放C++的dll然后通过GetProcAddress得到函数指针。通过Marshal.GetDelegateForFunctionPointer转换成委托。
然后调用委托即可。
利:和静态方法相反,部署灵活,可以讲引用的C++ dll放置在任何地方,但是如果这个C++的dll也引用了其它dll,可能需要先加载这些引用的dll,不然单纯的放在dll一起,因为您启动的exe的程序目录不在同一个地方。
弊:使用较为繁琐,所有方法必须转换成委托。
二者的运行效率没有做具体的测试,但是实现机理类似,DllImport可以视为一种DotNet提供的简化版调用,所以我认为二者的运行效率应该相差不大。有兴趣的朋友可以测试一下。
关于C#和C++的类型对应请参见文章:C#与C++之间类型的对应
下面贴上DotNet的全套代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
namespace InvokeTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport(“InvokeDll.dll”,EntryPoint=”Test1″,CharSet=CharSet.Unicode)]
public static extern void Test1(string str);
[DllImport(“InvokeDll.dll”, EntryPoint = “Test2”, CharSet = CharSet.Unicode)]
public static extern void Test2(StringBuilder sb);
private void button1_Click(object sender, EventArgs e)
{
// 静态调用
Test1(“测试cadgj.com”);
StringBuilder sb = new StringBuilder(200);
Test2(sb);
MessageBox.Show(sb.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
//动态调用
InvokeDllInvoke.DllFileName = Path.Combine(Application.StartupPath, “InvokeDll.dll”);
InvokeDllInvoke.InitModule();
InvokeDllInvoke.Test1(“测试cadgj.com”);
StringBuilder sb = new StringBuilder(200);
InvokeDllInvoke.Test2(sb);
MessageBox.Show(sb.ToString());
InvokeDllInvoke.UnInitModule();
}
}
public 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);
}
}
public static class InvokeDllInvoke
{
/// <summary>
/// DLL文件名称
/// </summary>
public static string DllFileName = “”;
/// <summary>
/// DLL实例
/// </summary>
private static int hModule = -1;
/// <summary>
/// 初始化实例
/// </summary>
/// <returns></returns>
public static bool InitModule()
{
if (hModule > 0)
{
return true;
}
hModule = DLLWrapper.LoadLibrary(DllFileName);
if (hModule == 0)
return false;
return true;
}
/// <summary>
/// 卸载实例
/// </summary>
/// <returns></returns>
public static bool UnInitModule()
{
if (hModule == 0)
return false;
DLLWrapper.FreeLibrary(hModule);
hModule = 0;
return true;
}
private delegate void DTest1([MarshalAs(UnmanagedType.LPWStr)] string str);
private delegate void DTest2([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb);
public static void Test1(string str)
{
DTest1 d = (DTest1)DLLWrapper.GetFunctionAddress(hModule, “Test1”, typeof(DTest1));
if (d == null)
return;
d(str);
}
public static void Test2(StringBuilder sb)
{
DTest2 d = (DTest2)DLLWrapper.GetFunctionAddress(hModule, “Test2”, typeof(DTest2));
if (d == null)
return;
d(sb);
}
}
}