该教程说明如何在 C# 中使用不安全代码(使 用指针的代码)。
教程
在 C# 中很少需要使用指针,但仍有一些需要使用的情况。例如,在下列 情况中使用允许采用指针的不安全上下文是正确的:
处理磁盘上的现有结构
涉及内部包 含指针的结构的高级 COM 或平台调用方案
性能关键代码
不鼓励在其他情况下使用不 安全上下文。具体地说,不应该使用不安全上下文尝试在 C# 中 编写 C 代码。
警告 使用不安全 上下文编写的代码无法被验证为安全的,因此只有在代码完全受信任 时才会执行该代码。换句话说,不可以在不受信任的环境中执行不安 全代码。例如,不能从 Internet 上直接运行不安全代码 。
该教程包括下列示例:
示例 1 使用指针复制一个字节数组。
示例 2 显示如何调用 Windows ReadFil e 函数。
示例 3 显示如何打印可执行 文件的 Win32 版本。
示例 1< br>
以下示例使用指针将一个字节数组从 src 复制 到 dst。用 /unsafe 选项编译此示例。
// fastcopy.cs
// compile with: /unsafe
using System ;
class Test
{
// The unsafe keyword allow s pointers to be used within // the following method:
static unsafe void Cop y(byte[] src, int srcIndex,
byte[] dst, int dstIn dex, int count)
{
if (src == null || srcIn dex <
0 ||
d st == null || dstIndex <
0 || count <
0)
{< br> throw new Argum entException();
} int srcLen = src.Len gth;
int dstLen = d st.Length;
if (srcL en - srcIndex <
count ||
dstLen - dstIndex <
count)
{
throw new ArgumentE xception();
}
< br>
// The fol lowing fixed statement pins th e location of
/ / the src and dst objects in m emory so that they will
// not be moved by ga rbage collection.
fixed (byte* pSrc = src, pDst = dst)
{
by te* ps = pSrc;
byte* pd = pDst;
// Loop over the c ount in blocks of 4 bytes, cop ying an
// inte ger (4 bytes) at a time:
for (int n =0 ;
n &l t;
count/4 ;
n++)
{
*((int *)pd) = *((int*)ps);
pd += 4;
ps += 4;
}
// Com plete the copy by moving any b ytes that weren't
// moved in blocks of 4:
for (int n =0;
n <
count%4;
n++)
{
*pd = *ps;
pd++;
ps++;
}
}< br> }
stat ic 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.Write Line("The first 10 elements ar e:");
for(int i=0;
i<
10;
++i)
C onsole.Write(b[i] + " ");
Console.WriteLine("\n") ;
}
}
输出示例
The first 10 elements are :
0 1 2 3 4 5 6 7 8 9
代码讨论
请注意使用了 unsa fe 关键字,这允许在 Copy 方法内使用指针。
< br> fixed 语句用于声明指向源和目标数组的指针。它锁 定 src 和 dst 对象在内存中的位置以便使其不会被垃圾 回收移动。当 fixed 块完成后,这些对象将被解除锁定。< br>
通过略过数组界限检查,不安全代码可提高性能。
示例 2
本示例显示如 何从 Platform SDK 调用 Windows Rea dFile 函数,这要求使用不安全上下文,因为读缓冲区需要将 指针作为参数。
// readfile.cs< br>// compile with: /unsafe
// arguments: readfile.cs
// Use the program to rea d and display a text file.
using System;
using System. Runtime.InteropServices;
us ing System.Text;
class FileReader
{
cons t uint GENERIC_READ = 0x800000 00;
const uint OPEN_E XISTING = 3;
IntPtr h andle;
[DllImpor t("kernel32", SetLastError=tru e)]
static extern uns afe IntPtr CreateFile(
string FileName, // file name
uint DesiredAccess , // access mo de
uint ShareMo de, // sha re mode
uint Se curityAttributes, / / Security Attributes
uint CreationDispositio n, // how to create< br> uint FlagsAndAt tributes, // file a ttributes
int h TemplateFile // handle to template file
);
[DllImport("kernel32", SetLas tError=true)]
static extern unsafe bool ReadFile( IntPtr hFile, // handle to file
void* p Buffer, / / data buffer
i nt NumberOfBytesToRead, // number of bytes to rea d
int* pNumberO fBytesRead, // numb er of bytes read
int Overlapped // overlapped buffer );
[DllImport("kernel32", SetLas tError=true)]
static extern unsafe bool CloseHandle (
IntPtr hObjec t // handle to object
);
public bool Open(string FileN ame)
{
// open the existing file for reading
handle = CreateFile(
FileName,
GENERIC_READ,
0,
0,
OPEN_EXISTING,
0,
0);
if (handle != IntPt r.Zero)
r eturn true;
els e
return false;
}
public unsafe int Read(byte[ ] buffer, int index, int count )
{
i nt n = 0;
fixed (byte* p = buffer)
{
i f (!ReadFile(handle, p + index , count, &
n, 0))
return 0;
}
return n;
}
public bool Close()
{
// close file handle
ret urn CloseHandle(handle);
}
}
class Test< br>{
public static in t Main(string[] args)
{
if (args.Len gth != 1)
{
Console.Writ eLine("Usage : ReadFile <
Fi leName>
");
return 1;
}
if (! System.IO.File.Exists (args[0]))
{
Console.Wri teLine("File " + args[0] + " n ot found.");
return 1;
}
byte[] b uffer = new byte[128];
FileReader fr = new Fi leReader();
if (fr.Open(args[ 0]))
{
// Assume that an ASCII f ile is being read
ASCIIEncoding Encodin g = new ASCIIEncoding();
int bytesRead;
do
{
bytesRead = fr.Read(bu ffer, 0, buffer.Length);
string c ontent = Encoding.GetString(bu ffer,0,bytesRead);
Console.Write( "{0}", content);
}
while ( bytesRead >
0);
fr.Close();
return 0;
}
else
{
Console.WriteLine("Fa iled to open requested file");
return 1 ;
}
}< br>}
输入示例
在编译和运行 此示例时,下列来自 readfile.txt 的输入将产生在 “输出示例”中显示的输出。
line 1
line 2
输出示例
lin e 1
line 2
代码讨论
< br>传递到 Read 函数的字节数组是托管类型。这意味着公 共语言运行库垃圾回收器可能会随意地对数组使用的内存进行重新定 位。fixed 语句允许您获取指向字节数组使用的内存的指针, 并且标记实例,以便垃圾回收器不会移动它。
在 fixed 块的末尾,将标记该实例以便可以移动它。此功能称为 声明式锁定。锁定的好处是系统开销非常小,除非在 fixed 块中发生垃圾回收(但此情况不太可能发生)。
示 例 3
本示例读取并显示可执行文件的 Win3 2 版本号,在本示例中此版本号与程序集版本号相同。本例中使用 的可执行文件为 printversion.exe。本示例使用 Platform SDK 函数 VerQueryValue 、GetFileVersionInfoSize 和 GetF ileVersionInfo 从指定的版本信息资源中检索指定 的版本信息。
本示例使用了指针,因为它可以简化 在其签名中使用指向指针的指针(这在 Win32 API 中很 常见)的方法的使用。
// printvers ion.cs
// compile with: /un safe
using System;
using System.Reflection;
using S ystem.Runtime.InteropServices;
// Give this assembly a version number:
[assembl y:AssemblyVersion("4.3.2.1")]< br>
public class Win32Impo rts
{
[DllImport( "version.dll")]
publi c static extern bool GetFileVe rsionInfo (string sFileName, int handle, int size, byte[] infoBuffer);
[DllImport("version.dll") ]
public static exter n int GetFileVersionInfoSize ( string sFileName,
out int handle);
// The third parameter - "out string pValue" - is auto matically
// marshale d from ANSI to Unicode:
[DllImport("version.dll")]< br> unsafe public static extern bool VerQueryValue (byt e[] pBlock,
str ing pSubBlock, out string pVal ue, out uint len);
// This VerQueryValue overload i s marked with 'unsafe' because
// it uses a short* :
[DllImport("version .dll")]
unsafe public static extern bool VerQueryVa lue (byte[] pBlock,
string pSubBlock, out sho rt *pValue, out uint len);
}
public class C
{
// Main is marked wi th 'unsafe' because it uses po inters:
unsafe public static int Main ()
{
try
{
int handle = 0;
// Figure out how much version info there is:
int size =
Win32Imp orts.GetFileVersionInfoSize("p rintversion.exe",
out handle);
if (si ze == 0) return -1;
byte[] buffer = new byte[size];
if (!Win32Import s.GetFileVersionInfo("printver sion.exe", handle, size, buffe r))
{
Consol e.WriteLine("Failed to query f ile version information.");
retur n 1;
}
short *subBlock = null;
uint len = 0;
// Get the local e info from the version info:< br> if (!Win3 2Imports.VerQueryValue (buffer , @"\VarFileInfo\Translation", out subBlock, out len))
{
Console.WriteLi ne("Failed to query version in formation.");
return 1;
}
string spv = @"\Stri ngFileInfo\" + subBlock[0].ToS tring("X4") + subBlock[1].ToSt ring("X4") + @"\ProductVersion ";
b yte *pVersion = null;
// Get the Produc tVersion value for this progra m:
string versionInfo;
if (!Win32Imports.Ver QueryValue (buffer, spv, out v ersionInfo, out len))
{
Console.WriteLine( "Failed to query version infor mation.");
return 1;
}
Console.WriteLine ("Prod uctVersion == {0}", versionInf o);
}
catch (Exception e)
{
Console.WriteLine ("Caugh t unexpected exception " + e.M essage);
}
return 0;
}
}
输出示例
ProductVersion == 4.3.2. 1