十二月 29 2008

c# 如何调用非托管函数2-传递结构和类

Category: .net ? 刘武 @ 21:34

在上一篇 c# 如何调用非托管函数 中介绍了调用非托管函数的简单方法,其中传递的都是简单的值类型,如uint等。本篇将介绍如何传递结构或类到非托管函数。

以系统函数 GetSystemTime 为例,以下分别演示了如何传递结构和类。

一 传递结构 

参考MSDN可以知道他的定义为:

void WINAPI GetSystemTime(__out LPSYSTEMTIME lpSystemTime);

其中LPSYSTEMTIME 就是一个结构体:

typedef struct _SYSTEMTIME {
  WORD wYear;
  WORD wMonth;
  WORD wDayOfWeek;
  WORD wDay;
  WORD wHour;
  WORD wMinute;
  WORD wSecond;
  WORD wMilliseconds;
} SYSTEMTIME,
 *PSYSTEMTIME;
根据原始定义我们可以把它转成C#的形式(word类型相当于c#中的ushort)
[StructLayout(LayoutKind.Sequential)]
public struct SystemTime
{
    public ushort wYear;
    public ushort wMonth;
    public ushort wDayOfWeek;
    public ushort wDay;
    public ushort wHour;
    public ushort wMinute;
    public ushort wSecond;
    public ushort wMilliseconds;
}
由于该结构是作为out参数传递的,因此在c#中我们必须通过引用来传递:
[DllImport("Kernel32.dll")]
public static extern void GetSystemTime(ref SystemTime st);
以下是完整代码:
public class Kernel32
{
    [StructLayout(LayoutKind.Sequential)]
    public struct SystemTime
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }

    [DllImport("Kernel32.dll")]
    public static extern void GetSystemTime(ref SystemTime st);
}

调用起来也比较简单:
private void btnGetSysTime_Click(object sender, EventArgs e)
{
    Kernel32.SystemTime sysTime = new Kernel32.SystemTime();
    Kernel32.GetSystemTime(ref sysTime);

    MessageBox.Show(string.Format("{0}-{1}-{2} {3}:{4}:{5}",
        sysTime.wYear,
        sysTime.wMonth,
        sysTime.wDay,
        sysTime.wHour,
        sysTime.wMinute,
        sysTime.wSecond));
}

二 传递类

对于以上的例子,我们也可以把结构改成类传递给非托管函数,因为类是引用类型,所以我们不再需要ref声明:

public class Kernel32
{
    [StructLayout(LayoutKind.Sequential)]
    public class SystemTime
    {
        public ushort wYear;
        public ushort wMonth;
        public ushort wDayOfWeek;
        public ushort wDay;
        public ushort wHour;
        public ushort wMinute;
        public ushort wSecond;
        public ushort wMilliseconds;
    }

    [DllImport("Kernel32.dll")]
    public static extern void GetSystemTime(SystemTime st);
}

注意这里只是一个特例,并不是所有的情况既可以定义成结构类型也可以定义成类类型,通常我们应遵循以下原则:

  • 在非托管函数不要求任何间接寻址时使用按值传递的结构。
  • 在非托管函数要求一级间接寻址时使用按引用传递或按类传递的结构。
  • 在非托管函数要求二级间接寻址时使用按引用传递的类。

参考:

Tags: ,

十二月 22 2008

c# 如何调用非托管函数

Category: .net ? 刘武 @ 23:09

在目前的项目当中经常需要调用系统API,或者第三方的API,而这些API通常都不是基于.NET的,也就是所说的非托管函数,还好.NET为我们提供了平台调用服务,通过这个服务,就可以轻松的实现我们的需求。

调用过程其实比较简单,主要分以下几个步骤:

1) 找到函数的定义以及他所在的链接库(DLL文件)

以系统提供的BEEP函数为例(用指定的频率和时间发出蜂鸣声),他就是在动态链接库kernel32.dll中定义的。

在MSDN上可以找到他的函数签名为 BOOL Beep(DWORD dwFreq,DWORD dwDuration),可以看出该函数应该接受两个无符号的整数作为参数,第一个为频率,第二个为持续时间,并返回一个bool值。

2)在托管代码中创建函数原型

知道了函数的位置和签名,就可以为他编写托管定义了。看下面的代码[code=c#]using System.Runtime.InteropServices;
[DllImport("kernel32.dll")]
static extern bool Beep(uint dwFreq, uint dwDuration);[/code]首先引用了命名空间System.Runtime.InteropServices,他包含了所有平台调用必须的类,然后使用DllImport属性告诉编译器该函数的定义在kernel32.dll链接库中,由于该链接库位于system32目录中,因此我们不需要指定绝对路径。注意这里必须使用static和extern修饰符,以声明该函数是外部实现的,同时函数的签名也应该和原来的保持一致。 当然,我们也可以修改函数的名称,此时就必须指定入口点,如:

[DllImport("kernel32.dll", EntryPoint = "Beep")]
static  extern bool Beep2(uint dwFreq, uint dwDuration);
EntryPoint就指明了原来函数的名称。

3)调用

自此我们就可以像使用托管代码一样调用该函数了 ,下面是完整的代码

using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (uint i = 100; i <= 20000; i++)
            {
                Beep(i, 5);
            }
        }

        [DllImport("kernel32.dll")]
        static  extern bool Beep(uint dwFreq, uint dwDuration);
    }
}

注:也可以把Beep函数放在一个单独的类里面,不过要声明为Public类型

当然,这只是一个最简单的例子,当涉及到数据类型的转换以及函数回调的时候,情况就会复杂多了,这个就姑且做为一个开篇吧。

附:

想查看系统API的定义,可参考MSDN Win32 and COM Development

想知道更多的API调用方法,请参考P/INVOKE

关于DllImport属性的更多说明,请参考MSDN

 

Tags: ,

十二月 16 2008

就这么开始了

Category: 你我他 ? 刘武 @ 00:00

是的,就这么开始了

争取一个星期写一篇吧

以技术为主,不会涉及太多的私生活

内容也许会有些粗燥,慢慢来吧

祝大家好运~

Tags: