2017年8月17日 星期四

C#呼叫cmd範例

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SecurityRight
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主要進入點。
        /// </summary>
        [STAThread]
        static void Main()
        {
            //只能是應用程式名稱,不能加入副檔名
            Process[] x = Process.GetProcessesByName("LINE");
            if (x.Length <= 0)
            {
                MessageBox.Show("沒有");
            }
            else
            {
                MessageBox.Show("有");
            }
            System.Diagnostics.Process[] p1 = System.Diagnostics.Process.GetProcesses();
            foreach (System.Diagnostics.Process pro in p1)
            {
                //只能是應用程式名稱,不能加入副檔名
                if (pro.ProcessName.ToUpper().Contains("LINE"))
                {
                    MessageBox.Show("有了");
                    //U r Operations
                }
            };
            if (p1.Length <= 0)
            {
                // string sDir = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System";
                // object v = Registry.GetValue(sDir, "EnableLUA", RegistryValueKind.DWord);
                //   if (v.ToString() == "1")
                //   {
                //  string str = "";

                // if (Directory.Exists(@"C:\Program Files (x86)\APM\APMS Client\Log"))
                //     str = @"C:\Program Files (x86)\APM\APMS Client\SecurityFile.exe";
                // if (Directory.Exists(@"C:\Program Files\APM\APMS Client\Log"))
                //      str = @"C:\Program Files\APM\APMS Client\SecurityFile.exe";
                System.Diagnostics.Process p = new System.Diagnostics.Process();
                p.StartInfo.FileName = "cmd.exe";
                //if (Directory.Exists(@"C:\Program Files (x86)\APM\APMS Client\Log"))
                //    p.StartInfo.Arguments = @"/k cd\Program Files (x86)\APM\APMS Client\";
                //if (Directory.Exists(@"C:\Program Files\APM\APMS Client\Log"))
                //    p.StartInfo.Arguments = @"/k cd\Program Files\APM\APMS Client\";
                p.StartInfo.UseShellExecute = false;
                p.StartInfo.RedirectStandardInput = true;
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.RedirectStandardError = true;
                p.StartInfo.CreateNoWindow = true;
                p.Start();
                p.StandardInput.WriteLine("C:");
                //if (Directory.Exists(@"C:\Program Files (x86)\APM\APMS Client\Log"))
                //    p.StandardInput.WriteLine(@"cd\Program Files (x86)\APM\APMS Client");
                //if (Directory.Exists(@"C:\Program Files\APM\APMS Client\Log"))
                //    p.StandardInput.WriteLine(@"cd\Program Files\APM\APMS Client");
                //判斷是否存在資料匣
                if (Directory.Exists(@"C:\Users\tony777\AppData\Local\LINE\bin"))
                    p.StandardInput.WriteLine(@"C:\Users\tony777\AppData\Local\LINE\bin\LineLauncher.exe");
                else
                {
                    // p.StandardInput.WriteLine(@"C:\Users\tony777\AppData\Local\LINE\bin\LineLauncher.exe");
                }
                p.StandardInput.WriteLine("exit");
                p.WaitForExit();
                p.Close();
                //     }
            }
            Application.Exit();
        }
    }
}

