0

I am trying to create a notify event program when some specific process opens. I follow this post, and it works. I can get event when notepad.exe or powerpoint process opened.

However the program just execute one time, after the first notepad opened, the program stop checking new notepad process.

I know I may need a loop, but where should I add the loop?

Another question is that I check two type of process(notepad & powerpoint), so no matter which process opened, the event will be trigger. Is it possible to know which process trigger the function?

Suppose I open notepad, and I can know notepad process trigger event, not powerpoint

Main.cpp

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <comdef.h>
#include <Wbemidl.h>
#include <atlcomcli.h>

#pragma comment(lib, "wbemuuid.lib")
#include "CreationEvent.h"

class EventSink : public IWbemObjectSink {
    friend void CreationEvent::registerCreationCallback(TNotificationFunc callback);

    CComPtr<IWbemServices> pSvc;
    CComPtr<IWbemObjectSink> pStubSink;
    LONG m_IRef;
    CreationEvent::TNotificationFunc m_callback;

public:
    EventSink(CreationEvent::TNotificationFunc callback) :m_IRef(0), m_callback(callback){}
    ~EventSink(){
    }

    virtual ULONG STDMETHODCALLTYPE AddRef() {
        return InterlockedIncrement(&m_IRef);
    }

    virtual ULONG STDMETHODCALLTYPE Release() {
        LONG IRef = InterlockedDecrement(&m_IRef);
        if (IRef == 0)
            delete this;
        return IRef;
    }

    virtual HRESULT STDMETHODCALLTYPE 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;
    }

    virtual HRESULT STDMETHODCALLTYPE Indicate(
        LONG lObjectCount,
        IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
    ){
        m_callback();
        /* Unregister event sink */
        pSvc->CancelAsyncCall(pStubSink);
        return WBEM_S_NO_ERROR;
    }
    virtual HRESULT STDMETHODCALLTYPE SetStatus(LONG IFlags, HRESULT hResult, BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) {
        return WBEM_S_NO_ERROR;
    }
};

void CreationEvent::registerCreationCallback(TNotificationFunc callback) {
    CComPtr<IWbemLocator> pLoc;
    CoInitializeEx(0, COINIT_MULTITHREADED);
    HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);

    if (FAILED(hres)) {
        cout << "Failed to create IWbemLocator object."
            << " Err code = 0x"
            << hex << hres << endl;
        throw std::exception("CreationEvent initialization failed");
    }
    CComPtr<EventSink> pSink(new EventSink(callback));

    hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSink->pSvc);
    if (FAILED(hres)) {
        cout << "Could not connect. Error code = 0x" << hex << hres << endl;
        throw std::exception("CreationEvent initialization failed");
    }
    hres = CoSetProxyBlanket(pSink->pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hres)) {
        cout << "Coult not set proxy blanket, Error code =0x" << hex << hres << endl;
        throw std::exception("CreationEvent initialization failed");
    }

    CComPtr<IUnsecuredApartment> pUnsecApp;
    hres = CoCreateInstance(CLSID_UnsecuredApartment, NULL, CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment, (void**)&pUnsecApp);
    CComPtr<IUnknown> pStubUnk;
    pUnsecApp->CreateObjectStub(pSink, &pStubUnk);
    pStubUnk->QueryInterface(IID_IWbemObjectSink, (void**)&pSink->pStubSink);


    char buffer[512];
    sprintf_s(buffer, "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' AND (TargetInstance.Name = 'Notepad.exe' OR TargetInstance.Name = 'Powerpnt.exe')");

    hres = pSink->pSvc->ExecNotificationQueryAsync(_bstr_t("WQL"), _bstr_t(buffer), WBEM_FLAG_SEND_STATUS, NULL, pSink->pStubSink);

    if (FAILED(hres)) {
        cout << "ExecNotificationQueryAsync failed with = 0x" << hex << hres << endl;
        throw std::exception("CreationEvent initialization failed");
    }
}

void k() { cout << "process open" << endl; }

int main() {
    CreationEvent::registerCreationCallback(k);
    CoUninitialize();
} 

Header.h

#pragma once
#ifndef _Header_h__
#define _Header_h__
#include <boost/function.hpp>
namespace CreationEvent {
    typedef boost::function<void(void)> TNotificationFunc;
    void registerCreationCallback(TNotificationFunc callback);
}
#endif
Kurt
  • 678
  • 1
  • 7
  • 24

1 Answers1

1

you should add a loop after the call to the ExecNotificationQueryAsyncat the end of the function registerCreationCallback, it can be a forever loop with done condition or you can use a WaitForSingleObject, then create an event and signaled when you want to finish your process, also you should release all the resources allocated.

For get the name of the process started, go to the Indicate function and change this

virtual HRESULT STDMETHODCALLTYPE Indicate(
        LONG lObjectCount,
        IWbemClassObject __RPC_FAR *__RPC_FAR *apObjArray
    ){
        for (int i = 0; i < lObjectCount; i++){
            _variant_t vtProp;
            _variant_t cn;

            hr = apObjArray[i]->Get(_bstr_t(L"TargetInstance"), 0, &vtProp, 0, 0)
            IUnknown* str = vtProp;
            HRESULT hr = str->QueryInterface(IID_IWbemClassObject, reinterpret_cast<void**>(&apObjArray[i]));
            if (SUCCEEDED(hr))
            {
                _variant_t cn;
                hr = apObjArray[i]->Get(L"Name", 0, &cn, NULL, NULL);
                //here is the name of the process
                //cn.bstrVal
                m_callback();
            }
        }

        /* Unregister event sink */
        //you should not unregister the event if you want to receive more notifications, do this after 
        //your forever loop in registerCreationCallback
        //pSvc->CancelAsyncCall(pStubSink);
        return WBEM_S_NO_ERROR;
    }

then you can send de name as a paremeter to your callback

  • Sorry, I got another problem. I can put this (pSvc->CancelAsyncCall(pStubSink); ) to unregister event in registerCreationCallback. I want to know is there any method do this from calling another function? – Kurt Nov 08 '18 at 02:38
  • The varibale CComPtr pSink(new EventSink(callback)); is storing the object who owns the information about the connection to the WMI server, this object only exist in the scope of the function registerCreationCallback, as soon as this function ends, this object is destroyed, you have to find a way to store this object on a variable out of that scope, a member variable of a class, or a global variable, etc.., just remember, pSvc is private on the EventSink class, add the code to handle this scenario. – Ricardo FuMa Nov 08 '18 at 07:57
  • I think I need to write a function that return pSink, am I right? But, I am confused there is no pSink in this(pSvc->CancelAsyncCall(pStubSink);) code, just pSvc and pStubSink. Will I need to return these two value? My goal is to execute this(pSvc->CancelAsyncCall(pStubSink);) code from other function. – Kurt Nov 09 '18 at 01:18
  • Thanks for the hints, I solve the problem. I create the global variable to strore pSink, then use pSink to call pSvc and pStubSink. – Kurt Nov 09 '18 at 03:45