標籤

2008年10月20日 星期一

雜亂的上課筆記

不同segment 必用 far call (push ip push cs 再jump)
strong weak ->Module 抽換的概念

2008年10月16日 星期四

CALL BACK

http://www.dev.idv.tw/mediawiki/index.php/%E4%BD%95%E8%AC%82callback_function%EF%BC%9F

Description
簡單的說,如果你使用了某個function,那麼你就是『call'了一個function。如果系統或是函式是要求你給一個function pointer,這個function pointer指到一個實際的函式(多半這個函式是你自己寫的)。然後它會在適當的時間呼叫此function,則此function就是所謂的 callback function。因為這個function是被『callback'了。

舉一個C的例子來說:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define DEFAULT_BLOCK_SIZE (4096)

// 定義callback function的prototype。
typedef void (* CALLBACK) (int);

// 定義了一個名為ShowPercentage的函式。這就是我們的callback函式。
// 他的prototype必須與前面的CALLBACK宣告一致。
void ShowPercentage(int percentage)
{
fprintf(stderr, "%dn%nn", percentage);
}

// 定義了一個CopyFile的函式,這個函式會將參數source所指定檔案複製到
// target參數所指定的檔案去。而且每複製DEFAULT_BLOCK_SIZE數量的資料
// 就會呼叫一次callback參數所指到function一次。
void CopyFile(const char *source, const char *target, CALLBACK callback)
{
char buf[DEFAULT_BLOCK_SIZE] ;
struct stat fs ;
int fdSrc, fdTrg ;
int readBytes = 0, totalReadBytes = 0, percentage = 0;
fdSrc = open(source, O_RDONLY);
fstat(fdSrc, &fs);
fdTrg = open(target,O_CREAT|O_TRUNC|O_RDWR);
// 主要複製資料的迴圈
while((readBytes=read(fdSrc, buf, DEFAULT_BLOCK_SIZE)) > 0)
{
write(fdTrg, buf, readBytes);
totalReadBytes += readBytes ;
//複製資料後就呼叫callback函式去做顯示百分比的動作。
callback( (totalReadBytes*100)/fs.st_size);
}
close(fdTrg);
close(fdSrc);
}

int main(void)
{
// 這個範例中只是利用callback來顯示目前的進度。
// 實際上我們可以利用callback來做更多的動作。
CopyFile("A.TXT", "B.TXT", ShowPercentage);
return 0 ;
}


2008年10月15日 星期三