[C#] 直接調用Win32 API DLL

  1. [DllImport("kernel32.dll")]是什麼意思?

這叫引入kernel32.dll這個動態連接庫。
這個動態連接庫裡麵包含了很多WindowsAPI函數,如果你想使用這面的函數,就需要這麼引入。舉個例子:
[DllImport("kernel32.dll")]
private static extern void函數名(參數,[參數]);
函數名就是一個屬於kernel32.dll裡的一個函數。完了你就可以用那個函數了。

kernel32.dll調用kernel32.dll這個DLL裡面的API接口!

系統API
例如
[DllImport("user32.dll")]//--引入API
public static extern ReturnType FunctionName(type arg1,type arg2,...);//--聲明方法

調用該方法是和調用普通方法沒區別
DLL Import屬性

現在是更深入地進行探討的時候了。在對託管代碼進行P/Invoke調用時,DllImportAttribute類型扮演著重要的角色。DllImportAttribute的主要作用是給CLR指示哪個DLL導出您想要調用的函數。相關DLL的名稱被作為一個構造函數參數傳遞給DllImportAttribute。

如果您無法肯定哪個DLL定義了您要使用的Windows API函數,Platform SDK文檔將為您提供最好的幫助資源。在Windows API函數主題文字臨近結尾的位置,SDK文檔指定了C應用程序要使用該函數必須鏈接的.lib文件。在幾乎所有的情況下,該.lib文件具有與定義該函數的系統DLL文件相同的名稱。例如,如果該函數需要C應用程序鏈接到Kernel32.lib,則該函數就定義在Kernel32.dll中。您可以在MessageBeep中找到有關MessageBeep的Platform SDK文檔主題。在該主題結尾處,您會注意到它指出庫文件是User32.lib;這表明MessageBeep是從User32.dll中導出的。

可選的DllImportAttribute屬性

除了指出宿主DLL外,DllImportAttribute還包含了一些可選屬性,其中四個特別有趣:EntryPoint、CharSet、SetLastError和CallingConvention。

EntryPoint在不希望外部託管方法具有與DLL導出相同的名稱的情況下,可以設置該屬性來指示導出的DLL函數的入口點名稱。當您定義兩個調用相同非託管函數的外部方法時,這特別有用。另外,在Windows中還可以通過它們的序號值綁定到導出的DLL函數。如果您需要這樣做,則諸如“#1”或“#129”的EntryPoint值指示DLL中非託管函數的序號值而不是函數名。

CharSet對於字符集,並非所有版本的Windows都是同樣創建的。Windows 9x系列產品缺少重要的Unicode支持,而Windows NT和Windows CE系列則一開始就使用Unicode。在這些操作系統上運行的CLR將Unicode用於String和Char數據的內部表示。但也不必擔心—當調用Windows 9x API函數時,CLR會自動進行必要的轉換,將其從Unicode轉換為ANSI。

如果DLL函數不以任何方式處理文本,則可以忽略DllImportAttribute的CharSet屬性。然而,當Char或String數據是等式的一部分時,應該將CharSet屬性設置為CharSet.Auto。這樣可以使CLR根據宿主OS使用適當的字符集。如果沒有顯式地設置CharSet屬性,則其默認值為CharSet.Ansi。這個默認值是有缺點的,因為對於在Windows 2000、Windows XP和Windows NT®上進行的interop調用,它會消極地影響文本參數封送處理的性能。

應該顯式地選擇CharSet.Ansi或CharSet.Unicode的CharSet值而不是使用CharSet.Auto的唯一情況是:您顯式地指定了一個導出函數,而該函數特定於這兩種Win32 OS中的某一種。ReadDirectoryChangesW API函數就是這樣的一個例子,它只存在於基於Windows NT的操作系統中,並且只支持Unicode;在這種情況下,您應該顯式地使用CharSet.Unicode。

有時,Windows API是否有字符集關係並不明顯。一種決不會有錯的確認方法是在Platform SDK中檢查該函數的C語言頭文件。(如果您無法肯定要看哪個頭文件,則可以查看Platform SDK文檔中列出的每個API函數的頭文件。)如果您發現該API函數確實定義為一個映射到以A或W結尾的函數名的宏,則字符集與您嘗試調用的函數有關係。Windows API函數的一個例子是在WinUser.h中聲明的GetMessage API,您也許會驚訝地發現它有A和W兩種版本。

SetLastError錯誤處理非常重要,但在編程時經常被遺忘。當您進行P/Invoke調用時,也會面臨其他的挑戰—處理託管代碼中Windows API錯誤處理和異常之間的區別。我可以給您一點建議。

如果您正在使用P/Invoke調用Windows API函數,而對於該函數,您使用GetLastError來查找擴展的錯誤信息,則應該在外部方法的DllImportAttribute中將SetLastError屬性設置為true。這適用於大多數外部方法。

這會導致CLR在每次調用外部方法之後緩存由API函數設置的錯誤。然後,在包裝方法中,可以通過調用類庫的System.Runtime.InteropServices.Marshal類型中定義的Marshal.GetLastWin32Error方法來獲取緩存的錯誤值。我的建議是檢查這些期望來自API函數的錯誤值,並為這些值引發一個可感知的異常。對於其他所有失敗情況(包括根本就沒意料到的失敗情況),則引發在System.ComponentModel命名空間中定義的Win32Exception,並將Marshal.GetLastWin32Error返回的值傳遞給它。如果您回頭看一下圖1中的代碼,您會看到我在extern MessageBeep方法的公共包裝中就採用了這種方法。

CallingConvention我將在此介紹的最後也可能是最不重要的一個DllImportAttribute屬性是CallingConvention。通過此屬性,可以給CLR指示應該將哪種函數調用約定用於堆棧中的參數。CallingConvention.Winapi的默認值是最好的選擇,它在大多數情況下都可行。然而,如果該調用不起作用,則可以檢查Platform SDK中的聲明頭文件,看看您調用的API函數是否是一個不符合調用約定標準的異常API。

通常,本機函數(例如Windows API函數或C-運行時DLL函數)的調用約定描述瞭如何將參數推入線程堆棧或從線程堆棧中清除。大多數Windows API函數都是首先將函數的最後一個參數推入堆棧,然後由被調用的函數負責清理該堆棧。相反,許多C-運行時DLL函數都被定義為按照方法參數在方法簽名中出現的順序將其推入堆棧,將堆棧清理工作交給調用者。

幸運的是,要讓P/Invoke調用工作只需要讓外圍設備理解調用約定即可。通常,從默認值CallingConvention.Winapi開始是最好的選擇。然後,在C運行時DLL函數和少數函數中,可能需要將約定更改為CallingConvention.Cdecl。
  1. [C#] 直接調用Win32 API DLL

若要宣告直接調用DLL方法,請按下列方法操作:

第一步: 引用System.Runtime.InteropServices
using System.Runtime.InteropServices;

第二步: 直接從 C# 調用 DLL,使用 C# 關鍵字 static 和 extern 聲明方法。
使用DllImportAttribute 類別來使用API
[DllImport("user32.dll")] //替換成所需的DLL檔
public static extern ReturnType FunctionName(type arg1,type arg2,...);//替換成所需的方法及參數

===================================================================
直接從 C# 調用 DLL 導出

若要聲明一個方法使其具有來自 DLL 導出的實現,請執行下列操作:

使用 C# 關鍵字 static 和 extern 聲明方法。

將 DllImport 屬性附加到該方法。DllImport 屬性允許您指定包含該方法的 DLL 的名稱。通常的做法是用與導出的方法相同的名稱命名 C# 方法,但也可以對 C# 方法使用不同的名稱。

還可以為方法的參數和返回值指定自定義封送處理信息,這將重寫 .NET Framework 的默認封送處理。

示例 1

本示例顯示如何使用 DllImport 屬性通過調用 msvcrt.dll 中的 puts 輸出消息。

// PInvokeTest.cs
using System;
using System.Runtime.InteropServices;

class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();

public static void Main()
{
puts("Test");
_flushall();
}
}

輸出

Test

代碼討論

前面的示例顯示了聲明在非託管 DLL 中實現的 C# 方法的最低要求。PlatformInvokeTest.puts 方法用 static 和 extern 修飾符聲明並且具有 DllImport 屬性,該屬性使用默認名稱 puts 通知編譯器此實現來自 msvcrt.dll。若要對 C# 方法使用不同的名稱(如 putstring),則必須在 DllImport 屬性中使用 EntryPoint 選項,如下所示:

[DllImport("msvcrt.dll", EntryPoint="puts")]

有關 DllImport 屬性的語法的更多信息,請參見 DllImportAttribute 類。

默認封送處理和為非託管方法的參數指定自定義封送處理

當從 C# 代碼中調用非託管函數時,公共語言運行庫必須封送參數和返回值。

對於每個 .NET Framework 類型均有一個默認非託管類型,公共語言運行庫將使用此非託管類型在託管到非託管的函數調用中封送數據。例如,C# 字符串值的默認封送處理是封送為 LPTSTR(指向 TCHAR 字符緩衝區的指針)類型。可以在非託管函數的 C# 聲明中使用 MarshalAs 屬性重寫默認封送處理。
===================================================================
示例 2

本示例使用 DllImport 屬性輸出一個字符串。它還顯示如何通過使用 MarshalAs 屬性重寫函數參數的默認封送處理。

// Marshal.cs
using System;
using System.Runtime.InteropServices;

class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(
[MarshalAs(UnmanagedType.LPStr)]
string m);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();


public static void Main()
{
puts("Hello World!");
_flushall();
}
}

