Skip to content

Commit

Permalink
Merge pull request #17 from MartinKuschnik/EventWatcher-Support
Browse files Browse the repository at this point in the history
Event watcher support
  • Loading branch information
MartinKuschnik committed Jun 8, 2024
2 parents 02cbb11 + 10de949 commit 78567e6
Show file tree
Hide file tree
Showing 26 changed files with 798 additions and 30 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<WmiObject>("TargetInstance").GetPropertyValue<string>("Name"))))
{
// ToDo: wait or do some other suff
}
}
```
ALternative way to 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

Expand Down
48 changes: 48 additions & 0 deletions WmiLight.Native/EventSinkProxy.cpp
Original file line number Diff line number Diff line change
@@ -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);
}
37 changes: 37 additions & 0 deletions WmiLight.Native/EventSinkProxy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <Wbemidl.h>

/// <summary>
/// Proxy that forewardes the event calls to the managed sink.
/// </summary>
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);
};
8 changes: 4 additions & 4 deletions WmiLight.Native/WmiLight.Native.rc
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ END
//

VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,1,1,0
PRODUCTVERSION 5,1,1,0
FILEVERSION 6,1,0,0
PRODUCTVERSION 6,1,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
Expand All @@ -61,12 +61,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Martin Kuschnik"
VALUE "FileDescription", "The native part of the WmiLight lib."
VALUE "FileVersion", "6.0.0.0"
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.0.0.0"
VALUE "ProductVersion", "6.1.0.0"
END
END
BLOCK "VarFileInfo"
Expand Down
2 changes: 2 additions & 0 deletions WmiLight.Native/WmiLight.Native.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,14 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="EventSinkProxy.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="EventSinkProxy.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
Expand Down
6 changes: 6 additions & 0 deletions WmiLight.Native/WmiLight.Native.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="EventSinkProxy.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
Expand All @@ -35,6 +38,9 @@
<ClCompile Include="WmiLightNative.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="EventSinkProxy.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="WmiLight.Native.rc">
Expand Down
77 changes: 70 additions & 7 deletions WmiLight.Native/WmiLightNative.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "pch.h"

#include "EventSinkProxy.h"

#include <Wbemidl.h>

#ifdef __cplusplus
Expand Down Expand Up @@ -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<LPVOID*>(locator));
return CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(locator));
}

__declspec(dllexport) HRESULT CreateWbemUnsecuredApartment(IWbemUnsecuredApartment** unsecuredApartment)
{
return CoCreateInstance(CLSID_UnsecuredApartment, nullptr, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, reinterpret_cast<void**>(unsecuredApartment));
}

__declspec(dllexport) HRESULT ReleaseIUnknown(IUnknown* pIUnknown)
Expand All @@ -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,
Expand Down Expand Up @@ -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<unsigned long>(wcslen(username));

Expand Down Expand Up @@ -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<void**>(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)
Expand Down
14 changes: 11 additions & 3 deletions WmiLight.TestApp-NetCore/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@ static void Main(string[] args)
{
Console.WriteLine($"#{process.GetPropertyValue<uint>("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<WmiObject>("TargetInstance").GetPropertyValue<string>("Name"))))
{
Console.WriteLine("Observing starting processes. Press any key to stop observing...");
Console.ReadKey();
}
}

Console.Write("Press Any Key To Exit...");
Console.ReadKey();
}
}
}
}
12 changes: 12 additions & 0 deletions WmiLight/Internal/InterfaceIdentifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace WmiLight
{
using System;

internal static class InterfaceIdentifier
{
/// <summary>
/// DC12A681-737F-11CF-884D-00AA004B2E24
/// </summary>
public static readonly Guid IWbemClassObject = new Guid(0xDC12A681, 0x737F, 0x11CF, 0x88, 0x4D, 0x00, 0xAA, 0x00, 0x4B, 0x2E, 0x24);
}
}
Loading

0 comments on commit 78567e6

Please sign in to comment.