EFI OVERVIEW










  • CPU RESET ENTRY 在fffffff0 ,正好在SEC CORE的位置,擺在ROM的最上面,SEC在CRB實際上也只有一行CODE,他只是一個MODULE而已,他與PEI放在一起,看成一個特殊的PEI MAIN就好。 EFI則是放在BIOS的最下面。
  • IA32,X64,3.5之後的CODE之後target processor都是用x64。
  • PEI_MAIN、NVSTORAGE(存放DATA的地方)、FV_DYNAMIC、FV_BACKUP、FV_RECOVERY。
  • 每一隻MODULE都是一個FILE
  • EFI想像成小型OS,想像HD是BIOS ROM,FV就是partition,Region name就是他的name。會有幾個partition呢,請看FLASHMAP。
  • DSC FILE 描述你有什麼COMPONENT
    ENV檔為COMPONENT的DEFINE
  • ENTRY點都在ASM裡幾乎不會去變動,做PROJECT出錯,要找RESET點,要去哪裡找呢,在IA32的resetvec.asm
  • 建立STACK才可以做FUNCTION CALL
  • package定義component type,告訴他是POST CODE,才不會COMPILER出錯(DSC檔中)
  • 為什麼第四個phase BDS不是擺在最後面,在CODE的前面先出現呢,BDS其實是做DRIVER的CONNECT,DXE只是載入記憶體,有些DRIVER需要CONNECT才可以開始啟動,想像成WINDOWS的DEVICE MANAGER,可以每個DRIVER ENABLE、DISABLE。
  • BDS想像為一個DXE DRIVER (PWR ON->SEC->PEI(Pre EFI initialization)->DXE(Driver Execution Environment)->BDS(Boot Device Selection)->OS)
  • 在SEC階段 SS可用 DS不可用,不要去動,在C中不要加入STATIC,不然會被放到DS中
  • SEC會LINK PEI MAIN載入它,這時想像PEI是個小型的OS, 此時有很多的FUNCTION CALL可以用,例如FV SERVICE。基本上是一個檔案一個檔案載入進來,順序是系統決定,以PEI來講,大致上跟我們寫的順序一樣,除非有特殊的DEPENDENCY。
  • 所謂的BOOT FV,就是放PEI MAIN的地方,包含PEI CORE 和 MODULES。沒有他就找不到NEXT FV。
  • PEI CODE大部分在ROM執行,但後面可能可以進到MEMORY了
  • Driver 有沒有進去可以看FVMAIN.INF 裡面會列出所有的component
  • FILE就是FILE拉~
  • 每一個INF檔是一個COMPONENT 被放在FVMAIN裡面
  • flashmap.h有所有的flash region的base、size、offset,sub region也有。
  • 有了GUID,PPI即可找到相對的記憶體位置
  • 不要把兩個INF檔放再同一個目錄
  • PEI沒有RAM,怎麼擺資料結構呢 :用STACK去建立HOB LIST
  • 一個PPI,就是一個GUID加上一個POINTER
  • proto type: 定義一個 function應該傳入什麼參數。
  • PEI到PXE的階段,必須把記憶體位址的INFORMATION PASS 給PXE
  • DXE想像為比PEI大一點的小型OS
  • DXE DEPENDENCY的概念:dxs檔描述他需要哪些PROTOCOL
  • 要看DXE DRIVER,先看INF檔描述的ENTRY POINT,傳HANDLE和SYSTEM TABLE,
    SYSTEM TABLE,
  • EFI的概念中,所有的檔案都是EFI檔,都可以執行,不僅可以放在ROM裡,還可以放在HD中,那我們要傳些什麼給他呢,就是一個PATH,有點像是C:\xxx\xxx,在EFI中,我們需要比較完整的路徑,稍後詳述。
  • DXE階段,有TIMER、EVENT的觀念。
  • 8259的IRQ1(KB)幾乎都是被DISABLE的,由於進入PROTECTED MODE,INT一進來我們看到的是32BIT的IDT,那KB是怎麼被抓進來呢,哈我聽不太懂= =,只說是有POOL的概念,參考SPEC的CH5。重聽(5)26:00
  • PROTOCAL

2008年10月14日 星期二

CPU ID

/*
用CPUID指令,首先你可以確定你用的CPU是Intel的。
然後執行:
MOV EAX,01H
CPUID
如果返回的EDX中,低18位為1,那麼這個CPU就是支持序列號的。
此時EAX就是序列號的高32位。這32位對同一型號的CPU是一樣的。
再執行:
MOV EAX,03H
CPUID
此時的EDX:ECX就是序列號的第64位。

要想關閉這個ID,可執行下列代碼:

MOV ECX,119H
RDMSR
OR EAX,00200000H
WRMSR

不過,一旦執行上述代碼,cpu將一直不能取id,直到下次reset。

*/

#include
#include

int main(int argc,char **argv)
{
unsigned long s1,s2;
unsigned char vendor_id[]="------------";
char sel;
printf("Select the function:\n1-------Read CPU id.\n2-------Disable CPU id.\n");
sel=getch();
switch(sel)
{
case '1':
asm xor eax,eax
asm cpuid
asm mov dword ptr vendor_id,ebx
asm mov dword ptr vendor_id[+4],edx
asm mov dword ptr vendor_id[+8],ecx
printf("%s-",vendor_id);
asm mov eax,01h
asm xor edx,edx
asm cpuid
asm mov s1,edx
asm mov s2,eax
printf("%08X\n%08X-",s1,s2);
asm mov eax,03h
asm xor ecx,ecx
asm xor edx,edx
asm cpuid
asm mov s1,edx
asm mov s2,ecx
printf("%08X-%08X\n",s1,s2);
break;
case '2':
asm{
mov ecx,119h
rdmsr
or eax,00200000h
wrmsr
}
printf("CPU id is disabled.\n");
break;
}
return 0;
}

最後說一下,那個關閉CPUID的功能我一直也不能成功,總是非法指令。

-*-*-PATCH-*-*-2002-07-19

找到一個比較正規底程式


#include
#include
#include

//#define cpuid asm emit 0fh asm emit 0a2h