輸出

運行此示例時,字符串

Hello World!

將顯示在控制台上。

代碼討論

在前面的示例中,puts 函數的參數的默認封送處理已從默認值 LPTSTR 重寫為 LPSTR。

MarshalAs 屬性可以放置在方法參數、方法返回值以及結構和類的字段上。若要設置方法返回值的封送處理,請將 MarshalAs 屬性與返回屬性位置重寫一起放置在方法上的屬性塊中。例如,若要顯式設置 puts 方法返回值的封送處理:

...
[DllImport("msvcrt.dll")]
[return : MarshalAs(UnmanagedType.I4)]
public static extern int puts(
...

有關 MarshalAs 屬性的語法的更多信息,請參見 MarshalAsAttribute 類。

注意   In 和 Out 屬性可用於批註非託管方法的參數。它們與 MIDL 源文件中的 in 和 out 修飾符的工作方式類似。請注意,Out 屬性與 C# 參數修飾符 out 不同。有關 In 和 Out 屬性的更多信息,請參見 InAttribute 類和 OutAttribute 類。

為用戶定義的結構指定自定義封送處理

可以為傳遞到非託管函數或從非託管函數返回的結構和類的字段指定自定義封送處理屬性。通過向結構或類的字段中添加 MarshalAs 屬性可以做到這一點。還必須使用 StructLayout 屬性設置結構的佈局,還可以控制字符串成員的默認封送處理,並設置默認封裝大小。
===================================================================
示例 3

本示例說明如何為結構指定自定義封送處理屬性。

請考慮下面的 C 結構:

typedef struct tagLOGFONT
{
LONG lfHeight;
LONG lfWidth;
LONG lfEscapement;
LONG lfOrientation;
LONG lfWeight;
BYTE lfItalic;
BYTE lfUnderline;
BYTE lfStrikeOut;
BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;

在 C# 中,可以使用 StructLayout 和 MarshalAs 屬性描述前面的結構,如下所示:

// logfont.cs
// compile with: /target:module
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public class LOGFONT
{
public const int LF_FACESIZE = 32;
public int lfHeight;
public int lfWidth;
public int lfEscapement;
public int lfOrientation;
public int lfWeight;
public byte lfItalic;
public byte lfUnderline;
public byte lfStrikeOut;
public byte lfCharSet;
public byte lfOutPrecision;
public byte lfClipPrecision;
public byte lfQuality;
public byte lfPitchAndFamily;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=LF_FACESIZE)]
public string lfFaceName;
}

