WQL Queries

WQL (WMI Query Language) is the SQL-like language used to query the Windows Management Instrumentation (WMI) repository. The client runs a query on the remote host and returns the rows.

Running a query

Build a WinRMClient[1], then prepare the query with wql(...) and run it with execute():

import java.time.Duration;
import org.metricshub.winrm.WinRMClient;
import org.metricshub.winrm.WqlResult;
import org.metricshub.winrm.WqlRow;

try (WinRMClient client = WinRMClient.builder("server.example.com")
        .credentials("DOMAIN\\Administrator", password)
        .timeout(Duration.ofSeconds(30))
        .build()) {

    WqlResult result = client.wql("SELECT Name, State FROM Win32_Service").execute();

    for (WqlRow row : result) {
        System.out.println(row.string("Name") + " is " + row.string("State"));
    }
}

One client can run any number of queries (and commands[2]) over the same authenticated connection — see the client options[3].

Query options

Everything between wql(...) and execute() is optional:

Option Default Meaning
namespace(String) the client's namespace (ROOT\CIMV2 unless set on the builder) The WMI namespace to query.
timeout(Duration) the client's timeout Wall-clock deadline for the whole query with execute(); inactivity timeout with stream(). See Timeouts and Errors[4].
pageSize(int) 32000 WS-Enumeration MaxElements: how many rows the server may return per protocol round trip.
pullTimeout(Duration) server default WS-Enumeration MaxTime: how long the server may hold a single Pull open before answering with the rows it has.
WqlResult events = client.wql("SELECT * FROM Win32_NTLogEvent")
    .namespace("root\\cimv2")
    .pageSize(5000)
    .pullTimeout(Duration.ofSeconds(10))
    .timeout(Duration.ofMinutes(2))
    .execute();

pageSize and pullTimeout matter for very large result sets (for example Windows event logs): a smaller page bounds each response's size, and a pull timeout keeps the server from holding a Pull open past your deadline while it gathers rows.

Streaming the rows

execute() collects the complete result in memory. For very large result sets — Windows event logs, software inventories — end the same request with stream() instead: it returns a lazy java.util.stream.Stream of WqlRow[5]s that yields each row as soon as it is parsed and pulls the next WS-Enumeration page from the server only as the stream advances. Memory stays bounded by one page (pageSize(int)), not by the whole result set.

try (Stream<WqlRow> rows = client.wql("SELECT * FROM Win32_NTLogEvent")
        .pageSize(5000)
        .stream()) {

    rows.filter(row -> "Error".equals(row.string("Type")))
        .limit(100)
        .forEach(this::process);
}

Points to know:

  • Close the stream — use try-with-resources, exactly like Files.lines(...). Closing before the last row tells the server to free the enumeration immediately (WS-Enumeration Release); an exhausted stream cleans up on its own.
  • The stream holds the client's serial connection while open: other operations on the same client wait until it is closed or exhausted (the same contract as a JDBC ResultSet on its connection). Never call the same client while consuming the stream; use a second client.
  • The timeout is an inactivity timeout — the longest silence tolerated from the server between two responses — not an overall deadline: consuming a huge result can take arbitrarily long as long as the server keeps answering. See Timeouts and Errors[4].
  • The stream is sequential and ordered; failures while iterating are reported through the same unchecked exceptions as execute() (see below).

Reading the result

WqlResult[6] is immutable and iterable:

Method Returns Description
columns() List<String> The property (column) names (see Column order and case).
rows() List<WqlRow[5]> The result rows.
size() / isEmpty() int / boolean Row count.
elapsed() java.time.Duration Wall-clock time of the query.

Each WqlRow[5] exposes the instance properties:

Method Returns Description
string(String) String The property value, or null when the row has no such property.
get(String) Object The same value (always a String).
asMap() Map<String, Object> All properties, in server order (unmodifiable).

Property lookup is case-insensitive, matching WMI semantics: row.string("name") and row.string("Name") return the same value.

Values are the text WinRM sends, unconverted: parse numbers, booleans and dates yourself. A WMI NULL comes back as an empty string.

A WMI array property (IPAddress in Win32_NetworkAdapterConfiguration, Capabilities in Win32_DiskDrive, …) comes back as a single string, its elements joined with |: "192.0.2.10|fe80::1". The builder's arraySeparator(String) changes the separator.

