0

I'm quite new to AutoHotKey and I'm having a problem with COM objects.

What I'm trying to mimic is this Powershell one-liner:

(Get-WMIObject Win32_PnPEntity).GetDeviceProperties("DEVPKEY_Device_BusReportedDeviceDesc").DeviceProperties.Data

The code I have, so far, is like this:

#NoEnv  ; Don't check empty variables to see if they are environment variables.
#Warn  ; Enable warnings.
#SingleInstance Force  ; Force single instance.
SendMode, Input  ; Send is an alias for SendInput. Faster and safer.
SetNumLockState, AlwaysOn  ; NumLock is always on, the key does not have any future effect.
SetWorkingDir, % A_Desktop
DetectHiddenWindows, On  ; Detect hidden windows, needed for PostMessage.

for device in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_PnPEntity")  {
    keys := ComObjArray(0xC, 1)  ; VT_VARIANT
    props := ComObjArray(0xC, 1)
    keys[0] := "DEVPKEY_Device_BusReportedDeviceDesc"
    result := device.GetDeviceProperties(keys, props)
    if (!result)
        MsgBox, % props[0]
}

The function call returns 0, which as far as I know is correct. But the props array seems to be empty.

I think I now the problem: props is a ByRef variable, but I tried with some code I found to create ByRef variables (here) in order to get return values, without any success.

Obviously I'm doing something wrong with the function call, because I'm absolutely clueless about how to use COM from AutoHotKey.

Yes, I know I could use setupapi.dll here, or some third-party library, or even the PowerShell one-liner, but I'm trying to learn how to use COM from AutoHotKey, how to call COM objects methods, etc.

So, any help is much appreciated. Thanks a lot in advance.

Here is GetDeviceProperties function signature, for reference.

  • 1
    Don't now AHK, but here is a C# version, if it can be of any help: https://stackoverflow.com/questions/69362886/get-devpkey-device-busreporteddevicedesc-from-win32-pnpentity-in-c-sharp – Simon Mourier Jan 13 '22 at 08:07
  • Thanks, Simon. I already knew how to do that in C# (or C for that matter), my problem is building the proper types, variables, etc. to do it in AHK, which is being very hard to do. – Raúl Núñez de Arenas Coronado Jan 13 '22 at 12:02
  • Well, not sure about AHK, but from what I understand, you're not calling it as I do in C#. Try to pass only one argument which is a VARIANT array which should contain 2 items. First item is a BSTR array, second one is an empty VARIANT (filled on return) – Simon Mourier Jan 13 '22 at 13:11
  • Simon, I was not using InvokeMethod on purpose, because I wanted to learn how to call COM object methods directly. Of course if I can't do that, I'll do it with InvokeMethod or using a DLLCall on setupapi.dll. All of those methods work, but I wanted to learn this :) But I'm going to try what you suggest, using just one argument, good idea. – Raúl Núñez de Arenas Coronado Jan 13 '22 at 17:21
  • Simon, I tried and it does not seem to work, either. The function call fails with an error signalling an invalid parameter, so the problem doesn't rely there. I think I'm missing something VERY obvious, but I can't see it for the life of me. – Raúl Núñez de Arenas Coronado Jan 13 '22 at 18:00
  • InvokeMethod doesn't exist as such, it's just the way .NET calls underlying method dynamically. I would try something like device.GetDeviceProperties(arrayOf2Params). Otherwise, is there an easy way to test AHK? – Simon Mourier Jan 13 '22 at 18:34
  • Simon, I already tested that and the function call returns an error, to wit, invalid parameters, so the problem does not lie there, unfortunately. This is stupidly complex, and I don't think of a way to learn this apart from trial and error or digging into AHK source code, to see how the parameters are translated. So, for now, is trial and error. Thanks for your help, by the way :) – Raúl Núñez de Arenas Coronado Jan 14 '22 at 15:02

1 Answers1

0

You can do something like:

items := ComObjGet("winmgmts:").ExecQuery("Select * from Win32_PnPEntity")._NewEnum
    while items[device]
        MsgBox %        device.Availability[0]
            .   "`n"    device.Caption[0]
            .   "`n"    device.ClassGuid[0]
            ; . "`n"    device.CompatibleID[][0]
            .   "`n"    device.ConfigManagerErrorCode[0]
            .   "`n"    device.ConfigManagerUserConfig[0]
            .   "`n"    device.CreationClassName[0]
            .   "`n"    device.Description[0]
            .   "`n"    device.DeviceID[0]
            .   "`n"    device.ErrorCleared[0]
            .   "`n"    device.ErrorDescription[0]
            ; . "`n"    device.HardwareID[][0]
            .   "`n"    device.InstallDate[0]
            .   "`n"    device.LastErrorCode[0]
            .   "`n"    device.Manufacturer[0]
            .   "`n"    device.Name[0]
            .   "`n"    device.PNPClass[0]
            .   "`n"    device.PNPDeviceID[0]
            .   "`n"    device.PowerManagementCapabilities[][0]
            .   "`n"    device.PowerManagementSupported[0]
            .   "`n"    device.Present[0]
            .   "`n"    device.Service[0]
            .   "`n"    device.Status[0]
            .   "`n"    device.StatusInfo[0]
            .   "`n"    device.SystemCreationClassName[0]
            .   "`n"    device.SystemName[0]
                            
End::
ExitApp

The commented lines was giving error here... you can uncommented and test if you like.

  • 1
    OP is trying to call a method on the `ComObject` with some specific arguments, this is in no way related. All those fields are already available in the normal for loop implementation, all you're doing here is manually calling `_NewEnum` instead of letting the for-loop take care of calling it. – 0x464e Jan 13 '22 at 14:24
  • Thanks Dieisson, but that's not what I needed. I know I can loop over the results of ComObjGet(), the problem is not accessing a property of the returned item, but to call a method. – Raúl Núñez de Arenas Coronado Jan 13 '22 at 17:19