From 730c49711b42aede722359afa5a63c9ec5396a5d Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 15:02:29 +0200 Subject: [PATCH 1/7] added support for WmiObject properies with the type object --- WmiLight/Internal/InterfaceIdentifier.cs | 12 ++++++++ WmiLight/Internal/VARIANT.cs | 3 ++ WmiLight/Wbem/WbemClassObject.cs | 12 ++++++++ WmiLight/WmiObject.cs | 35 +++++++++++++++++++++++- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 WmiLight/Internal/InterfaceIdentifier.cs diff --git a/WmiLight/Internal/InterfaceIdentifier.cs b/WmiLight/Internal/InterfaceIdentifier.cs new file mode 100644 index 0000000..c6c01fe --- /dev/null +++ b/WmiLight/Internal/InterfaceIdentifier.cs @@ -0,0 +1,12 @@ +namespace WmiLight +{ + using System; + + internal static class InterfaceIdentifier + { + /// + /// DC12A681-737F-11CF-884D-00AA004B2E24 + /// + public static readonly Guid IWbemClassObject = new Guid(0xDC12A681, 0x737F, 0x11CF, 0x88, 0x4D, 0x00, 0xAA, 0x00, 0x4B, 0x2E, 0x24); + } +} diff --git a/WmiLight/Internal/VARIANT.cs b/WmiLight/Internal/VARIANT.cs index 0fc5b0b..53795cb 100644 --- a/WmiLight/Internal/VARIANT.cs +++ b/WmiLight/Internal/VARIANT.cs @@ -49,5 +49,8 @@ internal struct VARIANT [FieldOffset(8)] public IntPtr BStrVal; + + [FieldOffset(8)] + public IntPtr Object; } } diff --git a/WmiLight/Wbem/WbemClassObject.cs b/WmiLight/Wbem/WbemClassObject.cs index ae615c9..af5579b 100644 --- a/WmiLight/Wbem/WbemClassObject.cs +++ b/WmiLight/Wbem/WbemClassObject.cs @@ -143,6 +143,9 @@ private static object VariantToObject(ref VARIANT value, CimType type) case CimType.DateTime: return VariantToArray(ref value, typeWithoutArrayFlag); + case CimType.Object: + return VariantToArray(ref value, typeWithoutArrayFlag); + default: throw new NotSupportedException($"CimType '{typeWithoutArrayFlag}[]' currently not supported."); } @@ -211,6 +214,15 @@ private static object VariantToObject(ref VARIANT value, CimType type) case CimType.DateTime: return Marshal.PtrToStringBSTR(value.BStrVal); + case CimType.Object: + + HResult hResult = NativeMethods.QueryInterface(value.Object, InterfaceIdentifier.IWbemClassObject, out IntPtr pWbemObject); + + if (hResult.Failed) + throw (Exception)hResult; + + return new WbemClassObject(pWbemObject); + default: throw new NotSupportedException($"CimType '{type}' currently not supported."); } diff --git a/WmiLight/WmiObject.cs b/WmiLight/WmiObject.cs index da12559..ac22908 100644 --- a/WmiLight/WmiObject.cs +++ b/WmiLight/WmiObject.cs @@ -325,7 +325,23 @@ public object GetPropertyValue(string propertyName) if (this.disposed) throw new ObjectDisposedException(nameof(WmiObject)); - return this.wbemClassObject.Get(propertyName); + object obj = this.wbemClassObject.Get(propertyName); + + if (obj is WbemClassObject wbemClassObject) + return new WmiObject(wbemClassObject); + + if (obj is WbemClassObject[] wbemClassObjectArray) + { + WmiObject[] wmiObjects = new WmiObject[wbemClassObjectArray.Length]; + + for (int i = 0; i < wmiObjects.Length; i++) + wmiObjects[i] = new WmiObject(wbemClassObjectArray[i]); + + return wmiObjects; + + } + + return obj; } #region Description @@ -341,6 +357,23 @@ public TResult GetPropertyValue(string propertyName) if (this.disposed) throw new ObjectDisposedException(nameof(WmiObject)); + if (typeof(TResult) == typeof(WmiObject)) + { + return (TResult)(object)new WmiObject(wbemClassObject.Get(propertyName)); + } + + if (typeof(TResult) == typeof(WmiObject[])) + { + WbemClassObject[] wbemClassObjectArray = wbemClassObject.Get(propertyName); + + WmiObject[] wmiObjects = new WmiObject[wbemClassObjectArray.Length]; + + for (int i = 0; i < wmiObjects.Length; i++) + wmiObjects[i] = new WmiObject(wbemClassObjectArray[i]); + + return (TResult)(object)wmiObjects; + } + return wbemClassObject.Get(propertyName); } From 9207d3c05bd087fa37235c09a1975171a4279b52 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 15:04:18 +0200 Subject: [PATCH 2/7] added missing native methode for object properties --- WmiLight/Internal/NativeMethods.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WmiLight/Internal/NativeMethods.cs b/WmiLight/Internal/NativeMethods.cs index 76dc96a..8233e68 100644 --- a/WmiLight/Internal/NativeMethods.cs +++ b/WmiLight/Internal/NativeMethods.cs @@ -21,6 +21,9 @@ internal static class NativeMethods [DllImport(NATIVE_DLL_NAME)] public static extern HResult ReleaseIUnknown(IntPtr pIUnknown); + [DllImport(NATIVE_DLL_NAME)] + public static extern HResult QueryInterface(IntPtr pIUnknown, Guid riid, out IntPtr ppvObject); + [DllImport(NATIVE_DLL_NAME)] public static extern HResult ConnectServer( IntPtr pWbemLocator, @@ -83,7 +86,7 @@ public static extern HResult Get( public static extern uint VariantGetElementCount(ref VARIANT variant); [DllImport("Propsys.dll")] - public static extern HResult InitVariantFromVariantArrayElem(ref VARIANT variant, uint iElem, ref VARIANT pvar); + public static extern HResult InitVariantFromVariantArrayElem(ref VARIANT variant, uint iElem, ref VARIANT pvar); [DllImport(NATIVE_DLL_NAME)] public static extern HResult GetType(IntPtr pClassObject, [MarshalAs(UnmanagedType.LPWStr)] string propertyName, out CimType cimType); From 5cca6220139a347d5e11595e1fa26a36859772b4 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 21:59:13 +0200 Subject: [PATCH 3/7] implemented wmi events --- WmiLight.Native/EventSinkProxy.cpp | 48 ++++++++ WmiLight.Native/EventSinkProxy.h | 37 ++++++ WmiLight.Native/WmiLight.Native.vcxproj | 2 + .../WmiLight.Native.vcxproj.filters | 6 + WmiLight.Native/WmiLightNative.cpp | 77 ++++++++++-- WmiLight.TestApp-NetCore/Program.cs | 14 ++- WmiLight/Internal/NativeMethods.cs | 39 ++++++ WmiLight/Wbem/IUnknown.cs | 4 +- WmiLight/Wbem/WbemClassObject.cs | 8 +- WmiLight/Wbem/WbemClassObjectEnumerator.cs | 4 +- WmiLight/Wbem/WbemLocator.cs | 2 +- WmiLight/Wbem/WbemObjectSink.cs | 116 ++++++++++++++++++ .../Wbem/WbemObjectSinkIndicatedEventArgs.cs | 21 ++++ WmiLight/Wbem/WbemServices.cs | 30 ++++- WmiLight/Wbem/WbemUnsecuredApartment.cs | 42 +++++++ WmiLight/WmiConnection.cs | 24 ++++ WmiLight/WmiConnectionExtensions.cs | 31 +++++ WmiLight/WmiEventArrivedEventArgs.cs | 48 ++++++++ WmiLight/WmiEventSubscription.cs | 55 +++++++++ WmiLight/WmiEventWatcher.cs | 109 ++++++++++++++++ 20 files changed, 694 insertions(+), 23 deletions(-) create mode 100644 WmiLight.Native/EventSinkProxy.cpp create mode 100644 WmiLight.Native/EventSinkProxy.h create mode 100644 WmiLight/Wbem/WbemObjectSink.cs create mode 100644 WmiLight/Wbem/WbemObjectSinkIndicatedEventArgs.cs create mode 100644 WmiLight/Wbem/WbemUnsecuredApartment.cs create mode 100644 WmiLight/WmiEventArrivedEventArgs.cs create mode 100644 WmiLight/WmiEventSubscription.cs create mode 100644 WmiLight/WmiEventWatcher.cs diff --git a/WmiLight.Native/EventSinkProxy.cpp b/WmiLight.Native/EventSinkProxy.cpp new file mode 100644 index 0000000..f1282c5 --- /dev/null +++ b/WmiLight.Native/EventSinkProxy.cpp @@ -0,0 +1,48 @@ +#include "pch.h" + +#include "EventSinkProxy.h" + +EventSinkProxy::EventSinkProxy(void* pEventSink, IndicateFunction indicateFunction, SetStatusFunction setStatusFunction) noexcept + : _pEventSink(pEventSink), _indicateFunction(indicateFunction), _setStatusFunction(setStatusFunction) +{ + _lRef = 0; +} + +ULONG EventSinkProxy::AddRef() +{ + return ::InterlockedIncrement(&_lRef); +} + +ULONG EventSinkProxy::Release() +{ + long lRef = ::InterlockedDecrement(&_lRef); + + if (lRef == 0) + delete this; + + return lRef; +} + +HRESULT EventSinkProxy::QueryInterface(REFIID riid, void** ppv) +{ + if (riid == IID_IUnknown || riid == IID_IWbemObjectSink) + { + *ppv = (IWbemObjectSink*)this; + + this->AddRef(); + + return WBEM_S_NO_ERROR; + } + + return E_NOINTERFACE; +} + +HRESULT EventSinkProxy::Indicate(long lObjectCount, IWbemClassObject** apObjArray) +{ + return this->_indicateFunction(this->_pEventSink, lObjectCount, apObjArray); +} + +HRESULT EventSinkProxy::SetStatus(long lFlags, HRESULT hResult, wchar_t* strParam, IWbemClassObject* pObjParam) +{ + return this->_setStatusFunction(this->_pEventSink, lFlags, hResult, strParam, pObjParam); +} \ No newline at end of file diff --git a/WmiLight.Native/EventSinkProxy.h b/WmiLight.Native/EventSinkProxy.h new file mode 100644 index 0000000..a6a56e8 --- /dev/null +++ b/WmiLight.Native/EventSinkProxy.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +/// +/// Proxy that forewardes the event calls to the managed sink. +/// +class EventSinkProxy : public IWbemObjectSink +{ +public: + + typedef HRESULT(*IndicateFunction)(void*, long, IWbemClassObject**); + typedef HRESULT(*SetStatusFunction)(void*, long, HRESULT, wchar_t*, IWbemClassObject*); + +private: + + long _lRef; + + void* _pEventSink; + const IndicateFunction _indicateFunction; + const SetStatusFunction _setStatusFunction; + +public: + EventSinkProxy(void* pEventSink, IndicateFunction indicateFunction, SetStatusFunction setStatusFunction) noexcept; + + ~EventSinkProxy() noexcept = default; + + virtual ULONG STDMETHODCALLTYPE AddRef(); + + virtual ULONG STDMETHODCALLTYPE Release(); + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv); + + virtual HRESULT STDMETHODCALLTYPE Indicate(long lObjectCount, IWbemClassObject** apObjArray); + + virtual HRESULT STDMETHODCALLTYPE SetStatus(long lFlags, HRESULT hResult, wchar_t* strParam, IWbemClassObject* pObjParam); +}; \ No newline at end of file diff --git a/WmiLight.Native/WmiLight.Native.vcxproj b/WmiLight.Native/WmiLight.Native.vcxproj index 97ad311..c0e2bb9 100644 --- a/WmiLight.Native/WmiLight.Native.vcxproj +++ b/WmiLight.Native/WmiLight.Native.vcxproj @@ -163,12 +163,14 @@ + + Create Create diff --git a/WmiLight.Native/WmiLight.Native.vcxproj.filters b/WmiLight.Native/WmiLight.Native.vcxproj.filters index 7975820..4839fa6 100644 --- a/WmiLight.Native/WmiLight.Native.vcxproj.filters +++ b/WmiLight.Native/WmiLight.Native.vcxproj.filters @@ -24,6 +24,9 @@ Header Files + + Header Files + @@ -35,6 +38,9 @@ Source Files + + Source Files + diff --git a/WmiLight.Native/WmiLightNative.cpp b/WmiLight.Native/WmiLightNative.cpp index 7002813..79a5d14 100644 --- a/WmiLight.Native/WmiLightNative.cpp +++ b/WmiLight.Native/WmiLightNative.cpp @@ -1,5 +1,7 @@ #include "pch.h" +#include "EventSinkProxy.h" + #include #ifdef __cplusplus @@ -46,12 +48,12 @@ extern "C" { // only need to export C interface if __declspec(dllexport) HRESULT CreateWbemLocator(IWbemLocator** locator) { - return CoCreateInstance( - CLSID_WbemLocator, - 0, - CLSCTX_INPROC_SERVER, - IID_IWbemLocator, - reinterpret_cast(locator)); + return CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast(locator)); + } + + __declspec(dllexport) HRESULT CreateWbemUnsecuredApartment(IWbemUnsecuredApartment** unsecuredApartment) + { + return CoCreateInstance(CLSID_UnsecuredApartment, nullptr, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, reinterpret_cast(unsecuredApartment)); } __declspec(dllexport) HRESULT ReleaseIUnknown(IUnknown* pIUnknown) @@ -64,6 +66,14 @@ extern "C" { // only need to export C interface if return S_OK; } + __declspec(dllexport) HRESULT QueryInterface(IUnknown* pIUnknown, REFIID riid, void** ppvObject) + { + if (pIUnknown == nullptr) + return E_POINTER; + + return pIUnknown->QueryInterface(riid, ppvObject); + } + __declspec(dllexport) HRESULT ConnectServer( IWbemLocator* wbemLocator, wchar_t* networkResource, @@ -116,7 +126,7 @@ extern "C" { // only need to export C interface if } SEC_WINNT_AUTH_IDENTITY authInfo{}; - + authInfo.User = (unsigned short*)username; authInfo.UserLength = username == nullptr ? 0 : static_cast(wcslen(username)); @@ -154,6 +164,59 @@ extern "C" { // only need to export C interface if return wbemServices->ExecQuery(ueryLanguage, query, behaviorOption, ctx, pEnumerator); } + __declspec(dllexport) HRESULT CreateEventSinkStub( + IWbemUnsecuredApartment* pUnsecApp, + void* pEventSink, + EventSinkProxy::IndicateFunction indicateFunction, + EventSinkProxy::SetStatusFunction setStatusFunction, + IWbemObjectSink** pEventSinkStub) + { + if (pUnsecApp == nullptr || pEventSink == nullptr) + return E_POINTER; + + EventSinkProxy* pSink = new EventSinkProxy(pEventSink, indicateFunction, setStatusFunction); + + // Allows us to delete the object from the heap by calling Release() if the CreateObjectStub() call fails. + pSink->AddRef(); + + IUnknown* pStubUnk = NULL; + HRESULT hr = pUnsecApp->CreateObjectStub(pSink, &pStubUnk); + + if (FAILED(hr)) + { + // delete the object from the heap because stub creation failed + pSink->Release(); + + return hr; + } + + hr = pStubUnk->QueryInterface(IID_IWbemObjectSink, reinterpret_cast(pEventSinkStub)); + + // This Release call does not delete the object from the heap because the sink is referended by the sink stub + pSink->Release(); + + // decrease ref count, object keeps alive by other referneces + pStubUnk->Release(); + + return hr; + } + + __declspec(dllexport) HRESULT CancelAsyncCall(IWbemServices* wbemServices, IWbemObjectSink* pEventSinkProxy) + { + if (wbemServices == nullptr || pEventSinkProxy == nullptr) + return E_POINTER; + + return wbemServices->CancelAsyncCall(pEventSinkProxy); + } + + __declspec(dllexport) HRESULT ExecNotificationQueryAsync(IWbemServices* wbemServices, wchar_t* ueryLanguage, wchar_t* query, IWbemContext* ctx, IWbemObjectSink* pStubSink) + { + if (wbemServices == nullptr) + return E_POINTER; + + return wbemServices->ExecNotificationQueryAsync(ueryLanguage, query, 0, ctx, pStubSink); + } + __declspec(dllexport) HRESULT Next( IEnumWbemClassObject* pEnumerator, IWbemClassObject** pClassObject) diff --git a/WmiLight.TestApp-NetCore/Program.cs b/WmiLight.TestApp-NetCore/Program.cs index 683c051..91ffa63 100644 --- a/WmiLight.TestApp-NetCore/Program.cs +++ b/WmiLight.TestApp-NetCore/Program.cs @@ -19,12 +19,20 @@ static void Main(string[] args) { Console.WriteLine($"#{process.GetPropertyValue("ProcessId")} - {process["Name"]}"); } - } - Console.WriteLine(""); + Console.WriteLine(""); + + const string notificationQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"; + + using (WmiEventSubscription sub = conncetion.CreateEventSubscription(notificationQuery, x => Console.WriteLine("Process '{0}' started", x.GetPropertyValue("TargetInstance").GetPropertyValue("Name")))) + { + Console.WriteLine("Observing starting processes. Press any key to stop observing..."); + Console.ReadKey(); + } + } Console.Write("Press Any Key To Exit..."); Console.ReadKey(); } } -} +} \ No newline at end of file diff --git a/WmiLight/Internal/NativeMethods.cs b/WmiLight/Internal/NativeMethods.cs index 8233e68..d8a9aba 100644 --- a/WmiLight/Internal/NativeMethods.cs +++ b/WmiLight/Internal/NativeMethods.cs @@ -13,11 +13,34 @@ internal static class NativeMethods { private const string NATIVE_DLL_NAME = "WmiLight.Native.dll"; + #region Delegates + + public delegate HResult Indicate( + IntPtr pEventSink, + int lObjectCount, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] + IntPtr[] apObjArray + ); + + public delegate HResult SetStatus( + IntPtr pEventSink, + int lFlags, + HResult hResult, + [MarshalAs(UnmanagedType.LPWStr)] + string strParam, + IntPtr pObjParam + ); + + #endregion + #region Methods [DllImport(NATIVE_DLL_NAME)] public static extern HResult CreateWbemLocator(out IntPtr pWbemLocator); + [DllImport(NATIVE_DLL_NAME)] + public static extern HResult CreateWbemUnsecuredApartment(out IntPtr pUnsecuredApartment); + [DllImport(NATIVE_DLL_NAME)] public static extern HResult ReleaseIUnknown(IntPtr pIUnknown); @@ -65,6 +88,22 @@ public static extern HResult ExecQuery( IntPtr ctx, out IntPtr pEnumerator); + [DllImport(NATIVE_DLL_NAME)] + public static extern HResult ExecNotificationQueryAsync( + IntPtr pWbemServices, + [MarshalAs(UnmanagedType.LPWStr)] + string ueryLanguage, + [MarshalAs(UnmanagedType.LPWStr)] + string query, + IntPtr ctx, + IntPtr pEventSinkProxy); + + [DllImport(NATIVE_DLL_NAME)] + public static extern HResult CancelAsyncCall(IntPtr pWbemServices, IntPtr pEventSinkProxy); + + [DllImport(NATIVE_DLL_NAME)] + public static extern HResult CreateEventSinkStub(IntPtr pUnsecApp, IntPtr pEventSink, Indicate indicateFunction, SetStatus setStatusFunction, out IntPtr eventSinkStub); + [DllImport(NATIVE_DLL_NAME)] public static extern HResult Next(IntPtr pEnumerator, out IntPtr pClassObject); diff --git a/WmiLight/Wbem/IUnknown.cs b/WmiLight/Wbem/IUnknown.cs index 8a028a1..9f8dc0f 100644 --- a/WmiLight/Wbem/IUnknown.cs +++ b/WmiLight/Wbem/IUnknown.cs @@ -9,7 +9,7 @@ internal class IUnknown : CriticalFinalizerObject, IDisposable private bool disposed = false; - protected IUnknown(IntPtr nativePointer) + internal IUnknown(IntPtr nativePointer) { this.nativePointer = nativePointer; } @@ -22,7 +22,7 @@ protected IUnknown(IntPtr nativePointer) throw (Exception)hResult; } - protected IntPtr NativePointer => this.nativePointer; + public static implicit operator IntPtr(IUnknown iUnknown) => iUnknown.nativePointer; protected bool Disposed => this.disposed; diff --git a/WmiLight/Wbem/WbemClassObject.cs b/WmiLight/Wbem/WbemClassObject.cs index af5579b..6af649e 100644 --- a/WmiLight/Wbem/WbemClassObject.cs +++ b/WmiLight/Wbem/WbemClassObject.cs @@ -18,7 +18,7 @@ public CimType GetType(string propertyName) CimType cimType; - HResult hResult = NativeMethods.GetType(this.NativePointer, propertyName, out cimType); + HResult hResult = NativeMethods.GetType(this, propertyName, out cimType); if (hResult.Failed) throw (Exception)hResult; @@ -36,7 +36,7 @@ public object Get(string propertyName) try { - HResult hResult = NativeMethods.Get(this.NativePointer, propertyName, ref value, out valueType); + HResult hResult = NativeMethods.Get(this, propertyName, ref value, out valueType); if (hResult.Failed) throw (Exception)hResult; @@ -62,7 +62,7 @@ public TResult Get(string propertyName) try { - HResult hResult = NativeMethods.Get(this.NativePointer, propertyName, ref value, out valueType); + HResult hResult = NativeMethods.Get(this, propertyName, ref value, out valueType); if (hResult.Failed) throw (Exception)hResult; @@ -85,7 +85,7 @@ internal IEnumerable GetNames() string[] names; - HResult hResult = NativeMethods.GetNames(this.NativePointer, out names); + HResult hResult = NativeMethods.GetNames(this, out names); if (hResult.Failed) throw (Exception)hResult; diff --git a/WmiLight/Wbem/WbemClassObjectEnumerator.cs b/WmiLight/Wbem/WbemClassObjectEnumerator.cs index 3b08d81..30572e2 100644 --- a/WmiLight/Wbem/WbemClassObjectEnumerator.cs +++ b/WmiLight/Wbem/WbemClassObjectEnumerator.cs @@ -16,7 +16,7 @@ internal bool Next(out WbemClassObject wbemClassObject) IntPtr pClassObject; - HResult hResult = NativeMethods.Next(this.NativePointer, out pClassObject); + HResult hResult = NativeMethods.Next(this, out pClassObject); if (hResult.Failed) throw (Exception)hResult; @@ -36,7 +36,7 @@ internal void Reset() if (this.Disposed) throw new ObjectDisposedException(nameof(WbemClassObjectEnumerator)); - HResult hResult = NativeMethods.Reset(this.NativePointer); + HResult hResult = NativeMethods.Reset(this); if (hResult.Failed) throw (Exception)hResult; diff --git a/WmiLight/Wbem/WbemLocator.cs b/WmiLight/Wbem/WbemLocator.cs index 32e0dcc..b5944de 100644 --- a/WmiLight/Wbem/WbemLocator.cs +++ b/WmiLight/Wbem/WbemLocator.cs @@ -89,7 +89,7 @@ internal WbemServices ConnectServer(string networkResource, string userName, str IntPtr wbemServices; - HResult hResult = NativeMethods.ConnectServer(this.NativePointer, networkResource, userName, userPassword, locale, wbemConnectOption, authority, ctx, out wbemServices); + HResult hResult = NativeMethods.ConnectServer(this, networkResource, userName, userPassword, locale, wbemConnectOption, authority, ctx, out wbemServices); if (hResult.Failed) { diff --git a/WmiLight/Wbem/WbemObjectSink.cs b/WmiLight/Wbem/WbemObjectSink.cs new file mode 100644 index 0000000..fcdc618 --- /dev/null +++ b/WmiLight/Wbem/WbemObjectSink.cs @@ -0,0 +1,116 @@ +namespace WmiLight.Wbem +{ + using System; + using System.Diagnostics; + using System.Runtime.InteropServices; + + /// + /// Object that received the notification from the native code. + /// + internal class WbemObjectSink : IDisposable + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private bool cancelled = false; + + private readonly IUnknown nativeStub; + + internal WbemObjectSink(IntPtr pUnsecApp) + { + IntPtr pNativeStub; + + HResult hResult = NativeMethods.CreateEventSinkStub(pUnsecApp, GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.WeakTrackResurrection)), WbemObjectSink.Indicate, WbemObjectSink.SetStatus, out pNativeStub); + + if (hResult.Failed) + throw (Exception)hResult; + + this.nativeStub = new IUnknown(pNativeStub); + } + + internal IUnknown NativeStub => this.nativeStub; + + internal bool Cancelled => this.cancelled; + + internal event EventHandler Indicated; + + /// + /// The method is called by a source to provide a notification. Typically, WMI calls the client implementation of this interface after the client executes one of the asynchronous methods. + /// In other cases, various types of providers call an implementation exported by WMI to deliver events. Therefore, client code may have to implement this interface in some cases, and use a different component's implementation in other cases. + /// + /// The pointer to the sink object. + /// Number of objects in the following array of pointers. + /// Array of objects. + /// This method returns an HRESULT that indicates the status of the method call. + internal static HResult Indicate(IntPtr pEventSink, int lObjectCount, IntPtr[] apObjArray) + { + GCHandle gcHandle = GCHandle.FromIntPtr(pEventSink); + + if (gcHandle.IsAllocated) + { + try + { + WbemObjectSink target = gcHandle.Target as WbemObjectSink; + + if (target != null) + { + EventHandler eventHandler = target.Indicated; + + if (eventHandler != null) + { + WbemClassObject[] objects = new WbemClassObject[lObjectCount]; + + for (int i = 0; i < lObjectCount; i++) + objects[i] = new WbemClassObject(apObjArray[i]); + + eventHandler.Invoke(target, new WbemObjectSinkIndicatedEventArgs(objects)); + } + } + } + catch (Exception ex) + { + Trace.WriteLine($"Unhandled Exception caught: {ex}"); + } + } + + return WbemStatus.WBEM_S_NO_ERROR; + } + + /// + /// The method is called by sources to indicate the end of a notification sequence, or to send other status codes to the sink. The method may or may not have been called before the call to . + /// + /// The pointer to the sink object. + /// Bitmask of status information. The status of the operation can be obtained by examining the parameter. + /// This parameter is set to the HRESULT of the asynchronous operation or notification. This is either an error code, if an error occurred, or the amount of progress that has been made on an asynchronous call. + /// Receives a pointer to a read-only string, if the original asynchronous operation returns a string. + /// In cases where a complex error or status object is returned, this contains a pointer to the error object. + /// This method returns an HRESULT that indicates the status of the method call. + internal static HResult SetStatus( + IntPtr pEventSink, + int lFlags, + HResult hResult, + [MarshalAs(UnmanagedType.LPWStr)] + string strParam, + IntPtr pObjParam + ) + { + GCHandle gcHandle = GCHandle.FromIntPtr(pEventSink); + + if (gcHandle.IsAllocated) + { + WbemObjectSink target = gcHandle.Target as WbemObjectSink; + + if (target != null) + { + if (hResult == WbemStatus.WBEM_E_CALL_CANCELLED) + target.cancelled = true; + } + } + + return WbemStatus.WBEM_S_NO_ERROR; + } + + public void Dispose() + { + this.nativeStub.Dispose(); + } + } +} diff --git a/WmiLight/Wbem/WbemObjectSinkIndicatedEventArgs.cs b/WmiLight/Wbem/WbemObjectSinkIndicatedEventArgs.cs new file mode 100644 index 0000000..5291840 --- /dev/null +++ b/WmiLight/Wbem/WbemObjectSinkIndicatedEventArgs.cs @@ -0,0 +1,21 @@ +namespace WmiLight.Wbem +{ + using System; + using System.Diagnostics; + + /// + /// Holds the event data for the event. + /// + internal class WbemObjectSinkIndicatedEventArgs : EventArgs + { + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly WbemClassObject[] objects; + + internal WbemObjectSinkIndicatedEventArgs(WbemClassObject[] objects) + { + this.objects = objects; + } + + internal WbemClassObject[] Objects => objects; + } +} diff --git a/WmiLight/Wbem/WbemServices.cs b/WmiLight/Wbem/WbemServices.cs index c6d4fe2..40f0d0b 100644 --- a/WmiLight/Wbem/WbemServices.cs +++ b/WmiLight/Wbem/WbemServices.cs @@ -20,7 +20,7 @@ internal void SetProxy(ImpersonationLevel impersonate, AuthenticationLevel authL if (this.Disposed) throw new ObjectDisposedException(nameof(WbemServices)); - HResult hResult = NativeMethods.SetProxy(this.NativePointer, null, null, null, impersonate, authLevel); + HResult hResult = NativeMethods.SetProxy(this, null, null, null, impersonate, authLevel); if (hResult.Failed) throw (Exception)hResult; @@ -31,7 +31,7 @@ internal void SetProxy(string userName, string password, string authority, Imper if (this.Disposed) throw new ObjectDisposedException(nameof(WbemServices)); - HResult hResult = NativeMethods.SetProxy(this.NativePointer, userName, password, authority, impersonate, authLevel); + HResult hResult = NativeMethods.SetProxy(this, userName, password, authority, impersonate, authLevel); if (hResult.Failed) throw (Exception)hResult; @@ -43,7 +43,7 @@ internal WbemClassObjectEnumerator ExecQuery(string query, WbemClassObjectEnumer throw new ObjectDisposedException(nameof(WbemServices)); IntPtr pEnumerator; - HResult hResult = NativeMethods.ExecQuery(this.NativePointer, "WQL", query, behaviorOption, ctx, out pEnumerator); + HResult hResult = NativeMethods.ExecQuery(this, "WQL", query, behaviorOption, ctx, out pEnumerator); if (hResult.Failed) throw (Exception)hResult; @@ -57,7 +57,7 @@ internal WbemClassObjectEnumerator ExecQuery(string query, WbemClassObjectEnumer throw new ObjectDisposedException(nameof(WbemServices)); IntPtr pEnumerator; - HResult hResult = NativeMethods.ExecQuery(this.NativePointer, "WQL", query, behaviorOption, ctx, out pEnumerator); + HResult hResult = NativeMethods.ExecQuery(this, "WQL", query, behaviorOption, ctx, out pEnumerator); if (hResult.Failed) throw (Exception)hResult; @@ -70,6 +70,28 @@ internal WbemClassObjectEnumerator ExecQuery(string query, WbemClassObjectEnumer return new WbemClassObjectEnumerator(pEnumerator); } + internal void ExecNotificationQueryAsync(string query, IntPtr ctx, WbemObjectSink sink) + { + if (this.Disposed) + throw new ObjectDisposedException(nameof(WbemServices)); + + HResult hResult = NativeMethods.ExecNotificationQueryAsync(this, "WQL", query, ctx, sink.NativeStub); + + if (hResult.Failed) + throw (Exception)hResult; + } + + internal void CancelAsyncCall(WbemObjectSink sink) + { + if (this.Disposed) + throw new ObjectDisposedException(nameof(WbemServices)); + + HResult hResult = NativeMethods.CancelAsyncCall(this, sink.NativeStub); + + if (hResult.Failed) + throw (Exception)hResult; + } + #endregion } } diff --git a/WmiLight/Wbem/WbemUnsecuredApartment.cs b/WmiLight/Wbem/WbemUnsecuredApartment.cs new file mode 100644 index 0000000..064b002 --- /dev/null +++ b/WmiLight/Wbem/WbemUnsecuredApartment.cs @@ -0,0 +1,42 @@ +namespace WmiLight.Wbem +{ + using System; + + internal class WbemUnsecuredApartment : IUnknown + { + #region Constructors + + #region Description + /// + /// Initializes a new instance of the class. + /// + #endregion + public WbemUnsecuredApartment() + : base(CreateWbemUnsecuredApartment()) + { + } + + #endregion + + #region Methods + + private static IntPtr CreateWbemUnsecuredApartment() + { + IntPtr nativeWbemUnsecuredApartment; + + HResult hResult = NativeMethods.CreateWbemUnsecuredApartment(out nativeWbemUnsecuredApartment); + + if (hResult.Failed) + throw (Exception)hResult; + + return nativeWbemUnsecuredApartment; + } + + internal WbemObjectSink CreateObjectSink() + { + return new WbemObjectSink(this); + } + + #endregion + } +} diff --git a/WmiLight/WmiConnection.cs b/WmiLight/WmiConnection.cs index 1e37235..56c30fa 100644 --- a/WmiLight/WmiConnection.cs +++ b/WmiLight/WmiConnection.cs @@ -91,6 +91,9 @@ public partial class WmiConnection : IDisposable [DebuggerBrowsable(DebuggerBrowsableState.Never)] private WbemServices wbemServices; + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private Lazy wbemUnsecuredApartment = new Lazy(); + #pragma warning disable 0649 // Field is never assigned to, and will always have its default value 'null'. #region Description /// @@ -368,6 +371,24 @@ public WmiObjectEnumerator ExecuteQuery(WmiQuery query) return new WmiObjectEnumerator(this.InternalExecuteQuery(query)); } + internal WmiEventSubscription ExecNotificationQueryAsync(string query, Action action) + { + if (this.isDisposed) + throw new ObjectDisposedException(nameof(WmiConnection)); + + if (query == null) + throw new ArgumentNullException(nameof(query)); + + this.Open(); + + WbemObjectSink sink = this.wbemUnsecuredApartment.Value.CreateObjectSink(); + + // no distinction between remote and local needed + this.wbemServices.ExecNotificationQueryAsync(query.ToString(), this.context, sink); + + return new WmiEventSubscription(this.wbemServices, sink, action); + } + #region Description /// /// Releases all resources used by the . @@ -379,6 +400,9 @@ public void Dispose() { this.Close(); + if (this.wbemUnsecuredApartment.IsValueCreated) + this.wbemUnsecuredApartment.Value.Dispose(); + this.isDisposed = true; } } diff --git a/WmiLight/WmiConnectionExtensions.cs b/WmiLight/WmiConnectionExtensions.cs index 31f33c2..cfb65b8 100644 --- a/WmiLight/WmiConnectionExtensions.cs +++ b/WmiLight/WmiConnectionExtensions.cs @@ -45,6 +45,37 @@ public static WmiQuery CreateQuery(this WmiConnection connection, string wql) return new WmiQuery(connection, wql); } + /// + /// Creates a that can be used to subscribes to temporary event notifications based on a specified event query. + /// + /// the extended object. + /// A WMI event query, which defines the events for which the watcher will listen. + /// The created . + /// is null. + public static WmiEventWatcher CreateEventWatcher(this WmiConnection connection, string wql) + { + if (wql == null) + throw new ArgumentNullException(nameof(wql)); + + return new WmiEventWatcher(connection, wql); + } + + /// + /// Creates a that notifies about wmi events. + /// + /// the extended object. + /// A WMI event query, which defines the events for which the watcher will listen. + /// Action that is invoked if the event notification occures. + /// The created . + /// is null. + public static WmiEventSubscription CreateEventSubscription(this WmiConnection connection, string wql, Action action) + { + if (wql == null) + throw new ArgumentNullException(nameof(wql)); + + return connection.ExecNotificationQueryAsync(wql, action); + } + #region Description /// /// Executes a query to retrieve objects. diff --git a/WmiLight/WmiEventArrivedEventArgs.cs b/WmiLight/WmiEventArrivedEventArgs.cs new file mode 100644 index 0000000..99222c7 --- /dev/null +++ b/WmiLight/WmiEventArrivedEventArgs.cs @@ -0,0 +1,48 @@ +namespace WmiLight +{ + using System; + using System.Diagnostics; + + /// + /// Holds the event data for the event. + /// + public class WmiEventArrivedEventArgs : EventArgs + { + #region Fields + + /// + /// The WMI event that was delivered. + /// + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly WmiObject newEvent; + + #endregion + + #region Constructors + + #region Description + /// + /// Creates a new instance of the class. + /// + /// The WMI event that was delivered. + /// is null. + #endregion + internal WmiEventArrivedEventArgs(WmiObject newEvent) + { + this.newEvent = newEvent ?? throw new ArgumentNullException(nameof(newEvent)); + } + + #endregion + + #region Properties + + #region Description + /// + /// Gets the WMI event that was delivered. + /// + #endregion + public WmiObject NewEvent => newEvent; + + #endregion + } +} diff --git a/WmiLight/WmiEventSubscription.cs b/WmiLight/WmiEventSubscription.cs new file mode 100644 index 0000000..cb9bc01 --- /dev/null +++ b/WmiLight/WmiEventSubscription.cs @@ -0,0 +1,55 @@ +namespace WmiLight +{ + using System; + using WmiLight.Wbem; + + /// + /// A subscription for a wmi event notifications. + /// + public class WmiEventSubscription : IDisposable + { + #region Fields + + private readonly WbemServices service; + + private readonly WbemObjectSink objectSink; + + private readonly Action callback; + + #endregion + + #region Constructors + + internal WmiEventSubscription(WbemServices service, WbemObjectSink objectSink, Action callback) + { + this.service = service ?? throw new ArgumentNullException(nameof(service)); + this.objectSink = objectSink ?? throw new ArgumentNullException(nameof(objectSink)); + this.callback = callback ?? throw new ArgumentNullException(nameof(callback)); + + this.objectSink.Indicated += this.OnObjectSinkIndicated; + } + + #endregion + + #region Methods + + private void OnObjectSinkIndicated(object sender, WbemObjectSinkIndicatedEventArgs e) + { + for (int i = 0; i < e.Objects.Length; i++) + this.callback.Invoke(new WmiObject(e.Objects[i])); + } + + /// + public void Dispose() + { + using (this.objectSink) + { + this.objectSink.Indicated -= this.OnObjectSinkIndicated; + + this.service.CancelAsyncCall(this.objectSink); + } + } + + #endregion + } +} diff --git a/WmiLight/WmiEventWatcher.cs b/WmiLight/WmiEventWatcher.cs new file mode 100644 index 0000000..9791102 --- /dev/null +++ b/WmiLight/WmiEventWatcher.cs @@ -0,0 +1,109 @@ +namespace WmiLight +{ + using System; + using System.Diagnostics; + + /// + /// Subscribes to temporary event notifications based on a specified event query. + /// + public class WmiEventWatcher : IDisposable + { + #region Fileds + + private readonly WmiConnection connection; + + private readonly string notificationQuery; + + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + private readonly object syncRoot = new object(); + + private WmiEventSubscription subscription; + + private bool disposed = false; + + #endregion + + #region Contructor + + /// + /// Creates a new instance of the class. + /// + /// The connection that should be used. + /// A WMI event query, which defines the events for which the watcher will listen. + /// The is null. + /// The is null. + public WmiEventWatcher(WmiConnection connection, string notificationQuery) + { + this.connection = connection ?? throw new ArgumentNullException(nameof(connection)); + this.notificationQuery = notificationQuery ?? throw new ArgumentNullException(nameof(notificationQuery)); + } + + #endregion + + #region Events + + /// + /// Occurs when a new event arrives. + /// + public event EventHandler EventArrived; + + #endregion + + #region Methods + + /// + /// Subscribes to events with the given query and delivers them, asynchronously, through the event. + /// + /// Object already disposed. + public void Start() + { + if (this.disposed) + throw new ObjectDisposedException(nameof(WmiEventWatcher)); + + lock (syncRoot) + { + if (this.subscription == null) + { + this.subscription = this.connection.ExecNotificationQueryAsync(this.notificationQuery, x => this.EventArrived?.Invoke(this, new WmiEventArrivedEventArgs(x))); + } + } + } + + /// + /// Cancels the subscription whether it is synchronous or asynchronous. + /// + /// Object already disposed. + public void Stop() + { + if (this.disposed) + throw new ObjectDisposedException(nameof(WmiEventWatcher)); + + lock (syncRoot) + { + if (this.subscription != null) + { + this.subscription.Dispose(); + + this.subscription = null; + } + } + } + + /// + public void Dispose() + { + if (!this.disposed) + { + this.disposed = true; + + lock (syncRoot) + { + if (this.subscription != null) + this.subscription.Dispose(); + } + } + } + + #endregion + } +} From 432b6f9b278018bb1f2f2c875890836bdda1b740 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 22:03:00 +0200 Subject: [PATCH 4/7] increased version to 6.1.0 --- WmiLight.Native/WmiLight.Native.rc | 66 +++++++++++++++--------------- WmiLight/WmiLight.csproj | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/WmiLight.Native/WmiLight.Native.rc b/WmiLight.Native/WmiLight.Native.rc index 5d43708..72e35e3 100644 --- a/WmiLight.Native/WmiLight.Native.rc +++ b/WmiLight.Native/WmiLight.Native.rc @@ -18,20 +18,20 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN - "resource.h\0" +"resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN - "\0" +"\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN - "\r\n" - "\0" +"\r\n" +"\0" END #endif // APSTUDIO_INVOKED @@ -43,36 +43,36 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 5,1,1,0 - PRODUCTVERSION 5,1,1,0 - FILEFLAGSMASK 0x3fL +FILEVERSION 6, 1, 1, 0 +PRODUCTVERSION 6, 1, 1, 0 +FILEFLAGSMASK 0x3fL #ifdef _DEBUG - FILEFLAGS 0x1L +FILEFLAGS 0x1L #else - FILEFLAGS 0x0L +FILEFLAGS 0x0L #endif - FILEOS 0x40004L - FILETYPE 0x2L - FILESUBTYPE 0x0L +FILEOS 0x40004L +FILETYPE 0x2L +FILESUBTYPE 0x0L BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "CompanyName", "Martin Kuschnik" - VALUE "FileDescription", "The native part of the WmiLight lib." - VALUE "FileVersion", "6.0.0.0" - VALUE "InternalName", "WmiLight.Native" - VALUE "LegalCopyright", "Copyright 2024 Martin Kuschnik" - VALUE "OriginalFilename", "WmiLight.Native.dll" - VALUE "ProductName", "WmiLight" - VALUE "ProductVersion", "6.0.0.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) - END +BLOCK "StringFileInfo" +BEGIN +BLOCK "040904b0" +BEGIN +VALUE "CompanyName", "Martin Kuschnik" +VALUE "FileDescription", "The native part of the WmiLight lib." +VALUE "FileVersion", "6.1.0.0" +VALUE "InternalName", "WmiLight.Native" +VALUE "LegalCopyright", "Copyright 2024 Martin Kuschnik" +VALUE "OriginalFilename", "WmiLight.Native.dll" +VALUE "ProductName", "WmiLight" +VALUE "ProductVersion", "6.1.0.0" +END +END +BLOCK "VarFileInfo" +BEGIN +VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) +END END #endif // English (United States) resources diff --git a/WmiLight/WmiLight.csproj b/WmiLight/WmiLight.csproj index dcb6102..1faadf3 100644 --- a/WmiLight/WmiLight.csproj +++ b/WmiLight/WmiLight.csproj @@ -23,7 +23,7 @@ - 6.0.0 + 6.1.0 WmiLight Martin Kuschnik Martin Kuschnik From a9fe25fd9914fed563ce745714ecaa8a293e6f72 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 22:13:44 +0200 Subject: [PATCH 5/7] updated readme --- README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/README.md b/README.md index 6803d83..bed7b2c 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,40 @@ using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt)) } } ``` +Get a notification if a process has started: +```C# +var opt = new WmiConnectionOptions() { EnablePackageEncryption = true }; + +using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt)) +{ + using (WmiEventSubscription sub = conncetion.CreateEventSubscription( + "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'", + x => Console.WriteLine("Process '{0}' started", x.GetPropertyValue("TargetInstance").GetPropertyValue("Name")))) + { + // ToDo: wait or do some other suff + } +} +``` +Get a notification if a process has started: +```C# +var opt = new WmiConnectionOptions() { EnablePackageEncryption = true }; + +using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt)) +{ + using (WmiEventWatcher eventWatcher = conncetion.CreateEventWatcher("SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'")) + { + eventWatcher.EventArrived += EventWatcher_EventArrived; + + eventWatcher.Start(); + + // ToDo: wait or do some other suff + + eventWatcher.Stop(); + + eventWatcher.EventArrived -= EventWatcher_EventArrived; + } +} +``` ## Native AOT deployment From 5db81fb2260f555d3ccde6067be08cae7cefec04 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 22:18:33 +0200 Subject: [PATCH 6/7] fixed indentation --- WmiLight.Native/WmiLight.Native.rc | 66 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/WmiLight.Native/WmiLight.Native.rc b/WmiLight.Native/WmiLight.Native.rc index 72e35e3..ce1308c 100644 --- a/WmiLight.Native/WmiLight.Native.rc +++ b/WmiLight.Native/WmiLight.Native.rc @@ -18,20 +18,20 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL // TEXTINCLUDE // -1 TEXTINCLUDE +1 TEXTINCLUDE BEGIN -"resource.h\0" + "resource.h\0" END -2 TEXTINCLUDE +2 TEXTINCLUDE BEGIN -"\0" + "\0" END -3 TEXTINCLUDE +3 TEXTINCLUDE BEGIN -"\r\n" -"\0" + "\r\n" + "\0" END #endif // APSTUDIO_INVOKED @@ -43,36 +43,36 @@ END // VS_VERSION_INFO VERSIONINFO -FILEVERSION 6, 1, 1, 0 -PRODUCTVERSION 6, 1, 1, 0 -FILEFLAGSMASK 0x3fL + FILEVERSION 6,1,0,0 + PRODUCTVERSION 6,1,0,0 + FILEFLAGSMASK 0x3fL #ifdef _DEBUG -FILEFLAGS 0x1L + FILEFLAGS 0x1L #else -FILEFLAGS 0x0L + FILEFLAGS 0x0L #endif -FILEOS 0x40004L -FILETYPE 0x2L -FILESUBTYPE 0x0L + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L BEGIN -BLOCK "StringFileInfo" -BEGIN -BLOCK "040904b0" -BEGIN -VALUE "CompanyName", "Martin Kuschnik" -VALUE "FileDescription", "The native part of the WmiLight lib." -VALUE "FileVersion", "6.1.0.0" -VALUE "InternalName", "WmiLight.Native" -VALUE "LegalCopyright", "Copyright 2024 Martin Kuschnik" -VALUE "OriginalFilename", "WmiLight.Native.dll" -VALUE "ProductName", "WmiLight" -VALUE "ProductVersion", "6.1.0.0" -END -END -BLOCK "VarFileInfo" -BEGIN -VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) -END + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Martin Kuschnik" + VALUE "FileDescription", "The native part of the WmiLight lib." + VALUE "FileVersion", "6.1.0.0" + VALUE "InternalName", "WmiLight.Native" + VALUE "LegalCopyright", "Copyright 2024 Martin Kuschnik" + VALUE "OriginalFilename", "WmiLight.Native.dll" + VALUE "ProductName", "WmiLight" + VALUE "ProductVersion", "6.1.0.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1252 // language neutral - multilingual (decimal) + END END #endif // English (United States) resources From 10de94996f875350e0c090915a8d4c5c353da7c4 Mon Sep 17 00:00:00 2001 From: Martin Kuschnik Date: Sat, 8 Jun 2024 22:19:49 +0200 Subject: [PATCH 7/7] adjusted readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bed7b2c..85f81ab 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ using (WmiConnection con = new WmiConnection(@"\\MACHINENAME\root\cimv2", opt)) } } ``` -Get a notification if a process has started: +ALternative way to get a notification if a process has started: ```C# var opt = new WmiConnectionOptions() { EnablePackageEncryption = true };