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

C#調(diào)用動態(tài)庫

 更新時間:2022年05月20日 09:18:56   作者:springsnow  
本文詳細(xì)講解了C#調(diào)用動態(tài)庫的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

一、引言

“為什么我們需要掌握互操作技術(shù)的呢?” 對于這個問題的解釋就是——掌握了.NET平臺下的互操作性技術(shù)可以幫助我們在.NET中調(diào)用非托管的dll和COM組件。

。.NET 平臺下提供了3種互操作性的技術(shù):

  • Platform Invoke(P/Invoke),即平臺調(diào)用,主要用于調(diào)用C庫函數(shù)和Windows API
  • C++ Introp, 主要用于Managed C++(托管C++)中調(diào)用C++類庫
  • COM Interop, 主要用于在.NET中調(diào)用COM組件和在COM中使用.NET程序集。

二、平臺調(diào)用

使用平臺調(diào)用的技術(shù)可以在托管代碼中調(diào)用動態(tài)鏈接庫(Dll)中實現(xiàn)的非托管函數(shù),如Win32 Dll和C/C++ 創(chuàng)建的dll。

2.1 在托管代碼中通過平臺調(diào)用來調(diào)用非托管代碼的步驟

(1).  獲得非托管函數(shù)的信息,即dll的名稱,需要調(diào)用的非托管函數(shù)名等信息

(2). 在托管代碼中對非托管函數(shù)進(jìn)行聲明,并且附加平臺調(diào)用所需要屬性

(3). 在托管代碼中直接調(diào)用第二步中聲明的托管函數(shù)

平臺調(diào)用的過程可以通過下圖更好地理解:

2.2、如何使用平臺調(diào)用Win32 函數(shù)——從實例開始

