Wim ten Brink
asked on
[Challenge] WMI translation...
I have imported the type library C:\WINNT\System32\wbem\wbe mdisp.TLB into my project. (Without component wrappers!)
Now I also have this small VBScript example that tells me how to start an executable through WMI:
-------------------------- ---------- ---------- ---------- -
set services = GetObject("winmgmts:root\c imv2")
Set obj = Services.Get("Win32_Proces s")
Set objIns = obj.Methods_("Create").InP arameters. SpawnInsta nce_
objIns.CommandLine = "calc.exe"
Set objOut = Services.ExecMethod("Win32 _Process", "Create", objIns)
MsgBox "Return value = " & objOut.returnvalue & VBCRLF & "Process ID = " & objout.processid
-------------------------- ---------- ---------- ---------- -
Now, the challenge is not to just execute the application. Neither is it a challenge to just run this script in Delphi. No, the challenge is to convert the VBScript commands into Delphi commands with associated objects and whatever else. The purpose is not starting an external application or running a VBScript. No, I want to see how this code translates to WMI and I don't have too much time to do my own research at this moment. ;-)
Part of it is simple. The GetObject method can be replaced by:
function ADsGetObject(lpszPathName: WideString; const riid: TGUID; out ppObject): HRESULT; safecall; external 'activeds.dll';
So getting the services object is done like this:
var
Services: SWbemServices;
begin
if Succeeded(ADsGetObject('wi nmgmts:roo t\cimv2'), SWbemServices, Services) then begin
end;
end;
Then, using the Services object I could create the other objects just like the VBScript does. However, I have problems translating the part where the 'Create' method is called to call the application and the execution of this command.
-------------------------- ---------- ---------- ------
This is not an easy question, thus the high amount of points. Be warned: I am researching this myself too so if no one else provides me an example I will solve it myself.
I don't want hints. I don't want to know that applications can be executed in different ways. I just need a working example of above VBScript code translated to Delphi. This is meant to learn, not to solve an existing problem.
Now I also have this small VBScript example that tells me how to start an executable through WMI:
--------------------------
set services = GetObject("winmgmts:root\c
Set obj = Services.Get("Win32_Proces
Set objIns = obj.Methods_("Create").InP
objIns.CommandLine = "calc.exe"
Set objOut = Services.ExecMethod("Win32
MsgBox "Return value = " & objOut.returnvalue & VBCRLF & "Process ID = " & objout.processid
--------------------------
Now, the challenge is not to just execute the application. Neither is it a challenge to just run this script in Delphi. No, the challenge is to convert the VBScript commands into Delphi commands with associated objects and whatever else. The purpose is not starting an external application or running a VBScript. No, I want to see how this code translates to WMI and I don't have too much time to do my own research at this moment. ;-)
Part of it is simple. The GetObject method can be replaced by:
function ADsGetObject(lpszPathName:
So getting the services object is done like this:
var
Services: SWbemServices;
begin
if Succeeded(ADsGetObject('wi
end;
end;
Then, using the Services object I could create the other objects just like the VBScript does. However, I have problems translating the part where the 'Create' method is called to call the application and the execution of this command.
--------------------------
This is not an easy question, thus the high amount of points. Be warned: I am researching this myself too so if no one else provides me an example I will solve it myself.
I don't want hints. I don't want to know that applications can be executed in different ways. I just need a working example of above VBScript code translated to Delphi. This is meant to learn, not to solve an existing problem.
ASKER
Looks good so far. Wished I could avoid the OleVariants in my code, though. Can't use Code Completion with OleVariants... :-)
And yes, you noticed what I noticed when I first tried to translate it. The VB shortcut for Item. Can't test it right now, though. But will check in about 12 hours when I'm back at work again.
The part about binding the moniker (CreateBindCtx & BindMoniker) are quite helpful, btw. But if I'm correct, you can specify the interface type with BindMoniker thus an OleVariant doesn't have to be required. I know VBScript only uses untyped variables but I prefer to avoid them as much as possible.
It looks good for now, though. :-)
And yes, you noticed what I noticed when I first tried to translate it. The VB shortcut for Item. Can't test it right now, though. But will check in about 12 hours when I'm back at work again.
The part about binding the moniker (CreateBindCtx & BindMoniker) are quite helpful, btw. But if I'm correct, you can specify the interface type with BindMoniker thus an OleVariant doesn't have to be required. I know VBScript only uses untyped variables but I prefer to avoid them as much as possible.
It looks good for now, though. :-)
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
try WMISet 1.5 - http://www.online-admin.com/wmiset.html
I remember seeing some other wmi delphi implementations .. I'll post if I remember/firnd them
I remember seeing some other wmi delphi implementations .. I'll post if I remember/firnd them
ASKER
Okay, so far I've gotten to this, thanks to rllibby:
function MonikerObject(Moniker: string; const riid: TGUID; out ppObject): HRESULT;
var
pmk: IMoniker;
pbc: IBindCtx;
cbEaten: LongInt;
begin
IUnknown(ppObject) := nil;
Result := CreateBindCtx(0, pbc);
if (Result = S_OK) then Result := MkParseDisplayName(pbc, StringToOleStr(Moniker), cbEaten, pmk);
if (Result = S_OK) then Result := BindMoniker(pmk, 0, IDispatch, ppObject);
if not (Result = S_OK) then IUnknown(ppObject) := nil;
pmk := nil;
pbc := nil;
end;
I created this function to create an object through the moniker. With WMI I will be mostly using monikers anyway so this code cleaned it up a bit. It also allows me make it more type-specific, like the ADsGetObject function.
However, the second part still has a flaw:
var
Services: SWbemServices;
Obj: SWbemObject;
ObjIns: SWbemObject;
ObjOut: SWbemObject;
varValue: OleVariant;
begin
if Succeeded(CoInitializeEx(n il, COINIT_MULTITHREADED or COINIT_DISABLE_OLE1DDE or COINIT_SPEED_OVER_MEMORY)) then begin
if Succeeded(MonikerObject('w inmgmts:ro ot\cimv2', SWbemServices, Services)) then begin
Obj := Services.Get('Win32_Proces s', 0, nil);
objIns := obj.Methods_.Item('Create' , 0).InParameters.SpawnInsta nce_(0);
Obj := nil;
// objIns.CommandLine := 'calc.exe'; // Fails since CommandLine is unknown.
varValue := 'calc.exe';
objIns.Properties_.Item('C ommandLine ', 0).Set_Value(varValue);
objOut := Services.ExecMethod('Win32 _Process', 'Create', objIns, 0, nil);
objIns := nil;
WriteLn('Return value = ', objOut.Properties_.Item('R eturnValue ', 0).Get_Value);
WriteLn('Process ID = ', objout.Properties_.Item('P rocessID', 0).Get_Value);
objOut := nil;
end;
CoUninitialize;
except
on E: Exception do begin
WriteLn(E.Message);
CoUninitialize;
end;
end;
end.
Yeah, setting the CommandLine property is the real pain here. I still need an OleVariant to pass a string to this SetValue function... Typecasting doesn't work since the Set_Value procedure declares the parameter as "var varValue: OleVariant"...
Oh, well... I can live with this... :-)
-------------------------- ---------- ---------- ---------- ------
Lee_Nover: I knew the link already. It's an interesting set of components for only $49 but I want to learn to do it myself, not depend on someone's else's components. :-) Another useful link is http://www.iisfaq.com/ExternalLink.aspx?L=1561&P=125 which is where I found my first working WMI code for Delphi. :-) It's called "A better Windows Management Instrumentation (WMI) Demo." and is somewhere in the middle of the page. The biggest problem I had was getting the object a bit more correctly which I can now do. (Goodbye ADsGetObject!) rllibby was a great help at that.
function MonikerObject(Moniker: string; const riid: TGUID; out ppObject): HRESULT;
var
pmk: IMoniker;
pbc: IBindCtx;
cbEaten: LongInt;
begin
IUnknown(ppObject) := nil;
Result := CreateBindCtx(0, pbc);
if (Result = S_OK) then Result := MkParseDisplayName(pbc, StringToOleStr(Moniker), cbEaten, pmk);
if (Result = S_OK) then Result := BindMoniker(pmk, 0, IDispatch, ppObject);
if not (Result = S_OK) then IUnknown(ppObject) := nil;
pmk := nil;
pbc := nil;
end;
I created this function to create an object through the moniker. With WMI I will be mostly using monikers anyway so this code cleaned it up a bit. It also allows me make it more type-specific, like the ADsGetObject function.
However, the second part still has a flaw:
var
Services: SWbemServices;
Obj: SWbemObject;
ObjIns: SWbemObject;
ObjOut: SWbemObject;
varValue: OleVariant;
begin
if Succeeded(CoInitializeEx(n
if Succeeded(MonikerObject('w
Obj := Services.Get('Win32_Proces
objIns := obj.Methods_.Item('Create'
Obj := nil;
// objIns.CommandLine := 'calc.exe'; // Fails since CommandLine is unknown.
varValue := 'calc.exe';
objIns.Properties_.Item('C
objOut := Services.ExecMethod('Win32
objIns := nil;
WriteLn('Return value = ', objOut.Properties_.Item('R
WriteLn('Process ID = ', objout.Properties_.Item('P
objOut := nil;
end;
CoUninitialize;
except
on E: Exception do begin
WriteLn(E.Message);
CoUninitialize;
end;
end;
end.
Yeah, setting the CommandLine property is the real pain here. I still need an OleVariant to pass a string to this SetValue function... Typecasting doesn't work since the Set_Value procedure declares the parameter as "var varValue: OleVariant"...
Oh, well... I can live with this... :-)
--------------------------
Lee_Nover: I knew the link already. It's an interesting set of components for only $49 but I want to learn to do it myself, not depend on someone's else's components. :-) Another useful link is http://www.iisfaq.com/ExternalLink.aspx?L=1561&P=125 which is where I found my first working WMI code for Delphi. :-) It's called "A better Windows Management Instrumentation (WMI) Demo." and is somewhere in the middle of the page. The biggest problem I had was getting the object a bit more correctly which I can now do. (Goodbye ADsGetObject!) rllibby was a great help at that.
ASKER
And you're gaining on me again, rllibby... :-)
nice :)
Thought I would throw my 2 cents in on this one...
First, don't give VB script too much credit. The code it uses to get the services interface DOES NOT map to ADsGetObject (it does not have a CLUE about ADsGetObject). If your looking for a true equivalent to the GetObject(), then the following is pretty close:
function GetObject(Value: String): OleVariant;
var pUnk: IUnknown;
pDisp: IDispatch;
pmk: IMoniker;
pbc: IBindCtx;
cbEaten: LongInt;
clsid: TGUID;
begin
// Resource protection
try
// Attempt to convert the string using the registry
clsid:=ProgIDToClassID(Val
// Check running object table
if (GetActiveObject(clsid, nil, pUnk) = S_OK) then
begin
// Return result as IDispatch
result:=pUnk as IDispatch;
// Release the refcount that we are holding on to
pUnk:=nil;
end
else
// Failed
result:=Unassigned;
except
// Try to access the object using a moniker
if (CreateBindCtx(0, pbc) = S_OK) then
begin
if (MkParseDisplayName(pbc, StringToOleStr(Value), cbEaten, pmk) = S_OK) then
begin
// Attempt to bind the moniker
if (BindMoniker(pmk, 0, IDispatch, pDisp) = S_OK) then
// Return the IDispatch
result:=pDisp
else
// Return unassigned
result:=Unassigned;
// Release refcounts
pDisp:=nil;
pmk:=nil;
end
else
// Return unassigned
result:=Unassigned;
// Release refcounts
pbc:=nil;
end
else
// Return unassigned
result:=Unassigned;
end;
end;
You will notice that the exception does get tripped (meaning the user did not pass a ProgID, but instead a moniker representation). But from there, the code goes through and binds up to the interface we are looking for.
The only other item of note is VB's allowance of shortcuts (which i hate because the code is no longer self documenting). The sample you displayed made use of this in accessing the methods collection. Delphi will require the .Item() syntax to work properly.
Then, using the above info, it becomes as simple as:
var services: OleVariant;
obj: OleVariant;
objIns: OleVariant;
objOut: OleVariant;
begin
services:=GetObject('winmg
obj:=Services.Get('Win32_P
objIns:=obj.Methods_.Item(
objIns.CommandLine:='calc.
objOut:=Services.ExecMetho
ShowMessage('Return value = '+IntToStr(objOut.returnva
end;
Hope this helps,
Russell