有關 StructLayout 屬性的語法的更多信息,請參見 StructLayoutAttribute 類。

然後即可將該結構用在 C# 代碼中,如下所示:

// pinvoke.cs
// compile with: /addmodule:logfont.netmodule
using System;
using System.Runtime.InteropServices;

class PlatformInvokeTest
{
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr CreateFontIndirect(
[In, MarshalAs(UnmanagedType.LPStruct)]
LOGFONT lplf // characteristics
);

[DllImport("gdi32.dll")]
public static extern bool DeleteObject(
IntPtr handle
);

public static void Main()
{
LOGFONT lf = new LOGFONT();
lf.lfHeight = 9;
lf.lfFaceName = "Arial";
IntPtr handle = CreateFontIndirect(lf);

if (IntPtr.Zero == handle)
{
Console.WriteLine("Can't creates a logical font.");
}
else
{

if (IntPtr.Size == 4)
Console.WriteLine("{0:X}", handle.ToInt32());
else
Console.WriteLine("{0:X}", handle.ToInt64());

// Delete the logical font created.
if (!DeleteObject(handle))
Console.WriteLine("Can't delete the logical font");
}
}
}

運行示例

C30A0AE5

代碼討論

在前面的示例中,CreateFontIndirect 方法使用了一個 LOGFONT 類型的參數。MarshalAs 和 In 屬性用於限定此參數。程序將由此方法返回的數值顯示為十六進制大寫字符串。

