欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

從C#程序中調(diào)用非受管DLLs的方法

 更新時(shí)間:2014年10月15日 11:06:38   投稿:shichen2014  
這篇文章主要介紹了從C#程序中調(diào)用非受管DLLs的方法,是非常實(shí)用的技巧,有助于深入理解Windows程序設(shè)計(jì),需要的朋友可以參考下

本文實(shí)例講述了從C#程序中調(diào)用非受管DLLs的方法。分享給大家供大家參考。具體方法如下:

前言:

從所周知,.NET已經(jīng)漸漸成為一種技術(shù)時(shí)尚,那么C#很自然也成為一種編程時(shí)尚。如何利用浩如煙海的Win32 API以及以前所編寫的 Win32 代碼已經(jīng)成為越來越多的C#程序員所關(guān)注的問題。本文將介紹如何從C#代碼中調(diào)用非受管DLLs。如果某個(gè)函數(shù)是一個(gè)帶有串類型(char*)輸出參數(shù)的Win32 API 或者是DLL輸出函數(shù),那么從C#中如何調(diào)用它呢?對(duì)于輸入?yún)?shù)的情形問題到不大,但如何獲取從參數(shù)中返回的串呢?此外,如何調(diào)用有結(jié)構(gòu)(struct)和回調(diào)(callback)作為參數(shù)的函數(shù),如GetWindowsRect 和EnumWindows?那我們又如何將參數(shù)從C++和MFC中轉(zhuǎn)換成C# 所要的類型呢?下面就讓我們來一一解決這些問題。

微軟.NET的一個(gè)最主要的優(yōu)勢(shì)是它提供一個(gè)語(yǔ)言無關(guān)的開發(fā)系統(tǒng)。我們可以用Visual Basic、C++、C#等等語(yǔ)言來編寫類,然后在其它語(yǔ)言中使用,我們甚至可以用不同的語(yǔ)言來派生類。但是如何調(diào)用以前開發(fā)的非受管DLL呢?方法是必須將.NET對(duì)象轉(zhuǎn)化成結(jié)構(gòu)、char*以及C語(yǔ)言的指針。用行話說就是參數(shù)必須被列集(marshal)。說到列集,用一兩句話也說不清楚。所幸的是實(shí)現(xiàn)列集并不要我們知道太多的東西。

為了從C# 中調(diào)用DLL函數(shù),首先必須要有一個(gè)聲明,就象長(zhǎng)期以來使用Visual Basic的程序員所做的那樣,只不過在C#中使用的是DllImport關(guān)鍵字:

復(fù)制代碼 代碼如下:
using System.Runtime.InteropServices; // DllImport所在的名字空間
public class Win32 {
  [DllImport("User32.Dll")]
  public static extern void SetWindowText(int h, String s);
}

在C#中,DllImport關(guān)鍵字作用是告訴編譯器入口點(diǎn)在哪里,并將打包函數(shù)捆綁在一個(gè)類中。我們可以為這類取任何名字,這里不妨將類名取為 Win32。我們甚至可以將這個(gè)類放到一個(gè)名字空間中,就象下面的代碼這樣:

Win32API.cs 源代碼

復(fù)制代碼 代碼如下:
// Win32API: 此為名字空間,打包所選的Win32 API 函數(shù)
// 編譯方法:
//    csc /t:library /out:Win32API.dll Win32API.cs
//
using System;
using System.Drawing;
using System.Text;
using System.Runtime.InteropServices;

/////////////////////////////////////////////////////////////////
// 包裝Win32 API函數(shù)的名字空間。想用哪個(gè)Win32 API,往里添加即可。
//
namespace Win32API {
   [StructLayout(LayoutKind.Sequential)]
   public struct POINT {
      public POINT(int xx, int yy) { x=xx; y=yy; }
      public int x;
      public int y;
      public override string ToString() {
         String s = String.Format("({0},{1})", x, y);
         return s;
      }
   }

