亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

從C#程序中調用非受管DLLs的方法

2020-01-24 02:21:35
字體:
來源:轉載
供稿:網友

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

前言:

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

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

為了從C# 中調用DLL函數,首先必須要有一個聲明,就象長期以來使用Visual Basic的程序員所做的那樣,只不過在C#中使用的是DllImport關鍵字:

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

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

Win32API.cs 源代碼

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

/////////////////////////////////////////////////////////////////
// 包裝Win32 API函數的名字空間。想用哪個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")]
      // 注意,運行時知道如何列集一個矩形
      public static extern int GetWindowRect(int hwnd, ref Rectangle rc);
   }
}

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

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

 
編譯器知道在user32.dll中找到SetWindowText,并在調用前自動將串轉換為LPTSTR (TCHAR*)。真是神奇!.NET是如何實現的呢?其實,每一個C#類型都有一個缺省的列集類型。對于串來說,它的列集類型就是LPTSTR。但如果調用的是GetWindowText,它的串參數是一個輸出參數,而非輸入參數,因為串是不變的,再象上面這樣處理就行不通了。我們可能一點都沒有注意到,不論什么時候處理一個串時,都會創建一個新串。要想修改這個串,必須用StringBuilder:

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

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

StringBuilder缺省的列集類型是LPTSTR,但是GetWindowText現在可以修改實際的串。

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

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

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

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

在C/C++中

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

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

一旦有了結構定義,便可以象下面這樣來打包實現:

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

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


 
注意這里用到了ref,這一點很重要,CLR會將RECT作為引用傳遞,以便函數可以修改我們的對象,而不是無名字的堆棧拷貝。定義了GetWindowRect之后,我們可以象下面這樣調用:

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


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

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

C#與C++類似,許多事情都可以殊途同歸,System.Drawing中已經有了一個Rectangle結構用來處理矩形,所以為什么要重新發明輪子呢?

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

運行時既然已經知道如何將Rectangle作為Win32 RECT進行列集。請注意,在實際的代碼中就沒有必要再調用GetWindowRect(Get/SetWindowText亦然),因為Windows.Forms.Control類已具有這樣的屬性:用Control.DisplayRectangle獲取窗口矩形,用Control.Text設置/獲取控件文本

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

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

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

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

然后對它進行打包處理:

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

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

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

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

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

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

下面是一個窗口數組類:

WinArray.cs

復制代碼 代碼如下:
// WinArray: 用EnumWindows 產生頂層窗口的清單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類型的委托,因為只有我使用它,其實沒必要這樣做。
      [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 類型方法,你需要調用的唯一方法
      public WindowArray() {
         GCHandle gch = GCHandle.Alloc(this);
         EnumWindowsCB ewcb = new EnumWindowsCB(MyEnumWindowsCB);
         EnumWindows(ewcb, (IntPtr)gch);
         gch.Free();
      }
   }
}

這個類將EnumWindows封裝在一個數組中,不用我們再去進行繁瑣的委托和回調,我們可以象下面這樣輕松使用這個類:

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

是不是很帥?。∥覀兩踔吝€可以在受管C++中使用DllImport風格的包裝類。在.NET環境中,只要能進行相應的轉換,便可以在受管和非受管世界之間隨心所欲地聘馳, 大多數情況下的轉換是自動的,不必關心太多的事情。需要進行MarshalAs或者打包GCHandle的情況很少。有關C#和非受管C++之間的平臺調用的其它細節問題,可以參考.NET的有關文檔。 下面是本文提供的一個帶有開關的控制臺小程序ListWin。它的功能是列出所有頂層窗口,輸出可以顯示HWNDs、窗口類名、窗口標題以及窗口矩形,用RECT或Rectangle。這些內容的顯示可用開關控制。

