C# 中调用C++ DLL

[DllImport("img.dll")]
public static extern Int32 MakeImage(Int32 width, Int32 height, string lpszSTR, string lpszFileName);
//extern "C" _declspec(dllexport) int MakeImage(int width, int height,char* lpszSTR, char* lpszFileName

string strFile = "E:\\123.gif";
            string strText = "abc";

            Int32 iResult = MakeImage(100,100, strText, strFile);

 

 

 

C++            C#
=====================================
WORD              ushort
DWORD             uint
UCHAR             int/byte   大部分情况都可以使用int代替,而如果需要严格对齐的话则应该用bytebyte 
UCHAR*            string/IntPtr
unsigned char*    [MarshalAs(UnmanagedType.LPArray)]byte[]/?(Intptr)
char*             string
LPCTSTR           string
LPTSTR            [MarshalAs(UnmanagedType.LPTStr)] string
long              int
ulong             uint
Handle            IntPtr
HWND              IntPtr
void*             IntPtr
int               int
int*              ref int
*int              IntPtr
unsigned int      uint
COLORREF          uint 

 

 

 

遇到问题:

对 PInvoke 函数“Compress!Compress.Form1::SYUNCompress”的调用导致堆栈不对称。原因可能是托管

的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管的目标签

名是否匹配。
解决方法:[DllImport("SYCompression.dll", CallingConvention = CallingConvention.Cdecl)]
Cdecl   StdCall   Winapi取决于:
#define FUNCEXPORT extern "C" _declspec(dllexport)应该用Cdecl。

无法封送处理“return value”: 无效的托管/非托管类型组合。
原因是byte[]不能直接做为返回类型,必须用IntPtr类型。
[DllImport("SYCompression.dll",  CallingConvention = CallingConvention.Cdecl)]     
public static extern IntPtr SYUNCompress(byte[] srcBuf, int srcLen, out int tarLen);      //返回参数必须用IntPtr,传递参数必须用byte[],都不能用string
loadCompressLib();
byte[] tarBuf = new byte[srcLen];
int lpByte = (int)SYUNCompress(data, srcLen, out tarLen);
byte[] vbyte = new byte[tarLen];
for (int i = 0; i < tarLen; i++)
{
   vbyte[i] = (byte)Marshal.PtrToStructure((IntPtr)(lpByte++), typeof(byte));

}
WriteFile(vbyte, tarLen);
freeBuf();

 

 

函数调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。  

改 [DllImport("winmm.dll", EntryPoint = "sndPlaySoundA",)]
  为[DllImport("winmm.dll", EntryPoint = "sndPlaySoundA" , CallingConvention = CallingConvention.Cdecl)]

