Wasin is a modern C++ library designed to simplify interactions with the Windows Management Instrumentation (WMI) API. It provides a high-level, easy-to-use interface for executing WMI queries, with a focus on asynchronous operations and performance. The name "Wasin" is derived from WMI Asyncronous.
- ✅ Asynchronous by Default: Perform multiple WMI queries concurrently without blocking, significantly improving performance for applications that require real-time system information.
- ✅ Synchronous Support: Offers a familiar synchronous query execution model for simpler use cases.
- ✅ Built-in Caching: Automatically caches query results to deliver near-instantaneous responses for repeated queries, with a configurable Time-to-Live (TTL).
- ✅ Simplified API: Abstracts away the complexities of the COM interface, providing a clean and modern C++ experience.
- ✅ Type-Safe Property Retrieval: Easily and safely convert WMI properties to C++ types.
- ✅ Automatic COM Management: Handles COM initialization and uninitialization automatically.
- ✅ Shared Instance: Provides a convenient singleton-like interface for accessing a shared WMI connection and cache.
Wasin's asynchronous capabilities offer a significant performance advantage, making it nearly 2.8x faster than traditional synchronous WMI queries in our tests. The table below shows a side-by-side comparison of executing 17 WMI queries using both methods. While the synchronous execution time is the sum of all individual queries, the asynchronous total is less than the wait time of any single query because they run concurrently.
| Query | Synchronous (ms) | Asynchronous (Wait Time ms) |
|---|---|---|
| Large Processes | 37 | 93 |
| Disk Drives | 1 | 94 |
| Processors | 1 | 94 |
| Memory Info | 3 | 94 |
| Running Services | 40 | 94 |
| Physical Disks | 2 | 94 |
| OS Information | 3 | 94 |
| Video Controllers | 7 | 92 |
| Network Adapters | 62 | 91 |
| USB Devices | 50 | 91 |
| User Accounts | 3 | 89 |
| System Drivers | 68 | 98 |
| Shared Folders | 1 | 97 |
| Environment Vars | 7 | 96 |
| Computer System | 0 | 97 |
| Scheduled Jobs | 0 | 95 |
| Processor Details | 1 | 95 |
| Total / Average | 296ms | ~94ms |
Note on Asynchronous Wait Times In the asynchronous example, the wait times for each query are very similar. This is because all queries are launched at the same time and run concurrently in the background. The total execution time reflects the time it takes for the longest query to complete, while the individual wait times are measured from the start of the execution until each specific query is finished. This is in contrast to the synchronous model, where each query runs sequentially, and the total time is the sum of all individual query times.
wasin is a header-only library. To use it in your project, simply include the wasin.hxx header file.
#include <wasin/wasin.hxx>Create an instance of the wasin::Interface. You can create a local instance or use a shared global instance.
Local Instance:
// Create a new WMI interface for the "cimv2" namespace, with caching enabled by default.
auto wmi = wasin::Interface::Create("cimv2");
// You can also disable the cache for performance testing or specific use cases.
auto wmi_no_cache = wasin::Interface::Create("cimv2", false);Shared Instance:
// Get a shared global instance of the WMI interface.
// This instance is thread-safe and shares its cache.
auto shared_wmi = wasin::Interface::GetSharedInstance("cimv2");Asynchronous queries are the core feature of wasin. They allow you to execute long-running WMI queries without blocking your main thread.
The ExecuteQueryAsync method returns an AsyncQueryResult object, which is a std::future<QueryResult>. You can use this future to get the QueryResult when it's ready.
#include <iostream>
#include <vector>
#include <future>
#include <wasin/wasin.hxx>
int main() {
try {
auto wmi = wasin::Interface::Create("cimv2");
std::vector<std::wstring> queries = {
L"SELECT Name, ProcessId FROM Win32_Process",
L"SELECT Name, Size FROM Win32_LogicalDisk"
};
std::vector<wasin::AsyncQueryResult> async_results;
for (const auto& query : queries) {
async_results.push_back(wmi->ExecuteQueryAsync(query));
}
std::cout << "Waiting for results..." << std::endl;
for (auto& async_result : async_results) {
wasin::QueryResult result = async_result.Get();
std::cout << "Query returned " << result.Count() << " objects." << std::endl;
}
} catch (const wasin::Exception& e) {
std::cerr << "WMI Error: " << e.what() << std::endl;
}
return 0;
}For simpler scenarios, you can use the ExecuteQuery method to perform a synchronous query. This method blocks until the query is complete and returns a QueryResult object directly.
#include <iostream>
#include <wasin/wasin.hxx>
int main() {
try {
auto wmi = wasin::Interface::Create("cimv2");
wasin::QueryResult result = wmi->ExecuteQuery(L"SELECT Name FROM Win32_Processor");
for (const auto& obj : result) {
auto name = obj.GetProperty(L"Name");
if (name) {
std::wcout << L"Processor Name: " << static_cast<const wchar_t*>(bstr_t(*name)) << std::endl;
}
}
} catch (const wasin::Exception& e) {
std::cerr << "WMI Error: " << e.what() << std::endl;
}
return 0;
}Both synchronous and asynchronous queries return a QueryResult object (the asynchronous version returns it through a std::future). The QueryResult contains a collection of Object instances, each representing a WMI object.
You can iterate over the QueryResult to access individual Objects.
wasin::QueryResult result = wmi->ExecuteQuery(L"SELECT * FROM Win32_Service");
std::cout << "Found " << result.Count() << " services." << std::endl;
for (const wasin::Object& service : result) {
// ...
}Each Object has a GetProperty method to retrieve the value of a specific property. You can retrieve it as a variant_t or specify the desired C++ type.
// Get property as a variant_t
auto name_variant = service.GetProperty(L"Name");
if (name_variant) {
std::wcout << L"Service Name: " << static_cast<const wchar_t*>(bstr_t(*name_variant)) << std::endl;
}
// Get property and convert to a specific type
auto start_mode = service.GetProperty<std::wstring>(L"StartMode");
if (start_mode) {
std::wcout << L"Start Mode: " << *start_mode << std::endl;
}
auto process_id = service.GetProperty<uint32_t>(L"ProcessId");
if (process_id) {
std::cout << "Process ID: " << *process_id << std::endl;
}Wasin includes a caching mechanism that delivers truly instant results for repeated queries. When caching is enabled (the default), the result of a WMI query is stored in memory. If the same query is executed again within the Time-to-Live (TTL, 30 seconds by default), the result is returned directly from the cache, completely bypassing the expensive WMI call. This behavior is confirmed by the implementation in wasin.hxx, where ExecuteQuery first checks for a valid cached result before proceeding to query WMI.
The cache is enabled by default when creating a wasin::Interface instance. You can explicitly disable it if needed.
See examples/cache_example.cpp and examples/cache_async_example.cpp for a demonstration of the performance benefits of caching.
The examples directory contains several files that demonstrate the features of the wasin library:
sync_example.cpp: Demonstrates how to perform synchronous WMI queries.async_example.cpp: Shows how to use the asynchronous API to run multiple queries concurrently.cache_example.cpp: Illustrates the performance benefits of the caching mechanism with synchronous queries.cache_async_example.cpp: A comprehensive example that showcases the power of combining asynchronous queries with caching.
To build and run the examples, you will need a C++ compiler that supports C++17 and the Windows SDK.
