wql-process-monitor

Monitor Windows process creation/deletion events via WMI (WQL)

Usage no npm install needed!

<script type="module">
  import wqlProcessMonitor from 'https://cdn.skypack.dev/wql-process-monitor';
</script>

README

About

Monitor Windows process creation/deletion events via WMI (WQL) in Node.js

Example

import { subscribe } from 'wql-process-monitor/promises';

const processMonitor = await subscribe();

processMonitor.on("creation", ([process,pid,filepath]) => {
  console.log(`creation: ${process}::${pid} ["${filepath}"]`);
});

processMonitor.on("deletion",([process,pid]) => {
  console.log(`deletion: ${process}::${pid}`);
});

/*
Keep alive
You don't need this if you have something else to keep the event loop running.
This is just an example so Node.js doesn't exit directly.
*/
setInterval(()=>{}, 1000 * 60 * 60);

Do something when a specific process is started :

const processMonitor = await subscribe({
  creation: true,
  deletion: false,
  filter: ["firefox.exe"],
  whitelist: true
});

processMonitor.on("creation", ([process,pid,filepath]) => {
  console.log(`creation: ${process}::${pid} ["${filepath}"]`);
});

Installation

npm install wql-process-monitor

Prerequisite: C/C++ build tools (Visual Studio) and Python 3.x (node-gyp) in order to build node-ffi-napi.

API

⚠️ This module is only available as an ECMAScript module (ESM) starting with version 2.0.0.
Previous version(s) are CommonJS (CJS) with an ESM wrapper.

💡 Promises are under the promises namespace.

import * as WQL from 'wql-process-monitor';
WQL.promises.createEventSink() //Promise
WQL.createEventSink() //Sync

Named export

subscribe

(option?: obj): AsyncEventEmitter

⚙️ Options:

  • creation | bool (default true)

    Subscribe to the creation event

  • deletion | bool (default true)

    Subscribe to the deletionn event

  • filterWindowsNoise | bool (default false)

    Exclude events originating from System32 and SysWOW64 Windows folder as well as integrated OneDrive FileCoAuth.exe.
    Ex: cmd.exe, powershell.exe, svchost.exe, RuntimeBroker.exe, and others Windows processes.

    ⚠️ NB: Using this will prevent you to catch any elevated process event.
    Unless you are also elevated. This is a permission issue (See #2).
    You can implement your own filter on top of the event emitter result instead.

  • filterUsualProgramLocations | bool (default false)

    Exclude events originating from Program Files, Program Files (x86), AppData local and AppData Roaming.

    ⚠️ NB: Using this will prevent you to catch any elevated process event.
    Unless you are also elevated. This is a permission issue (See #2).
    You can implement your own filter on top of the event emitter result instead.

  • filter | array of string (default none)

    Custom list of process to exclude.
    eg: ["firefox.exe","chrome.exe",...]

    NB: There are limits to the number of AND and OR keywords that can be used in WQL queries. Large numbers of WQL keywords used in a complex query can cause WMI to return the WBEM_E_QUOTA_VIOLATION error code as an HRESULT value. The limit of WQL keywords depends on how complex the query is
    cf: https://docs.microsoft.com/en-us/windows/win32/wmisdk/querying-with-wql
    If you have a huge list consider implementing your own filter on top of the event emitter result instead.

  • whitelist | bool (default false)

    Use filter option as a whitelist.
    filterWindowsNoise / filterUsualProgramLocations can still be used.
    Previously mentioned limitation(s) still apply.

✔️ Return a non-blocking async event emitter (emittery):

.on("creation", ([process,pid,filepath]) => {})
.on("deletion", ([process,pid]) => {})
Value Description Example
process process name firefox.exe
pid process identifier 16804
filepath file location path (if available¹) C:\Program Files\Mozilla Firefox\firefox.exe

¹filepath is only available in "creation" (well it doesn't make sense to open a deleted process for its information ^^) and will sometimes be empty because of permission to access a process information and in the same fashion 32bits can not access 64 bits.

💡 Don't forget to keep the node.js event loop alive.

createEventSink

(): void

Initialize the event sink.
This is required to do before you can subscribe to any events.
If the event sink is already initialized then nothing will be done.

💡 Since version >= 2.0.0 this is automatically done for you when you call subscribe().
Method was merely kept for backward compatibility.

⚠️ If your application (the caller thread) is initializing a COM library you need to set the thread model to COINIT_MULTITHREADED For this reason using this in Electron's main process isn't viable. If you really need to use Electron's main process; I suggest that you either

  • fork a node child process or
  • use web workers or
  • use a hidden browser window and communicate between the main process and background window via Electron's IPC.

closeEventSink

(): void

Properly close the event sink.
There is no 'un-subscribe' thing to do prior to closing the sink. Just close it.
It is recommended to properly close the event sink when you are done if you intend to re-open it later on.
Most of the time you wouldn't have to bother with this but it's here in case you need it.