//C++中的DLL函数原型为
  //extern "C" __declspec(dllexport) bool 方法名一(const char* 变量名1, unsigned char* 变量名2)
  //extern "C" __declspec(dllexport) bool 方法名二(const unsigned char* 变量名1, char* 变量名2)


  //C#调用C++的DLL搜集整理的所有数据类型转换方式,可能会有重复或者多种方案,自己多测试
  //c++:HANDLE(void *) ---- c#:System.IntPtr  
  //c++:Byte(unsigned char) ---- c#:System.Byte  
  //c++:SHORT(short) ---- c#:System.Int16  
  //c++:WORD(unsigned short) ---- c#:System.UInt16  
  //c++:INT(int) ---- c#:System.Int16
  //c++:INT(int) ---- c#:System.Int32  
  //c++:UINT(unsigned int) ---- c#:System.UInt16
  //c++:UINT(unsigned int) ---- c#:System.UInt32
  //c++:LONG(long) ---- c#:System.Int32  
  //c++:ULONG(unsigned long) ---- c#:System.UInt32  
  //c++:DWORD(unsigned long) ---- c#:System.UInt32  
  //c++:DECIMAL ---- c#:System.Decimal  
  //c++:BOOL(long) ---- c#:System.Boolean  
  //c++:CHAR(char) ---- c#:System.Char  
  //c++:LPSTR(char *) ---- c#:System.String  
  //c++:LPWSTR(wchar_t *) ---- c#:System.String  
  //c++:LPCSTR(const char *) ---- c#:System.String  
  //c++:LPCWSTR(const wchar_t *) ---- c#:System.String  
  //c++:PCAHR(char *) ---- c#:System.String  
  //c++:BSTR ---- c#:System.String  
  //c++:FLOAT(float) ---- c#:System.Single  
  //c++:DOUBLE(double) ---- c#:System.Double  
  //c++:VARIANT ---- c#:System.Object  
  //c++:PBYTE(byte *) ---- c#:System.Byte[]  


  //c++:BSTR ---- c#:StringBuilder
  //c++:LPCTSTR ---- c#:StringBuilder
  //c++:LPCTSTR ---- c#:string
  //c++:LPTSTR ---- c#:[MarshalAs(UnmanagedType.LPTStr)] string  
  //c++:LPTSTR 输出变量名 ---- c#:StringBuilder 输出变量名
  //c++:LPCWSTR ---- c#:IntPtr
  //c++:BOOL ---- c#:bool   
  //c++:HMODULE ---- c#:IntPtr   
  //c++:HINSTANCE ---- c#:IntPtr  
  //c++:结构体 ---- c#:public struct 结构体{};  
  //c++:结构体 **变量名 ---- c#:out 变量名 //C#中提前申明一个结构体实例化后的变量名
  //c++:结构体 &变量名 ---- c#:ref 结构体 变量名
    


  //c++:WORD ---- c#:ushort
  //c++:DWORD ---- c#:uint
  //c++:DWORD ---- c#:int


  //c++:UCHAR ---- c#:int
  //c++:UCHAR ---- c#:byte
  //c++:UCHAR* ---- c#:string
  //c++:UCHAR* ---- c#:IntPtr


  //c++:GUID ---- c#:Guid
  //c++:Handle ---- c#:IntPtr
  //c++:HWND ---- c#:IntPtr
  //c++:DWORD ---- c#:int
  //c++:COLORREF ---- c#:uint




  //c++:unsigned char ---- c#:byte
  //c++:unsigned char * ---- c#:ref byte
  //c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]
  //c++:unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr


  //c++:unsigned char & ---- c#:ref byte
  //c++:unsigned char 变量名 ---- c#:byte 变量名
  //c++:unsigned short 变量名 ---- c#:ushort 变量名
  //c++:unsigned int 变量名 ---- c#:uint 变量名
  //c++:unsigned long 变量名 ---- c#:ulong 变量名


  //c++:char 变量名 ---- c#:byte 变量名 //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示
  //c++:char 数组名[数组大小] ---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)] public string 数组名; ushort


  //c++:char * ---- c#:string //传入参数
  //c++:char * ---- c#:StringBuilder//传出参数
  //c++:char *变量名 ---- c#:ref string 变量名
  //c++:char *输入变量名 ---- c#:string 输入变量名
  //c++:char *输出变量名 ---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名


  //c++:char ** ---- c#:string
  //c++:char **变量名 ---- c#:ref string 变量名
  //c++:const char * ---- c#:string
  //c++:char[] ---- c#:string
  //c++:char 变量名[数组大小] ---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名;  


  //c++:struct 结构体名 *变量名 ---- c#:ref 结构体名 变量名
  //c++:委托 变量名 ---- c#:委托 变量名


  //c++:int ---- c#:int
  //c++:int ---- c#:ref int
  //c++:int & ---- c#:ref int
  //c++:int * ---- c#:ref int //C#中调用前需定义int 变量名 = 0;


  //c++:*int ---- c#:IntPtr
  //c++:int32 PIPTR * ---- c#:int32[]
  //c++:float PIPTR * ---- c#:float[]
    


  //c++:double** 数组名 ---- c#:ref double 数组名
  //c++:double*[] 数组名 ---- c#:ref double 数组名
  //c++:long ---- c#:int
  //c++:ulong ---- c#:int
    
  //c++:UINT8 * ---- c#:ref byte //C#中调用前需定义byte 变量名 = new byte();   




  //c++:handle ---- c#:IntPtr
  //c++:hwnd ---- c#:IntPtr
    
    
  //c++:void * ---- c#:IntPtr   
  //c++:void * user_obj_param ---- c#:IntPtr user_obj_param
  //c++:void * 对象名称 ---- c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称




    
  //c++:char, INT8, SBYTE, CHAR ---- c#:System.SByte   
  //c++:short, short int, INT16, SHORT ---- c#:System.Int16   
  //c++:int, long, long int, INT32, LONG32, BOOL , INT ---- c#:System.Int32   
  //c++:__int64, INT64, LONGLONG ---- c#:System.Int64   
  //c++:unsigned char, UINT8, UCHAR , BYTE ---- c#:System.Byte   
  //c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t ---- c#:System.UInt16   
  //c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT ---- c#:System.UInt32   
  //c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG ---- c#:System.UInt64   
  //c++:float, FLOAT ---- c#:System.Single   
  //c++:double, long double, DOUBLE ---- c#:System.Double   


  //Win32 Types ---- CLR Type   
    


  //Struct需要在C#里重新定义一个Struct
  //CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);


  //unsigned char** ppImage替换成IntPtr ppImage
  //int& nWidth替换成ref int nWidth
  //int*, int&, 则都可用 ref int 对应
  //双针指类型参数,可以用 ref IntPtr
  //函数指针使用c++: typedef double (*fun_type1)(double); 对应 c#:public delegate double fun_type1(double);
  //char* 的操作c++: char*; 对应 c#:StringBuilder;
  //c#中使用指针:在需要使用指针的地方 加 unsafe




  //unsigned char对应public byte
  /*
  * typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg);
  * typedef void (*CALLBACKFUN1A)(char*, void* pArg);
  * bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void* pArg);
  * 调用方式为
  * [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  * public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)] StringBuilder strName, IntPtr pArg);
  *  
  *  
  */

 

 