int GetProcessorSerialNumber(unsigned int processor, unsigned int &psn_00_31, unsigned int &psn_32_63, unsigned int &psn_64_95)
{
unsigned int wCPU_SerialNo_00_31 = 0;
unsigned int wCPU_SerialNo_32_63 = 0;
unsigned int wCPU_SerialNo_64_93 = 0;

#define PSN_FLAG 0x4000//(0x1 << 18) int errCode = 0; int psn_falg = PSN_FLAG; DWORD_PTR dwThreadAffinitiMask = (0x1 << processor); SetThreadAffinityMask(GetCurrentThread(), dwThreadAffinitiMask); asm { pushad mov eax,0 cpuid cmp ebx, 'uneG' jne wrong_processor cmp edx, 'Ieni' jne wrong_processor cmp ecx, 'letn' jne wrong_processor // now we have an Intel-Processor: // get CPU feature flag.. mov eax,1 cpuid mov wCPU_SerialNo_64_93,eax and edx,PSN_FLAG cmp edx,PSN_FLAG jne psn_not_supported_or_disabled // get the PSN mov eax,3 cpuid mov wCPU_SerialNo_32_63,edx mov wCPU_SerialNo_00_31,ecx jmp _exit_ wrong_processor: mov errCode, 1; jmp _exit_; psn_not_supported_or_disabled: mov errCode, 2; _exit_: popad } psn_00_31 = wCPU_SerialNo_00_31; psn_32_63 = wCPU_SerialNo_32_63; psn_64_95 = wCPU_SerialNo_64_93; dwThreadAffinitiMask = 0xffffffff; SetThreadAffinityMask(GetCurrentThread(), dwThreadAffinitiMask); return errCode; } int _tmain(int argc, _TCHAR* argv[]) { unsigned int psn_00_31; unsigned int psn_32_63; unsigned int psn_64_95; int iRet = GetProcessorSerialNumber(0, psn_00_31, psn_32_63, psn_64_95); _tprintf(_T("\nPSN (%d): %.8x-%.8x-%.8x\n"), iRet, psn_64_95, psn_32_63, psn_00_31); return 0; } For more info see: Intel?Processor Identification and the CPUID Instruction http://www.intel.com/design/xeon/applnots/241618.htm -*-*-PATCH-*-*-2002-07-19   誰有讀取CPU序列號或其他硬體唯一標識碼的源碼,100大洋相送! -------------------------------------------------------------------------------- amiao_107@163.com amiao107@sohu.com -------------------------------------------------------------------------------- procedure GetCpuInfo; var R: array[0..19] of Char; var CpuID: Integer; begin FillChar(R, 20, 0); asm mov eax, 0 db 0fh, 0a2h // 其實就是cpuid匯編指令
mov dword ptr R[0], ebx
mov dword ptr R[4], edx
mov dword ptr R[8], ecx
mov eax, 1
db 0fh, 0a2h // cpuid
mov CpuID, edx
end;
ShowMessage('CPU製造商為:' + R);
ShowMessage('序列號為:' + IntToStr(CpuID));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
GetCpuInfo;
end;

--------------------------------------------------------------------------------

GZ

--------------------------------------------------------------------------------

procedure GetCpuInfo;
var R: array[0..19] of Char;
var CpuID: Integer;
begin
FillChar(R, 20, 0);
asm
mov eax, 0
db 0fh, 0a2h // 其實就是cpuid匯編指令
mov dword ptr R[0], ebx
mov dword ptr R[4], edx
mov dword ptr R[8], ecx
mov eax, 1
db 0fh, 0a2h // cpuid
mov CpuID, edx
end;
ShowMessage('CPU製造商為:' + R);
ShowMessage('序列號為:' + IntToStr(CpuID));
end;

--------------------------------------------------------------------------------

呵呵,我試試

--------------------------------------------------------------------------------

gz

--------------------------------------------------------------------------------

不行啊,在多台機上測試得出的CPU的ID相同,都為25426431,這是為什麼?

--------------------------------------------------------------------------------

學習!
試了一下:
CPU製造商為:GenuineIntel
序列號為:8452697

--------------------------------------------------------------------------------

怎麼解決這樣的問題,在多台機器上得到的CPU的ID都相同,為什麼會有這樣的情況出現

--------------------------------------------------------------------------------

函數原形如下:
VOID GetSystemInfo(LPSYSTEM_INFO lpSystemInfo);//指向系統信息結構的指針;
例:
var :System_Info;
begin
GetSystemInfo(SysInfo);//獲取CPU信息;
Edit1.text:='系統共有'+IntTostr(SysInfo.dwNumberOfProcessors)+'個CPU';
end;

--------------------------------------------------------------------------------

學習。
試了一下luoweicaisd(笑三少) 的代碼:
CPU製造商為:GenuineIntel
序列號為:8452607
正確!

--------------------------------------------------------------------------------

支持2000嗎?

--------------------------------------------------------------------------------

OK,好使

--------------------------------------------------------------------------------

用上面那段代碼,為什麼多台機器測出的CPU ID 都是一樣的?兩台塞羊(型號一樣)和一台奔三,得到的結果都是一樣的。

--------------------------------------------------------------------------------

下面是得到BIOS資訊的代碼:procedure TForm1.BiosInfo;
const
Subkey: string = ''Hardware\description\system'';
var
hkSB: HKEY;
rType: LongInt;
ValueSize, OrigSize: Longint;
ValueBuf: array[0..1000] of char;
procedure ParseValueBuf(const VersionType: string);
var
I, Line: Cardinal;
S: string;
begin
i := 0;
Line := 0;
while ValueBuf[i] <> #0 do
begin
S := StrPas(@ValueBuf[i]); // move the Pchar into a string
Inc(Line);
Memo1.Lines.Append(Format(''%s Line %d = %s'',
[VersionType, Line, S])); // add it to a Memo
inc(i, Length(S) + 1);
// to point to next sz, or to #0 if at
end
end;
end;
begin
if RegOpenKeyEx(HKEY_LOCAL_MACHINE, PChar(Subkey), 0,
KEY_READ, hkSB) = ERROR_SUCCESS then
try
OrigSize := sizeof(ValueBuf);
ValueSize := OrigSize;
rType := REG_MULTI_SZ;
if RegQueryValueEx(hkSB, ''SystemBiosVersion'', nil, @rType,
@ValueBuf, @ValueSize) = ERROR_SUCCESS then
ParseValueBuf(''System BIOS Version'');
ValueSize := OrigSize;
rType := REG_SZ;
if RegQueryValueEx(hkSB, ''SystemBIOSDate'', nil, @rType,
@ValueBuf, @ValueSize) = ERROR_SUCCESS then
Memo1.Lines.Append(''System BIOS Date '' + ValueBuf);
ValueSize := OrigSize;
rType := REG_MULTI_SZ;
if RegQueryValueEx(hkSB, ''VideoBiosVersion'', nil, @rType,
@ValueBuf, @ValueSize) = ERROR_SUCCESS then
ParseValueBuf(''Video BIOS Version'');
ValueSize := OrigSize;
rType := REG_SZ;
if RegQueryValueEx(hkSB, ''VideoBIOSDate'', nil, @rType,
@ValueBuf, @ValueSize) = ERROR_SUCCESS then
Memo1.Lines.Append(''Video BIOS Date '' + ValueBuf);
finally
RegCloseKey(hkSB);
end;
end;


--------------------------------------------------------------------------------

讀硬盤的序列號
procedure GetHardDriveinfo;
var
VolumeSerialNumber : DWORD;
MaximumComponentLength : DWORD;
FileSystemFlags : DWORD;
SerialNumber : string;
drv:string;
begin
drv:='c:\';//可以改成其他硬盤
GetVolumeInformation(pchar(drv) , nil,0,@VolumeSerialNumber,
MaximumComponentLength, FileSystemFlags,
nil,0);
SerialNumber:=inttostr(VolumeSerialNumber);
ShowMessage(SerialNumber);
end;

--------------------------------------------------------------------------------

許多FAQ中推薦使用GetVolumeInformation來獲取硬盤序列號。但是那獲取的是卷的序列號,而不是硬盤的序列號。卷的序列號是在分區格式化時生成或修改。一些公司使用復制工具來為全部新計算機安裝軟件----通過將一個硬盤復制到其他硬盤,當然,所有這些計算機上卷的資訊(包括序列號)都是相同的。

我建議另外的一個方法:獲取真正硬盤的序列號。
不幸的是,下列代碼只能工作在IDE硬盤上。


//獲取第一個IDE硬盤的序列號
function GetIdeSerialNumber : SerialNumber;
const IDENTIFY_BUFFER_SIZE = 512;
type
TIDERegs = packed record
bFeaturesReg : BYTE; // Used for specifying SMART "commands".
bSectorCountReg : BYTE; // IDE sector count register
bSectorNumberReg : BYTE; // IDE sector number register
bCylLowReg : BYTE; // IDE low order cylinder value
bCylHighReg : BYTE; // IDE high order cylinder value
bDriveHeadReg : BYTE; // IDE drive/head register
bCommandReg : BYTE; // Actual IDE command.
bReserved : BYTE; // reserved for future use. Must be zero.
end;
TSendCmdInParams = packed record
// Buffer size in bytes
cBufferSize : DWORD;
// Structure with drive register values.
irDriveRegs : TIDERegs;
// Physical drive number to send command to (0,1,2,3).
bDriveNumber : BYTE;
bReserved : Array[0..2] of Byte;
dwReserved : Array[0..3] of DWORD;
bBuffer : Array[0..0] of Byte; // Input buffer.
end;
TIdSector = packed record
wGenConfig : Word;
wNumCyls : Word;
wReserved : Word;
wNumHeads : Word;
wBytesPerTrack : Word;
wBytesPerSector : Word;
wSectorsPerTrack : Word;
wVendorUnique : Array[0..2] of Word;
sSerialNumber : Array[0..19] of CHAR;
wBufferType : Word;
wBufferSize : Word;
wECCSize : Word;
sFirmwareRev : Array[0..7] of Char;
sModelNumber : Array[0..39] of Char;
wMoreVendorUnique : Word;
wDoubleWordIO : Word;
wCapabilities : Word;
wReserved1 : Word;
wPIOTiming : Word;
wDMATiming : Word;
wBS : Word;
wNumCurrentCyls : Word;
wNumCurrentHeads : Word;
wNumCurrentSectorsPerTrack : Word;
ulCurrentSectorCapacity : DWORD;
wMultSectorStuff : Word;
ulTotalAddressableSectors : DWORD;
wSingleWordDMA : Word;
wMultiWordDMA : Word;
bReserved : Array[0..127] of BYTE;
end;
PIdSector = ^TIdSector;
TDriverStatus = packed record
// 驅動器返回的錯誤代碼,無錯則返回0
bDriverError : Byte;
// IDE出錯寄存器的內容,只有當bDriverError 為 SMART_IDE_ERROR 時有效
bIDEStatus : Byte;
bReserved : Array[0..1] of Byte;
dwReserved : Array[0..1] of DWORD;
end;
TSendCmdOutParams = packed record
// bBuffer的大小
cBufferSize : DWORD;
// 驅動器狀態
DriverStatus : TDriverStatus;
// 用於保存從驅動器讀出的數據的緩沖區,實際長度由cBufferSize決定
bBuffer : Array[0..0] of BYTE;
end;

var hDevice : THandle;
cbBytesReturned : DWORD;
ptr : PChar;
SCIP : TSendCmdInParams;
aIdOutCmd : Array [0..(SizeOf(TSendCmdOutParams)+IDENTIFY_BUFFER_SIZE-1)-1] of Byte;
IdOutCmd : TSendCmdOutParams absolute aIdOutCmd;

procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1)-1 do
begin
c := ptr^;
ptr^ := (ptr+1)^;
(ptr+1)^ := c;
Inc(ptr,2);
end;
end;