註冊回調方法

若要註冊調用非託管函數的託管回調,請用相同的參數列表聲明一個委託並通過 PInvoke 傳遞它的一個實例。在非託管端,它將顯示為一個函數指針。有關 PInvoke 和回調的更多信息,請參見平台調用詳解。

例如,考慮以下非託管函數 MyFunction,此函數要求 callback 作為其參數之一:

typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyFunction(PFN_ MYCALLBACK callback);

若要從託管代碼調用 MyFunction,請聲明該委託,將 DllImport 附加到函數聲明,並根據需要封送任何參數或返回值:

public delegate void MyCallback();
[DllImport("MYDLL.DLL")]
public static extern void MyFunction(MyCallback callback);

同時,請確保委託實例的生存期覆蓋非託管代碼的生存期;否則,委託在經過垃圾回收後將不再可用。

C#中調用Windows API的要點

C#中調用Windows API的要點
在.Net Framework SDK文檔中,關於調用Windows API的指示比較零散,並且其中稍全面一點的是針對Visual Basic .net講述的。本文將C#中調用API的要點彙集如下,希望給未在C#中使用過API的朋友一點幫助。另外如果安裝了Visual Studio .net的話,在C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples\Technologies\Interop\PlatformInvoke\WinAPIs\CS目錄下有大量的調用API的例子。
  一、調用格式
using System.Runtime.InteropServices; //引用此名稱空間,簡化後面的代碼
...
//使用DllImportAttribute特性來引入api函數,注意聲明的是空方法,即方法體為空。
[DllImport("user32.dll")]
public static extern ReturnType FunctionName(type arg1,type arg2,...);
//調用時與調用其他方法並無區別
  可以使用欄位進一步說明特性,用逗號隔開,如:
[ DllImport( "kernel32", EntryPoint="GetVersionEx" )]
  DllImportAttribute特性的公共欄位如下:
  1、CallingConvention 指示向非託管實現傳遞方法參數時所用的 CallingConvention 值。
  CallingConvention.Cdecl : 調用方清理堆疊。它使您能夠調用具有 varargs 的函數。
  CallingConvention.StdCall : 被調用方清理堆疊。它是從託管代碼調用非託管函數的默認約定。
  2、CharSet 控制調用函數的名稱版本及指示如何向方法封送 String 參數。
  此欄位被設置為 CharSet 值之一。如果 CharSet 欄位設置為 Unicode,則所有字串參數在傳遞到非託管實現之前都轉換成 Unicode 字元。這還導致向 DLL EntryPoint 的名稱中追加字母“W”。如果此欄位設置為 Ansi,則字串將轉換成 ANSI 字串,同時向 DLL EntryPoint 的名稱中追加字母“A”。大多數 Win32 API 使用這種追加“W”或“A”的約定。如果 CharSet 設置為 Auto,則這種轉換就是與平臺有關的(在 Windows NT 上為 Unicode,在 Windows 98 上為 Ansi)。CharSet 的預設值為 Ansi。CharSet 欄位也用於確定將從指定的 DLL 導入哪個版本的函數。CharSet.Ansi 和 CharSet.Unicode 的名稱匹配規則大不相同。對於 Ansi 來說,如果將 EntryPoint 設置為“MyMethod”且它存在的話,則返回“MyMethod”。如果 DLL 中沒有“MyMethod”,但存在“MyMethodA”,則返回“MyMethodA”。對於 Unicode 來說則正好相反。如果將 EntryPoint 設置為“MyMethod”且它存在的話,則返回“MyMethodW”。如果 DLL 中不存在“MyMethodW”,但存在“MyMethod”,則返回“MyMethod”。如果使用的是 Auto,則匹配規則與平臺有關(在 Windows NT 上為 Unicode,在 Windows 98 上為 Ansi)。如果 ExactSpelling 設置為 true,則只有當 DLL 中存在“MyMethod”時才返回“MyMethod”。
  3、EntryPoint 指示要調用的 DLL 入口點的名稱或序號。
  如果你的方法名不想與api函數同名的話,一定要指定此參數,例如:
