在合作开发时,C#时常需要调用C++DLL。研究了一下C#,发现其强大简洁, 在跨语言调用方面封装的很彻底,提供了强大的API与之交互.这点比JNA方便多了. java与C#都只能调用C格式导出动态库,因为C数据类型比较单一,容易映射. 两者都是在本地端提供一套与之映射的C#/java描述接口,通过底层处理这种映射关系达到调用的目的.
1、调用例子(例子中提供了传值调用和传址调用两种方法):
C++代码(C++生成动态库DLL.dll):
Lib.h
//文件:lib.h#PRagma once#include <string> using namespace std; #define JNAAPI extern "C" __declspec(dllexport) // C方式导出函数typedef struct CARDINFO{ int majorVersion; int minorVersion; int cardType; char szDescribe[128];};// 1. 获取版本信息(传递结构体指针) JNAAPI bool GetVersionPtr(CARDINFO *info);// 2.获取版本信息(传递结构体引用) JNAAPI bool GetVersionRef(CARDINFO &info);Lib.cpp
#include "stdafx.h"#include "lib.h"#include <stdlib.h>using namespace std;// 1. 获取版本信息(传递结构体指针) bool GetVersionPtr(CARDINFO *info){ info->majorVersion = 1; info->minorVersion = 22; info->cardType = 3; memcpy(info->szDescribe, "hello world", 128); return true;}// 2.获取版本信息(传递结构体引用) bool GetVersionRef(CARDINFO &info){ info.majorVersion = 1; info.minorVersion = 22; info.cardType = 3; memcpy(info.szDescribe, "hello world", 128); return true;}C#代码(C++生成动态库DLL.dll):
class CCommand{ const string dllpathfile = "..//..//..//..//Lib//DLL.dll"; // CARDINFO定义 [StructLayout(LayoutKind.Sequential)] public struct CARDINFO { public int majorVersion; public int minorVersion; public int cardType; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] public string szDescribe; } [DllImport(dllpathfile, EntryPoint = "GetVersionPtr")] public static extern bool GetVersionPtr(ref CARDINFO info); [DllImport(dllpathfile, EntryPoint = "GetVersionRef")] public static extern bool GetVersionRef(ref CARDINFO info);} 调用://C#调用C++库 CCommand.CARDINFO cardInfo = new CCommand.CARDINFO(); CCommand.GetVersionPtr(ref cardInfo);2、类型转换说明:
类型对照:
C++类型 | C#类型 |
BSTR | StringBuilder |
LPCTSTR | StringBuilder |
LPCWSTR | IntPtr |
handle | IntPtr |
hwnd | IntPtr |
char * | string |
int * | ref int |
int & | ref int |
void * | IntPtr |
unsigned char * | ref byte |
CLR Type | Win32 Types |
System.SByte | char, INT8, SBYTE, CHAR |
System.Int16 | short, short int, INT16, SHORT |
System.Int32 | int, long, long int, INT32, LONG32, BOOL , INT |
System.Int64 | __int64, INT64, LONGLONG |
System.Byte | unsigned char, UINT8, UCHAR , BYTE |
System.UInt16 | unsigned short, UINT16, USHORT, Word, ATOM, WCHAR , __wchar_t |
System.UInt32 | unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT |
System.UInt64 | unsigned __int64, UINT64, DWORDLONG, ULONGLONG |
System.Single | float, FLOAT |
System.Double | double, long double, DOUBLE |
注:
C#中类型转换接口:
将string转为IntPtr:
IntPtrSystem.Runtime.InteropServices.Marshal.StringToCoTaskMemAuto(string)
将IntPtr转为string:
stringSystem.Runtime.InteropServices.MarshalPtrToStringAuto(IntPtr)
3、代码分析
C#为了用上C++的代码,只好研究下从C# 中调用DLL,首先必须要有一个声明,使用的是DllImport关键字,包含DllImport所在的名字空间
using System.Runtime.InteropServices;class CCommand{ conststringdllpathfile ="..//..//..//..//Lib//DLL.dll"; [DllImport(dllpathfile, EntryPoint ="GetVersionPtr")] publicstaticexternboolGetVersionPtr(refCARDINFOinfo);}DllImport关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中,在类中,直接调用GetVersionPtr,在其他的类中调用CCommand.GetVersionPtr
[DllImport(dllpathfile)]在申明的时候还可以添加几个属性[DllImport(dllpathfile, EntryPoint="GetVersionPtr ",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]EntryPoint: 指定要调用的 DLL 入口点。默认入口点名称是托管方法的名称 。CharSet: 控制名称重整和封送 String 参数的方式 (默认是UNICODE)CallingConvention指示入口点的函数调用约定(默认WINAPI) SetLastError 指示被调用方在从属性化方法返回之前是否调用 SetLastError Win32 API 函数 (C#中默认false )
新闻热点
疑难解答
图片精选