Доступ к BcdStore из Delphi

Я пытаюсь преобразовать этот фрагмент кода в Delphi, и я застрял на for each objWBL in colObjects,

if not objBcdStore.EnumerateObjects( &h10200003, colObjects ) then
    WScript.Echo "ERROR objBcdStore.EnumerateObjects( &h10200003 ) failed."
    WScript.Quit(1)
end if

for each objWBL in colObjects
    WScript.Echo ""
    WScript.Echo "Windows Boot Loader"
    WScript.Echo "-------------------"

    WScript.Echo "identifier              " & GetBcdId( objWBL.Id )

    If objWBL.Id = current then

      if not objWBL.GetElement(BcdOSLoaderInteger_NumberOfProcessors, objElement ) then
          WScript.Echo "ERROR WBL GetElement for " & Hex(BcdOSLoaderInteger_NumberOfProcessors) & " failed."
          WScript.Quit(1)
      end if
      WScript.Echo "numproc              " & objElement.Integer

      if not objWBL.GetElement(BcdOSLoaderBoolean_UseBootProcessorOnly, objElement ) then
          WScript.Echo "ERROR WBL GetElement for " & Hex(BcdOSLoaderBoolean_UseBootProcessorOnly) & " failed."
          WScript.Quit(1)
      end if
      WScript.Echo "onecpu              " & objElement.Boolean

    end if
next

Мой частичный перевод вопросов и ответов (осторожно, для запуска должен быть Admin):

uses
  OleAuto,
  ActiveX;

function GetObject(const objectName: String): IDispatch;
var
  bindCtx: IBindCtx;
  moniker: IMoniker;
  chEaten: Integer;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(bindCtx, StringToOleStr(objectName), chEaten, moniker));
  OleCheck(moniker.BindToObject(bindCtx, nil, IDispatch, Result));
end;

procedure TForm44.btnClick(Sender: TObject);
var
  colObjects   : OleVariant;
  objBcdStore  : OleVariant;
  objWMIService: OleVariant;
begin
  objWMIService := GetObject('winmgmts:{impersonationlevel=Impersonate,(Backup,Restore)}!root/wmi:BcdStore');
  if not objWMIService.OpenStore('', objBcdStore) then
    Caption := 'error'
  else begin
    objBcdStore.EnumerateObjects($10200003, colObjects);
    //???
  end;
end;

EnumerateObjects определяется как

boolean EnumerateObjects(
  [in]   uint32 Type,
  [out]  BcdObject Objects[]
);

Я понятия не имею, как пройти через массив BcdObject в Delphi.

2 ответа

Решение

Вы должны использовать VarArrayLowBound а также VarArrayHighBound функции, чтобы получить границы массива вариантов, возвращаемого EnumerateObjects функция, а затем вы можете использовать for цикл для перебора элементов массива.

проверьте этот образец

Uses
 ComObj,
 ActiveX;

function GetObject(const objectName: String): IDispatch;
var
  bindCtx: IBindCtx;
  moniker: IMoniker;
  chEaten: Integer;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(bindCtx, StringToOleStr(objectName), chEaten, moniker));
  OleCheck(moniker.BindToObject(bindCtx, nil, IDispatch, Result));
end;


procedure TForm44.Button1Click(Sender: TObject);
var
  colObjects   : OleVariant;
  objBcdStore  : OleVariant;
  objWMIService: OleVariant;

  i : Integer;
  objWBL : OleVariant;
begin
  objWMIService := GetObject('winmgmts:{impersonationlevel=Impersonate,(Backup,Restore)}!root/wmi:BcdStore');
  if not objWMIService.OpenStore('', objBcdStore) then
    Caption := 'error'
  else
  begin
    objBcdStore.EnumerateObjects($10200003, colObjects);
       if not VarIsNull(colObjects) and VarIsArray(colObjects) then
       for i := VarArrayLowBound(colObjects, 1) to VarArrayHighBound(colObjects, 1) do
       begin
          objWBL:=colObjects[i];
          //do your stuff here


       end;
  end;
end;

Спасибо RRUZ за ответ - я узнал что-то новое - но, к сожалению, правильный ответ был "Не делай этого" (как это типично!).