[DllImport("user32.dll",CharSet="CharSet.Auto",EntryPoint="MessageBox")]
public static extern int MsgBox(IntPtr hWnd,string txt,string caption, int type);
  4、ExactSpelling 指示是否應修改非託管 DLL 中的入口點的名稱,以與 CharSet 欄位中指定的 CharSet 值相對應。如果為 true,則當 DllImportAttribute.CharSet 欄位設置為 CharSet 的 Ansi 值時,向方法名稱中追加字母 A,當 DllImportAttribute.CharSet 欄位設置為 CharSet 的 Unicode 值時,向方法的名稱中追加字母 W。此欄位的預設值是 false。
  5、PreserveSig 指示託管方法簽名不應轉換成返回 HRESULT、並且可能有一個對應於返回值的附加 [out, retval] 參數的非託管簽名。
  6、SetLastError 指示被調用方在從屬性化方法返回之前將調用 Win32 API SetLastError。 true 指示調用方將調用 SetLastError,默認為 false。運行時封送拆收器將調用 GetLastError 並緩存返回的值,以防其被其他 API 調用重寫。用戶可通過調用 GetLastWin32Error 來檢索錯誤代碼。
  二、參數類型:
  1、數值型直接用對應的就可。(DWORD -> int , WORD -> Int16)
  2、API中字串指標類型 -> .net中string
  3、API中控制碼 (dWord) -> .net中IntPtr
  4、API中結構 -> .net中結構或者類。注意這種情況下,要先用StructLayout特性限定聲明結構或類
  公共語言運行庫利用StructLayoutAttribute控制類或結構的資料欄位在託管記憶體中的物理佈局,即類或結構需要按某種方式排列。如果要將類傳遞給需要指定佈局的非託管代碼,則顯式控制類佈局是重要的。它的構造函數中用LayoutKind值初始化 StructLayoutAttribute 類的新實例。 LayoutKind.Sequential 用於強制將成員按其出現的順序進行順序佈局。
  LayoutKind.Explicit 用於控制每個資料成員的精確位置。利用 Explicit, 每個成員必須使用 FieldOffsetAttribute 指示此欄位在類型中的位置。如:
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime 
{
[FieldOffset(0)]public ushort wYear; 
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek; 
[FieldOffset(6)]public ushort wDay; 
[FieldOffset(8)]public ushort wHour; 
[FieldOffset(10)]public ushort wMinute; 
[FieldOffset(12)]public ushort wSecond; 
[FieldOffset(14)]public ushort wMilliseconds; 
}
  下面是針對API中OSVERSIONINFO結構,在.net中定義對應類或結構的例子:
/**********************************************
* API中定義原結構聲明
* OSVERSIONINFOA STRUCT
* dwOSVersionInfoSize DWORD ?
* dwMajorVersion DWORD ?
* dwMinorVersion DWORD ?
* dwBuildNumber DWORD ?
* dwPlatformId DWORD ?
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ <OSVERSIONINFOA>
*********************************************/
//.net中聲明為類
[ StructLayout( LayoutKind.Sequential )] 
public class OSVersionInfo 

public int OSVersionInfoSize;
public int majorVersion; 
public int minorVersion;
public int buildNumber;
public int platformId;
[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )] 
public String versionString;
}
//或者
//.net中聲明為結構
[ StructLayout( LayoutKind.Sequential )] 
public struct OSVersionInfo2 
{
public int OSVersionInfoSize;
public int majorVersion; 
public int minorVersion;
public int buildNumber;
public int platformId;
[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )] 
public String versionString;
}
  此例中用到MashalAs特性,它用於描述欄位、方法或參數的封送處理格式。用它作為參數首碼並指定目標需要的資料類型。例如,以下代碼將兩個參數作為資料類型長指標封送給 Windows API 函數的字串 (LPStr): 
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;
  注意結構作為參數時候,一般前面要加上ref修飾符,否則會出現錯誤:物件的引用沒有指定物件的實例。
[ DllImport( "kernel32", EntryPoint="GetVersionEx" )] 
public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi );
  三、如何保證使用託管物件的平臺調用成功?
  如果在調用平臺 invoke 後的任何位置都未引用託管物件,則垃圾回收器可能將完成該託管物件。這將釋放資源並使控制碼無效,從而導致平臺invoke 調用失敗。用 HandleRef 包裝控制碼可保證在平臺 invoke 調用完成前,不對託管物件進行垃圾回收。
  例如下麵:
