2009-11-03 65 views
0

我想爲我的C#應用​​程序添加cpuid功能。我在網上發現this有趣的博客文章。我可能需要MASM來編譯這個,但是:編譯用於C#的X86/X64程序集#

  1. 我該如何啓動?
  2. 我懷疑我將不得不爲X86和X64編譯一個dll,但我不知道如何去處理這樣的事情(而且我有點時間緊張)。

所以,任何幫助將超過歡迎!

回答

2

CPUID是一個巨大的痛苦,我會建議不要沿着這條路走下去,如果你能避免它。英特爾和AMD處理器之間的CPUID結果不同(至少對於超線程和緩存拓撲結構等有趣的內容),並且在不同處理器版本之間並不是特別穩定。 (較新的Intel i7處理器引入了一個新的CPUID值(eax = 0xb),它取代了早期處理器上CPUID支持的信息)。

如果你能擺脫它,你最好的選擇是使用WMI(見Win32_Processor)或GetLogicalProcessorInformation

如果在您的平臺上支持這些解決方案(要獲得邏輯處理器信息,要求WinXP sp3或更新版本的客戶端或Windows Server 2008或更新的服務器端,這兩種解決方案中的任何一種都將成爲一種更簡單,更易於管理的解決方案)。

如果你真的想用CPUID來試試你的運氣,我建議做的就是創建一個能夠執行CPUID並將結果返回給託管代碼的簡單存根(你將需要不同版本的32位和64位),並在託管應用程序的上下文中執行這些操作。我通過編譯本機應用程序來完成此任務,然後將我的CPUID方法的原始指令字節抽取到可從託管代碼執行的字節數組中。

這應該讓你開始只有32位支持:

using System; 
using System.Runtime.InteropServices; 

static class Program { 
    static void Main() { 
    //Allocate the executable buffer on a distinct page 
    // rather than just pinning it in place because we 
    // need to mark the page as executable. 
    // Failing to do this would cause NX-enabled machines 
    // to have access violations attempting to execute. 
    IntPtr pExecutableBuffer = VirtualAlloc(
     IntPtr.Zero, 
     new IntPtr(CPUID_32.Length), 
     AllocationType.MEM_COMMIT | AllocationType.MEM_RESERVE, 
     MemoryProtection.PAGE_EXECUTE_READWRITE 
    ); 

    Marshal.Copy(CPUID_32, 0, pExecutableBuffer, CPUID_32.Length); 
    CPUID executeHandler = (CPUID)Marshal.GetDelegateForFunctionPointer(
     pExecutableBuffer, typeof(CPUID)); 
    CPUID_Args args = new CPUID_Args(); 
    args.eax = 0; 
    executeHandler(ref args); 
    Console.WriteLine("eax: {0} ebx: {1} ecx: {2} edx: {3}", 
     args.eax, 
     args.ebx, 
     args.ecx, 
     args.edx); 
    VirtualFree(
     pExecutableBuffer, 
     IntPtr.Zero, 
     FreeType.MEM_RELEASE); 
    } 

    [UnmanagedFunctionPointer(CallingConvention.StdCall)] 
    delegate void CPUID(ref CPUID_Args args); 

    private static readonly byte[] CPUID_32 = new byte[] { 
    0x53,   // push ebx 
    0x57,   // push edi 
    0x8B, 0x7C, 0x24, 0x0C, // mov edi,dword ptr [esp+0Ch] 
    0x8B, 0x07,  // mov eax,dword ptr [edi] 
    0x8B, 0x4F, 0x08, // mov ecx,dword ptr [edi+8] 
    0x0F, 0xA2,  // cpuid  
    0x89, 0x07,  // mov dword ptr [edi],eax 
    0x89, 0x5F, 0x04, // mov dword ptr [edi+4],ebx 
    0x89, 0x4F, 0x08 , // movdword ptr [edi+8],ecx 
    0x89, 0x57, 0x0C , // mov dword ptr [edi+0Ch],edx 
    0x5F,   // pop  edi 
    0x5B,   // pop  ebx 
    0xC2, 0x04, 0x00  // ret 
    }; 

    [Flags] 
    enum AllocationType { 
    MEM_COMMIT = 0x1000, 
    MEM_RESERVE = 0x2000, 
    } 

    [Flags] 
    enum MemoryProtection { 
    PAGE_EXECUTE_READWRITE = 0x40, 
    } 

    [Flags] 
    enum FreeType { 
    MEM_RELEASE = 0x8000 
    } 

    [DllImport("kernel32.dll")] 
    static extern IntPtr VirtualAlloc(
    IntPtr lpAddress, 
    IntPtr dwSize, 
    AllocationType flAllocationType, 
    MemoryProtection flProtect); 

    [DllImport("kernel32.dll")] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool VirtualFree(
    IntPtr lpAddress, 
    IntPtr dwSize, 
    FreeType dwFreeType); 
} 

[StructLayout(LayoutKind.Sequential)] 
struct CPUID_Args { 
    public uint eax; 
    public uint ebx; 
    public uint ecx; 
    public uint edx; 
} 
+0

現在這是一個有用的答案:) 你是第一個提到這可能是32位和64位之間的真正區別。你能否就這些差異在哪裏提供一些額外的指導? 您的一般策略似乎與此相符: http://devpinoy.org/blogs/cvega/archive/2006/04/07/2658.aspx 可能您有任何想法,我可以如何正確編譯位模式? – Kris 2009-11-03 20:48:20

+0

我剛剛意識到,您的答案中的代碼可能只能用於32位,因爲X64位編譯不允許內聯asm? – Kris 2009-11-03 20:51:13

+0

該代碼可以在運行32位操作系統的64位機器上運行(這可能不是您要求的)。真正支持64位更復雜......首先,您必須決定是否支持IA64或僅支持x64/AMD64 /無論您想調用它。然後,您需要一個不同版本的字節數組(稱爲CPUID_64)和代碼,以確保在您要調用CPUID時執行正確版本的字節數組。然而,這個例子本身並沒有使用「內聯彙編」。 x86正在動態加載和執行,它不是二進制文件的可執行部分。 – StarPacker 2009-11-03 20:57:22

-1

我想你可以在C++代碼中使用內聯彙編語法(__asm)構建一個本地CLR程序集。

+0

我似乎記得這在X64模式下無法正常工作。 – Kris 2009-11-03 20:16:13

+0

哦,我在Windows上與C#/ C++混淆了一段時間,這肯定是win32。 – 2009-11-03 20:39:48