Оказалось, что, перечисляя Windows Boot Loader объекты, невозможно определить, какой из объектов является "текущим". Как я наконец узнал (благодаря статье Hey, Scripting Guy! Из выпуска TechNet Magazine за июль 2008 года), правильный способ - открыть объект загрузчика напрямую по его известному идентификатору. (Что, опять же, хорошо скрыто и малоизвестно. Единственная официальная документация, которую мне удалось найти по известным GUID BCD, - это данные конфигурации загрузки в документе Windows Vista.)

Пример кода ниже сначала открывает объект BCD WMI. Затем он открывает магазин по умолчанию и показывает информацию для {current} объект загрузочной записи (доступ по общеизвестному идентификатору).

Затем он открывает Windows Boot Manager объект (используя его известный идентификатор), читает его DefaultObject элемент для определения GUID загрузочной записи по умолчанию, открывает этот объект по его GUID и показывает информацию.

ShowLoaderInfo только отображает ID (GUID),, Description а также NumberOfProcessors (это была информация, которую я хотел получить из BCD). Забавный трюк заключается в том, что последний из BcdIntegerElement тип, который возвращает свое значение через свойство Integer, с "признаком", что возвращаемое значение является не целым числом, а строкой. Примечание от MSDN объясняет:

Целочисленное значение элемента. Значение передается в виде строки, поскольку Automation не поддерживает 64-разрядные целые числа.

(Да, отлично! Почему он все равно должен быть 64-битным? Разве 2 миллиарда процессоров недостаточно?)

Для полного списка поддерживаемых Windows Boot Loader элементы видят BcdOSLoaderElementTypes.

program ShowBCDInfo;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  ComObj,
  ActiveX;

const
  Description                 = $12000004; //http://msdn.microsoft.com/en-us/aa362652(v=VS.85)
  UseBootProcessorOnly        = $26000060; //http://msdn.microsoft.com/en-us/aa362641(v=VS.85)
  NumberOfProcessors          = $25000061;
  ForceMaximumProcessors      = $26000062;
  ProcessorConfigurationFlags = $25000063;
  DefaultObject               = $23000003; //http://msdn.microsoft.com/en-us/aa362641(v=VS.85)

  CurrentGUID = '{fa926493-6f1c-4193-a414-58f0b2456d1e}'; //http://msdn.microsoft.com/en-us/windows/hardware/gg463059.aspx
  WBMGUID     = '{9dea862c-5cdd-4e70-acc1-f32b344d4795}';

function GetObject(const objectName: String): IDispatch;
var
  bindCtx: IBindCtx;
  moniker: IMoniker;
  chEaten: Integer;
begin
  OleCheck(CreateBindCtx(0, bindCtx));
  OleCheck(MkParseDisplayName(bindCtx, StringToOleStr(objectName), chEaten, moniker));
  OleCheck(moniker.BindToObject(bindCtx, nil, IDispatch, Result));
end;

procedure ShowLoaderInfo(const name: string; const obj: OleVariant);
var
  objElement: OleVariant;
begin
  Writeln(Format('%s ID: %s', [name, string(obj.id)]));
  if obj.GetElement(Description, objElement) then
    Writeln(Format('Description: %s', [objElement.String]));
  if obj.GetElement(NumberOfProcessors, objElement) then
    Writeln(Format('NumProc: %s', [objElement.Integer]));
end;

procedure ShowBcdInfo;
var
  objBcdStore  : OleVariant;
  objWBL       : OleVariant;
  objWBM       : OleVariant;
  objWMIService: OleVariant;
begin
  objWMIService := GetObject('winmgmts:{(Backup,Restore)}\\.\root\wmi:BcdStore');
  if not objWMIService.OpenStore('', objBcdStore) then
    Writeln('*** error opening store')
  else begin
    if objBcdStore.OpenObject(CurrentGUID, objWBL) then
      ShowLoaderInfo('{current}', objWBL);
    if objBcdStore.OpenObject(WBMGuid, objWBM) and
       objWBM.GetElement(DefaultObject, objWBL) and
       objBcdStore.OpenObject(string(objWBL.ID), objWBL)
    then
      ShowLoaderInfo('{default}', objWBL);
  end;
end;

begin
  try
    OleInitialize(nil);
    try
      ShowBCDInfo;
    finally OleUninitialize; end;
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
Другие вопросы по тегам