C# 中调用C++ DLL (P/Invoke)

(原文:http://blog.chinaunix.net/u/18297/showart_296028.html

为了能用上原来的C++代码,只好研究下从C# 中调用DLL
首先必须要有一个声明,使用的是DllImport关键字:
包含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关键字作用是告诉编译器入口点在哪里,并将打包函数捆绑在这个类中
在调用的时候
在类中的时候 直接   mySum(a,b);就可以了
在其他类中调用: XXXX. mySum(a,b);
[DllImport(“MyDLL.dll”)]在申明的时候还可以添加几个属性
[DllImport(“MyDLL.dll", EntryPoint=" mySum",CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)
]
EntryPoint: 指定要调用的 DLL 入口点。默认入口点名称是托管方法的名称 。
CharSet: 控制名称重整和封送 String 参数的方式 (默认是UNICODE)
CallingConvention指示入口点的函数调用约定(默认WINAPI)(上次报告讲过的)

Cdecl 调用方清理堆栈。这使您能够调用具有 varargs 的函数(如 Printf),使之可用于接受可变数目的参数的方法。 
  FastCall 不支持此调用约定。 
  StdCall 被调用方清理堆栈。这是使用平台 invoke 调用非托管函数的默认约定。 
  ThisCall 第一个参数是 this 指针,它存储在寄存器 ECX 中。其他参数被推送到堆栈上。此调用约定用于对从非托管 DLL 导出的类调用方法。 
Winapi 此成员实际上不是调用约定,而是使用了默认平台调用约定。例如,在 Windows 上默认为 StdCall,在 Windows CE.NET 上默认为 Cdecl。 

SetLastError 指示被调用方在从属性化方法返回之前是否调用 SetLastError Win32 API 函数 (C#中默认false )
int 类型
[DllImport(“MyDLL.dll")]
//返回个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;
}
//参数传递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不会被改变
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 回调函数
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
using System;
using System.Runtime.InteropServices;
public delegate bool CallBack(int hwnd, int lParam); //定义委托函数类型
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  传递结构   (见代码)
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 回调函数,传递结构 想看的msdn里面都有专题介绍,看的我都是晕晕的:)
其他参考请搜索:
在C#程序设计中使用Win32类库
C#中调用C++托管Dll
如何在C#中加载自己编写的动态链接库
相关文章:Creating a P/Invoke Library
能用上DLL以后感觉还是很好的,原来的C++代码只要修改编译通过就可以了,
高兴没多久,发现.net2005居然可以用VB,VC开发智能设备项目,可以创建MFC智能设备项目
晕晕,难道可以直接用MFC来开发smartphone的程序了,赶紧看看,,,

 
 
 
 
 
 
 
 

C#调用DLL的方法介绍

2009-12-06  来自:网上整理  字体大小:【 大  中  小】
  • 摘要:本文介绍C#调用DLL的方法,每种编程语言调用DLL的方法都不尽相同,首先,您需要了解什么是托管,什么是非托管。
  • -

一.C#调用DLL与应用程序

    动态链接库(也称为DLL,即为“Dynamic Link Library”的缩写)是Microsoft Windows最重要的组成要素之一,打开Windows系统文件夹,你会发现文件夹中有很多DLL文件,Windows就是将一些主要的系统功能以DLL模块的形式实现。

    动态链接库是不能直接执行的,也不能接收消息,它只是一个独立的文件,其中包含能被程序或其它DLL调用来完成一定操作的函数(方法。注:C#中一般称为“方法”),但这些函数不是执行程序本身的一部分,而是根据进程的需要按需载入,此时才能发挥作用。

    DLL只有在应用程序需要时才被系统加载到进程的虚拟空间中,成为调用进程的一部分,此时该DLL也只能被该进程的线程访问,它的句柄可以被调用进程所使用,而调用进程的句柄也可以被该DLL所使用。在内存中,一个DLL只有一个实例,且它的编制与具体的编程语言和编译器都没有关系,所以可以通过DLL来实现混合语言编程。DLL函数中的代码所创建的任何对象(包括变量)都归调用它的线程或进程所有。

    下面列出了当程序使用 DLL 时提供的一些优点:

    1)使用较少的资源

    当多个程序使用同一个函数库时,DLL 可以减少在磁盘和物理内存中加载的代码的重复量。这不仅可以大大影响在前台运行的程序,而且可以大大影响其他在 Windows 操作系统上运行的程序。

    2)推广模块式体系结构

    DLL 有助于促进模块式程序的开发。这可以帮助您开发要求提供多个语言版本的大型程序或要求具有模块式体系结构的程序。模块式程序的一个示例是具有多个可以在运行时动态加载的模块的计帐程序。

    3)简化部署和安装

    当 DLL 中的函数需要更新或修复时,部署和安装 DLL 不要求重新建立程序与该 DLL 的链接。此外,如果多个程序使用 同一个 DLL,那么多个程序都将从该更新或修复中获益。当您使用定期更新或修复的第三方 DLL 时,此问题可能会更频繁地出现。

二.DLL的调用

    每种编程语言调用DLL的方法都不尽相同,在此只对用C#调用DLL的方法进行介绍。首先,您需要了解什么是托管,什么是非托管。一般可以认为:非托管代码主要是基于win 32平台开发的DLL,activeX的组件,托管代码是基于.net平台开发的。如果您想深入了解托管与非托管的关系与区别,及它们的运行机制,请您自行查找资料,本文件在此不作讨论。

    C#调用DLL中的非托管函数一般方法

    首先,应该在C#语言源程序中声明外部方法,其基本形式是:

    [DLLImport(“DLL文件”)]

    修饰符 extern 返回变量类型 方法名称 (参数列表)

    其中:

    DLL文件:包含定义外部方法的库文件。修饰符: 访问修饰符,除了abstract以外在声明方法时可以使用的修饰符。返回变量类型:在DLL文件中你需调用方法的返回变量类型。方法名称:在DLL文件中你需调用方法的名称。参数列表:在DLL文件中你需调用方法的列表。

    注意:需要在程序声明中使用System.Runtime.InteropServices命名空间。

    DllImport只能放置在方法声明上。

    DLL文件必须位于程序当前目录或系统定义的查询路径中(即:系统环境变量中Path所设置的路径)。

    返回变量类型、方法名称、参数列表一定要与DLL文件中的定义相一致。

    若要使用其它函数名,可以使用EntryPoint属性设置,如:
    [DllImport("user32.dll", EntryPoint="MessageBoxA")]
    static extern int MsgBox(int hWnd, string msg, string caption, int type);

    其它可选的 DllImportAttribute 属性:
    ◆CharSet 指示用在入口点中的字符集,如:CharSet=CharSet.Ansi;
    ◆SetLastError 指示方法是否保留 Win32"上一错误",如:SetLastError=true;
    ◆ExactSpelling 指示 EntryPoint 是否必须与指示的入口点的拼写完全匹配,如:ExactSpelling=false;
    ◆PreserveSig指示方法的签名应当被保留还是被转换, 如:PreserveSig=true;
    ◆CallingConvention指示入口点的调用约定, 如:CallingConvention=CallingConvention.Winapi;




使用 DllImport 属性

Visual Studio .NET 2003
3(共 4)对本文的评价是有帮助-评价此主题

本主题说明 DllImport 属性的常见用法。第一节讨论使用 DllImport 从托管应用程序调用本机代码的优点。第二节集中讨论封送处理和DllImport 属性的各个方面。

从托管应用程序调用非托管代码

当在托管应用程序中重用现有的非托管代码时,DllImport 属性非常有用。例如,托管应用程序可能需要调用非托管 WIN32 API。

下面的代码示例说明此通用方案,此示例将调用 MessageBox(位于 User32.lib 中):

复制
#using 
using namespace System::Runtime::InteropServices; 
// for DllImportAttribute

namespace SysWin32
{
   [DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
   int MessageBox(void* hWnd, wchar_t* lpText, wchar_t* lpCaption, 
                  unsigned int uType);
}

int main( )
{
   SysWin32::MessageBox( 0, L"Hello world!", L"Greetings", 0 );
}

主要注意包含 DllImport 的代码行。此代码行根据参数值通知编译器,使之声明位于 User32.dll 中的函数并将签名中出现的所有字符串(如参数或返回值)视为 Unicode 字符串。如果缺少EntryPoint参数,则默认值为函数名。另外,由于 CharSet 参数指定 Unicode,因此公共语言运行库将首先查找称为MessageBoxW(有 W 是因为 Unicode 规范)的函数。如果运行库未找到此函数,它将根据调用约定查找MessageBox 以及相应的修饰名。受支持的调用约定只有 __cdecl__stdcall

当调用用户定义的 DLL 中所包含的函数时,有必要将 extern "C" 添加在 DLL 函数声明之前,如下所示:

复制
// The function declaration in SampleDLL.h file
extern "C" SAMPLEDLL_API int fnSampleDLL(void);

有关受支持的其他参数值的更多信息,请参见 DllImport。

将非结构化参数由托管封送处理为非托管

除使用上述方法外,还可以使用另一种方法将托管参数(来自托管应用程序)封送处理为非托管参数(在非托管 DLL 中)。

以下代码示例说明封送处理技术:

复制
#using 
using namespace System; // To bring System::String in
using namespace System::Runtime::InteropServices; 
// for DllImportAttribute
namespace SysWin32
{
   [DllImport("user32.dll", EntryPoint = "MessageBox", CharSet = Unicode)]
   Int32 MessageBox( Int32 hWnd, String* lpText, String* lpCaption, 
                     UInt32 uType );
}

int main( )
{
   SysWin32::MessageBox(0, S"Hello world!", S"Greetings", 0);
}

完成实际的调用后,由于 CharSet 参数值的作用,所有参数字符串都自动转换为 wchar_t*。同样,所有Int32 参数类型都转换为非托管int,而 UInt32 参数类型转换为非托管unsigned int

下表提供关于转换非托管和托管上下文的指导:

非托管代码 C++ 的托管扩展
int Int32
unsigned int UInt32
short Int16
char* 用于 [in] 参数的 String* (CharSet = Ansi),用于 [out] 参数或返回值的Text::StringBuilder*
wchar_t* 用于 [in] 参数的 String* (CharSet = Unicode),用于 [out] 参数或返回值的Text::StringBuilder*
函数指针(回调)
限制:函数指针必须具有 __stdcall 调用约定,因为这是 DllImport 支持的唯一类型。
委托类型
数组(如 wchar_t*[])
限制:CharSet 参数仅应用于函数参数的根类型。因此,无论 CharSet 的值是什么,String* __gc[] 都将被封送处理为wchar_t* []
相应类型的托管数组(如 String*__gc[]

将结构化类型由非托管封送处理为托管

除简单类型外,运行库还提供了一种机制,可以将简单结构由托管上下文封送处理为非托管上下文。简单结构不包含任何内部数据成员指针、结构化类型的成员或其他元素。

例如,本主题显示如何调用本机 DLL 中具有以下签名的函数:

复制
#include 
struct S
{
   char* str;
   int n;
};

int __cdecl func( struct S* p )
{
   printf( "%s\n", p->str );
   return p->n;
}

若要创建此函数的托管包装,请将 StructLayout 属性应用到调用类。此属性确定封送处理结构时结构的组织方式。若要确保以传统的 C 格式组织结构,请指定顺序布局 (LayoutKind::Sequential)。结果代码如下:

复制
#using 
using namespace System;
using namespace System::Runtime::InteropServices;

// CharSet = Ansi(Unicode) means that everything that is a string 
// in this structure should be marshaled as Ansi(Unicode) 
// strings
[StructLayout( LayoutKind::Sequential, CharSet=Ansi )]
__gc class MS // To be compatible with the type in the native 
// code, this structure should have the members laid out in
// the same order as those in the native struct
{
public:
   String* m_str;
   Int32 m_n;
};

[DllImport("some.dll")]
Int32 func( MS* ptr );
int main( )
{
   MS* p = new MS;
   p->m_str = S"Hello native!";
   p->m_n = 7;
   Console::WriteLine(func(p)); // Should print 7
}

也可以在托管应用程序中使用 __nogc 关键字,以确保不发生封送处理:

复制
#include 
#include 
#using 
using namespace System;
using namespace System::Runtime::InteropServices;
__nogc class UMS
{
public:
   char* m_str;
   int m_n;
};
[DllImport("some.dll")]
Int32 func( UMS* ptr );
int main( )
{
   UMS* p = new UMS;
   p->m_str = strdup( "Hello native!" );
   p->m_n = 7;
   Console::WriteLine(func(p)); // Should print 7
   free( p->m_str );
   delete p;
}

第二个方案是:

复制
#include 
struct S
{
   wchar_t* str;
   int n;
};
int __cdecl func( struct S p )
{
   printf( "%S\n", p.str );
   return p.n;
}

注意参数是通过值传递的。若要在托管应用程序中包装此调用,请使用值而不是 __gc 类型。结果代码如下:

复制
#using 
using namespace System;
using namespace System::Runtime::InteropServices;
[StructLayout( LayoutKind::Sequential, CharSet=Unicode )]
__value class VS
{
public:
   String* m_str;
   Int32 m_n;
};
[DllImport( "some.dll" )]
Int32 func( VS ptr );
int main( )
{
   VS v;
   v.m_str = S"Hello native!";
   v.m_n = 7;
   Console::WriteLine(func(v)); // should print 7 also
}

你可能感兴趣的:(C# 中调用C++ DLL)