There is no trailing separator, and WinRM sends no type information: a one-element array reads like a scalar ("fr-FR" for MUILanguages), and an empty or NULL array is left out of the response, so string() returns null for it (unlike a scalar NULL, an empty string). The instance is still a row, even when that array is the only selected property.

Object paths: __PATH and references

WinRM never returns WMI system properties, except __PATH: when you select it, the client asks for each instance's key properties and builds its object path. A reference property (Antecedent, Dependent, …) gets the same format, so you can match the two:

SELECT __PATH, CurrentClockSpeed FROM Win32_Processor
-- __PATH = Win32_Processor.DeviceID="CPU0"

The path is relative to the namespace of the query, without server or namespace, like the __PATH and references returned by the WMI client. A reference nested in a key (the __PATH of an association like Win32_SessionProcess) stays absolute, with . (the local machine, for WMI) as the server, since WinRM sends no server name. WinRM sends no key types either: every key value is quoted, even a number. The path works as is in an ASSOCIATORS OF {...} or REFERENCES OF {...} query.

Column order and case

These rules apply to WqlResult.columns(); each row's asMap() keeps the server's order, and lookups in it are case-sensitive.

  • When you select explicit properties (SELECT Name, State FROM ...), the columns keep the order of the query and the exact case reported by WMI.
  • With SELECT * FROM ..., the columns are the properties of every row (a row may lack an empty array that others have), in alphabetical order (case-insensitive).
  • If the query returns no rows, the columns are the selected property names in lower case (WMI's own casing cannot be recovered from an empty result set), or an empty list for SELECT *.

Supported WQL syntax

The client validates that the query is a simple SELECT, or an ASSOCIATORS OF or REFERENCES OF query:

SELECT * FROM Win32_OperatingSystem
SELECT Name, State, StartMode FROM Win32_Service
SELECT Name FROM Win32_Process WHERE Name = 'explorer.exe'
ASSOCIATORS OF {Win32_Service.Name='W32Time'} WHERE ResultClass = Win32_Process
REFERENCES OF {Win32_Service.Name='W32Time'}
SELECT DeviceId, Wear FROM ASSOCIATORS OF {MSFT_PhysicalDisk.ObjectId="..."} WHERE ResultClass = MSFT_StorageReliabilityCounter

The grammar is a single SELECT of either * or a comma-separated property list, a FROM clause naming one class or an ASSOCIATORS OF {...} / REFERENCES OF {...} statement, and an optional WHERE clause; the SELECT ... FROM part may be left out of an ASSOCIATORS OF / REFERENCES OF statement. WMI itself does not accept a property list on these statements: the client sends the bare statement, and the property list only sets the result's columns (each row still holds every property of the instance). Line breaks are accepted wherever whitespace is, so queries written as Java text blocks work as is. Anything else, such as event queries (WITHIN), is rejected: an invalid query raises a WqlSyntaxException[7] before anything is sent to the host.

Choosing a namespace

Most Windows classes live under the default ROOT\CIMV2 namespace. To query a different one — for example ROOT\Microsoft\SqlServer or ROOT\WMI — set it per query with namespace(...), or set a client-wide default with namespace(...) on the builder. Both ROOT\WMI and ROOT/WMI are accepted.

Exceptions

execute() and stream() report failures as unchecked WinRMClientException[8]s; see Timeouts and Errors[9] for the full list. An unknown class or namespace raises a WinRMFaultException[10] whose getFaultDetail() carries the WMI error, e.g. WBEM_E_INVALID_CLASS or WBEM_E_INVALID_NAMESPACE.

From the command line

The standalone jar runs queries in the default ROOT\CIMV2 namespace through its wql subcommand, streaming the rows to stdout as JSON Lines — see the Command-Line Client[11] manual.

wql wmi query win32 namespace root cimv2 cim winrm wql wmi windows ntlm kerberos java remote management
Links:
  • [1] apidocs/org/metricshub/winrm/WinRMClient.html
  • [2] commands.html
  • [3] index.html#client-options
  • [4] timeouts-and-errors.html
  • [5] apidocs/org/metricshub/winrm/WqlRow.html
  • [6] apidocs/org/metricshub/winrm/WqlResult.html
  • [7] apidocs/org/metricshub/winrm/exceptions/WqlSyntaxException.html
  • [8] apidocs/org/metricshub/winrm/exceptions/WinRMClientException.html
  • [9] timeouts-and-errors.html#the-exception-surface
  • [10] apidocs/org/metricshub/winrm/exceptions/WinRMFaultException.html
  • [11] cli.html
Searching...
No results.