FileStream fs = new FileStream( "a.txt", FileMode.Open );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
ReadFile(fs.Handle, buffer, 5, out read, 0 ); //調用Win API中的ReadFile函數
  由於fs是託管物件,所以有可能在平臺調用還未完成時候被垃圾回收站回收。將文件流的控制碼用HandleRef包裝後,就能避免被垃圾站回收:
[ DllImport( "Kernel32.dll" )]
public static extern bool ReadFile( 
HandleRef hndRef, 
StringBuilder buffer, 
int numberOfBytesToRead, 
out int numberOfBytesRead, 
ref Overlapped flag );
......
......
FileStream fs = new FileStream( "HandleRef.txt", FileMode.Open );
HandleRef hr = new HandleRef( fs, fs.Handle );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
// platform invoke will hold reference to HandleRef until call ends
ReadFile( hr, buffer, 5, out read, 0 );
C# 託管資源和非託管資源

託管資源指的是.NET可以自動進行回收的資源,主要是指託管堆上分配的內存資源。託管資源的回收工作是不需要人工干預的,有.NET運行庫在合適調用垃圾回收器進行回收。

非託管資源指的是.NET不知道如何回收的資源,最常見的一類非託管資源是包裝作業系統資源的對象,例如文件,窗口,網絡連接,資料庫連接,畫刷,圖標等。這類資源,垃圾回收器在清理的時候會調用Object.Finalize方法。默認情況下,方法是空的,對於非託管對象,需要在此方法中編寫回收非託管資源的代碼,以便垃圾回收器正確回收資源。

在.NET中,Object.Finalize方法是無法重載的,編譯器是根據類的析構函數來自動生成Object.Finalize方法的,所以對於包含非託管資源的類,可以將釋放非託管資源的代碼放在析構函數。

注意,不能在析構函數中釋放託管資源,因為析構函數是有垃圾回收器調用的,可能在析構函數調用之前,類包含的託管資源已經被回收了,從而導致無法預知的結果。
本來如果按照上面做法,非託管資源也能夠由垃圾回收器進行回收,但是非託管資源一般是有限的,比較寶貴的,而垃圾回收器是由CRL自動調用的,這樣就無法保證及時的釋放掉非託管資源,因此定義了一個Dispose方法,讓使用者能夠手動的釋放非託管資源。Dispose方法釋放類的託管資源和非託管資源,使用者手動調用此方法後,垃圾回收器不會對此類實例再次進行回收。Dispose方法是由使用者調用的,在調用時,類的託管資源和非託管資源肯定都未被回收,所以可以同時回收兩種資源。

Microsoft為非託管資源的回收專門定義了一個接口:IDisposable,接口中只包含一個Dispose方法。任何包含非託管資源的類,都應該繼承此接口。
在一個包含非託管資源的類中,關於資源釋放的標準做法是:
(1) 繼承IDisposable接口;
(2) 實現Dispose方法,在其中釋放託管資源和非託管資源,並將對象本身從垃圾回收器中移除(垃圾回收器不在回收此資源);
(3) 實現類析構函數,在其中釋放非託管資源。
在使用時,顯示調用Dispose方法,可以及時的釋放資源,同時通過移除Finalize方法的執行,提高了性能;如果沒有顯示調用Dispose方法,垃圾回收器也可以通過析構函數來釋放非託管資源,垃圾回收器本身就具有回收託管資源的功能,從而保證資源的正常釋放,只不過由垃圾回收器回收會導致非託管資源的未及時釋放的浪費。

在.NET中應該儘可能的少用析構函數釋放資源。在沒有析構函數的對象在垃圾處理器一次處理中從內存刪除,但有析構函數的對象,需要兩次,第一次調用析構函數,第二次刪除對象。而且在析構函數中包含大量的釋放資原始碼,會降低垃圾回收器的工作效率,影響性能。所以對於包含非託管資源的對象,最好及時的調用Dispose方法來回收資源,而不是依賴垃圾回收器。

上面就是.NET中對包含非託管資源的類的資源釋放機制,只要按照上面要求的步驟編寫代碼,類就屬於資源安全的類。
下面用一個例子來總結一下.NET非託管資源回收機制:

