Generally, my go-to debugging technique is Time Travel Tracing (TTD.exe). However, without full Symbols for Windows, some types of analysis can be a little tedious creating scripts to dump the API parameters etc. In addition, while you can use breakpoint commands to dump API call parameters, the performance is extremely slow, especially with many breakpoints. So although it has not been updated in some time, API Monitor is till a fantastic tool to have on hand, with a very nice simple XML definition system to custom define APIs to monitor. However, as it has not been updated in so long the very useful process notification feature breaks in Windows 8.1/Server 2012 and later, due to an incompatibility with the process notification driver.
As a workaround, I made this simple tool that will wait for a process matching a filter, then automatically attach API monitor to it. You won’t capture the full start of the process, but it is much faster than manually trying to do so.
This is using UI automation which at times can be unreliable, in my current testing on Windows 11 for my use cases it seemed to work well enough.
To use:
- Start API Monitor ( http://www.rohitab.com/apimonitor#Download )
- Select the type of APIs you want to monitor.
- Launch the x86/x64 version depending on target exe you want to monitor, wild cards with * and ? are supported. If API monitor is elevated, this tool must be launched elevated; however if you are monitoring non-elevated EXE you should not need to run either tools elevated.
AutoAttachApiMon_x86.exe c*.exe
4. When processes are launched they should get detected and start being monitored. Note API Monitor can crash some processes, and may need to adjust API Monitor settings to handle those scenario
The code is available here:
https://github.com/chentiangemalc/AutoAttachApiMon
This can be built with Visual Studio 2022 with C++ / Windows SDK
How it works:
- Finds the Window for API Monitor
- Uses WMI query Select * From __InstanceCreationEvent Within 1 Where TargetInstance ISA ‘Win32_Process’ to notify when new processes start
- When a new process starts the Process ID is identified
- We save current foreground window
- API monitor is brought to the foreground, the list of running processes in the API monitor Running Processes window is analyzed and searched for matching process ID
- When found we make sure to scroll to that item, then simulate a right click, down key, and enter.
- Restore the foreground window that was active before we did this
Use the x64 build with API Monitor x64 for 64-bit processes and Win32 build with API Monitor x86 for 32-bit processes.
This was written as a proof of concept / testing so may have flaws and is not fully tested, feel free to submit improvements via github.
AutoAttachApiMon.cpp
#include <windows.h>
#include <commctrl.h>
#include <iostream>
#include <vector>
#include <string>
#include <conio.h>
#include "ProcessCreatedEventDispatcher.h"
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "comctl32.lib")
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam);
HWND FindChildWindowByClass(HWND parent, LPCWSTR className);
int GetListViewItemCount(HWND hwndListView);
int GetListViewColumnCount(HWND hwndListView);
std::wstring GetListViewItemText(HWND hwndListView, int itemIndex, int subItemIndex);
bool WildcardMatch(const std::wstring& str, const std::wstring& pattern);
bool EnableDebugPrivilege();
void BringWindowToForeground(HWND hWnd);
// Global variables
HWND g_hListView = nullptr;
HWND g_hwndMain = nullptr;
std::wstring processFilter;
int main()
{
LPWSTR commandLine = GetCommandLineW();
int argc;
LPWSTR* argv = CommandLineToArgvW(commandLine, &argc);
if (argc < 2) {
std::cout << "At least one argument is required." << std::endl;
return 1; // Exit with error code 1
}
processFilter = std::wstring(argv[1]);
LocalFree(argv);
// needed to monitor when running elevated
EnableDebugPrivilege();
// Find the main window
#ifdef _WIN64 // Check if building for 64-bit architecture
g_hwndMain = FindWindow(NULL, L"Monitoring - API Monitor v2 64-bit (Administrator)");
if (!g_hwndMain)
{
g_hwndMain = FindWindow(NULL, L"Monitoring - API Monitor v2 64-bit");
}
#else
g_hwndMain = FindWindow(NULL, L"Monitoring - API Monitor v2 32-bit (Administrator)");
if (!g_hwndMain)
{
g_hwndMain = FindWindow(NULL, L"Monitoring - API Monitor v2 32-bit");
}
#endif
if (!g_hwndMain) {
std::wcerr << L"API Monitor 64-bit not running!" << std::endl;
return 1;
}
HWND hwndRunningProcesses = FindWindowEx(g_hwndMain, NULL, NULL, L"Running Processes");
if (!hwndRunningProcesses)
{
std::wcerr << L"Running processes not found - make sure monitoring is on!" << std::endl;
return 1;
}
g_hListView = FindChildWindowByClass(hwndRunningProcesses, L"SysListView32");
if (!g_hListView) {
std::wcerr << L"SysListView32 control not found" << std::endl;
return 1;
}
// Extract table info
int columnCount = GetListViewColumnCount(g_hListView);
if (columnCount == 0)
{
std::wcout << L"Unable to detect any running processes in API Monitor. If API monitor is running as admin, make sure this is running as admin too." << std::endl;
return 1;
}
std::wcout << L"Current running processes in API monitor ...";
std::wcout << L"ColumnCount = " << columnCount << std::endl;
int rowCount = GetListViewItemCount(g_hListView);
std::wcout << L"RowCount = " << rowCount << std::endl;
// Print table content
for (int i = 0; i < rowCount; ++i) {
for (int j = 0; j < columnCount; ++j) {
std::wcout << GetListViewItemText(g_hListView, i, j) << L"\t";
}
std::wcout << std::endl;
}
ProcessCreatedEventDispatcher ProcessCreatedEventDispatcher{};
ProcessCreatedEventDispatcher.NewProcessCreatedListeners.emplace_back([](auto processName, auto processId) {
std::wcout << L"Process Name: " << processName << L" Process Id:" << processId << std::endl;
if (WildcardMatch(processName, processFilter))
{
std::wcout << "monitoring!" << std::endl;
ClickContextMenuItem(g_hListView, processId);
}
std::flush(std::cout);
});
#ifdef _WIN64
std::wcout << L"Waiting for 64-bit processes matching '" << processFilter << L"'" << std::endl;
#else
std::wcout << L"Waiting for 32-bit processes matching '" << processFilter << L"'" << std::endl;
#endif
// Wait for key press to exit the program
std::cout << "Press any key to terminate" << std::endl;
while (!_kbhit()) {}
return 0;
}
void BringWindowToForeground(HWND hwnd) {
if (!hwnd) {
std::cerr << "Invalid window handle." << std::endl;
return;
}
// Show the window if it is minimized
if (IsIconic(hwnd)) {
ShowWindow(hwnd, SW_RESTORE);
}
else {
ShowWindow(hwnd, SW_SHOW);
}
// Bring the window to the foreground and set focus
SetForegroundWindow(hwnd);
SetFocus(hwnd);
}
void EnsureVisible(HWND hwndListView, int itemIndex) {
SendMessage(hwndListView, LVM_ENSUREVISIBLE, (WPARAM)itemIndex, TRUE);
}
bool ClickContextMenuItem(HWND hwndListView, const std::wstring& matchText) {
HWND hwndForeground = GetForegroundWindow();
BringWindowToForeground(g_hwndMain);
int itemCount = GetListViewItemCount(hwndListView);
bool itemFound = false;
int itemIndex = -1;
for (int i = 0; i < itemCount; ++i) {
std::wstring itemText = GetListViewItemText(hwndListView, i, 1); // 2nd column is index 1
if (itemText == matchText) {
itemIndex = i;
itemFound = true;
break;
}
}
if (!itemFound) {
std::wcerr << L"No matching item found." << std::endl;
return false;
}
// Select the found item
// Ensure the item is visible
EnsureVisible(hwndListView, itemIndex);
// Get the process ID of the target process
DWORD processId;
GetWindowThreadProcessId(hwndListView, &processId);
// Open the target process
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, processId);
if (!hProcess) {
std::wcerr << L"Failed to open process. Error: " << GetLastError() << std::endl;
return false;
}
// Allocate memory in the target process for the RECT structure
RECT* pRemoteRect = (RECT*)VirtualAllocEx(hProcess, NULL, sizeof(RECT), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!pRemoteRect) {
std::wcerr << L"Failed to allocate memory in target process. Error: " << GetLastError() << std::endl;
CloseHandle(hProcess);
return false;
}
// Send the LVM_GETITEMRECT message to the ListView control
SendMessage(hwndListView, LVM_GETITEMRECT, (WPARAM)itemIndex, (LPARAM)pRemoteRect);
// Read the RECT structure from the target process
RECT itemRect;
if (!ReadProcessMemory(hProcess, pRemoteRect, &itemRect, sizeof(RECT), NULL)) {
std::wcerr << L"Failed to read item rectangle from target process. Error: " << GetLastError() << std::endl;
VirtualFreeEx(hProcess, pRemoteRect, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// Free the allocated memory in the target process
VirtualFreeEx(hProcess, pRemoteRect, 0, MEM_RELEASE);
CloseHandle(hProcess);
// Calculate the middle point of the item rectangle
POINT pt = {
(itemRect.left + itemRect.right) / 2,
(itemRect.top + itemRect.bottom) / 2
};
// Convert to screen coordinates
ClientToScreen(hwndListView, &pt);
// Set the cursor position to the item's center
SetCursorPos(pt.x, pt.y);
// Simulate mouse down and up to perform a left click
INPUT inputs[7] = {};
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
// DOWN arrow key down
inputs[2].type = INPUT_KEYBOARD;
inputs[2].ki.wVk = VK_DOWN; // Virtual-key code for the DOWN arrow key
inputs[2].ki.dwFlags = 0; // Key down event
// DOWN arrow key up
inputs[3].type = INPUT_KEYBOARD;
inputs[3].ki.wVk = VK_DOWN; // Virtual-key code for the DOWN arrow key
inputs[3].ki.dwFlags = KEYEVENTF_KEYUP; // Key up event
// ENTER key down
inputs[4].type = INPUT_KEYBOARD;
inputs[4].ki.wVk = VK_RETURN; // Virtual-key code for the ENTER key
inputs[4].ki.dwFlags = 0; // Key down event
// ENTER key up
inputs[5].type = INPUT_KEYBOARD;
inputs[5].ki.wVk = VK_RETURN; // Virtual-key code for the ENTER key
inputs[5].ki.dwFlags = KEYEVENTF_KEYUP; // Key up event
SendInput(6, inputs, sizeof(INPUT));
BringWindowToForeground(hwndForeground);
return true;
}
bool EnableDebugPrivilege()
{
HANDLE hToken;
// Open a handle to the access token for the calling process.
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
std::cerr << "OpenProcessToken error: " << GetLastError() << std::endl;
return false;
}
TOKEN_PRIVILEGES tp;
LUID luid;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
std::cerr << "LookupPrivilegeValue error: " << GetLastError() << std::endl;
return false;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// Adjust Token Privileges
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) {
std::cerr << "AdjustTokenPrivileges error: " << GetLastError() << std::endl;
return false;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
std::cerr << "Unable to enable debug privilege. This is expected if not running elevated." << std::endl;
return false;
}
return true;
}
bool WildcardMatch(const std::wstring& str, const std::wstring& pattern) {
if (pattern.empty()) return str.empty();
if (pattern[0] == L'*') {
return WildcardMatch(str, pattern.substr(1)) || (!str.empty() && WildcardMatch(str.substr(1), pattern));
}
else if (pattern[0] == L'?' || pattern[0] == str[0]) {
return WildcardMatch(str.substr(1), pattern.substr(1));
}
else {
return false;
}
}
// Enumerate child windows and find the target window
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
HWND* result = reinterpret_cast<HWND*>(lParam);
wchar_t className[256];
GetClassName(hwnd, className, sizeof(className) / sizeof(className[0]));
if (wcscmp(className, L"SysListView32") == 0) {
*result = hwnd;
return FALSE;
}
return TRUE;
}
// Find child window by class name
HWND FindChildWindowByClass(HWND parent, LPCWSTR className)
{
HWND result = nullptr;
EnumChildWindows(parent, EnumChildProc, reinterpret_cast<LPARAM>(&result));
return result;
}
// Get the number of items in the ListView
int GetListViewItemCount(HWND hwndListView)
{
return static_cast<int>(SendMessage(hwndListView, LVM_GETITEMCOUNT, 0, 0));
}
// Get the number of columns in the ListView
int GetListViewColumnCount(HWND hwndListView)
{
HWND header = (HWND)SendMessage(hwndListView, LVM_GETHEADER, 0, 0);
return static_cast<int>(SendMessage(header, HDM_GETITEMCOUNT, 0, 0));
}
// Get the text of a specific item in the ListView
std::wstring GetListViewItemText(HWND hwndListView, int itemIndex, int subItemIndex)
{
DWORD processId;
GetWindowThreadProcessId(hwndListView, &processId);
HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, processId);
if (!hProcess) {
int err = GetLastError();
std::wcerr << L"Failed to open process err#" << err << std::endl;
return L"";
}
// Allocate memory in the target process
SIZE_T bufferSize = 256 * sizeof(wchar_t);
SIZE_T lvItemSize = sizeof(LVITEM);
SIZE_T totalSize = lvItemSize + bufferSize;
LPVOID pRemoteBuffer = VirtualAllocEx(hProcess, nullptr, totalSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!pRemoteBuffer) {
std::wcerr << L"Failed to allocate memory in target process" << std::endl;
CloseHandle(hProcess);
return L"";
}
// Write the LVITEM structure to the target process memory
LVITEM lvItem = { 0 };
lvItem.mask = LVIF_TEXT;
lvItem.iItem = itemIndex;
lvItem.iSubItem = subItemIndex;
lvItem.pszText = (LPWSTR)((LPBYTE)pRemoteBuffer + lvItemSize);
lvItem.cchTextMax = 256;
if (!WriteProcessMemory(hProcess, pRemoteBuffer, &lvItem, lvItemSize, nullptr)) {
std::wcerr << L"Failed to write LVITEM to target process memory" << std::endl;
VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return L"";
}
// Send the LVM_GETITEMTEXT message to the ListView control
SendMessage(hwndListView, LVM_GETITEMTEXT, itemIndex, (LPARAM)pRemoteBuffer);
// Read the text back from the target process memory
wchar_t buffer[256];
if (!ReadProcessMemory(hProcess, (LPBYTE)pRemoteBuffer + lvItemSize, buffer, bufferSize, nullptr)) {
std::wcerr << L"Failed to read item text from target process memory" << std::endl;
VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return L"";
}
// Clean up
VirtualFreeEx(hProcess, pRemoteBuffer, 0, MEM_RELEASE);
CloseHandle(hProcess);
return std::wstring(buffer);
}
ProcessCreatedEventDispatcher.h
#pragma once
#include <functional>
#include <vector>
#include <Wbemidl.h>
#include <wrl.h>
#include <string>
using namespace Microsoft::WRL;
class ProcessCreatedEventDispatcher : public IWbemObjectSink {
public:
ProcessCreatedEventDispatcher();
~ProcessCreatedEventDispatcher();
ULONG STDMETHODCALLTYPE AddRef() override;
ULONG STDMETHODCALLTYPE Release() override;
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) override;
HRESULT STDMETHODCALLTYPE Indicate(LONG lObjectCount, IWbemClassObject __RPC_FAR* __RPC_FAR* apObjArray) override;
HRESULT STDMETHODCALLTYPE SetStatus(LONG lFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR* pObjParam) override;
using NewProcessCreatedListener = void(std::wstring processName, std::wstring processId);
std::vector<std::function<NewProcessCreatedListener>> NewProcessCreatedListeners{};
private:
LONG m_lRef{};
ComPtr<IWbemServices> pSvc{};
ComPtr<IWbemLocator> pLoc{};
ComPtr<IUnsecuredApartment> pUnsecApp{};
ComPtr<IUnknown> pStubUnk{};
ComPtr<IWbemObjectSink> pStubSink{};
};
ProcessCreatedEventDispatcher.cpp
#include "ProcessCreatedEventDispatcher.h"
# pragma comment(lib, "wbemuuid.lib")
#include <iostream>
#include <functional>
#include <string>
#include <vector>
#define _WIN32_DCOM
#include <Windows.h>
#include <comdef.h>
#include <Wbemidl.h>
#include <wrl.h>
using namespace std;
using namespace Microsoft::WRL;
ProcessCreatedEventDispatcher::ProcessCreatedEventDispatcher() {
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres)) {
cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
return; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hres = CoInitializeSecurity(NULL,
-1, // COM negotiates service
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres)) {
cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
CoUninitialize();
return; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)pLoc.GetAddressOf());
if (FAILED(hres)) {
cout << "Failed to create IWbemLocator object. " << "Err code = 0x" << hex << hres << endl;
CoUninitialize();
return; // Program has failed.
}
// Step 4: ---------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
// Connect to the local root\cimv2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = pLoc->ConnectServer(_bstr_t(L"root\\CIMV2"),
NULL,
NULL,
0,
NULL,
0,
0,
&pSvc
);
if (FAILED(hres)) {
cout << "Could not connect. Error code = 0x" << hex << hres << endl;
pLoc->Release();
CoUninitialize();
return; // Program has failed.
}
cout << "Connected to root\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(pSvc.Get(), // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres)) {
cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return; // Program has failed.
}
// Step 6: -------------------------------------------------
// Receive event notifications -----------------------------
// Use an unsecured apartment for security
hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&pUnsecApp);
this->ProcessCreatedEventDispatcher::AddRef();
pUnsecApp->CreateObjectStub(this, &pStubUnk);
pStubUnk->QueryInterface(IID_IWbemObjectSink, &pStubSink);
_bstr_t WQL = L"Select * From __InstanceCreationEvent Within 1 "
L"Where TargetInstance ISA 'Win32_Process' ";
// The ExecNotificationQueryAsync method will call
// The EventQuery::Indicate method when an event occurs
hres = pSvc->ExecNotificationQueryAsync(_bstr_t("WQL"), WQL, WBEM_FLAG_SEND_STATUS, NULL, pStubSink.Get());
// Check for errors.
if (FAILED(hres)) {
printf("ExecNotificationQueryAsync failed with = 0x%X\n", hres);
pSvc->Release();
pLoc->Release();
pUnsecApp->Release();
pStubUnk->Release();
this->ProcessCreatedEventDispatcher::Release();
pStubSink->Release();
CoUninitialize();
return;
}
}
ProcessCreatedEventDispatcher::~ProcessCreatedEventDispatcher() {
auto Result = pSvc->CancelAsyncCall(pStubSink.Get());
pSvc->Release();
pLoc->Release();
pUnsecApp->Release();
pStubUnk->Release();
this->ProcessCreatedEventDispatcher::Release();
pStubSink->Release();
CoUninitialize();
}
ULONG ProcessCreatedEventDispatcher::AddRef() {
return InterlockedIncrement(&m_lRef);
}
ULONG ProcessCreatedEventDispatcher::Release() {
LONG lRef = InterlockedDecrement(&m_lRef);
if (lRef == 0)
delete this;
return lRef;
}
HRESULT ProcessCreatedEventDispatcher::QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IUnknown || riid == IID_IWbemObjectSink) {
*ppv = (IWbemObjectSink*)this;
AddRef();
return WBEM_S_NO_ERROR;
}
else return E_NOINTERFACE;
}
HRESULT ProcessCreatedEventDispatcher::Indicate(long lObjectCount, IWbemClassObject** apObjArray) {
HRESULT hr = S_OK;
_variant_t vtProp;
for (int i = 0; i < lObjectCount; i++) {
hr = apObjArray[i]->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0);
if (!FAILED(hr)) {
ComPtr<IUnknown> pUnk = static_cast<IUnknown*>(vtProp);
IWbemClassObject* pObj = nullptr;
hr = pUnk->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&pObj));
if (SUCCEEDED(hr)) {
_variant_t cn, pid, name;
hr = pObj->Get(L"Handle", 0, &cn, NULL, NULL);
hr = pObj->Get(L"ProcessId", 0, &pid, NULL, NULL);
hr = pObj->Get(L"Name", 0, &name, NULL, NULL);
if (SUCCEEDED(hr)) {
if ((cn.vt == VT_NULL) || (cn.vt == VT_EMPTY))
std::cout << "Handle : " << ((cn.vt == VT_NULL) ? "NULL" : "EMPTY") << endl;
else if ((cn.vt & VT_ARRAY))
std::cout << "Handle : " << "Array types not supported (yet)" << endl;
else {
std::wstring WideProcessHandle = std::wstring(cn.bstrVal);
std::wstring WideProcessId = std::to_wstring(pid.lVal);
std::wstring WideProcessName = std::wstring(name.bstrVal);
// Pass the process ID, process name, and handle to the listener
for (auto& NewProcessCreatedListener : NewProcessCreatedListeners) {
NewProcessCreatedListener(WideProcessName, WideProcessId);
}
}
}
VariantClear(&cn);
VariantClear(&pid);
VariantClear(&name);
}
pObj->Release();
}
VariantClear(&vtProp);
}
return WBEM_S_NO_ERROR;
}
HRESULT ProcessCreatedEventDispatcher::SetStatus(
/* [in] */ LONG lFlags,
/* [in] */ HRESULT hResult,
/* [in] */ BSTR strParam,
/* [in] */ IWbemClassObject __RPC_FAR* pObjParam
) {
if (lFlags == WBEM_STATUS_COMPLETE) {
printf("Call complete. hResult = 0x%X\n", hResult);
}
else if (lFlags == WBEM_STATUS_PROGRESS) {
printf("Call in progress.\n");
}
return WBEM_S_NO_ERROR;
} // end of EventSin