使用Delphi获取真实的硬盘序列号

10

我正在尝试制作Delphi程序的服务器和客户端以保护我的应用程序,确保所有用户都在控制之下。为了给他们提供一个独特的密钥,以防止失去处理它们的能力,我认为应该是硬盘序列号+ BIOS SN。但是,我记得BIOS可以更改,当移除主板电池时,它将无法使用。所以现在我选择的是HDD真实序列号,我尝试使用以下代码来获取它,但它没有起作用。

    unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,WbemScripting_TLB,ActiveX;

type
  TForm4 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

function GetWMIstring (wmiHost, wmiClass, wmiProperty : string):string;
var  // These are all needed for the WMI querying process
  Locator:  ISWbemLocator;
  Services: ISWbemServices;
  SObject:  ISWbemObject;
  ObjSet:   ISWbemObjectSet;
  SProp:    ISWbemProperty;
  Enum:     IEnumVariant;
  Value:    Cardinal;
  TempObj:  OleVariant;
  SN: string;
begin
  try
  Locator := CoSWbemLocator.Create;  // Create the Location object
  // Connect to the WMI service, with the root\cimv2 namespace
   Services :=  Locator.ConnectServer(wmiHost, 'root\cimv2', '', '', '','', 0, nil);
  ObjSet := Services.ExecQuery('SELECT * FROM '+wmiClass, 'WQL',
    wbemFlagReturnImmediately and wbemFlagForwardOnly , nil);
  Enum :=  (ObjSet._NewEnum) as IEnumVariant;
  while (Enum.Next(1, TempObj, Value) = S_OK) do
  begin
    SObject := IUnknown(tempObj) as ISWBemObject;
    SProp := SObject.Properties_.Item(wmiProperty, 0);
    if VarIsNull(SProp.Get_Value) then
      result := ''
    else
    begin
      SN := SProp.Get_Value;
      result :=  SN;
    end;
  end;
  except // Trap any exceptions (Not having WMI installed will cause one!)
   on exception do
    result := '';
   end;
end;

procedure TForm4.Button1Click(Sender: TObject);
var
x:string;
Y:string;

begin


    X:=GetWMIstring('','Win32_BIOS','SerialNumber');
    Y:=GetWMIstring('','Win32_DiskDrive"','SerialNumber')     ;

    ShowMessage(x+';'+y);
end;

end.

*所以请问有人能够帮忙校正我的代码或者给我其他的建议吗? 最好的祝福。*

2个回答

24

你的代码无法正常工作是因为在 WMI 类名中传递了双引号。

请修改以下代码

GetWMIstring('','Win32_DiskDrive"','SerialNumber');

对于这个

GetWMIstring('','Win32_DiskDrive','SerialNumber');

顺便提一下,如果你按照这个问题的答案How can I improve the WMI performance using delphi?的建议来改进你的WMI函数(GetWMIstring),那么你可以大幅提高它的性能。

试用这个示例(这段代码使用后期绑定,不需要WbemScripting_TLB单元)

  uses
      ActiveX,
      ComObj;

    var
      FSWbemLocator : OLEVariant;
      FWMIService   : OLEVariant;

    function  GetWMIstring(const WMIClass, WMIProperty:string): string;
    const
      wbemFlagForwardOnly = $00000020;
    var
      FWbemObjectSet: OLEVariant;
      FWbemObject   : OLEVariant;
      oEnum         : IEnumvariant;
      iValue        : LongWord;
    begin;
      Result:='';
      FWbemObjectSet:= FWMIService.ExecQuery(Format('Select %s from %s',[WMIProperty, WMIClass]),'WQL',wbemFlagForwardOnly);
      oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
      if oEnum.Next(1, FWbemObject, iValue) = 0 then

  if not VarIsNull(FWbemObject.Properties_.Item(WMIProperty).Value) then

     Result:=FWbemObject.Properties_.Item(WMIProperty).Value;

    FWbemObject:=Unassigned;
    end;

    procedure TForm4.Button1Click(Sender: TObject);
    var
      x:string;
      Y:string;
    begin
      FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
      FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');

      X:=GetWMIstring('Win32_BIOS','SerialNumber');
      Y:=GetWMIstring('Win32_PhysicalMedia','SerialNumber');

      ShowMessage(x+';'+y);
    end;

1
+1。眼力真好!在你指出它之前,那个额外的"真的很难被发现。 :-) - Ken White
非常感谢您的快速回复,我已经从类中删除了双引号,但我仍然收到“未找到”错误。关于将其改进为Delphi控制台应用程序,我不知道如何将其转换为Delphi应用程序。 - Anajehad Walaa
是的,我是 :) 那么现在我能做什么? - Anajehad Walaa
请改用Win32_PhysicalMedia WMI类来检索数据,然后修改您的函数以检索WMI数据,我在我的答案中发布了一个示例。 - RRUZ
谢谢,我尝试了一下,但是从函数返回的数字太短了,所以我认为它不是真正的硬盘序列号,因此我尝试使用Win32_DiskDrive。 - Anajehad Walaa
显示剩余4条评论

0

在编程中,应该避免当安装了更多设备时,PHYSICALDRIVE0可能不是WMI数据库中的第一个。在我的情况下(在我的电脑上),PHYSICALDRIVE1是结果中的第一个,而PHYSICALDRIVE0是按顺序排列的第二个。我在SQL查询中添加了where来仅选择PHYSICALDRIVE0,现在可以正常工作了。 对于Win32_DiskDrive,属性名为DeviceID,对于Win32_PhysicalDrive,属性名为Tag。以下是代码的更改部分:

function  GetWMIstring(const WMIClass, WMIProperty, WMIWhere:string): string;
var qry:string;
...
  qry:='SELECT '+WMIProperty+' FROM '+WMIClass;
  if WMIWhere<>'' then qry:=qry + ' WHERE '+WMIWhere;
  FWbemObjectSet:= FWMIService.ExecQuery(qry,'WQL',wbemFlagForwardOnly);
...
  x:=GetWMIstring('Win32_DiskDrive','SerialNumber','DeviceID="\\\\.\\PHYSICALDRIVE0"'));
  y:=GetWMIstring('Win32_PhysicalMedia','SerialNumber','Tag="\\\\.\\PHYSICALDRIVE0"'));

不要忘记在调用此函数之前调用 CoInitialize !!!

你能否编辑你的回答,使其更加清晰明了?“is first be me”是什么意思?如果你告诉我要调用“CoInitialize”,那么你的代码(第二行和第六行有三个点看起来非常奇怪)如何能够正常工作而不调用它呢? - Nico Haase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接