   [StructLayout(LayoutKind.Sequential)]
   public struct SIZE {
      public SIZE(int cxx, int cyy) { cx=cxx; cy=cyy; }
      public int cx;
      public int cy;
      public override string ToString() {
         String s = String.Format("({0},{1})", cx, cy);
         return s;
      }
   }

   [StructLayout(LayoutKind.Sequential)]
   public struct RECT {
      public int left;
      public int top;
      public int right;
      public int bottom;
      public int Width()      { return right - left; }
      public int Height()     { return bottom - top; }
      public POINT TopLeft()  { return new POINT(left,top); }
      public SIZE  Size()     { return new SIZE(Width(), Height()); }
      public override string ToString() {
         String s = String.Format("{0}x{1}", TopLeft(), Size());
         return s;
      }
   }

   public class Win32 {
      [DllImport("user32.dll")]
      public static extern bool IsWindowVisible(int hwnd);

      [DllImport("user32.dll")]
      public static extern int GetWindowText(int hwnd,
         StringBuilder buf, int nMaxCount);

      [DllImport("user32.dll")]
      public static extern int GetClassName(int hwnd,
         [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf,
         int nMaxCount);

      [DllImport("user32.dll")]
      public static extern int GetWindowRect(int hwnd, ref RECT rc);

      [DllImport("user32.dll")]
      // 注意,運(yùn)行時(shí)知道如何列集一個(gè)矩形
      public static extern int GetWindowRect(int hwnd, ref Rectangle rc);
   }
}

用下面的命令行可以編譯這段代碼: csc /t:library /out:Win32API.dll Win32API.cs    
成功編譯后,我們就有了一個(gè)可以在C#工程中使用的動(dòng)態(tài)庫(kù)了(Win32API.dll)。

復(fù)制代碼 代碼如下:
using Win32API;
int hwnd = // get it
String s = "I''''m so cute." ;
Win32.SetWindowText(hwnd, s);

 
編譯器知道在user32.dll中找到SetWindowText,并在調(diào)用前自動(dòng)將串轉(zhuǎn)換為L(zhǎng)PTSTR (TCHAR*)。真是神奇!.NET是如何實(shí)現(xiàn)的呢?其實(shí),每一個(gè)C#類型都有一個(gè)缺省的列集類型。對(duì)于串來說,它的列集類型就是LPTSTR。但如果調(diào)用的是GetWindowText,它的串參數(shù)是一個(gè)輸出參數(shù),而非輸入?yún)?shù),因?yàn)榇遣蛔兊模傧笊厦孢@樣處理就行不通了。我們可能一點(diǎn)都沒有注意到,不論什么時(shí)候處理一個(gè)串時(shí),都會(huì)創(chuàng)建一個(gè)新串。要想修改這個(gè)串,必須用StringBuilder:

復(fù)制代碼 代碼如下:
using System.Text; // StringBuilder所在的名字空間

public class Win32 {
  [DllImport("user32.dll")]
  public static extern int GetWindowText(int hwnd,
    StringBuilder buf, int nMaxCount);
}

StringBuilder缺省的列集類型是LPTSTR,但是GetWindowText現(xiàn)在可以修改實(shí)際的串。

復(fù)制代碼 代碼如下:
int hwnd = // get it
StringBuilder sb = new StringBuilder(256);
Win32.GetWindowText(hwnd, sb, sb.Capacity);

所以我們第一個(gè)問題的答案就是:使用StringBuilder。 前面討論的方法固然可以行得通,但有一種情況沒有考慮,那就是如果缺省的列集類型不是你想要的類型怎么辦?例如想要調(diào)用GetClassName,Windows編程高手都知道,GetClassName的參數(shù)與大多數(shù)其它的API函數(shù)的參數(shù)有所不同,它的串參數(shù)是LPSTR (char*),甚至是Unicode串。如果傳遞一個(gè)串,公共語(yǔ)言運(yùn)行時(shí)(CLR)將把它轉(zhuǎn)換成TCHARs——是不是很糟啊!不用害怕,我們可以用MarshalAs來改寫缺省的處理:

復(fù)制代碼 代碼如下:
[DllImport("user32.dll")]
public static extern int GetClassName(int hwnd,
  [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf,
  int nMaxCount);

現(xiàn)在我們調(diào)用GetClassName,.NET將串作為ANSI字符傳遞,而不是寬字符,搞掂! 以上我們解決了如何獲取函數(shù)載參數(shù)中返回的字符串。下面我們來看看結(jié)構(gòu)參數(shù)和回調(diào)參數(shù)的情形。不用說,.NET肯定有辦法處理它們。就拿GetWindowRect為例。這個(gè)函數(shù)用窗口屏幕坐標(biāo)填充一個(gè)RECT。

在C/C++中

復(fù)制代碼 代碼如下:
RECT rc;
HWND hwnd = FindWindow("foo",NULL);
::GetWindowRect(hwnd, &rc);     
在C#中如何調(diào)用呢?如何傳遞RECT呢?方法是將它作為一個(gè)C#結(jié)構(gòu),用另一個(gè)屬性:它就是StructLayout:

[StructLayout(LayoutKind.Sequential)]
public struct RECT {
  public int left;
  public int top;
  public int right;
  public int bottom;
}

一旦有了結(jié)構(gòu)定義,便可以象下面這樣來打包實(shí)現(xiàn):

復(fù)制代碼 代碼如下:
[DllImport("user32.dll")]

public static extern int
  GetWindowRect(int hwnd, ref RECT rc);


 
注意這里用到了ref,這一點(diǎn)很重要,CLR會(huì)將RECT作為引用傳遞,以便函數(shù)可以修改我們的對(duì)象,而不是無名字的堆??截悺6x了GetWindowRect之后,我們可以象下面這樣調(diào)用:

復(fù)制代碼 代碼如下:
RECT rc = new RECT();
int hwnd = // get it
Win32.GetWindowRect(hwnd, ref rc);


注意這里必須聲明并使用ref——羅嗦!C# 結(jié)構(gòu)的缺省列集類型還能是什么?——LPStruct,所以就不必再用MarshalAs了。但如果RECT是個(gè)類,而非結(jié)構(gòu)的話,那就必須象下面這樣實(shí)現(xiàn)打包:

復(fù)制代碼 代碼如下:
// 如果RECT 是個(gè)類,而不是結(jié)構(gòu)
[DllImport("user32.dll")]
public static extern int
  GetWindowRect(int hwnd,
    [MarshalAs(UnmanagedType.LPStruct)] RECT rc);

C#與C++類似,許多事情都可以殊途同歸,System.Drawing中已經(jīng)有了一個(gè)Rectangle結(jié)構(gòu)用來處理矩形,所以為什么要重新發(fā)明輪子呢?

復(fù)制代碼 代碼如下:
[DllImport("user32.dll")]
public static extern int GetWindowRect(int hwnd, ref Rectangle rc);

運(yùn)行時(shí)既然已經(jīng)知道如何將Rectangle作為Win32 RECT進(jìn)行列集。請(qǐng)注意,在實(shí)際的代碼中就沒有必要再調(diào)用GetWindowRect(Get/SetWindowText亦然),因?yàn)閃indows.Forms.Control類已具有這樣的屬性:用Control.DisplayRectangle獲取窗口矩形,用Control.Text設(shè)置/獲取控件文本

復(fù)制代碼 代碼如下:
Rectangle r = mywnd.DisplayRectangle;
mywnd.Text = "I''''m so cute";

如果出于某種原因已知的是某個(gè)HWND,而不是一個(gè)控件派生對(duì)象,那么只需要象示范的那樣來打包API。以上我們已經(jīng)搞掂了串、結(jié)構(gòu)以及矩形……還有什么呢?對(duì)了,還有回調(diào)(callbacks)。如何將回調(diào)從C#傳遞到非受管代碼呢?記住只要用委托(delegate)即可: delegate bool EnumWindowsCB(int hwnd,     int lparam);     
一旦聲明了委托/回調(diào)類型,就可以象下面這樣打包:

復(fù)制代碼 代碼如下:
[DllImport("user32")]
public static extern int
  EnumWindows(EnumWindowsCB cb, int lparam);

 
上面的delegate僅僅是聲明了一個(gè)委托類型,我們還必須在類中提供一個(gè)實(shí)際的委托實(shí)現(xiàn):
 
復(fù)制代碼 代碼如下:
// 在類中
public static bool MyEWP(int hwnd, int lparam) {
  // do something
  return true;
}

然后對(duì)它進(jìn)行打包處理:

復(fù)制代碼 代碼如下:
EnumWindowsCB cb = new EnumWindowsCB(MyEWP);
Win32.EnumWindows(cb, 0);

聰明的讀者回注意到我們這里掩飾了lparam的問題,在C中,如果你給EnumWindows一個(gè)LPARAM,則Windows會(huì)用它通知回調(diào)函數(shù)。一般典型的lparam是一個(gè)結(jié)構(gòu)或類指針,其中包含著我們需要的上下文信息。但是記住,在.NET中絕對(duì)不能提到"指針"!那么如何做呢?這是可以將lparam聲明為IntPtr并用GCHandle對(duì)它進(jìn)行打包:

復(fù)制代碼 代碼如下:
// 現(xiàn)在lparam 是 IntPtr
delegate bool EnumWindowsCB(int hwnd,     IntPtr lparam);

// 在GCHandle中打包對(duì)象
MyClass obj = new MyClass();
GCHandle gch = GCHandle.Alloc(obj);
EnumWindowsCB cb = new EnumWindowsCB(MyEWP);
   Win32.EnumWindows(cb, (IntPtr)gch);
   gch.Free();

最后不要忘了調(diào)用Free! C#中有時(shí)也需要與以往一樣必須要我們自己釋放占用的內(nèi)存。為了存取載枚舉器中的lparam"指針",必須使用

復(fù)制代碼 代碼如下:
GCHandle.Target。 public static bool MyEWP(int hwnd, IntPtr param) {
  GCHandle gch = (GCHandle)param;
  MyClass c = (MyClass)gch.Target;
  //  use it
  return true;
}

下面是一個(gè)窗口數(shù)組類:

WinArray.cs

復(fù)制代碼 代碼如下:
// WinArray: 用EnumWindows 產(chǎn)生頂層窗口的清單ArrayList
//
using System;
using System.Collections;
using System.Runtime.InteropServices;

namespace WinArray {

