該教程說明如何在 C# 中使用不安全代碼(使用指針的代碼)。
在 C# 中很少需要使用指針,但仍有一些需要使用的情況。例如,在下列情況中使用允許采用指針的不安全上下文是正確的:
不鼓勵在其他情況下使用不安全上下文。具體地說,不應該使用不安全上下文嘗試在 C# 中編寫 C 代碼。
警告 使用不安全上下文編寫的代碼無法被驗證為安全的,因此只有在代碼完全受信任時才會執行該代碼。換句話說,不可以在不受信任的環境中執行不安全代碼。例如,不能從 Internet 上直接運行不安全代碼。
該教程包括下列示例:
以下示例使用指針將一個字節數組從 src
復制到 dst
。用 /unsafe 選項編譯此示例。
// fastcopy.cs // compile with: /unsafe using System; class Test { // The unsafe keyword allows pointers to be used within // the following method: static unsafe void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int count) { if (src == null || srcIndex < 0 || dst == null || dstIndex < 0 || count < 0) { throw new ArgumentException(); } int srcLen = src.Length; int dstLen = dst.Length; if (srcLen - srcIndex < count || dstLen - dstIndex < count) { throw new ArgumentException(); } // The following fixed statement pins the location of // the src and dst objects in memory so that they will // not be moved by garbage collection. fixed (byte* pSrc = src, pDst = dst) { byte* ps = pSrc; byte* pd = pDst; // Loop over the count in blocks of 4 bytes, copying an // integer (4 bytes) at a time: for (int n =0 ; n < count/4 ; n++) { *((int*)pd) = *((int*)ps); pd += 4; ps += 4; } // Complete the copy by moving any bytes that weren't // moved in blocks of 4: for (int n =0; n < count%4; n++) { *pd = *ps; pd++; ps++; } } } static void Main(string[] args) { byte[] a = new byte[100]; byte[] b = new byte[100]; for(int i=0; i<100; ++i) a[i] = (byte)i; Copy(a, 0, b, 0, 100); Console.WriteLine("The first 10 elements are:"); for(int i=0; i<10; ++i) Console.Write(b[i] + " "); Console.WriteLine("\n"); } }
The first 10 elements are: 0 1 2 3 4 5 6 7 8 9
Copy
方法內使用指針。src
和 dst
對象在內存中的位置以便使其不會被垃圾回收移動。當 fixed 塊完成後,這些對象將被解除鎖定。本
[1] [2] [3] [4] 下一頁
示例顯示如何從 Platform SDK 調用 Windows ReadFile 函數,這要求使用不安全上下文,因為讀緩沖區需要將指針作為參數。
// readfile.cs // compile with: /unsafe // arguments: readfile.cs // Use the program to read and display a text file. using System; using System.Runtime.InteropServices; using System.Text; class FileReader { const uint GENERIC_READ = 0x80000000; const uint OPEN_EXISTING = 3; IntPtr handle; [DllImport("kernel32", SetLastError=true)] static extern unsafe IntPtr CreateFile( string FileName, // file name uint DesiredAccess, // access mode uint ShareMode, // share mode uint SecurityAttributes, // Security Attributes uint CreationDisposition, // how to create uint FlagsAndAttributes, // file attributes int hTemplateFile // handle to template file ); [DllImport("kernel32", SetLastError=true)] static extern unsafe bool ReadFile( IntPtr hFile, // handle to file void* pBuffer, // data buffer int NumberOfBytesToRead, // number of bytes to read int* pNumberOfBytesRead, // number of bytes read int Overlapped // overlapped buffer ); [DllImport("kernel32", SetLastError=true)] static extern unsafe bool CloseHandle( IntPtr hObject // handle to object ); public bool Open(string FileName) { // open the existing file for reading handle = CreateFile( FileName, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); if (handle != IntPtr.Zero) return true; else return false; } public unsafe int Read(byte[] buffer, int index, int count) { int n = 0; fixed (byte* p = buffer) { if (!ReadFile(handle, p + index, count, &n, 0)) return 0; } return n; } public bool Close() { // close file handle return CloseHandle(handle); } } class Test { public static int Main(string[] args) { if (args.Length != 1) { Console.WriteLine("Usage : ReadFile <FileName>"); return 1; } if (! System.IO.File.Exists(args[0])) { Console.WriteLine("File " + args[0] + " not found."); return 1; } byte[] buffer = new byte[128]; FileReader fr = new FileReader(); if (fr.Open(args[0])) { // Assume that an ASCII file is being read ASCIIEncoding Encoding = new ASCIIEncoding(); int bytesRead; do { bytesRead = fr.Read(buffer, 0, buffer.Length); string content = Encoding.GetString(buffer,0,bytesRead); Console.Write("{0}", content); } while ( bytes上一頁 [1] [2] [3] [4] 下一頁
Read > 0); fr.Close(); return 0; } else { Console.WriteLine("Failed to open requested file"); return 1; } } }
在編譯和運行此示例時,下列來自 readfile.txt 的輸入將產生在“輸出示例”中顯示的輸出。
line 1 line 2
line 1 line 2
傳遞到 Read 函數的字節數組是托管類型。這意味著公共語言運行庫垃圾回收器可能會隨意地對數組使用的內存進行重新定位。fixed 語句允許您獲取指向字節數組使用的內存的指針,並且標記實例,以便垃圾回收器不會移動它。
在 fixed 塊的末尾,將標記該實例以便可以移動它。此功能稱為聲明式鎖定。鎖定的好處是系統開銷非常小,除非在 fixed 塊中發生垃圾回收(但此情況不太可能發生)。
本示例讀取並顯示可執行文件的 Win32 版本號,在本示例中此版本號與程序集版本號相同。本例中使用的可執行文件為 printversion.exe。本示例使用 Platform SDK 函數 VerQueryValue、GetFileVersionInfoSize 和 GetFileVersionInfo 從指定的版本信息資源中檢索指定的版本信息。
本示例使用了指針,因為它可以簡化在其簽名中使用指向指針的指針(這在 Win32 API 中很常見)的方法的使用。
// printversion.cs // compile with: /unsafe using System; using System.Reflection; using System.Runtime.InteropServices; // Give this assembly a version number: [assembly:AssemblyVersion("4.3.2.1")] public class Win32Imports { [DllImport("version.dll")] public static extern bool GetFileVersionInfo (string sFileName, int handle, int size, byte[] infoBuffer); [DllImport("version.dll")] public static extern int GetFileVersionInfoSize (string sFileName, out int handle); // The third parameter - "out string pValue" - is automatically // marshaled from ANSI to Unicode: [DllImport("version.dll")] unsafe public static extern bool VerQueryValue (byte[] pBlock, string pSubBlock, out string pValue, out uint len); // This VerQueryValue overload is marked with 'unsafe' because // it uses a short*: [DllImport("version.dll")] unsafe public static extern bool VerQueryValue (byte[] pBlock, string pSubBlock, out short *pValue, out uint len); } public class C { // Main is marked with 'unsafe' because it uses pointers: unsafe public static int Main () { try { int handle = 0; // Figure out how much version info there is: int size = Win32Imports.GetFileVersionInfoSize("printversion.exe", out handle); if (size == 0) return -1; byte[] buffer = new byte[size]; if (!Win32Imports.GetFileVersionInfo("printversion.exe", handle, size, buffer)) { Console.WriteLine("Failed to query file version information."); return 1; } short *subBlock = null; uint len = 0; // Get the locale info from the version info: if (!Win32Imports.VerQueryValue (buffer, @"\VarFileInfo\Translation", out subBlock, out len)) { Console.WriteLine("Failed to query version information."); return 1; } string spv = @"\StringFileInfo\" + subBlock[0].ToString("X4") + subBlock[1].ToString("X4") + @"\ProductVersion"; byte *pVersion = null; // Get the ProductVersion value for this program: string ver上一頁 [1] [2] [3] [4] 下一頁
sionInfo; if (!Win32Imports.VerQueryValue (buffer, spv, out versionInfo, out len)) { Console.WriteLine("Failed to query version information."); return 1; } Console.WriteLine ("ProductVersion == {0}", versionInfo); } catch (Exception e) { Console.WriteLine ("Caught unexpected exception " + e.Message); } return 0; } }
ProductVersion == 4.3.2.1
上一頁 [1] [2] [3] [4]