第一步就需要知道非托管函數(shù)聲明,為了找到需要需要調(diào)用的非托管函數(shù),可以借助兩個工具——Visual Studio自帶的dumpbin.exe和depends.exe.

  • dumpbin.exe 是一個命令行工具,可以用于查看從非托管DLL中導(dǎo)出的函數(shù)等信息,可以通過打開Visual Studio 2010 Command Prompt(中文版為Visual Studio 命令提示(2010)),然后切換到DLL所在的目錄,輸入 dummbin.exe/exports dllName, 如 dummbin.exe/exports User32.dll 來查看User32.dll中的函數(shù)聲明,關(guān)于更多命令的參數(shù)可以參看MSDN;
  • 然而 depends.exe是一個可視化界面工具,大家可以從 “VS安裝目錄\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Tools\Bin\”  這個路徑找到,然后雙擊  depends.exe 就可以出來一個可視化界面(如果某些人安裝的VS沒有附帶這個工具,也可以從官方網(wǎng)站下載:http://www.dependencywalker.com/),如下圖:

上圖中 用紅色標(biāo)示出 MessageBox 有兩個版本,而MessageBoxA 代表的就是ANSI版本,而MessageBoxW 代筆的就是Unicode版本,這也是上面所說的依據(jù)。下面就看看 MessageBox的C++聲明的(更多的函數(shù)的定義大家可以從MSDN中找到,這里提供MessageBox的定義在MSDN中的鏈接:http://msdn.microsoft.com/en-us/library/windows/desktop/ms645505(v=vs.85).aspx ):

int WINAPI MessageBox(
  _In_opt_  HWND hWnd,
  _In_opt_  LPCTSTR lpText,
  _In_opt_  LPCTSTR lpCaption,
  _In_      UINT uType
);

現(xiàn)在已經(jīng)知道了需要調(diào)用的Win32 API 函數(shù)的定義聲明,下面就依據(jù)平臺調(diào)用的步驟,在.NET 中實現(xiàn)對該非托管函數(shù)的調(diào)用,下面就看看.NET中的代碼的:

using System;

// 使用平臺調(diào)用技術(shù)進(jìn)行互操作性之前,首先需要添加這個命名空間
using System.Runtime.InteropServices;

namespace 平臺調(diào)用Demo
{
    class Program
    {
        // 在托管代碼中對非托管函數(shù)進(jìn)行聲明,并且附加平臺調(diào)用所需要屬性 在默認(rèn)情況下,CharSet為CharSet.Ansi
        // 指定調(diào)用哪個版本的方法有兩種——通過DllImport屬性的CharSet字段和通過EntryPoint字段指定 在托管函數(shù)中聲明注意一定要加上 static 和extern 這兩個關(guān)鍵字

        //第一種指定方式,通過CharSet字段指定,在默認(rèn)情況下CharSet為CharSet.Ansi
        [DllImport("user32.dll")]
        public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

        [DllImport("user32.dll")]
        public static extern int MessageBoxA(IntPtr hWnd, String text, String caption, uint type);

        // [DllImport("user32.dll", CharSet = CharSet.Unicode)] public static extern int
        // MessageBox(IntPtr hWnd, String text, String caption, uint type);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        public static extern int MessageBoxW(IntPtr hWnd, String text, String caption, uint type);

        // 通過EntryPoint字段指定
        [DllImport("user32.dll", EntryPoint = "MessageBoxA")]
        public static extern int MessageBox3(IntPtr hWnd, String text, String caption, uint type);

        [DllImport("user32.dll", EntryPoint = "MessageBoxW", CharSet = CharSet.Unicode)]
        public static extern int MessageBox4(IntPtr hWnd, String text, String caption, uint type);

        static void Main(string[] args)
        {
            // 在托管代碼中直接調(diào)用聲明的托管函數(shù) 使用CharSet字段指定的方式,要求在托管代碼中聲明的函數(shù)名必須與非托管函數(shù)名一樣 否則就會出現(xiàn)找不到入口點的運行時錯誤

            // 下面的調(diào)用都可以運行正確
            MessageBox(new IntPtr(0), "Learning Hard", "歡迎", 0);
            MessageBoxA(new IntPtr(0), "Learning Hard", "歡迎", 0);
            MessageBoxW(new IntPtr(0), "Learning Hard", "歡迎", 0);

            //使用指定函數(shù)入口點的方式調(diào)用,OK
            MessageBox3(new IntPtr(0), "Learning Hard", "歡迎", 0);
            MessageBox4(new IntPtr(0), "Learning Hard", "歡迎", 0);
        }
    }
}

2.3使用平臺調(diào)用技術(shù)中,還需要注意下面4點

(1). DllImport屬性的ExactSpelling字段如果設(shè)置為true時,則在托管代碼中聲明的函數(shù)名必須與要調(diào)用的非托管函數(shù)名完全一致,因為從ExactSpelling字面意思可以看出為 "準(zhǔn)確拼寫"的意思,當(dāng)ExactSpelling設(shè)置為true時,此時會改變平臺調(diào)用的行為,此時平臺調(diào)用只會根據(jù)根函數(shù)名進(jìn)行搜索,而找不到的時候不會添加 A或者W來進(jìn)行再搜索,.

[DllImport("user32.dll", ExactSpelling=true)]
       public static extern int MessageBox(IntPtr hWnd, String text, String caption, uint type);

(2). 如果采用設(shè)置CharSet的值來控制調(diào)用函數(shù)的版本時,則需要在托管代碼中聲明的函數(shù)名必須與根函數(shù)名一致,否則也會調(diào)用出錯

[DllImport("user32.dll")]
        public static extern int MessageBox1(IntPtr hWnd, String text, String caption, uint type);

(3). 如果通過指定DllImport屬性的EntryPoint字段的方式來調(diào)用函數(shù)版本時,此時必須相應(yīng)地指定與之匹配的CharSet設(shè)置,意思就是——如果指定EntryPoint為 MessageBoxW,那么必須將CharSet指定為CharSet.Unicode,如果指定EntryPoint為 MessageBoxA,那么必須將CharSet指定為CharSet.Ansi或者不指定,因為 CharSet默認(rèn)值就是Ansi。

 (4). CharSet還有一個可選字段為——CharSet.Auto, 如果把CharSet字段設(shè)置為CharSet.Auto,則平臺調(diào)用會針對目標(biāo)操作系統(tǒng)適當(dāng)?shù)刈詣臃馑妥址?。?Windows NT、Windows 2000、Windows XP 和 Windows Server 2003 系列上,默認(rèn)值為 Unicode;在 Windows 98 和 Windows Me 上,默認(rèn)值為 Ansi。

2.3、獲得Win32函數(shù)的錯誤信息

捕捉由托管定義導(dǎo)致的異常演示代碼:

            try
            {
                MessageBox1(new IntPtr(0), "Learning Hard", "歡迎", 0);
            }
            catch (DllNotFoundException dllNotFoundExc)
            {
                Console.WriteLine("DllNotFoundException 異常發(fā)生,異常信息為: " + dllNotFoundExc.Message);
            }
            catch (EntryPointNotFoundException entryPointExc)
            {
                Console.WriteLine("EntryPointNotFoundException 異常發(fā)生,異常信息為: " + entryPointExc.Message);
            }

捕獲由Win32函數(shù)本身返回異常的演示代碼如下:要想獲得在調(diào)用Win32函數(shù)過程中出現(xiàn)的錯誤信息,首先必須將DllImport屬性的SetLastError字段設(shè)置為true,只有這樣,平臺調(diào)用才會將最后一次調(diào)用Win32產(chǎn)生的錯誤碼保存起來,然后會在托管代碼調(diào)用Win32失敗后,通過Marshal類的靜態(tài)方法GetLastWin32Error獲得由平臺調(diào)用保存的錯誤碼,從而對錯誤進(jìn)行相應(yīng)的分析和處理。

    class Program
    {
        // Win32 API 
        //  DWORD WINAPI GetFileAttributes(
        //  _In_  LPCTSTR lpFileName
        //);

        // 在托管代碼中對非托管函數(shù)進(jìn)行聲明
        [DllImport("Kernel32.dll",SetLastError=true,CharSet=CharSet.Unicode)]
        public static extern uint GetFileAttributes(string filename);

        static void Main(string[] args)
        {
            // 試圖獲得一個不存在文件的屬性
            // 此時調(diào)用Win32函數(shù)會發(fā)生錯誤
            GetFileAttributes("FileNotexist.txt");

            // 在應(yīng)用程序的Bin目錄下存在一個test.txt文件,此時調(diào)用會成功
            //GetFileAttributes("test.txt");

            // 獲得最后一次獲得的錯誤
            int lastErrorCode = Marshal.GetLastWin32Error();

            // 將Win32的錯誤碼轉(zhuǎn)換為托管異常
            //Win32Exception win32exception = new Win32Exception();
            Win32Exception win32exception = new Win32Exception(lastErrorCode);
            if (lastErrorCode != 0)
            {
                Console.WriteLine("調(diào)用Win32函數(shù)發(fā)生錯誤,錯誤信息為 : {0}", win32exception.Message);
            }
            else
            {
                Console.WriteLine("調(diào)用Win32函數(shù)成功,返回的信息為: {0}", win32exception.Message);
            }

            Console.Read();
        }
    }

2.4 數(shù)據(jù)封送

數(shù)據(jù)封送是——在托管代碼中對非托管函數(shù)進(jìn)行互操作時,需要通過方法的參數(shù)和返回值在托管內(nèi)存和非托管內(nèi)存之間傳遞數(shù)據(jù)的過程,數(shù)據(jù)封送處理的過程是由CLR(公共語言運行時)的封送處理服務(wù)(即封送拆送器)完成的。

封送時需要處理的數(shù)據(jù)類型分為兩種——可直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型(blittable)和非直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型(non-bittable)。

2.4.1 可直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型

把在托管內(nèi)存和非托管內(nèi)存中有相同表現(xiàn)形式的數(shù)據(jù)類型稱為——可直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型,這些數(shù)據(jù)類型不需要封送拆送器進(jìn)行任何特殊的處理就可以在托管和非托管代碼之間傳遞,

下面列出一些課直接復(fù)制到本機(jī)結(jié)構(gòu)中的簡單數(shù)據(jù)類型:

 Windows 數(shù)據(jù)類型

非托管數(shù)據(jù)類型

托管數(shù)據(jù)類型

托管數(shù)據(jù)類型解釋

 BYTE/Uchar/UInt8

unsigned char

System.Byte

無符號8位整型

 Sbyte/Char/Int8

char

System.SByte

有符號8位整型

 Short/Int16

short

System.Int16

有符號16位整型

 USHORT/WORD/UInt16/WCHAR

unsigned short

System.UInt16

無符號16位整型

 Bool/HResult/Int/Long

long/int

System.Int32

有符號32位整型

 DWORD/ULONG/UINT

unsigned long/unsigned int

System.UInt32

無符號32位整型

 INT64/LONGLONG

_int64

System.Int64

有符號64位整型

 UINT64/DWORDLONG/ULONGLONG

_uint64

System.UInt64

無符號64位整型

 INT_PTR/hANDLE/wPARAM

void*/int或_int64

System.IntPtr

有符號指針類型

 HANDLE

void*

System.UIntPtr

無符號指針類型

 FLOAT

float

System.Single

單精度浮點數(shù)

 DOUBLE

double

System.Double

雙精度浮點數(shù)

除了上表列出來的簡單類型之外,還有一些復(fù)制類型也屬于可直接復(fù)制到本機(jī)結(jié)構(gòu)中的數(shù)據(jù)類型:

(1) 數(shù)據(jù)元素都是可直接復(fù)制到本機(jī)結(jié)構(gòu)中的一元數(shù)組,如整數(shù)數(shù)組,浮點數(shù)組等

(2)只包含可直接復(fù)制到本機(jī)結(jié)構(gòu)中的格式化值類型

(3)成員變量全部都是可復(fù)制到本機(jī)結(jié)構(gòu)中的類型且作為格式化類型封送的類

上面提到的格式化指的是——在類型定義時,成員的內(nèi)存布局在聲明時就明確指定的類型。在代碼中用StructLayout屬性修飾被指定的類型,并將StructLayout的LayoutKind屬性設(shè)置為Sequential或Explicit,例如:

using System.Runtime.InteropServices;

// 下面的結(jié)構(gòu)體也屬于可直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型
[StructLayout(LayoutKind.Sequential)]
public struct Point {
   public int x;
   public int y;
}

2.4.2 非直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型

對于這種類型,封送器需要對它們進(jìn)行相應(yīng)的類型轉(zhuǎn)換之后再復(fù)制到被調(diào)用的函數(shù)中,下面列出一些非直接復(fù)制到本機(jī)結(jié)構(gòu)中的數(shù)據(jù)類型:

 Windows 數(shù)據(jù)類型

非托管數(shù)據(jù)類型

托管數(shù)據(jù)類型

托管數(shù)據(jù)類型解釋

 Bool

bool

System.Boolean

布爾類型

 WCHAR/TCHAR

char/ wchar_t

System.Char

ANSI字符/Unicode字符

 LPCSTR/LPCWSTR/LPCTSTR/LPSTR/LPWSTR/LPTSTR

const char*/const wchar_t*/char*/wchar_t*

System.String

ANSI字符串/Unicode字符串,如果非托管代碼不需要更新此字符串時,此時用String類型在托管代碼中聲明字符串類型

 LPSTR/LPWSTR/LPTSTR

Char*/wchar_t*

System.StringBuilder

ANSI字符串/Unicode字符串,如果非托管代碼需要更新此字符串,然后把更新的字符串傳回托管代碼中,此時用StringBuilder類型在托管代碼中聲明字符串

除了上表中列出的類型之外,還有很多其他類型屬于非直接復(fù)制到本機(jī)結(jié)構(gòu)中的類型,例如其他指針類型和句柄類型等。

2.4.3、封送字符串的處理

封送作為返回值的字符串,下面是一段演示代碼,代碼中主要是調(diào)用Win32 GetTempPath函數(shù)來獲得返回臨時路徑,此時拆送器就需要把返回的字符串封送回托管代碼中。使用System.StringBuilder托管數(shù)據(jù)類型。

    // 托管函數(shù)中的返回值封送回托管函數(shù)的例子
    class Program
    {

        // Win32 GetTempPath函數(shù)的定義如下:   
        //DWORD WINAPI GetTempPath(
        //  _In_   DWORD nBufferLength,
        //  _Out_  LPTSTR lpBuffer
        //);  

        // 主要是注意如何在托管代碼中定義該函數(shù)原型       
        [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError=true)]
        public static extern uint GetTempPath(int bufferLength, StringBuilder buffer);
        static void Main(string[] args)
        {
            StringBuilder buffer = new StringBuilder(300);
            uint tempPath=GetTempPath(300, buffer);
            string path = buffer.ToString();
            if (tempPath == 0)
            {
                int errorcode =Marshal.GetLastWin32Error();
                Win32Exception win32expection = new Win32Exception(errorcode);
                Console.WriteLine("調(diào)用非托管函數(shù)發(fā)生異常,異常信息為:" +win32expection.Message);
            }

            Console.WriteLine("調(diào)用非托管函數(shù)成功。");
            Console.WriteLine("Temp 路徑為:" + buffer); 
            Console.Read();
        }
    }