   public class WindowArray : ArrayList {
      private delegate bool EnumWindowsCB(int hwnd, IntPtr param);

      // 這里聲明的是private類型的委托,因?yàn)橹挥形沂褂盟?,其?shí)沒必要這樣做。
      [DllImport("user32")]
      private static extern int EnumWindows(EnumWindowsCB cb,
         IntPtr param);

      private static bool MyEnumWindowsCB(int hwnd, IntPtr param) {
         GCHandle gch = (GCHandle)param;
         WindowArray itw = (WindowArray)gch.Target;
         itw.Add(hwnd);
         return true;
      }

      // 這是唯一的public 類型方法,你需要調(diào)用的唯一方法
      public WindowArray() {
         GCHandle gch = GCHandle.Alloc(this);
         EnumWindowsCB ewcb = new EnumWindowsCB(MyEnumWindowsCB);
         EnumWindows(ewcb, (IntPtr)gch);
         gch.Free();
      }
   }
}

這個(gè)類將EnumWindows封裝在一個(gè)數(shù)組中,不用我們?cè)偃ミM(jìn)行繁瑣的委托和回調(diào),我們可以象下面這樣輕松使用這個(gè)類:

復(fù)制代碼 代碼如下:
WindowArray wins = new WindowArray();
foreach (int hwnd in wins) {
 // do something
}

