C#中x86 / x64 CPUID
与我的另一个问题相关 ,请帮我debugging“未知的模块中发生未处理的typesSystem.AccessViolationException的exception其他信息:尝试读取或写入受保护的内存,这往往表明其他内存已损坏。 通过代码,一切工作,直到del()的实际调用,并在该行失败。
这段代码是基于这篇文章的示例和这个在python中工作的python代码 。 我无法得到代码示例,要么(同样的例外),但我希望这只是一点点过时或什么。
编辑:看看编辑历史,如果你关心我们如何到达这里,这是无趣的。
完成的工作版本:
public static class CpuID { public static byte[] Invoke(int level) { IntPtr codePointer = IntPtr.Zero; try { // compile byte[] codeBytes; if (IntPtr.Size == 4) { codeBytes = x86CodeBytes; } else { codeBytes = x64CodeBytes; } codePointer = VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), AllocationType.COMMIT | AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE ); Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length); CpuIDDelegate cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIDDelegate)); // invoke GCHandle handle = default(GCHandle); var buffer = new byte[16]; try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); cpuIdDelg(level, buffer); } finally { if (handle != default(GCHandle)) { handle.Free(); } } return buffer; } finally { if (codePointer != IntPtr.Zero) { VirtualFree(codePointer, 0, 0x8000); codePointer = IntPtr.Zero; } } } [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private delegate void CpuIDDelegate(int level, byte[] buffer); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [DllImport("kernel32")] private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType); [Flags()] private enum AllocationType : uint { COMMIT = 0x1000, RESERVE = 0x2000, RESET = 0x80000, LARGE_PAGES = 0x20000000, PHYSICAL = 0x400000, TOP_DOWN = 0x100000, WRITE_WATCH = 0x200000 } [Flags()] private enum MemoryProtection : uint { EXECUTE = 0x10, EXECUTE_READ = 0x20, EXECUTE_READWRITE = 0x40, EXECUTE_WRITECOPY = 0x80, NOACCESS = 0x01, READONLY = 0x02, READWRITE = 0x04, WRITECOPY = 0x08, GUARD_Modifierflag = 0x100, NOCACHE_Modifierflag = 0x200, WRITECOMBINE_Modifierflag = 0x400 } // Basic ASM strategy -- // void x86CpuId(int level, byte* buffer) // { // eax = level // cpuid // buffer[0] = eax // buffer[4] = ebx // buffer[8] = ecx // buffer[12] = edx // } private readonly static byte[] x86CodeBytes = { 0x55, // push ebp 0x8B, 0xEC, // mov ebp,esp 0x53, // push ebx 0x57, // push edi 0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8] (move level into eax) 0x0F, 0xA2, // cpuid 0x8B, 0x7D, 0x0C, // mov edi, dword ptr [ebp+12] (move address of buffer into edi) 0x89, 0x07, // mov dword ptr [edi+0], eax (write eax, ... to buffer) 0x89, 0x5F, 0x04, // mov dword ptr [edi+4], ebx 0x89, 0x4F, 0x08, // mov dword ptr [edi+8], ecx 0x89, 0x57, 0x0C, // mov dword ptr [edi+12],edx 0x5F, // pop edi 0x5B, // pop ebx 0x8B, 0xE5, // mov esp,ebp 0x5D, // pop ebp 0xc3 // ret }; private readonly static byte[] x64CodeBytes = { 0x53, // push rbx this gets clobbered by cpuid // rcx is level // rdx is buffer. // Need to save buffer elsewhere, cpuid overwrites rdx // Put buffer in r8, use r8 to reference buffer later. // Save rdx (buffer addy) to r8 0x49, 0x89, 0xd0, // mov r8, rdx // Move ecx (level) to eax to call cpuid, call cpuid 0x89, 0xc8, // mov eax, ecx 0x0F, 0xA2, // cpuid // Write eax et al to buffer 0x41, 0x89, 0x40, 0x00, // mov dword ptr [r8+0], eax 0x41, 0x89, 0x58, 0x04, // mov dword ptr [r8+4], ebx 0x41, 0x89, 0x48, 0x08, // mov dword ptr [r8+8], ecx 0x41, 0x89, 0x50, 0x0c, // mov dword ptr [r8+12], edx 0x5b, // pop rbx 0xc3 // ret }; }
请注意,CPUID0需要按照正确的顺序读取:
//a twelve character ASCII string stored in EBX, EDX, ECX - in that order var cpuid0s = new string(ASCIIEncoding.ASCII.GetChars( cpuid0.Skip(4).Take(4).Concat( cpuid0.Skip(12).Take(4)).Concat( cpuid0.Skip(8).Take(4)).ToArray()));
我相当肯定你被DEP封锁了。 x_CPUIDy_INSNS
字节数组位于标记为数据且不可执行的内存段中。
编辑:
这就是说,我已经得到一个编译和运行的版本,但我不认为得到正确的价值。 也许这会让你一路顺风。
编辑2:
我想我现在有了正确的价值观。 随意validation。
namespace CPUID { using System; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; internal static class Program { [Flags] private enum AllocationTypes : uint { Commit = 0x1000, Reserve = 0x2000, Reset = 0x80000, LargePages = 0x20000000, Physical = 0x400000, TopDown = 0x100000, WriteWatch = 0x200000 } [Flags] private enum MemoryProtections : uint { Execute = 0x10, ExecuteRead = 0x20, ExecuteReadWrite = 0x40, ExecuteWriteCopy = 0x80, NoAccess = 0x01, ReadOnly = 0x02, ReadWrite = 0x04, WriteCopy = 0x08, GuartModifierflag = 0x100, NoCacheModifierflag = 0x200, WriteCombineModifierflag = 0x400 } [Flags] private enum FreeTypes : uint { Decommit = 0x4000, Release = 0x8000 } [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private unsafe delegate void CPUID0Delegate(byte* buffer); [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private unsafe delegate void CPUID1Delegate(byte* buffer); private static void Main() { Console.WriteLine("CPUID0: {0}", string.Join(", ", CPUID0().Select(x => x.ToString("X2", CultureInfo.InvariantCulture)))); Console.WriteLine("CPUID0: {0}", new string(ASCIIEncoding.ASCII.GetChars(CPUID0()))); Console.WriteLine("CPUID1: {0}", string.Join(", ", CPUID1().Select(x => x.ToString("X2", CultureInfo.InvariantCulture)))); Console.ReadLine(); } private static unsafe byte[] CPUID0() { byte[] buffer = new byte[12]; if (IntPtr.Size == 4) { IntPtr p = NativeMethods.VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)x86_CPUID0_INSNS.Length), AllocationTypes.Commit | AllocationTypes.Reserve, MemoryProtections.ExecuteReadWrite); try { Marshal.Copy(x86_CPUID0_INSNS, 0, p, x86_CPUID0_INSNS.Length); CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate)); fixed (byte* newBuffer = &buffer[0]) { del(newBuffer); } } finally { NativeMethods.VirtualFree(p, 0, FreeTypes.Release); } } else if (IntPtr.Size == 8) { IntPtr p = NativeMethods.VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)x64_CPUID0_INSNS.Length), AllocationTypes.Commit | AllocationTypes.Reserve, MemoryProtections.ExecuteReadWrite); try { Marshal.Copy(x64_CPUID0_INSNS, 0, p, x64_CPUID0_INSNS.Length); CPUID0Delegate del = (CPUID0Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID0Delegate)); fixed (byte* newBuffer = &buffer[0]) { del(newBuffer); } } finally { NativeMethods.VirtualFree(p, 0, FreeTypes.Release); } } return buffer; } private static unsafe byte[] CPUID1() { byte[] buffer = new byte[12]; if (IntPtr.Size == 4) { IntPtr p = NativeMethods.VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)x86_CPUID1_INSNS.Length), AllocationTypes.Commit | AllocationTypes.Reserve, MemoryProtections.ExecuteReadWrite); try { Marshal.Copy(x86_CPUID1_INSNS, 0, p, x86_CPUID1_INSNS.Length); CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate)); fixed (byte* newBuffer = &buffer[0]) { del(newBuffer); } } finally { NativeMethods.VirtualFree(p, 0, FreeTypes.Release); } } else if (IntPtr.Size == 8) { IntPtr p = NativeMethods.VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)x64_CPUID1_INSNS.Length), AllocationTypes.Commit | AllocationTypes.Reserve, MemoryProtections.ExecuteReadWrite); try { Marshal.Copy(x64_CPUID1_INSNS, 0, p, x64_CPUID1_INSNS.Length); CPUID1Delegate del = (CPUID1Delegate)Marshal.GetDelegateForFunctionPointer(p, typeof(CPUID1Delegate)); fixed (byte* newBuffer = &buffer[0]) { del(newBuffer); } } finally { NativeMethods.VirtualFree(p, 0, FreeTypes.Release); } } return buffer; } private static class NativeMethods { [DllImport("kernel32.dll", SetLastError = true)] internal static extern IntPtr VirtualAlloc( IntPtr lpAddress, UIntPtr dwSize, AllocationTypes flAllocationType, MemoryProtections flProtect); [DllImport("kernel32")] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool VirtualFree( IntPtr lpAddress, uint dwSize, FreeTypes flFreeType); } #region ASM private static readonly byte[] x86_CPUID0_INSNS = new byte[] { 0x53, // push %ebx 0x31, 0xc0, // xor %eax,%eax 0x0f, 0xa2, // cpuid 0x8b, 0x44, 0x24, 0x08, // mov 0x8(%esp),%eax 0x89, 0x18, // mov %ebx,0x0(%eax) 0x89, 0x50, 0x04, // mov %edx,0x4(%eax) 0x89, 0x48, 0x08, // mov %ecx,0x8(%eax) 0x5b, // pop %ebx 0xc3 // ret }; private static readonly byte[] x86_CPUID1_INSNS = new byte[] { 0x53, // push %ebx 0x31, 0xc0, // xor %eax,%eax 0x40, // inc %eax 0x0f, 0xa2, // cpuid 0x5b, // pop %ebx 0xc3 // ret }; private static readonly byte[] x64_CPUID0_INSNS = new byte[] { 0x49, 0x89, 0xd8, // mov %rbx,%r8 0x49, 0x89, 0xc9, // mov %rcx,%r9 0x48, 0x31, 0xc0, // xor %rax,%rax 0x0f, 0xa2, // cpuid 0x4c, 0x89, 0xc8, // mov %r9,%rax 0x89, 0x18, // mov %ebx,0x0(%rax) 0x89, 0x50, 0x04, // mov %edx,0x4(%rax) 0x89, 0x48, 0x08, // mov %ecx,0x8(%rax) 0x4c, 0x89, 0xc3, // mov %r8,%rbx 0xc3 // retq }; private static readonly byte[] x64_CPUID1_INSNS = new byte[] { 0x53, // push %rbx 0x48, 0x31, 0xc0, // xor %rax,%rax 0x48, 0xff, 0xc0, // inc %rax 0x0f, 0xa2, // cpuid 0x5b, // pop %rbx 0xc3 // retq }; #endregion } }
我决定改进你的答案。 它不需要不安全的编译了,它只需要两个汇编块就可以读出任何和所有的cpuid块,因为它只是把eax / ebx / ecx / edx写到一个16字节的字节数组中。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Runtime.InteropServices; namespace CpuID { public class CpuID : IDisposable { [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] public delegate void CpuIDDelegate(int level, byte[] buffer); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc( IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect ); [DllImport("kernel32")] private static extern bool VirtualFree( IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType ); [Flags()] public enum AllocationType : uint { COMMIT = 0x1000, RESERVE = 0x2000, RESET = 0x80000, LARGE_PAGES = 0x20000000, PHYSICAL = 0x400000, TOP_DOWN = 0x100000, WRITE_WATCH = 0x200000 } [Flags()] public enum MemoryProtection : uint { EXECUTE = 0x10, EXECUTE_READ = 0x20, EXECUTE_READWRITE = 0x40, EXECUTE_WRITECOPY = 0x80, NOACCESS = 0x01, READONLY = 0x02, READWRITE = 0x04, WRITECOPY = 0x08, GUARD_Modifierflag = 0x100, NOCACHE_Modifierflag = 0x200, WRITECOMBINE_Modifierflag = 0x400 } private CpuIDDelegate cpuIdDelg; private IntPtr codePointer; // void x86CpuId(int level, byte* buffer) // { // eax = level // cpuid // buffer[0] = eax // buffer[4] = ebx // buffer[8] = ecx // buffer[12] = edx // } private byte[] x86CodeBytes = { 0x55, // push ebp 0x8B, 0xEC, // mov ebp,esp 0x53, // push ebx 0x57, // push edi 0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8] (move level into eax) 0x0F, 0xA2, // cpuid 0x8B, 0x7D, 0x0C, // mov edi, dword ptr [ebp+12] (move address of buffer into edi) 0x89, 0x07, // mov dword ptr [edi+0], eax (write eax, ... to buffer) 0x89, 0x5F, 0x04, // mov dword ptr [edi+4], ebx 0x89, 0x4F, 0x08, // mov dword ptr [edi+8], ecx 0x89, 0x57, 0x0C, // mov dword ptr [edi+12],edx 0x5F, // pop edi 0x5B, // pop ebx 0x8B, 0xE5, // mov esp,ebp 0x5D, // pop ebp 0xc3 // ret }; private byte[] x64CodeBytes = { 0x53, // push rbx this gets clobbered by cpuid // rcx is level // rdx is buffer. // Need to save buffer elsewhere, cpuid overwrites rdx // Put buffer in r8, use r8 to reference buffer later. // Save rdx (buffer addy) to r8 0x49, 0x89, 0xd0, // mov r8, rdx // Move ecx (level) to eax to call cpuid, call cpuid 0x89, 0xc8, // mov eax, ecx 0x0F, 0xA2, // cpuid // Write eax et al to buffer 0x41, 0x89, 0x40, 0x00, // mov dword ptr [r8+0], eax 0x41, 0x89, 0x58, 0x04, // mov dword ptr [r8+4], ebx 0x41, 0x89, 0x48, 0x08, // mov dword ptr [r8+8], ecx 0x41, 0x89, 0x50, 0x0c, // mov dword ptr [r8+12], edx 0x5b, // pop rbx 0xc3 // ret }; public CpuID() { Compile(); } ~CpuID() { Dispose(false); } private void Compile() { byte[] codeBytes; if (IntPtr.Size == 4) { codeBytes = x86CodeBytes; } else { codeBytes = x64CodeBytes; } this.codePointer = VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), AllocationType.COMMIT | AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE ); Marshal.Copy(codeBytes, 0, this.codePointer, codeBytes.Length); this.cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(this.codePointer, typeof(CpuIDDelegate)); } public void Invoke(int level, byte[] buffer) { GCHandle handle = default(GCHandle); if (buffer.Length < 16) { throw new ArgumentException("buffer must be at least 16 bytes long"); } try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); this.cpuIdDelg(level, buffer); } finally { if (handle != default(GCHandle)) { handle.Free(); } } } public void Dispose() { Dispose(true); } public void Dispose(bool disposing) { if (this.codePointer != IntPtr.Zero) { VirtualFree(this.codePointer, 0, 0x8000); this.codePointer = IntPtr.Zero; } } } }
我拿@ antiduh的代码,并将其重构为静态方法,所以没有对象生命周期来pipe理。 这样做比较慢,因为ASM代码在Invoke()调用之间没有被重用,但是为了简单起见,速度的权衡对于我的用例来说是有意义的。 这个新版本可以在我的机器上以15毫秒的时间调用CPUID 1000次。
感谢美妙的代码人!
public static class CpuID { public static byte[] Invoke(int level) { IntPtr codePointer = IntPtr.Zero; try { // compile byte[] codeBytes; if (IntPtr.Size == 4) { codeBytes = x86CodeBytes; } else { codeBytes = x64CodeBytes; } codePointer = VirtualAlloc( IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), AllocationType.COMMIT | AllocationType.RESERVE, MemoryProtection.EXECUTE_READWRITE ); Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length); CpuIDDelegate cpuIdDelg = (CpuIDDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIDDelegate)); // invoke GCHandle handle = default(GCHandle); var buffer = new byte[16]; try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); cpuIdDelg(level, buffer); } finally { if (handle != default(GCHandle)) { handle.Free(); } } return buffer; } finally { if (codePointer != IntPtr.Zero) { VirtualFree(codePointer, 0, 0x8000); codePointer = IntPtr.Zero; } } } [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private delegate void CpuIDDelegate(int level, byte[] buffer); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect); [DllImport("kernel32")] private static extern bool VirtualFree(IntPtr lpAddress, UInt32 dwSize, UInt32 dwFreeType); [Flags()] private enum AllocationType : uint { COMMIT = 0x1000, RESERVE = 0x2000, RESET = 0x80000, LARGE_PAGES = 0x20000000, PHYSICAL = 0x400000, TOP_DOWN = 0x100000, WRITE_WATCH = 0x200000 } [Flags()] private enum MemoryProtection : uint { EXECUTE = 0x10, EXECUTE_READ = 0x20, EXECUTE_READWRITE = 0x40, EXECUTE_WRITECOPY = 0x80, NOACCESS = 0x01, READONLY = 0x02, READWRITE = 0x04, WRITECOPY = 0x08, GUARD_Modifierflag = 0x100, NOCACHE_Modifierflag = 0x200, WRITECOMBINE_Modifierflag = 0x400 } // Basic ASM strategy -- // void x86CpuId(int level, byte* buffer) // { // eax = level // cpuid // buffer[0] = eax // buffer[4] = ebx // buffer[8] = ecx // buffer[12] = edx // } private readonly static byte[] x86CodeBytes = { 0x55, // push ebp 0x8B, 0xEC, // mov ebp,esp 0x53, // push ebx 0x57, // push edi 0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8] (move level into eax) 0x0F, 0xA2, // cpuid 0x8B, 0x7D, 0x0C, // mov edi, dword ptr [ebp+12] (move address of buffer into edi) 0x89, 0x07, // mov dword ptr [edi+0], eax (write eax, ... to buffer) 0x89, 0x5F, 0x04, // mov dword ptr [edi+4], ebx 0x89, 0x4F, 0x08, // mov dword ptr [edi+8], ecx 0x89, 0x57, 0x0C, // mov dword ptr [edi+12],edx 0x5F, // pop edi 0x5B, // pop ebx 0x8B, 0xE5, // mov esp,ebp 0x5D, // pop ebp 0xc3 // ret }; private readonly static byte[] x64CodeBytes = { 0x53, // push rbx this gets clobbered by cpuid // rcx is level // rdx is buffer. // Need to save buffer elsewhere, cpuid overwrites rdx // Put buffer in r8, use r8 to reference buffer later. // Save rdx (buffer addy) to r8 0x49, 0x89, 0xd0, // mov r8, rdx // Move ecx (level) to eax to call cpuid, call cpuid 0x89, 0xc8, // mov eax, ecx 0x0F, 0xA2, // cpuid // Write eax et al to buffer 0x41, 0x89, 0x40, 0x00, // mov dword ptr [r8+0], eax 0x41, 0x89, 0x58, 0x04, // mov dword ptr [r8+4], ebx 0x41, 0x89, 0x48, 0x08, // mov dword ptr [r8+8], ecx 0x41, 0x89, 0x50, 0x0c, // mov dword ptr [r8+12], edx 0x5b, // pop rbx 0xc3 // ret }; }
我可以build议以下页面: http : //devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx
此页面将显示CPUID的汇编源代码,如何将其编译为DLL以及如何从C#调用它。
另外如果你需要其他的硬件识别程序,可以build议这个页面: http : //www.codeproject.com/KB/system/GetHardwareInformation.aspx
本页显示如何获取主板信息,硬盘信息,CPU信息,显卡信息等信息。
另外,要获得CPUID4,还需要一个参数。 这里是你如何获得CPUID0,CPUID1,CPUID2,CPUID4。
byte[] cpuid0 = Invoke(0, 0); byte[] cpuid1 = Invoke(1, 0); byte[] cpuid2 = Invoke(2, 0); List<byte[]> cpuid4L = new List<byte[]>(); for (int i = 0; true; i++) { byte[] cpuid4 = Invoke(4, (uint)i); if ( (cpuid4[0] & 0x0F) == 0) break; cpuid4L.Add(cpuid4); } private static byte[] Invoke(uint functionNum, uint ecx) { IntPtr codePointer = IntPtr.Zero; try { // Select a code byte[] codeBytes; if (IntPtr.Size == 4) codeBytes = x86CodeBytes; else codeBytes = x64CodeBytes; codePointer = NativeMethods.VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)codeBytes.Length), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); Marshal.Copy(codeBytes, 0, codePointer, codeBytes.Length); CpuIdDelegate cpuIdDelg = (CpuIdDelegate)Marshal.GetDelegateForFunctionPointer(codePointer, typeof(CpuIdDelegate)); // Invoke the code GCHandle handle = default(GCHandle); var buffer = new byte[16]; try { handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); cpuIdDelg(ecx, functionNum, buffer); // Run the assembly code. } finally { if (handle != default(GCHandle)) { handle.Free(); } } return buffer; } finally { if (codePointer != IntPtr.Zero) { NativeMethods.VirtualFree(codePointer, (UIntPtr) 0, MEM_RELEASE); codePointer = IntPtr.Zero; } } } private readonly static byte[] x86CodeBytes = { 0x55, 0x8B, 0xEC, 0x53, 0x57, 0x8B, 0x4D, 0x08, 0x8B, 0x45, 0x0C, 0x0F, 0xA2, 0x8B, 0x7D, 0x10, 0x89, 0x07, 0x89, 0x5F, 0x04, 0x89, 0x4F, 0x08, 0x89, 0x57, 0x0C, 0x5F, 0x5B, 0x8B, 0xE5, 0x5D, 0xc3 }; private readonly static byte[] x64CodeBytes = { 0x53, 0x89, 0xD0, 0x0F, 0xA2, 0x41, 0x89, 0x40, 0x00, 0x41, 0x89, 0x58, 0x04, 0x41, 0x89, 0x48, 0x08, 0x41, 0x89, 0x50, 0x0c, 0x5b, 0xc3 };
感谢@antiduh的解决scheme。 为了更好的可用性,我会稍微改变一下Invoke签名,所以你不需要分配一个结果作为一组寄存器
// This is a modification to https://stackoverflow.com/a/7964376/725903 [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] private delegate void CpuIDDelegate(int level, IntPtr ptr); [StructLayout(LayoutKind.Sequential, Size = 16)] public struct CpuIdResult { public int Eax; public int Ebx; public int Ecx; public int Edx; } public CpuIdResult Invoke(int level) { CpuIdResult result; IntPtr buffer = Marshal.AllocHGlobal(16); try { this.cpuIdDelg(level, buffer); result = (CpuIdResult)Marshal.PtrToStructure(buffer, typeof(CpuIdResult)); } finally { Marshal.FreeHGlobal(buffer); } return result; }