Доступ к 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.