2.4.4、封送結(jié)構(gòu)體的處理

在我們實際調(diào)用Win32 API函數(shù)時,經(jīng)常需要封送結(jié)構(gòu)體和類等復(fù)制類型,下面就以Win32 函數(shù)GetVersionEx為例子來演示如何對作為參數(shù)的結(jié)構(gòu)體進(jìn)行封送處理。

下面是GetVersionEx非托管定義(更多關(guān)于該函數(shù)的信息可以參看MSDN鏈接:http://msdn.microsoft.com/en-us/library/ms885648.aspx ):

BOOL GetVersionEx( 
  LPOSVERSIONINFO lpVersionInformation
);

參數(shù)lpVersionInformation是一個指向 OSVERSIONINFO結(jié)構(gòu)體的指針類型,所以我們在托管代碼中為函數(shù)GetVersionEx函數(shù)之前,必須知道 OSVERSIONINFO結(jié)構(gòu)體的非托管定義,然后再在托管代碼中定義一個等價的結(jié)構(gòu)體類型作為參數(shù)。以下是OSVERSIONINFO結(jié)構(gòu)體的非托管定義:

typedef struct  _OSVERSIONINFO{
    DWORD dwOSVersionInfoSize;       //在使用GetVersionEx之前要將此初始化為結(jié)構(gòu)的大小
    DWORD dwMajorVersion;               //系統(tǒng)主版本號
    DWORD dwMinorVersion;               //系統(tǒng)次版本號
    DWORD dwBuildNumber;               //系統(tǒng)構(gòu)建號
    DWORD dwPlatformId;                  //系統(tǒng)支持的平臺
    TCHAR szCSDVersion[128];          //系統(tǒng)補丁包的名稱
    WORD wServicePackMajor;            //系統(tǒng)補丁包的主版本
    WORD wServicePackMinor;            //系統(tǒng)補丁包的次版本
    WORD wSuiteMask;                      //標(biāo)識系統(tǒng)上的程序組
    BYTE wProductType;                    //標(biāo)識系統(tǒng)類型
    BYTE wReserved;                         //保留,未使用
} OSVERSIONINFO;

知道了OSVERSIONINFO結(jié)構(gòu)體在非托管代碼中的定義之后, 現(xiàn)在我們就需要在托管代碼中定義一個等價的結(jié)構(gòu),并且要保證兩個結(jié)構(gòu)體在內(nèi)存中的布局相同。托管代碼中的結(jié)構(gòu)體定義如下:

        // 因為Win32 GetVersionEx函數(shù)參數(shù)lpVersionInformation是一個指向 OSVERSIONINFO的數(shù)據(jù)結(jié)構(gòu)
        // 所以托管代碼中定義個結(jié)構(gòu)體,把結(jié)構(gòu)體對象作為非托管函數(shù)參數(shù)
        [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
        public struct OSVersionInfo
        {
            public UInt32 OSVersionInfoSize; // 結(jié)構(gòu)的大小,在調(diào)用方法前要初始化該字段
            public UInt32 MajorVersion; // 系統(tǒng)主版本號
            public UInt32 MinorVersion; // 系統(tǒng)此版本號
            public UInt32 BuildNumber;  // 系統(tǒng)構(gòu)建號
            public UInt32 PlatformId;  // 系統(tǒng)支持的平臺

            // 此屬性用于表示將其封送成內(nèi)聯(lián)數(shù)組
            [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
            public string CSDVersion; // 系統(tǒng)補丁包的名稱
            public UInt16 ServicePackMajor; // 系統(tǒng)補丁包的主版本
            public UInt16 ServicePackMinor;  // 系統(tǒng)補丁包的次版本
            public UInt16 SuiteMask;   //標(biāo)識系統(tǒng)上的程序組
            public Byte ProductType;    //標(biāo)識系統(tǒng)類型
            public Byte Reserved;  //保留,未使用
        }

從上面的定義可以看出, 托管代碼中定義的結(jié)構(gòu)體有以下三個方面與非托管代碼中的結(jié)構(gòu)體是相同的:

  • 字段聲明的順序
  • 字段的類型
  • 字段在內(nèi)存中的大小

并且在上面結(jié)構(gòu)體的定義中,我們使用到了 StructLayout 屬性,該屬性屬于System.Runtime.InteropServices命名空間(所以在使用平臺調(diào)用技術(shù)必須添加這個額外的命名空間)。這個類的作用就是允許開發(fā)人員顯式指定結(jié)構(gòu)體或類中數(shù)據(jù)字段的內(nèi)存布局,為了保證結(jié)構(gòu)體中的數(shù)據(jù)字段在內(nèi)存中的順序與定義時一致,所以指定為 LayoutKind.Sequential(該枚舉也是默認(rèn)值)。

下面就具體看看在托管代碼中調(diào)用的代碼:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace 封送結(jié)構(gòu)體的處理
{
    class Program
    {
        // 對GetVersionEx進(jìn)行托管定義
        // 為了傳遞指向結(jié)構(gòu)體的指針并將初始化的信息傳遞給非托管代碼,需要用ref關(guān)鍵字修飾參數(shù)
        // 這里不能使用out關(guān)鍵字,如果使用了out關(guān)鍵字,CLR就不會對參數(shù)進(jìn)行初始化操作,這樣就會導(dǎo)致調(diào)用失敗
        [DllImport("Kernel32",CharSet=CharSet.Unicode,EntryPoint="GetVersionEx")]
        private static extern Boolean GetVersionEx_Struct(ref  OSVersionInfo osVersionInfo);

        // 因為Win32 GetVersionEx函數(shù)參數(shù)lpVersionInformation是一個指向 OSVERSIONINFO的數(shù)據(jù)結(jié)構(gòu)
        // 所以托管代碼中定義個結(jié)構(gòu)體,把結(jié)構(gòu)體對象作為非托管函數(shù)參數(shù)
        [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
        public struct OSVersionInfo
        {
            public UInt32 OSVersionInfoSize; // 結(jié)構(gòu)的大小,在調(diào)用方法前要初始化該字段
            public UInt32 MajorVersion; // 系統(tǒng)主版本號
            public UInt32 MinorVersion; // 系統(tǒng)此版本號
            public UInt32 BuildNumber;  // 系統(tǒng)構(gòu)建號
            public UInt32 PlatformId;  // 系統(tǒng)支持的平臺

            // 此屬性用于表示將其封送成內(nèi)聯(lián)數(shù)組
            [MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]
            public string CSDVersion; // 系統(tǒng)補丁包的名稱
            public UInt16 ServicePackMajor; // 系統(tǒng)補丁包的主版本
            public UInt16 ServicePackMinor;  // 系統(tǒng)補丁包的次版本
            public UInt16 SuiteMask;   //標(biāo)識系統(tǒng)上的程序組
            public Byte ProductType;    //標(biāo)識系統(tǒng)類型
            public Byte Reserved;  //保留,未使用
        }

        // 獲得操作系統(tǒng)信息
        private static string GetOSVersion()
        {
            // 定義一個字符串存儲版本信息
            string versionName = string.Empty;

            // 初始化一個結(jié)構(gòu)體對象
            OSVersionInfo osVersionInformation = new OSVersionInfo();

            // 調(diào)用GetVersionEx 方法前,必須用SizeOf方法設(shè)置結(jié)構(gòu)體中OSVersionInfoSize 成員
            osVersionInformation.OSVersionInfoSize = (UInt32)Marshal.SizeOf(typeof(OSVersionInfo));

            // 調(diào)用Win32函數(shù)
            Boolean result = GetVersionEx_Struct(ref osVersionInformation);

            if (!result)
            {
                // 如果調(diào)用失敗,獲得最后的錯誤碼
                int errorcode = Marshal.GetLastWin32Error();
                Win32Exception win32Exc = new Win32Exception(errorcode);
                Console.WriteLine("調(diào)用失敗的錯誤信息為: " + win32Exc.Message);

                // 調(diào)用失敗時返回為空字符串
                return string.Empty;
            }
            else
            {
                Console.WriteLine("調(diào)用成功");
                switch (osVersionInformation.MajorVersion)
                {
                    // 這里僅僅討論 主版本號為6的情況,其他情況是一樣討論的
                    case 6:
                        switch (osVersionInformation.MinorVersion)
                        {
                            case 0:
                                if (osVersionInformation.ProductType == (Byte)0)
                                {
                                    versionName = " Microsoft Windows Vista";
                                }
                                else
                                {
                                    versionName = "Microsoft Windows Server 2008"; // 服務(wù)器版本
                                }
                                break;
                            case 1:
                                if (osVersionInformation.ProductType == (Byte)0)
                                {
                                    versionName = " Microsoft Windows 7";
                                }
                                else
                                {
                                    versionName = "Microsoft Windows Server 2008 R2";
                                }
                                break;
                            case 2:
                                versionName = "Microsoft Windows 8";
                                break;
                        }
                        break;
                    default:
                        versionName = "未知的操作系統(tǒng)";
                        break;
                }
                return versionName;
            }
        }

        static void Main(string[] args)
        {
            string OS=GetOSVersion();
            Console.WriteLine("當(dāng)前電腦安裝的操作系統(tǒng)為:{0}", OS);
            Console.Read();
        }
    }
}

2.4.5、封送類的處理

下面直接通過GetVersionEx函數(shù)進(jìn)行封送類的處理的例子,具體代碼如下:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

namespace 封送類的處理
{
    class Program
    {
        // 對GetVersionEx進(jìn)行托管定義
        // 由于類的定義中CSDVersion為String類型,String是非直接復(fù)制到本機(jī)結(jié)構(gòu)類型,
        // 所以封送拆送器需要進(jìn)行復(fù)制操作。
        // 為了是非托管代碼能夠獲得在托管代碼中對象設(shè)置的初始值(指的是OSVersionInfoSize字段,調(diào)用函數(shù)前首先初始化該值),
        // 所以必須加上[In]屬性;函數(shù)返回時,為了將結(jié)果復(fù)制到托管對象中,必須同時加上 [Out]屬性
        // 這里不能是用ref關(guān)鍵字,因為 OsVersionInfo是類類型,本來就是引用類型,如果加ref 關(guān)鍵字就是傳入的為指針的指針了,這樣就會導(dǎo)致調(diào)用失敗
        [DllImport("Kernel32", CharSet = CharSet.Unicode, EntryPoint = "GetVersionEx")]
        private static extern Boolean GetVersionEx_Struct([In, Out]  OSVersionInfo osVersionInfo);

        // 獲得操作系統(tǒng)信息
        private static string GetOSVersion()
        {
            // 定義一個字符串存儲操作系統(tǒng)信息
            string versionName = string.Empty;

            // 初始化一個類對象
            OSVersionInfo osVersionInformation = new OSVersionInfo();

            // 調(diào)用Win32函數(shù)
            Boolean result = GetVersionEx_Struct(osVersionInformation);

            if (!result)
            {
                // 如果調(diào)用失敗,獲得最后的錯誤碼
                int errorcode = Marshal.GetLastWin32Error();
                Win32Exception win32Exc = new Win32Exception(errorcode);
                Console.WriteLine("調(diào)用失敗的錯誤信息為: " + win32Exc.Message);

                // 調(diào)用失敗時返回為空字符串
                return string.Empty;
            }
            else
            {
                Console.WriteLine("調(diào)用成功");
                switch (osVersionInformation.MajorVersion)
                {
                    // 這里僅僅討論 主版本號為6的情況,其他情況是一樣討論的
                    case 6:
                        switch (osVersionInformation.MinorVersion)
                        {
                            case 0:
                                if (osVersionInformation.ProductType == (Byte)0)
                                {
                                    versionName = " Microsoft Windows Vista";
                                }
                                else
                                {
                                    versionName = "Microsoft Windows Server 2008"; // 服務(wù)器版本
                                }
                                break;
                            case 1:
                                if (osVersionInformation.ProductType == (Byte)0)
                                {
                                    versionName = " Microsoft Windows 7";
                                }
                                else
                                {
                                    versionName = "Microsoft Windows Server 2008 R2";
                                }
                                break;
                            case 2:
                                versionName = "Microsoft Windows 8";
                                break;
                        }
                        break;
                    default:
                        versionName = "未知的操作系統(tǒng)";
                        break;
                }
                return versionName;
            }
        }

        static void Main(string[] args)
        {
            string OS = GetOSVersion();
            Console.WriteLine("當(dāng)前電腦安裝的操作系統(tǒng)為:{0}", OS);
            Console.Read();
        }
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public class OSVersionInfo
    {
        public UInt32 OSVersionInfoSize = (UInt32)Marshal.SizeOf(typeof(OSVersionInfo));
        public UInt32 MajorVersion = 0;
        public UInt32 MinorVersion = 0;
        public UInt32 BuildNumber = 0;
        public UInt32 PlatformId = 0;

        // 此屬性用于表示將其封送成內(nèi)聯(lián)數(shù)組
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string CSDVersion = null;

        public UInt16 ServicePackMajor = 0;
        public UInt16 ServicePackMinor = 0;
        public UInt16 SuiteMask = 0;

        public Byte ProductType = 0;
        public Byte Reserved;
    }
}

三、COM Interop

為了解決在.NET中的托管代碼能夠調(diào)用COM組件的問題,.NET 平臺下提供了COM Interop,即COM互操作技術(shù)。

在.NET中使用COM對象,主要方法:使用TlbImp工具為COM組件創(chuàng)建一個互操作程序集來綁定早期的COM對象,這樣就可以在程序中添加互操作程序集來調(diào)用COM對象。

在.NET 中使用COM對象的步驟:

  • 找到要使用的COM 組件并注冊它。使用 regsvr32.exe 注冊或注銷 COM DLL。                
  • 在項目中添加對 COM 組件或類型庫的引用。                
  • 添加引用時,Visual Studio 會用到Tlbimp.exe(類型庫導(dǎo)入程序),Tlbimp.exe程序?qū)⑸梢粋€ .NET Framework 互操作程序集。該程序集又稱為運行時可調(diào)用包裝 (RCW),其中包含了包裝COM組件中的類和接口。Visual Studio 將生成組件的引用添加至項目。

  • 創(chuàng)建RCW中類的實例,這樣就可以使用托管對象一樣來使用COM對象。

在.NET中使用COM組件的過程:

如何在C#中調(diào)用COM組件——訪問Office 互操作對象

在新建的控制臺程序里添加”Microsoft.Office.Interop.Word 14.0.0.0 “ 這個引用

Microsoft.Office.Interop.Word.dll 確實是一個.NET程序集,并且它也叫做COM組件的互操作程序集,這個程序集中包含了COM組件中定義的類型的元數(shù)據(jù), 托管代碼通過調(diào)用互操作程序集中公開的接口或?qū)ο髞黹g接地調(diào)用COM對象和接口的。

關(guān)于通過Tlblmp.exe工具來生成互操作程序集步驟,這里我就不多詳細(xì)訴說了,大家可以參考MSDN中這個工具詳細(xì)使用說明 :http://msdn.microsoft.com/zh-cn/library/tt0cf3sx(v=VS.80).aspx 。

然而我們也可以使用Visual Studio中內(nèi)置的支持來完成為COM類型庫創(chuàng)建互操作程序集的工作,我們只需要在VS中為.NET 項目添加對應(yīng)的COM組件的引用,此時VS就會自動將COM類型庫中的COM類型庫轉(zhuǎn)化為程序集中的元數(shù)據(jù),并在項目的Bin目錄下生成對于的互操作程序集,所以在VS中添加COM引用,其實最后程序中引用的是互操作程序集,然后通過RCW來對COM組件進(jìn)行調(diào)用。 然而對于Office中的Microsoft.Office.Interop.Wordd.dll,這個程序集也是互操作程序集,但是它又是主互操作程序集,即PIA(Primary Interop Assemblies)。主互操作程序集是一個由供應(yīng)商提供的唯一的程序集,為了生成主互操作程序集,可以在使用TlbImp命令是打開 /primary 選項。

using System;
using System.Collections.Generic;
using System.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Word = Microsoft.Office.Interop.Word;


namespace OfficeProgramminWalkthruComplete
{
    class Walkthrough
    {
        static void Main(string[] args)
        {
            // Create a list of accounts.
            var bankAccounts = new List<Account> 
            {
                new Account { 
                              ID = 345678,
                              Balance = 541.27
                            },
                new Account {
                              ID = 1230221,
                              Balance = -127.44
                            }
            };

            // Display the list in an Excel spreadsheet.
            DisplayInExcel(bankAccounts);

            // Create a Word document that contains an icon that links to
            // the spreadsheet.
            CreateIconInWordDoc();
        }

        static void DisplayInExcel(IEnumerable<Account> accounts)
        {
            var excelApp = new Excel.Application();
            // Make the object visible.
            excelApp.Visible = true;

            // Create a new, empty workbook and add it to the collection returned 
            // by property Workbooks. The new workbook becomes the active workbook.
            // Add has an optional parameter for specifying a praticular template. 
            // Because no argument is sent in this example, Add creates a new workbook. 
            excelApp.Workbooks.Add();

            // This example uses a single workSheet. 
            Excel._Worksheet workSheet = excelApp.ActiveSheet;

            // Earlier versions of C# require explicit casting.
            //Excel._Worksheet workSheet = (Excel.Worksheet)excelApp.ActiveSheet;

            // Establish column headings in cells A1 and B1.
            workSheet.Cells[1, "A"] = "ID Number";
            workSheet.Cells[1, "B"] = "Current Balance";

            var row = 1;
            foreach (var acct in accounts)
            {
                row++;
                workSheet.Cells[row, "A"] = acct.ID;
                workSheet.Cells[row, "B"] = acct.Balance;
            }

            workSheet.Columns[1].AutoFit();
            workSheet.Columns[2].AutoFit();

            // Call to AutoFormat in Visual C#. This statement replaces the 
            // two calls to AutoFit.
            workSheet.Range["A1", "B3"].AutoFormat(
                Excel.XlRangeAutoFormat.xlRangeAutoFormatClassic2);

            // Put the spreadsheet contents on the clipboard. The Copy method has one
            // optional parameter for specifying a destination. Because no argument  
            // is sent, the destination is the Clipboard.
            workSheet.Range["A1:B3"].Copy();
        }

        static void CreateIconInWordDoc()
        {
            var wordApp = new Word.Application();
            wordApp.Visible = true;

            // The Add method has four reference parameters, all of which are 
            // optional. Visual C# allows you to omit arguments for them if
            // the default values are what you want.
            wordApp.Documents.Add();

            // PasteSpecial has seven reference parameters, all of which are 
            // optional. This example uses named arguments to specify values 
            // for two of the parameters. Although these are reference 
            // parameters, you do not need to use the ref keyword, or to create 
            // variables to send in as arguments. You can send the values directly.
            wordApp.Selection.PasteSpecial(Link: true, DisplayAsIcon: true);
        }
    }

    public class Account
    {
        public int ID { get; set; }
        public double Balance { get; set; }
    }
}

錯誤處理

            try
            {
                // 如果文檔不存在時,就會出現(xiàn)調(diào)用COM對象失敗的情況
                // 打開Word文檔
                wordDoc = wordApp.Documents.Open(wordPath);
                // 向Word中插入文本
                Range wordRange = wordDoc.Range(0, 0);
                wordRange.Text = "這是插入的文本";

                // 保存文檔
                wordDoc.Save();
            }
            catch(Exception ex)
            {          
                // 獲得異常相對應(yīng)的HRESULT值
                // 因為COM中根據(jù)方法返回的HRESULT來判斷調(diào)用是否成功的
                int HResult = Marshal.GetHRForException(ex);
                // 設(shè)置控制臺的前景色,即輸出文本的顏色
                Console.ForegroundColor = ConsoleColor.Red;
                // 下面把HRESULT值以16進(jìn)制輸出
                Console.WriteLine("調(diào)用拋出異常,異常類型為:{0}, HRESULT= 0x{1:x}", ex.GetType().Name, HResult);
                Console.WriteLine("異常信息為:" + ex.Message.Replace('\r', ' '));
            }
            finally
            {
                // 關(guān)閉文檔并
                if (wordDoc != null)
                {
                    wordDoc.Close();
                }
                // 退出Word程序
                wordApp.Quit();
            }

從上面的結(jié)果我們看到了一個 HRESULT值,這個值真是COM代碼中返回返回的。在COM中,COM方法通過返回 HRESULT 來報告錯誤;.NET 方法則通過引發(fā)異常來報告錯誤,為了方便地在托管代碼中獲得COM代碼中出現(xiàn)的錯誤和異常信息,CLR提供了兩者之間的轉(zhuǎn)換,每一個代表錯誤發(fā)生的HRESULT都會被映射到.NET Framework中的一個異常類,對于具體的映射關(guān)系可以參考MSDN中 的文章: http://msdn.microsoft.com/zh-cn/library/9ztbc5s1(VS.80).aspx。

到此這篇關(guān)于C#調(diào)用動態(tài)庫的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 結(jié)合.net框架在C#派生類中觸發(fā)基類事件及實現(xiàn)接口事件

    結(jié)合.net框架在C#派生類中觸發(fā)基類事件及實現(xiàn)接口事件

    這篇文章主要介紹了結(jié)合.net框架在C#派生類中觸發(fā)基類事件及實現(xiàn)接口事件,示例的事件編程中包括接口和類的繼承等面向?qū)ο蟮幕A(chǔ)知識,需要的朋友可以參考下
    2016-02-02
  • C#中的事務(wù)用法實例分析

    C#中的事務(wù)用法實例分析

    這篇文章主要介紹了C#中的事務(wù)用法,以一個簡單實例形式分析了C#創(chuàng)建及使用事物的相關(guān)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-09-09
  • Unity3D Shader實現(xiàn)掃描顯示效果

    Unity3D Shader實現(xiàn)掃描顯示效果

    這篇文章主要為大家詳細(xì)介紹了Unity3D Shader實現(xiàn)掃描顯示效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • C#根據(jù)反射和特性實現(xiàn)ORM映射實例分析

    C#根據(jù)反射和特性實現(xiàn)ORM映射實例分析

    這篇文章主要介紹了C#根據(jù)反射和特性實現(xiàn)ORM映射的方法,實例分析了反射的原理、特性與ORM的實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • c#中datagridview處理非綁定列的方法

    c#中datagridview處理非綁定列的方法

    這篇文章主要介紹了c#中datagridview處理非綁定列的方法,實例分析了C#中datagridview的使用技巧,需要的朋友可以參考下
    2015-06-06
  • 基于C#實現(xiàn)簡單離線注冊碼生成與驗證

    基于C#實現(xiàn)簡單離線注冊碼生成與驗證

    本文使用RSA非對稱加密和Base64簡單地實現(xiàn)離線注冊碼的生成與驗證功能。感興趣的朋友跟著小編一起學(xué)習(xí)吧
    2015-09-09
  • WPF中TreeView控件的用法

    WPF中TreeView控件的用法

    這篇文章介紹了WPF中TreeView控件的用法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • Unity接入百度AI實現(xiàn)貨幣識別

    Unity接入百度AI實現(xiàn)貨幣識別

    本文主要介紹了在Unity中接入百度AI,從而實現(xiàn)貨幣識別,可以返回貨幣的名稱、代碼、面值、年份信息等,感興趣的可以跟隨小編學(xué)習(xí)一下
    2022-01-01
  • c#解析jobject的數(shù)據(jù)結(jié)構(gòu)

    c#解析jobject的數(shù)據(jù)結(jié)構(gòu)

    這篇文章介紹了c#解析jobject數(shù)據(jù)結(jié)構(gòu)的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-07-07
  • c#通用登錄模塊分享

    c#通用登錄模塊分享

    這是一款簡單的ASP.NETC#注冊登錄模塊制作思路,非常簡單實用,雖然沒怎么考慮登陸的安全性,但作為C#的朋友學(xué)習(xí)交流使用。
    2016-07-07

最新評論