Public class BaseResource:IDisposable

{

PrivateIntPtr handle; // 句柄,屬於非託管資源

PrivateComponet comp; // 組件,託管資源

Privateboo isDisposed = false; // 是否已釋放資源的標誌

PublicBaseResource

{

}

//實現接口方法

//由類的使用者,在外部顯示調用,釋放類資源

Publicvoid Dispose

{

Dispose(true);// 釋放託管和非託管資源

//將對象從垃圾回收器鍊表中移除,

// 從而在垃圾回收器工作時,只釋放託管資源,而不執行此對象的析構函數

GC.SuppressFinalize(this);

}

//由垃圾回收器調用,釋放非託管資源
~BaseResource
{

Dispose(false);// 釋放非託管資源

}

//參數為true表示釋放所有資源,只能由使用者調用

//參數為false表示釋放非託管資源,只能由垃圾回收器自動調用

//如果子類有自己的非託管資源,可以重載這個函數,添加自己的非託管資源的釋放

//但是要記住,重載此函數必須保證調用基類的版本,以保證基類的資源正常釋放

Protectedvirtual void Dispose(bool disposing)

{

If(!this.disposed)// 如果資源未釋放 這個判斷主要用了防止對象被多次釋放

{

If(disposing)

{

Comp.Dispose;// 釋放託管資源

}

closeHandle(handle);// 釋放非託管資源

handle= IntPtr.Zero;

}

this.disposed= true; // 標識此對象已釋放

}

}

析構函數只能由垃圾回收器調用。

Despose方法只能由類的使用者調用。

在C#中,凡是繼承了IDisposable接口的類,都可以使用using語句,從而在超出作用域後,讓系統自動調用Dispose方法。 一個資源安全的類,都實現了IDisposable接口和析構函數。提供手動釋放資源和系統自動釋放資源的雙保險。

原文網址:https://read01.com/xmA7D.html

MarshalAs的使用

參考:http://blog.sina.com.cn/s/blog_4e4ee8ed0100elou.html

作用:

MarshalAs屬性指示如何在託管代碼和非託管代碼之間封送數據。

使用方法:

[MarshalAs(UnmanagedType unmanagedType, 命名參數)]

實際上相當於構造一個MarshalAsAttribute類的對象

常用的UnmanagedType枚舉值:(詳細內容查MSDN)

BStr 長度前綴為雙字節的Unicode 字符串;

LPStr 單字節、空終止的ANSI 字符串。;

LPWStr 一個2 字節、空終止的Unicode 字符串;

ByValArray 用於在結構中出現的內聯定長字符數組,應始終使用MarshalAsAttribute的SizeConst字段來指示數組的大小。

注意:

在用Marshal.SizeOf(),即獲取對象的非託管大小時,獲得的是自己定義的大小;

但在實際處理的時候,是按照實際的大小來獲取的

示例:

定義一個固定大小的結構體,代碼如下:

結構的聲明:
結構的使用:
注意:
size=16+16+256
可見,獲取到的非託管大小為288
但是,查看myInfo對象可以看到其實際大小如下所示:
http://hi.csdn.net/attachment/201111/30/0_1322620745Hgu1.gif
問題:
這種實際大小和固定大小的不一致性,導致了在用Marshal類進行託管對象和非託管對象的轉換時,會有如下錯誤提示:“未能封送類型,因為嵌入數組實例的長度與佈局中聲明的長度不匹配。”
解決辦法還沒想到......
小結:
MarshalAs這個屬性很難用,很容易用錯,用好需要對C#、C++和COM數據的佈局方式有一定的了解才能做。所以做好使用一些工具來幫你,可以參照我下面的文章:
http://blog.csdn.net/Donjuan/archive/2009/02/05/3865026.aspx
如果你只是感興趣的話,那就忘了這個屬性吧,在.NET 4.0以後,微軟會盡量解決掉這個屬性。

WPF聊天室应用(ASP.NET Core SignalR)

  WPF聊天室应用(ASP.NET Core SignalR) https://www.bilibili.com/video/BV1Q741187Si?p=2 https://www.bilibili.com/video/BV1UV411e75T?from=search...