C # Study Notes 15,
1.Platform interoperability and insecure code: C # is powerful, but sometimes its performance is still somewhat inadequate, so we can only discard all the security it provides, return to the memory address and pointer world.
C # provides support in three ways.
(1) The first method is to call the APIS exposed by the unmanaged code DLL through the Platform call (Platform Invoke, P/Invoke.
(2) The second method is through Insecure code, which allows us to access the memory pointer and address. In many cases, the code needs to use both methods.
(3) The third method is interoperation through COM Interop (COM ).
2.Platform call:
(1) External function declaration: After determining the target function to be called, the next step of P/Invoke is to declare the function with the managed code, just like a common method, the target API must be declared in a class, but an extern modifier must be added to it to declare it as an external function. The extern method is always a static method and therefore does not contain any implementation. Instead, the DllImport feature appended before the method Declaration points to the implementation. This feature requires the "name" of the DLL of the function to be defined. The imported DLL must be in the path, which contains the directory of the executable file so that it can be loaded successfully. "RunTime" determines the function name based on the method name. However, you can use the EntryPoint parameter to override the default behavior and provide a function name.
(2) parameter data type: when determining the target DLL and export function, you must identify or create a hosted data type that corresponds to the unmanaged data type in the external function.
3.Use StructLayoutAttribute for the ordered layout: Some APIs do not have the corresponding managed types. To call these APIs, you must run the managed code to re-declare the types. For example, you can use managed code to declare an unmanaged COLORREF struct, such as a ColorRef structure list. The key to declaration in the Code is StructLayoutAttribute. By default, managed code can optimize the memory layout. Therefore, the memory layout may not be stored sequentially from one field to another. In order to force the ordered layout and allow direct ing of types, and to replicate data between hosted and unmanaged code, you need to add the StructLayoutAttribute feature and specify the LayoutKind. Sequential enumeration value.
4.Platform call (P/Invoke) error handling: One of the inconveniences of Win32 API programming is that errors are often reported in different ways, if an API returns 0, 1, or false, an API uses the out parameter for processing. Win32 error reports in unmanaged code are rarely generated by exceptions. The P/Invoke designer provides a corresponding processing mechanism for this mechanism. To enable this mechanism, set the SetLastError parameter of the DllImport feature to true, so that a System. ComponentModel. Win32Exception can be instantiated. After P/Invoke is called, it is automatically initialized with Win32 error data, such as the code list of VirtualMemoryManger class. In this way, developers can provide custom error checks for each API and report errors in a standard way.
5.Use SafeHandle: in many cases, P/Invoke involves a resource, such as a Window handle. After these resources are used up, the code needs to clear them. However, do not force developers to remember this and write code manually each time. Instead, provide classes that implement the IDisposable interface and terminator. To provide built-in support for this, the following VirtualMemoryPtr class is derived from System. Runtime. InteropServices. SafeHandle. The SafeHandle class contains two abstract members: IsInvalid and ReleaseHandle (). In the latter, you can add the code to clean up the resource. The former indicates whether the resource cleaning code has been executed. You can view the VirtualMemoryPtr code list.
6.P/Invoke guiding principles:
(1) Verify that no hosting type has published the APIS you want.
(2) define an external API method as private or Internal in simple cases.
(3) provide public packaging methods around external methods and perform data type conversion and error handling.
(4) Reload the packaging method and insert the default value for the external method call to reduce the number of required parameters.
(5) When declaring an API, use enum or const to provide a constant value for the API.
(6) For all P/Invoke methods that support GetLastError (), set the value of the SetLastError naming feature to true. In this way, you can report an error through System. ComponentModel. Win32Exception.
(7) package resources such as handles in a class derived from System. Runtime. InteropServices. SafeHandle or supporting IDisposable.
(8) function pointers in unmanaged code are mapped to delegated instances in managed code. Generally, this requires a specific delegate type, which matches the signature of the non-hosted function pointer.
(9) map input/output parameters and output parameters to ref parameters instead of pointers.
7.Unsafe code: You can use unsafe as a modifier for a specific member of a type or type. The unsafe modifier does not affect the generated template code. It is only a pre-compiled instruction, which indicates to the compiler that the pointer and address can be operated in unsafe code blocks.
8.Pointer Declaration: Since the pointer (which only points to some integer values of the memory address) will not be reclaimed, C # does not allow the referenced object types other than the unmanaged type. In other words, a type cannot be a reference type, a generic type, or a reference type. For example, byte * pData; pointer is a brand new type. Unlike structure, enumeration, and class, the ultimate base class of pointer is not System. object, or even cannot be converted to System. object. On the contrary, they can be converted to System. intPtr (the latter can be converted to System. object ).
9.Pointer assignment: we need to use the address operator (&) to obtain the value type address. Either way, to assign some data addresses to a pointer, the requirements are as follows.
(1) data must belong to a variable.
(2) data must be an unmanaged type.
(3) variables must be fixed with fixed and cannot be moved.
For example, if byte * pData = & bytes [0]; // a compilation error occurs, data may be moved and needs to be fixed.
For example, byte [] bytes = new bytes [24]; fixed (byte * pData = & bytes [0]) {} // The compilation is correct.
10.Pointer resolution: to access a type value referenced by the pointer, you must unreference the pointer, that is, add an indirect addressing operator before the pointer type *. For example, byte data = * pData; The unreferenced operator cannot be applied to void * type pointers. void * data type indicates pointing to an unknown type pointer. Because the data type is unknown, it cannot be referenced to another type. On the contrary, to access the data referenced by void *, you must convert it to any other pointer type variable and then unreference the latter type.
[StructLayout (LayoutKind. sequential)] public struct ColorRef {public byte Red; public byte Green; public byte Blue; private byte Unused; public ColorRef (byte red, byte green, byte blue): this () {Red = red; Green = green; Blue = blue; Unused = 0 ;}} public class VirtualMemoryManger {[DllImport ("kernel32.dll", SetLastError = true)] private static extern bool VirtualFreeEx (IntPtr hProcess, IntPtr lpAd Dress, IntPtr dwSize, IntPtr dwFreeType); [maid ("kernel32.dll", EntryPoint = "GetCurrentProcess")] internal static extern IntPtr GetCurrentProcessHandle (); [DllImport ("kernel32.dll ", setLastError = true)] private static extern IntPtr VirtualAllocEx (IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize, AllocationType flAllocationType, uint flProtect); [DllImport ("temperature", SetLastError = true )] Private static extern bool VirtualProtectEx (IntPtr hPorcess, IntPtr lpAddress, IntPtr dwSize, uint flNewProtect, ref uint lpflOldProtect); public static IntPtr AllocExecutionBlock (int size, IntPtr hProcess) {IntPtr codeBytesPtr = VirtualAllocEx (hProcess, IntPtr. zero, (IntPtr) size, AllocationType. reserve | AllocationType. commit, (uint) ProtectionOptions. pageExecuteReadWrite); if (codeBytesPtr = IntPtr. Zero) {throw new Win32Exception ();} uint lpflOldProtect = 0; if (! VirtualProtectEx (hProcess, codeBytesPtr, (IntPtr) size, (uint) ProtectionOptions. pageExecuteReadWrite, ref lpflOldProtect) {throw new Win32Exception ();} return codeBytesPtr;} public static IntPtr AllocExecutionBlock (int size) {// methods should be encapsulated in public packages, this reduces the complexity of calling P/Invoke APIs, enhances the availability of APIS, and facilitates the transition to an object-oriented type structure. Return AllocExecutionBlock (size, GetCurrentProcessHandle ();} public static bool VirtualFreeEx (IntPtr hProcess, IntPtr lpAddress, IntPtr dwSize) {bool result = VirtualFreeEx (hProcess, lpAddress, dwSize, (IntPtr) memoryFreeType. decommit); if (! Result) {throw new Win32Exception ();} return result;} public static bool VirtualFreeEx (IntPtr lpAddress, IntPtr dwSize) {// no matter the error processing, struct, or constant value, excellent API developers should provide a simplified hosting API and package Win32API at the lower layer. Return VirtualFreeEx (random (), lpAddress, dwSize) ;}} public class VirtualMemoryPtr: SafeHandle {public readonly IntPtr AllocatedPointer; private readonly IntPtr ProcessHandle; private readonly IntPtr MemorySize; private bool Disposed; public VirtualMemoryPtr (int memorySize): base (IntPtr. zero, true) {ProcessHandle = VirtualMemoryManger. getCurrentProcessHandle (); MemorySize = (IntP Tr) memorySize; AllocatedPointer = VirtualMemoryManger. allocExecutionBlock (memorySize, ProcessHandle); Disposed = false;} public static implicit operator IntPtr (VirtualMemoryPtr virtualAMemoryPointer) {return virtualAMemoryPointer. allocatedPointer;} protected override bool ReleaseHandle () {if (! Disposed) {Disposed = true; GC. suppressFinalize (this); VirtualMemoryManger. virtualFreeEx (ProcessHandle, AllocatedPointer, MemorySize);} return true;} public override bool IsInvalid {get {return Disposed;} [Flags] public enum AllocationType {Reserve = 0x2000, commit = 0x1000, Reset = 0x8000, Physical = 0x400000, TopDown = 0x100000,} [Flags] public enum ProtectionOptions {PageExecuteReadWrite = 0x40, pageExecuteRead = 0x20, Execute = 0x10} [Flags] public enum MemoryFreeType {Decommit = 0x4000, Release = 0x8000}
View Code