希望本文所述對大家的C#程序設計有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕在线亚洲| 中文字幕欧美精品在线| 亚洲精品国产精品自产a区红杏吧| 国产婷婷色综合av蜜臀av| 国产精自产拍久久久久久| 亚洲一区中文字幕在线观看| 日韩av一区二区在线| 麻豆乱码国产一区二区三区| 精品视频在线播放色网色视频| 国产精品久久久久久久久粉嫩av| 亚洲视频在线看| 国产一区二区三区在线视频| 成人中文字幕+乱码+中文字幕| 日韩av在线一区二区| 一区二区三区在线播放欧美| 欧美精品中文字幕一区| 日韩专区在线播放| 人人爽久久涩噜噜噜网站| 亚洲成人黄色在线观看| 亚洲精品免费一区二区三区| 久热精品视频在线观看| 亚洲美女在线看| 超碰精品一区二区三区乱码| 26uuu另类亚洲欧美日本老年| 日韩精品一二三四区| 日韩欧美中文字幕在线播放| 精品福利免费观看| 亚洲精品视频在线观看视频| 久久精品电影网| 最近免费中文字幕视频2019| 亚洲国产欧美在线成人app| 亚洲欧美中文在线视频| 国产婷婷色综合av蜜臀av| 国产一区二区三区在线| 亚洲人成在线观看网站高清| 456国产精品| 亚洲无av在线中文字幕| 狠狠爱在线视频一区| 亚洲电影免费观看高清完整版在线观看| 久久精品国产亚洲7777| 国产欧美日韩最新| **欧美日韩vr在线| 久久天天躁狠狠躁夜夜躁2014| 2019精品视频| 日韩69视频在线观看| 亚洲一区二区三区毛片| 色综合久久中文字幕综合网小说| 97在线看免费观看视频在线观看| 久久av在线播放| 国产丝袜高跟一区| 夜夜嗨av一区二区三区四区| 国产精品视频久久久| 668精品在线视频| 日韩在线激情视频| 精品久久久久久国产91| 国产在线观看一区二区三区| 日韩在线高清视频| 国产欧美日韩亚洲精品| 亚洲第一天堂av| 5566日本婷婷色中文字幕97| 中文字幕日本精品| 国产精品夜色7777狼人| 亚洲一区二区三区成人在线视频精品| 成人综合国产精品| 日韩av在线高清| 日本不卡高字幕在线2019| 国产一区二区三区视频免费| 国产一区二区三区高清在线观看| 国产欧美一区二区三区视频| 黑丝美女久久久| 国产999在线| 在线视频一区二区| 久久影视电视剧免费网站| 日韩欧美成人网| 亚洲精品国精品久久99热| 亚洲精品一区二区在线| 精品亚洲一区二区三区在线播放| 欧美日韩中国免费专区在线看| 曰本色欧美视频在线| 中文字幕欧美日韩| 日韩精品电影网| 久久伊人91精品综合网站| 狠狠躁夜夜躁人人爽超碰91| 国产区精品视频| 中文日韩在线观看| 国产精品wwwwww| 欧美成人免费va影院高清| 高清欧美一区二区三区| 91精品国产综合久久久久久久久| 欧美在线视频观看| 亚洲一区二区三区777| 日韩电影中文字幕在线| 91国在线精品国内播放| 国产精品久久久久久久久久久新郎| 日韩av手机在线观看| 亚洲成avwww人| 日韩欧美国产免费播放| 免费成人高清视频| 国产精品日韩欧美大师| 精品久久久久久亚洲精品| 久久久久久久97| 欧美裸体xxxx极品少妇软件| 亚洲字幕一区二区| 热久久美女精品天天吊色| 日韩中文字幕在线播放| 午夜伦理精品一区| 中国日韩欧美久久久久久久久| 在线观看亚洲视频| 欧美裸体xxxxx| 国产综合久久久久| 亚洲自拍高清视频网站| 一区二区亚洲欧洲国产日韩| 欧美性少妇18aaaa视频| 日韩成人在线视频观看| 久久中文字幕一区| 亚洲www视频| 国产激情久久久| 日韩成人激情影院| 国产精品久久色| 久久久久久有精品国产| 中文字幕视频在线免费欧美日韩综合在线看| 国产精品视频网站| 亚洲中国色老太| 美女撒尿一区二区三区| 欧美在线视频一二三| 欧美日韩亚洲一区二区三区| 国产精品盗摄久久久| 国产精品久久久久久久久久东京| 国产精品入口尤物| 亚洲精品按摩视频| 久久福利视频导航| 欧美视频在线视频| 欧美高清视频在线播放| 亚洲女人天堂色在线7777| 精品欧美激情精品一区| 日韩中文字幕在线精品| 欧洲亚洲女同hd| 亚洲人成77777在线观看网| 国产精品免费视频xxxx| 美日韩精品免费观看视频| 久久免费在线观看| 欧美精品第一页在线播放| 亚洲精品日韩丝袜精品| 精品国产乱码久久久久酒店| 中文字幕自拍vr一区二区三区| 欧美做受高潮电影o| 中文字幕日韩欧美在线视频| 欧美午夜片欧美片在线观看| 欧美激情一区二区三区高清视频| 8050国产精品久久久久久| 日韩激情在线视频| 国产日韩欧美日韩| 欧美日韩亚洲精品内裤| 国产精品88a∨| 国产伊人精品在线| 欧美大片网站在线观看| 亚洲精品99999| 成人欧美在线视频| 国产成人中文字幕| 欧美黑人国产人伦爽爽爽| 日韩精品一区二区视频| 亚洲黄色av网站| 欧美日韩在线视频观看| 日韩av免费一区|