begin
Result := ''; // 如果出錯則返回空串
if SysUtils.Win32Platform=VER_PLATFORM_WIN32_NT then // Windows NT, Windows 2000
begin
// 提示! 改變名稱可適用於其他驅動器,如第二個驅動器: '\\.\PhysicalDrive1\'
hDevice := CreateFile( '\\.\PhysicalDrive0', GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
end
else // Version Windows 95 OSR2, Windows 98
hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil, CREATE_NEW, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
FillChar(SCIP,SizeOf(TSendCmdInParams)-1,#0);
FillChar(aIdOutCmd,SizeOf(aIdOutCmd),#0);
cbBytesReturned := 0;
// Set up data structures for IDENTIFY command.
with SCIP do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
// bDriveNumber := 0;
with irDriveRegs do
begin
bSectorCountReg := 1;
bSectorNumberReg := 1;
// if Win32Platform=VER_PLATFORM_WIN32_NT then bDriveHeadReg := $A0
// else bDriveHeadReg := $A0 or ((bDriveNum and 1) shl 4);
bDriveHeadReg := $A0;
bCommandReg := $EC;
end;
end;
if not DeviceIoControl( hDevice, $0007c088, @SCIP, SizeOf(TSendCmdInParams)-1,
@aIdOutCmd, SizeOf(aIdOutCmd), cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
with PIdSector(@IdOutCmd.bBuffer)^ do
begin
ChangeByteOrder( sSerialNumber, SizeOf(sSerialNumber) );
(PChar(@sSerialNumber)+SizeOf(sSerialNumber))^ := #0;
Result := PChar(@sSerialNumber);
end;
end;

// 更多關於 S.M.A.R.T. ioctl 的信息可查看:
// http://www.microsoft.com/hwdev/download/respec/iocltapi.rtf

// MSDN庫中也有一些簡單的例子
// Windows Development -> Win32 Device Driver Kit ->
// SAMPLE: SmartApp.exe Accesses SMART stats in IDE drives

// 還可以查看 http://www.mtgroup.ru/~alexk
// IdeInfo.zip - 一個簡單的使用了S.M.A.R.T. Ioctl API的Delphi應用程式

// 注意:

// WinNT/Win2000 - 你必須擁有對硬盤的讀/寫訪問權限

// Win98
// SMARTVSD.VXD 必須安裝到 \windows\system\iosubsys
// (不要忘記在復制後重新啟動系統)

--------------------------------------------------------------------------------

在我的前一篇文章(http://www.ocloud.com/article/viewart.php?id=88)裏我描述了一個通過調用以DFP_RECEIVE_DRIVE_DATA為控制碼的DeviceIoControl函數來獲取序列號,但在NT上只有擁有系統管理員權限的用戶才能使用。現在我找到了讓所有用戶都可以使用的方法。
{ 構件下載:IdeSN.zip }
提示! 在Win9x系統中必須安裝smartvsd.vxd:你只需簡單的將它從\windows\system\目錄中復制到\windows\system\iosubsys\下,然後重新啟動系統。

// (c) Alex Konshin 30 jul 2000

program IdeSN;

// 目的:簡單的控制臺程式以獲取第一個IDE硬盤的序列號

{$APPTYPE CONSOLE}

uses
Windows,
SysUtils; // only for Win32Platform and SysErrorMessage

//-------------------------------------------------------------
function GetIdeDiskSerialNumber : String;
type
TSrbIoControl = packed record
HeaderLength : ULONG;
Signature : Array[0..7] of Char;
Timeout : ULONG;
ControlCode : ULONG;
ReturnCode : ULONG;
Length : ULONG;
end;
SRB_IO_CONTROL = TSrbIoControl;
PSrbIoControl = ^TSrbIoControl;

TIDERegs = packed record
bFeaturesReg : Byte; // Used for specifying SMART "commands".
bSectorCountReg : Byte; // IDE sector count register
bSectorNumberReg : Byte; // IDE sector number register
bCylLowReg : Byte; // IDE low order cylinder value
bCylHighReg : Byte; // IDE high order cylinder value
bDriveHeadReg : Byte; // IDE drive/head register
bCommandReg : Byte; // Actual IDE command.
bReserved : Byte; // reserved. Must be zero.
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;

TSendCmdInParams = packed record
cBufferSize : DWORD;
irDriveRegs : TIDERegs;
bDriveNumber : Byte;
bReserved : Array[0..2] of Byte;
dwReserved : Array[0..3] of DWORD;
bBuffer : Array[0..0] of Byte;
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;

TIdSector = packed record
wGenConfig : Word;
wNumCyls : Word;
wReserved : Word;
wNumHeads : Word;
wBytesPerTrack : Word;
wBytesPerSector : Word;
wSectorsPerTrack : Word;
wVendorUnique : Array[0..2] of Word;
sSerialNumber : Array[0..19] of Char;
wBufferType : Word;
wBufferSize : Word;
wECCSize : Word;
sFirmwareRev : Array[0..7] of Char;
sModelNumber : Array[0..39] of Char;
wMoreVendorUnique : Word;
wDoubleWordIO : Word;
wCapabilities : Word;
wReserved1 : Word;
wPIOTiming : Word;
wDMATiming : Word;
wBS : Word;
wNumCurrentCyls : Word;
wNumCurrentHeads : Word;
wNumCurrentSectorsPerTrack : Word;
ulCurrentSectorCapacity : ULONG;
wMultSectorStuff : Word;
ulTotalAddressableSectors : ULONG;
wSingleWordDMA : Word;
wMultiWordDMA : Word;
bReserved : Array[0..127] of Byte;
end;
PIdSector = ^TIdSector;

const
IDE_ID_FUNCTION = $EC;
IDENTIFY_BUFFER_SIZE = 512;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
IOCTL_SCSI_MINIPORT = $0004d008;
IOCTL_SCSI_MINIPORT_IDENTIFY = $001b0501;
DataSize = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
BufferSize = SizeOf(SRB_IO_CONTROL)+DataSize;
W9xBufferSize = IDENTIFY_BUFFER_SIZE+16;
var
hDevice : THandle;
cbBytesReturned : DWORD;
pInData : PSendCmdInParams;
pOutData : Pointer; // PSendCmdOutParams
Buffer : Array[0..BufferSize-1] of Byte;
srbControl : TSrbIoControl absolute Buffer;

procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1)-1 do
begin
c := ptr^;
ptr^ := (ptr+1)^;
(ptr+1)^ := c;
Inc(ptr,2);
end;
end;

begin
Result := '';
FillChar(Buffer,BufferSize,#0);
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin // Windows NT, Windows 2000
// 獲取 SCSI port handle
hDevice := CreateFile( '\\.\Scsi0:',
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
System.Move('SCSIDISK',srbControl.Signature,8);
srbControl.Timeout := 2;
srbControl.Length := DataSize;
srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
pInData := PSendCmdInParams(PChar(@Buffer)
+SizeOf(SRB_IO_CONTROL));
pOutData := pInData;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT,
@Buffer, BufferSize, @Buffer, BufferSize,
cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end
else
begin // Windows 95 OSR2, Windows 98
hDevice := CreateFile( '\\.\SMARTVSD', 0, 0, nil,
CREATE_NEW, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
pInData := PSendCmdInParams(@Buffer);
pOutData := @pInData^.bBuffer;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA,
pInData, SizeOf(TSendCmdInParams)-1, pOutData,
W9xBufferSize, cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end;
with PIdSector(PChar(pOutData)+16)^ do
begin
ChangeByteOrder(sSerialNumber,SizeOf(sSerialNumber));
SetString(Result,sSerialNumber,SizeOf(sSerialNumber));
end;
end;


//=============================================================
var s : String;
rc : DWORD;
begin
s := GetIdeDiskSerialNumber;
if s='' then
begin
rc := GetLastError;
if rc=0 then WriteLn('IDE drive is not support SMART feature')
else WriteLn(SysErrorMessage(rc));
end
else WriteLn('Disk serial number: ''', s,'''');
end.

--------------------------------------------------------------------------------

以上文章均轉載於淩雲天地,未加編輯,不是本人所作

--------------------------------------------------------------------------------

感謝fontain等網友的關注,為表感謝,送分了,接分了...

--------------------------------------------------------------------------------

你讀取硬體唯一標識碼的目的是為了控制你的軟件只在一台機器上運行,是嗎?那我建議你讀取網卡的物理位址,可行性有:1、眾所周知:網卡的物理位址獨一無二的;2、網卡的價格很低;怎麼樣?

簡單的 nmake 使用的 Makefile 範例

# 這是一個簡單的 nmake 使用的 Makefile 範例
# 編譯: c:> nmake
# 清除: c:> nmake clean
# 井民全
#

# 對應 GNU make (如果你想使用 MSYS 編譯的話)
# SRC=$(shell ls *.cpp)
SRC=*.cpp

#替換原則: Objs 檔案群= SRC 檔案群換成 .obj
Objs=$(SRC:cpp=obj)
CFLAGS=-nologo /I"$(VC_DIR)include"

# 主要編譯指令: 先編譯所有的 Objs, 然後建立 exe
all:$(Objs) demo.exe

$(Objs):

# 連結指令
# 注意: 不能用 LINK.exe /OUT:$*.exe $<, 因為建立 exe 檔需要所有的 object 檔案, 不能只有改變的檔
demo.exe: $(Objs)
@echo "Linking ..."
LINK.exe /OUT:$*.exe $(Objs)

# 編譯指令
# 定義所有的 obj 檔 都 depend on .cpp 檔
# 所有 .cpp 更改, 都會重新編譯更改的檔案
.cpp.obj::
echo "Compiling ..."
$(CC) $(CFLAGS) -Fd$O\ -c $<

clean:
del *.obj *.exe

#FAQ
# the list of compiler options
# ms-help://MS.MSDNQTR.2006JAN.1033/vccore/html/vcrefcompileroptionslistedalphabetically.htm