C#调用DLL时参数问题

C#导入DLL时,参数怎么定义是一个比较头痛的问题。特别是指针类型的参数,关于此问题本人有点不成熟的经验。

以 GetComputerName这个函数为例。

函数原型如下:


BOOL GetComputerName(
  LPTSTR lpBuffer,
  LPDWORD lpnSize
);

这个lpBuffer就是下个string型的指针,其实无论是什么类型的指针,对于Windows来说都是一个32位的无符号的整数,也就是一个内在地址,函数之所以使用指针就是要向指针所指向的内存空间写入数据。

我们用C#调用时也要给它传递一个指针,还要对应一块分配的空间 。

下面是代码: 
using System.Runtime.InteropServices;   
       [DllImport("kernel32.dll")]   
       static extern bool GetComputerName(IntPtr p , ref int lpnSize);   
       //分配空间   
       IntPtr p = Marshal.AllocHGlobal(128);   
       //Console.WriteLine(p);  指针内容 也就是内存地址   
       int len = 128;   
  
       GetComputerName(p, ref len);   
  
       //Console.WriteLine(p); 和上次的内容一样   
 


            //下面是关键   
            //p所指向的是一块非托管的空间    要将其转化成托管下的string
            string str = Marshal.PtrToStringAnsi(p);

            Console.WriteLine(str);

其实和C++在调用的本质是一样的,都要分配空间并将空间的地址传给函数。只是C#是运行在托管环境,所以对空间的分配的数据的读取都要特殊处理。还有更简单的方法,就是用StringBuilder。本质上还是和上面一样的,只不过是C#替你做了得多工作。
[DllImport("kernel32.dll")]
static extern bool GetComputerName(StringBuilder lpBuffer, ref int lpnSize);
 
对于结构体也是一样,以下面结构体为例说明一下。


BOOL GetVersionEx( 
  LPOSVERSIONINFO lpVersionInformation
);

typedef struct _OSVERSIONINFO{ 
  DWORD dwOSVersionInfoSize; 
  DWORD dwMajorVersion; 
  DWORD dwMinorVersion; 
  DWORD dwBuildNumber; 
  DWORD dwPlatformId; 
  TCHAR szCSDVersion[128]; 
} OSVERSIONINFO; 

参数是一个结构体的指针,函数会填充结构体的各个字段,其实就是向这块内存空间的不同位置写入不同的数据。

[DllImport("kernel32.dll", EntryPoint = "GetVersionEx")]   
static extern bool GetVersionEx(IntPtr p);   
  
IntPtr pv = Marshal.AllocHGlobal(148);   
Marshal.WriteInt32(pv, 148);   
if (GetVersionEx(pv))   
       Console.WriteLine("OK");   
 Console.WriteLine("MajorVersion:" + Marshal.ReadInt32(pv, 4));   
 Console.WriteLine("BuildNumber:" + Marshal.ReadInt32(pv, 12));    
 Console.WriteLine(Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32() + 20)));  
          

根据结构体的定义有5个DWORD和一个128位的CHAR数组,所以要给这个结构体分配148位空间。结构体的第一个字段这是个结构体的大小,我 们用 Marshal.WriteInt32(pv, 148);写入结构体的大小(这也是Windows API在使用结构体的一个特点,就是大部分结构体在传给函数填充前要指定其大小。),然后执行函数。如果函数正常返回,就可以根据结构体的定义从相应的位 置读出数据。

Marshal.ReadInt32(pv, 4);读取第二个整数也就是MajorVersion,Marshal.ReadInt32(pv, 12);第四个是BuildNumber,Marshal.PtrToStringAnsi((IntPtr)(pv.ToInt32() + 20));五个整数之后是CSDVersion。

写上面的内容只是想分析一下机理,如果学习过汇编就很好理解了。

下面是通常的做法:

[StructLayout(LayoutKind.Sequential)]   
public struct OSVersionInfo   
{   
    public int OSVersionInfoSize;   
    public int majorVersion;   
    public int minorVersion;   
    public int buildNumber;   
    public int platformId;   
    [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]   
    public string versionString;   
}   
  
  
[DllImport("kernel32.dll", EntryPoint = "GetVersionEx")]   
static extern bool GetVersionEx(ref OSVersionInfo osinfo);     
  
 OSVersionInfo info = new OSVersionInfo();   
 //结构体的大小的指定   
 info.OSVersionInfoSize = Marshal.SizeOf(typeof(OSVersionInfo));   
  
if (GetVersionEx(ref info))   
        Console.WriteLine("OK");  
        [StructLayout(LayoutKind.Sequential)]
        public struct OSVersionInfo
        {
            public int OSVersionInfoSize;
            public int majorVersion;
            public int minorVersion;
            public int buildNumber;
            public int platformId;
            [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
            public string versionString;
        }
         [DllImport("kernel32.dll", EntryPoint = "GetVersionEx")]
        static extern bool GetVersionEx(ref OSVersionInfo osinfo); 

         OSVersionInfo info = new OSVersionInfo();
         //结构体的大小的指定
         info.OSVersionInfoSize = Marshal.SizeOf(typeof(OSVersionInfo));

        if (GetVersionEx(ref info))
                Console.WriteLine("OK");

< type="text/JavaScript"> < src="http://a.alimama.cn/inf.js" type="text/javascript">


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值