StackExchange / wmi

WMI for Go
http://godoc.org/github.com/StackExchange/wmi
MIT License
433 stars 173 forks source link

Implement CallMethod for calling WMI class methods #45

Closed mholt closed 3 years ago

mholt commented 5 years ago

Closes #6

Enables https://github.com/restic/restic/issues/340

The API here isn't my absolute favorite but it works. However, I don't totally understand WMI/OLE so I've probably missed something. (A lot of this is black magic to me.) Feel free to offer feedback or make changes directly.

The biggest change, other than adding the CallMethod method, was to refactor some of the coinit stuff out into its own (unexported) method so the logic could be shared by Query and CallMethod.

mholt commented 5 years ago

Sounds good. I used it on a Windows 10 VM just fine (as out-of-order defers have bitten me before) -- would always appreciate a second check!

mholt commented 5 years ago

Given the scope of this PR, I think I'm done with my changes so far.

To truly address the feedback given more ideally, we'd need to refactor the API of the package.

But, I know that what I've implemented so far is at least functional for my use cases. 🤷‍♂️

joelnb commented 5 years ago

@mholt thanks for taking a stab at this, it's something I've been meaning to get working for a while & while I don't think your work here totally covers my exact use case it's definitely a step in the right direction which will make things easier for me going forward.

jazzy-crane commented 4 years ago

Does this only work for class-level function, or does it work for instances? (i.e. I would like to call https://docs.microsoft.com/en-us/previous-versions/windows/desktop/vdswmi/dismount-method-in-class-win32-volume on a specific volume)

I think it's the former and I'm not sure how it would work for the latter? But thought I would confirm

joelnb commented 4 years ago

@jazzy-crane I think you're correct that it's the former & the latter is still unsupported. I have been able to get calling methods on instances working in specific cases but I'm not sure how it can be implemented in a generic way which would be useful in this library.

jazzy-crane commented 4 years ago
type Win32_Volume struct {
    DeviceID string
}

func (wv *Win32_Volume) Dismount(force bool, permanent bool) (int32, error) {
    return wmi.CallMethod([]interface{}{}, fmt.Sprintf(`Win32_Volume.DeviceID="%s"`, strings.ReplaceAll(wv.DeviceID, `\`, `\\`)), "Dismount", []interface{}{force, permanent})
}

Worked for me. No idea what's going on with needing the backslash escaping, but there you go. Seems this matches the __RELPATH

I don't know if there's a smart way to populate all the methods using reflection. I don't think you can, but I won't be investigating that too hard.

I suppose the wmi module could always stash the __RELPATH in a pointer->__RELPATH map to avoid needing any user action to include the DeviceID in their struct definition, but that's a little grim. But you then might be able to have a generic CallInstanceMethod function that takes {interface{}, method, params...} , gets the pointer to the interface{}, resolves __RELPATH, and calls the method.

But all outside the scope of this MR of course

joelnb commented 4 years ago

I think that's actually quite different to what I did & possibly simpler so thank you - will have to see if this works better for my use-case.

yusufozturk commented 3 years ago

Are you going to merge this PR? We are also looking for same functionality.

Thanks.

tlimoncelli commented 3 years ago

Sadly the original creators of this module are no longer with Stack Overflow, but I feel that the change looks low-risk as it only adds functionality. Therefore I'm merging it.