C#调用C++Dll封装时遇到的一系列问题 参考
在合作開發(fā)時(shí),C#時(shí)常需要調(diào)用C++DLL,當(dāng)傳遞參數(shù)時(shí)時(shí)常遇到問題,尤其是傳遞和返回字符串是,現(xiàn)總結(jié)一下,分享給大家:
VC++中主要字符串類型為:LPSTR,LPCSTR, LPCTSTR, string, CString, LPCWSTR, LPWSTR等
但轉(zhuǎn)為C#類型卻不完全相同。
主要有如下幾種轉(zhuǎn)換:
將string轉(zhuǎn)為IntPtr:IntPtr System.Runtime.InteropServices.Marshal.StringToCoTaskMemAuto(string)
將IntPtr轉(zhuǎn)為string:string System.Runtime.InteropServices.MarshalPtrToStringAuto(IntPtr)
?
類型對(duì)照:
BSTR ---------? StringBuilder
LPCTSTR --------- StringBuilder
LPCWSTR ---------? IntPtr
handle---------IntPtr
hwnd-----------IntPtr
char *----------string
int * -----------ref int
int &-----------ref int
void *----------IntPtr
unsigned char *-----ref byte
Struct需要在C#里重新定義一個(gè)Struct
CallBack回調(diào)函數(shù)需要封裝在一個(gè)委托里,delegate static extern int FunCallBack(string str);
注意在每個(gè)函數(shù)的前面加上public static extern +返回的數(shù)據(jù)類型,如果不加public ,函數(shù)默認(rèn)為私有函數(shù),調(diào)用就會(huì)出錯(cuò)。
在C#調(diào)用C++ DLL封裝庫(kù)時(shí)會(huì)出現(xiàn)兩個(gè)問題:
1. 數(shù)據(jù)類型轉(zhuǎn)換問題?
2. 指針或地址參數(shù)傳送問題
??? 首先是數(shù)據(jù)類型轉(zhuǎn)換問題。因?yàn)镃#是.NET語言,利用的是.NET的基本數(shù)據(jù)類型,所以實(shí)際上是將C++的數(shù)據(jù)類型與.NET的基本數(shù)據(jù)類型進(jìn)行對(duì)應(yīng)。
??? 例如C++的原有函數(shù)是:
int __stdcall FunctionName(unsigned char param1, unsigned short param2)
??? 其中的參數(shù)數(shù)據(jù)類型在C#中,必須轉(zhuǎn)為對(duì)應(yīng)的數(shù)據(jù)類型。如:
[DllImport(“ COM DLL path/file ”)]?
extern static int FunctionName(byte param1, ushort param2)
??? 因?yàn)檎{(diào)用的是__stdcall函數(shù),所以使用了P/Invoke的調(diào)用方法。其中的方法FunctionName必須聲明為靜態(tài)外部函數(shù),即加上extern static聲明頭。我們可以看到,在調(diào)用的過程中,unsigned char變?yōu)榱薭yte,unsigned short變?yōu)榱藆short。變換后,參數(shù)的數(shù)據(jù)類型不變,只是聲明方式必須改為.NET語言的規(guī)范。
??? 我們可以通過下表來進(jìn)行這種轉(zhuǎn)換:
Win32 Types?
CLR Type
char, INT8, SBYTE, CHAR?
System.SByte
short, short int, INT16, SHORT?
System.Int16
int, long, long int, INT32, LONG32, BOOL , INT?
System.Int32
__int64, INT64, LONGLONG?
System.Int64
unsigned char, UINT8, UCHAR , BYTE?
System.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t?
System.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT?
System.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG?
System.UInt64
float, FLOAT?
System.Single
double, long double, DOUBLE?
System.Double
??? 之后再將CLR的數(shù)據(jù)類型表示方式轉(zhuǎn)換為C#的表示方式。這樣一來,函數(shù)的參數(shù)類型問題就可以解決了。
??? 現(xiàn)在,我們?cè)賮砜紤]下一個(gè)問題,如果要調(diào)用的函數(shù)參數(shù)是指針或是地址變量,怎么辦?
??? 對(duì)于這種情況可以使用C#提供的非安全代碼來進(jìn)行解決,但是,畢竟是非托管代碼,垃圾資源處理不好的話對(duì)應(yīng)用程序是很不利的。所以還是使用C#提供的ref以及out修飾字比較好。
??? 同上面一樣,我們也舉一個(gè)例子:
int __stdcall FunctionName(unsigned char ¶m1, unsigned char *param2)
??? 在C#中對(duì)其進(jìn)行調(diào)用的方法是:
[DllImport(“ file ”)]?
extern static int FunctionName(ref byte param1, ref byte param2)
??? 看到這,可能有人會(huì)問,&是取地址,*是傳送指針,為何都只用ref就可以了呢?一種可能的解釋是ref是一個(gè)具有重載特性的修飾符,會(huì)自動(dòng)識(shí)別是取地址還是傳送指針。
??? 在實(shí)際的情況中,我們利用參數(shù)傳遞地址更多還是用在傳送數(shù)組首地址上。?
如:byte[] param1 = new param1(6);
??? 在這里我們聲明了一個(gè)數(shù)組,現(xiàn)在要將其的首地址傳送過去,只要將param1數(shù)組的第一個(gè)元素用ref修飾。具體如下:
[DllImport(“ file ”)]?
extern static int FunctionName(ref byte param1[1], ref byte param2)?
??
文章出處:DIY部落(http://www.diybl.com/course/3_program/c++/cppjs/200886/134816.html)
?
?
C# 中調(diào)用DLL?
為了能用上原來的C++代碼,只好研究下從C# 中調(diào)用DLL
首先必須要有一個(gè)聲明,使用的是DllImport關(guān)鍵字:?
包含DllImport所在的名字空間?
using System.Runtime.InteropServices;?
public class XXXX{
[DllImport(“MyDLL.dll")]?
public static extern int mySum (int a,int b);?
}
[DllImport(“MyDLL.dll")]?
public static extern int mySum (int a,int b);?
代碼中DllImport關(guān)鍵字作用是告訴編譯器入口點(diǎn)在哪里,并將打包函數(shù)捆綁在這個(gè)類中?
在調(diào)用的時(shí)候?
在類中的時(shí)候 直接 ? mySum(a,b);就可以了?
在其他類中調(diào)用: XXXX. mySum(a,b);?
?
[DllImport(“MyDLL.dll”)]在申明的時(shí)候還可以添加幾個(gè)屬性?
[DllImport(“MyDLL.dll", EntryPoint=" mySum ",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)?
]?
EntryPoint: 指定要調(diào)用的 DLL 入口點(diǎn)。默認(rèn)入口點(diǎn)名稱是托管方法的名稱 。?
CharSet: 控制名稱重整和封送 String 參數(shù)的方式 (默認(rèn)是UNICODE)?
CallingConvention指示入口點(diǎn)的函數(shù)調(diào)用約定(默認(rèn)WINAPI)(上次報(bào)告講過的)?
SetLastError 指示被調(diào)用方在從屬性化方法返回之前是否調(diào)用 SetLastError Win32 API 函數(shù) (C#中默認(rèn)false )
int 類型?
[DllImport(“MyDLL.dll")]?
//返回個(gè)int 類型?
public static extern int mySum (int a1,int b1);?
//DLL中申明?
extern “C” __declspec(dllexport)? int WINAPI mySum(int a2,int b2)?
{?
//a2 b2不能改變a1 b1
//a2=..
//b2=...
?return a+b;?
}
//參數(shù)傳遞int 類型?
public static extern int mySum (ref int a1,ref int b1);?
//DLL中申明?
extern “C” __declspec(dllexport)? int WINAPI mySum(int *a2,int *b2)?
{?
//可以改變 a1, b1
*a2=...
*b2=...
?return a+b;?
}?
DLL 需傳入char *類型?
[DllImport(“MyDLL.dll")]?
//傳入值?
public static extern int mySum (string? astr1,string bstr1);?
//DLL中申明?
extern “C” __declspec(dllexport)? int WINAPI mySum(char * astr2,char * bstr2)?
{?
//改變astr2 bstr 2? ,astr1 bstr1不會(huì)被改變
?return a+b;?
}
DLL 需傳出char *類型?
[DllImport(“MyDLL.dll")]?
// 傳出值
public static extern int mySum (StringBuilder abuf, StringBuilder bbuf );?
//DLL中申明?
extern “C” __declspec(dllexport)? int WINAPI mySum(char * astr,char * bstr)?
{?
//傳出char * 改變astr bstr -->abuf, bbuf可以被改變
?return a+b;?
}?
?
DLL 回調(diào)函數(shù)?
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)?
using System;?
using System.Runtime.InteropServices;?
public delegate bool CallBack(int hwnd, int lParam); //定義委托函數(shù)類型?
public class EnumReportApp?
{?
[DllImport("user32")]?
public static extern int EnumWindows(CallBack x, int y);?
public static void Main() {?
CallBack myCallBack = new CallBack(EnumReportApp.Report); EnumWindows(myCallBack, 0);?
}?
public static bool Report(int hwnd, int lParam)?
{?
Console.Write("Window handle is ");?
Console.WriteLine(hwnd); return true;?
}?
}?
?
DLL? 傳遞結(jié)構(gòu)??
BOOL PtInRect(const RECT *lprc, POINT pt);?
using System.Runtime.InteropServices;?
[StructLayout(LayoutKind.Sequential)]?
public struct Point {
?public int x;?
public int y;
?}?
[StructLayout(LayoutKind.Explicit)]?
?public struct Rect?
?{?
[FieldOffset(0)] public int left;?
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;?
[FieldOffset(12)] public int bottom;
?}?
Class XXXX {?
?[DllImport("User32.dll")]?
public static extern bool PtInRect(ref? Rect r, Point p);?
?}
DLL 回調(diào)函數(shù),傳遞結(jié)構(gòu) 想看的msdn里面都有專題介紹,看的我都是暈暈的:)
其他參考請(qǐng)搜索:
在C#程序設(shè)計(jì)中使用Win32類庫(kù)
C#中調(diào)用C++托管Dll
如何在C#中加載自己編寫的動(dòng)態(tài)鏈接庫(kù)
相關(guān)文章:Creating a P/Invoke Library
能用上DLL以后感覺還是很好的,原來的C++代碼只要修改編譯通過就可以了,
高興沒多久,發(fā)現(xiàn).net2005居然可以用VB,VC開發(fā)智能設(shè)備項(xiàng)目,可以創(chuàng)建MFC智能設(shè)備項(xiàng)目
暈暈,難道可以直接用MFC來開發(fā)smartphone的程序了,趕緊看看,,,
總結(jié)
以上是生活随笔為你收集整理的C#调用C++Dll封装时遇到的一系列问题 参考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果产品开发的绝密工作流程
- 下一篇: ffmpeg的编译(for x86,fo