是不是很帥啊!我們甚至還可以在受管C++中使用DllImport風(fēng)格的包裝類。在.NET環(huán)境中,只要能進(jìn)行相應(yīng)的轉(zhuǎn)換,便可以在受管和非受管世界之間隨心所欲地聘馳, 大多數(shù)情況下的轉(zhuǎn)換是自動(dòng)的,不必關(guān)心太多的事情。需要進(jìn)行MarshalAs或者打包GCHandle的情況很少。有關(guān)C#和非受管C++之間的平臺(tái)調(diào)用的其它細(xì)節(jié)問題,可以參考.NET的有關(guān)文檔。 下面是本文提供的一個(gè)帶有開關(guān)的控制臺(tái)小程序ListWin。它的功能是列出所有頂層窗口,輸出可以顯示HWNDs、窗口類名、窗口標(biāo)題以及窗口矩形,用RECT或Rectangle。這些內(nèi)容的顯示可用開關(guān)控制。

希望本文所述對(duì)大家的C#程序設(shè)計(jì)有所幫助。

相關(guān)文章

  • c#使用反射調(diào)用類型成員示例

    c#使用反射調(diào)用類型成員示例

    學(xué)習(xí)C#的時(shí)候就知道使用反射可以對(duì)我們編程提供極大的便利(動(dòng)態(tài)的獲取信息、調(diào)用類型成員、創(chuàng)建實(shí)例等等),下面示例說明一下使用方法
    2014-01-01
  • 深入理解C#中foreach遍歷的使用方法

    深入理解C#中foreach遍歷的使用方法

    在c#中通過foreach遍歷一個(gè)列表是經(jīng)常拿用的方法,使用起來也方便,下面這篇文章先給大家介紹了關(guān)于C#中foreach遍歷的使用方法,后面介紹了c#使用foreach注意的一些是,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。
    2017-08-08
  • C#操作windows注冊(cè)表的方法

    C#操作windows注冊(cè)表的方法

    這篇文章主要介紹了C#操作windows注冊(cè)表的方法,涉及C#針對(duì)注冊(cè)表的讀取與寫入技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-04-04
  • C#實(shí)現(xiàn)的JS操作類實(shí)例

    C#實(shí)現(xiàn)的JS操作類實(shí)例

    這篇文章主要介紹了C#實(shí)現(xiàn)的JS操作類,封裝了C#關(guān)于javascript的彈出對(duì)話框、返回上一頁(yè)、跳轉(zhuǎn)等常用操作,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-03-03
  • C#.NET字符串比較中忽略符號(hào)的方法

    C#.NET字符串比較中忽略符號(hào)的方法

    C#.NET字符串比較中忽略符號(hào)的方法,需要的朋友可以參考一下
    2013-04-04
  • C#制作網(wǎng)站掛機(jī)程序的實(shí)現(xiàn)示例

    C#制作網(wǎng)站掛機(jī)程序的實(shí)現(xiàn)示例

    本文主要介紹了C#制作網(wǎng)站掛機(jī)程序,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C#微信接口之推送模板消息功能示例

    C#微信接口之推送模板消息功能示例

    這篇文章主要介紹了C#微信接口之推送模板消息功能,結(jié)合實(shí)例形式分析了C#操作微信推送模板消息接口的具體操作步驟與相關(guān)使用技巧,需要的朋友可以參考下
    2017-07-07
  • c#中使用自動(dòng)屬性減少代碼輸入量

    c#中使用自動(dòng)屬性減少代碼輸入量

    .Net 3.0中的自動(dòng)屬性可以大幅度降低我們輸入的代碼量,需要的朋友可以參考下
    2012-12-12
  • 基于C#實(shí)現(xiàn)XML文件讀取工具類

    基于C#實(shí)現(xiàn)XML文件讀取工具類

    這篇文章主要介紹了基于C#實(shí)現(xiàn)XML文件讀取工具類,涉及C#針對(duì)XML文件各節(jié)點(diǎn)獲取的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • 使用C#實(shí)現(xiàn)替換文件中的IP地址

    使用C#實(shí)現(xiàn)替換文件中的IP地址

    這篇文章主要為大家詳細(xì)介紹了如何使用C#來處理一個(gè)實(shí)際的編程挑戰(zhàn):讀取一個(gè)配置文件并替換其中的IP地址,感興趣的小伙伴可以了解下
    2024-01-01

最新評(píng)論