在合作開發時,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 )
新聞熱點
疑難解答
圖片精選