// PSLWMI.cpp : Implementation of CPSLWMI
#include "stdafx.h"
#include "PSLWMI.h"
#include "PSLTable.h"
#include <Wbemidl.h>
/*
WMI is configurable via UI invoked via command wmimgmt.msc
Using wmimgmt.msc one can change default namespace, security for WMI, do backup/restore;
*/
CPSLWMI::CPSLWMI()
{
m_LastError = 0;
m_sDefaultNamespace = _T("root\\cimv2");
m_DefaultImpersonationLevel = impUnknown;
m_sInstallationDir = _T("");
m_sVersion = _T("");
tstring buffer;
buffer.resize(MAX_FILE_PATH);
ULONG nChars = MAX_FILE_PATH;
CRegKey key;
if(key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\WBEM\\Scripting"), KEY_READ) == ERROR_SUCCESS)
{
if(key.QueryStringValue(_T("Default Namespace"), (LPTSTR)buffer.c_str(), &nChars) == ERROR_SUCCESS)
m_sDefaultNamespace = buffer.c_str();
DWORD dwValue = 0;
if(key.QueryDWORDValue(_T("Default Impersonation Level"), dwValue) == ERROR_SUCCESS)
m_DefaultImpersonationLevel = static_cast<PSLImpersonationLevel>(dwValue);
key.Close();
}
if(key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\WBEM"), KEY_READ) == ERROR_SUCCESS)
{
nChars = MAX_FILE_PATH;
if(key.QueryStringValue(_T("BUILD"), (LPTSTR)buffer.c_str(), &nChars) == ERROR_SUCCESS)
m_sVersion = buffer.c_str();
nChars = MAX_FILE_PATH;
if(key.QueryStringValue(_T("Installation Directory"), (LPTSTR)buffer.c_str(), &nChars) == ERROR_SUCCESS)
{
m_sInstallationDir = buffer.c_str();
if(!_tcsnicmp(m_sInstallationDir, _T("%SystemRoot%"), 12))
{
::GetWindowsDirectory((LPTSTR)buffer.c_str(), MAX_FILE_PATH);
tstring tmp = m_sInstallationDir;
m_sInstallationDir = tmp.replace(0, 12, buffer.c_str()).c_str();
}
}
key.Close();
}
m_pLocator = NULL;
m_pServices = NULL;
::InitializeCriticalSection(&m_csLocator);
::InitializeCriticalSection(&m_csServices);
}
CPSLWMI::~CPSLWMI()
{
::DeleteCriticalSection(&m_csServices);
::DeleteCriticalSection(&m_csLocator);
}
HRESULT CPSLWMI::FinalConstruct()
{
return S_OK;
}
void CPSLWMI::FinalRelease()
{
::EnterCriticalSection(&m_csServices);
if(m_pServices)
{
m_pServices.Release();
m_sServiceName = _T("");
}
::LeaveCriticalSection(&m_csServices);
::EnterCriticalSection(&m_csLocator);
if(m_pLocator)
{
m_pLocator.Release();
m_pLocator = NULL;
}
::LeaveCriticalSection(&m_csLocator);
}
HRESULT CPSLWMI::RaiseWMIException(long lErrorCode)
{
switch(lErrorCode)
{
case WBEM_E_ACCESS_DENIED:
{
SetException(exAccessDenied);
break;
}
case WBEM_E_INVALID_CLASS:
case WBEM_E_INVALID_QUERY:
case WBEM_E_INVALID_PARAMETER:
case WBEM_E_INVALID_NAMESPACE:
case WBEM_E_INVALID_OBJECT:
case WBEM_E_INVALID_QUERY_TYPE:
case WBEM_E_NOT_FOUND:
case E_INVALIDARG:
{
SetException(exInvalidParameter);
break;
}
case WBEM_E_OUT_OF_MEMORY:
case E_OUTOFMEMORY:
{
SetException(exLowMemory);
break;
}
case WBEM_E_FAILED:
case WBEM_E_PROVIDER_FAILURE:
case WBEM_E_TYPE_MISMATCH:
case WBEM_E_INVALID_CONTEXT:
case WBEM_E_NOT_AVAILABLE:
case WBEM_E_CRITICAL_ERROR:
case WBEM_E_NOT_SUPPORTED:
case WBEM_E_INVALID_STREAM:
case WBEM_E_INVALID_SUPERCLASS:
case WBEM_E_PROVIDER_NOT_FOUND:
case WBEM_E_INVALID_PROVIDER_REGISTRATION:
case WBEM_E_PROVIDER_LOAD_FAILURE:
case WBEM_E_INITIALIZATION_FAILURE:
case WBEM_E_TRANSPORT_FAILURE:
case WBEM_E_INVALID_OPERATION:
case WBEM_E_ALREADY_EXISTS:
case WBEM_E_UNEXPECTED:
case WBEM_S_TIMEDOUT:
case WBEM_S_FALSE:
{
SetException(exWMIError);
break;
}
case WBEM_E_SHUTTING_DOWN:
{
// special case, need to re-create interfaces again!
SetException(exWMIError);
break;
}
case DISP_E_BADINDEX:
{
SetException(exIndexOutOfRange);
break;
}
default:
{
SetException(exGeneric);
break;
}
}
m_LastError = lErrorCode;
return GetExitCode();
}
/*
Function GetWQLColumnsFromCSV;
Can parse WQL type of CSV;
Example1: "Col1, Col2"
Example2: "Col1 Col2"
Example3: "Col1;Col2"
Returns true, if at least one column name has been found.
*/
bool CPSLWMI::GetWQLColumnsFromCSV(LPCTSTR sText, vector<tstring> & Columns)
{
tstring src = sText;
LPTSTR start = (LPTSTR)src.c_str();
size_t l = src.length();
LPTSTR end = start + l;
for(size_t i = 0;i < l;i ++)
if(start[i] == '\t' || start[i] == ',' || start[i] == ';' || start[i] == '\n' || start[i] == '\r' || start[i] == '*')
start[i] = ' ';
LPTSTR cols = start;
long nCols = 0; // number of columns found;
while(cols < end)
{
while(cols[0] == ' ' && cols < end)
cols ++;
if(cols == end)
break;
LPCTSTR colStart = cols;
while(cols[0] != ' ' && cols < end)
cols ++;
tstring colName;
colName.resize(cols - colStart);
::_tcsncpy_s((LPTSTR)colName.c_str(), cols - colStart + 1, sText + (colStart - start), cols - colStart);
Columns.push_back(colName);
nCols ++;
}
return (nCols > 0);
}
bool CPSLWMI::ExtractSELECTDetails(LPCTSTR sQuery, tstring & sClassName, vector<tstring> & Columns, bool & bAllCols, bool & bHasSpecials)
{
bAllCols = false;
tstring src = sQuery;
LPTSTR start = (LPTSTR)src.c_str();
::_tcslwr_s(start, src.length() + 1);
size_t l = src.length();
for(size_t i = 0;i < l;i ++)
if(start[i] == '\t' || start[i] == ',')
start[i] = ' ';
LPCTSTR select = ::_tcsstr(start, _T("select "));
LPCTSTR from = ::_tcsstr(start, _T(" from "));
if(!select || !from || select > from)
return false;
from ++;
LPCTSTR columns = select;
while(columns[0] != ' ' && columns < from)
columns ++;
if(columns == from) // Got as far as FROM statement but no columns found;
return false;
int cols = 0; // number of columns found;
while(columns < from)
{
while(columns[0] == ' ' && columns < from)
columns ++;
if(columns == from)
break;
LPCTSTR colStart = columns;
while(columns[0] != ' ' && columns < from)
columns ++;
if(columns == from)
break;
if(colStart[0] == '*')
{
bAllCols = true;
break;
}
if(columns - colStart >= 2 && colStart[0] == '_' && colStart[1] == '_')
bHasSpecials = true;
tstring colName;
colName.resize(columns - colStart);
::_tcsncpy_s((LPTSTR)colName.c_str(), columns - colStart + 1, sQuery + (colStart - start), columns - colStart);
Columns.push_back(colName);
cols ++;
}
if(!cols && !bAllCols)
return false; // no columns found;
LPCTSTR table = from;
while(table[0] && table[0] != ' ')
table ++;
if(!table[0])
return false;
while(table[0] && table[0] == ' ')
table ++;
if(!table[0])
return false;
LPCTSTR end = table;
while(end[0] && end[0] != ' ')
end ++;
::_tcsncpy_s((LPTSTR)src.c_str(), end - table + 1, sQuery + (table - start), end - table);
sClassName = src.c_str();
return true;
}
CComPtr<IWbemLocator> CPSLWMI::GetLocator(long & lErrorCode)
{
CCritSecLock cs(m_csLocator);
if(!m_pLocator)
lErrorCode = m_pLocator.CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER);
return m_pLocator;
}
CComPtr<IWbemServices> CPSLWMI::GetServices(BSTR NameSpace, long & lErrorCode)
{
tstring NS;
if(NameSpace)
NS = NameSpace;
CCritSecLock cs(m_csServices);
if(NS.length() < 1)
NS = m_sDefaultNamespace;
if(m_sServiceName.length() > 0 && !_tcsicmp(m_sServiceName, NS.c_str())) // The same namespace as requested the last time;
return m_pServices;
// New WMI namespace is being requested;
if(m_pServices)
{
m_sServiceName = _T("");
m_pServices.Release();
}
CComPtr<IWbemLocator> pLocator = GetLocator(lErrorCode);
if(pLocator)
{
lErrorCode = pLocator->ConnectServer(_bstr_t(NS.c_str()), NULL, NULL, NULL, 0, NULL, NULL, &m_pServices);
if(lErrorCode == WBEM_S_NO_ERROR)
{
m_sServiceName = NS.c_str();
// Authenticate calls into the interface. Unlikely to ever fail.
::CoSetProxyBlanket(m_pServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
}
}
return m_pServices;
}
CComPtr<IEnumWbemClassObject> CPSLWMI::ExecuteQuery(BSTR NameSpace, LPCTSTR sWQL, long & lErrorCode)
{
CComPtr<IEnumWbemClassObject> pEnumObject;
CComPtr<IWbemServices> pServices = GetServices(NameSpace, lErrorCode);
if(pServices)
lErrorCode = pServices->ExecQuery(_bstr_t(_T("WQL")), _bstr_t(sWQL), WBEM_FLAG_RETURN_IMMEDIATELY|WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumObject);
return pEnumObject;
}
////////////////////////////////////////////////////////////////////////
// Interface Implementation;
////////////////////////////////////////////////////////////////////////
STDMETHODIMP CPSLWMI::get_LastError(long * pValue)
{
PSL_BEGIN
*pValue = m_LastError;
PSL_END
}
STDMETHODIMP CPSLWMI::get_DefaultNamespace(BSTR * pValue)
{
PSL_BEGIN
CCritSecLock cs(m_csServices);
*pValue = m_sDefaultNamespace.copy();
PSL_END
}
STDMETHODIMP CPSLWMI::put_DefaultNamespace(BSTR newValue)
{
PSL_BEGIN
CCritSecLock cs(m_csServices);
m_sDefaultNamespace = newValue;
PSL_END
}
STDMETHODIMP CPSLWMI::get_DefaultImpersonationLevel(PSLImpersonationLevel * pValue)
{
PSL_BEGIN
*pValue = m_DefaultImpersonationLevel;
PSL_END
}
STDMETHODIMP CPSLWMI::get_Version(BSTR * pValue)
{
PSL_BEGIN
*pValue = m_sVersion.copy();
PSL_END
}
STDMETHODIMP CPSLWMI::get_InstallationDir(BSTR * pValue)
{
PSL_BEGIN
*pValue = m_sInstallationDir.copy();
PSL_END
}
STDMETHODIMP CPSLWMI::GetRowValues(BSTR NameSpace, BSTR ClassName, BSTR ValueName, SAFEARRAY ** ppValue)
{
PSL_BEGIN
*ppValue = NULL;
if(!ClassName || !ValueName)
return MakeException(exInvalidParameter);
_bstr_t name(ValueName);
vector<tstring> Columns;
if(!GetWQLColumnsFromCSV(name, Columns) || Columns.size() != 1)
return MakeException(exInvalidParameter);
tstring sQuery;
sQuery.resize(64);
::wsprintf((LPTSTR)sQuery.c_str(), _T("SELECT %s FROM %s"), (LPCTSTR)name, (LPCTSTR)_bstr_t(ClassName));
long lErrorCode = 0;
CComPtr<IEnumWbemClassObject> pEnumObject = ExecuteQuery(NameSpace, _bstr_t(sQuery.c_str()), lErrorCode);
if(!pEnumObject)
return RaiseWMIException(lErrorCode);
vector<_variant_t> values;
do
{
ULONG uCount = 1, uReturned;
CComPtr<IWbemClassObject> pClassObject;
lErrorCode = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
if(lErrorCode != WBEM_S_FALSE && lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
if(lErrorCode == WBEM_S_NO_ERROR)
{
_variant_t v;
lErrorCode = pClassObject->Get(name, 0, &v, 0, 0);
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
values.push_back(v);
v.Clear();
}
}
while(lErrorCode == WBEM_S_NO_ERROR);
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = (ULONG)values.size();
*ppValue = ::SafeArrayCreate(VT_VARIANT, 1, rgsabound); // Create Safe Array;
if(!*ppValue)
return MakeException(exLowMemory);
long Idx = 0;
for(vector<_variant_t>::iterator i = values.begin();i != values.end();i ++)
{
::SafeArrayPutElement(*ppValue, &Idx, &(*i)); // Put string into array;
Idx ++;
i->Clear();
}
PSL_END
}
STDMETHODIMP CPSLWMI::GetColValues(BSTR NameSpace, BSTR ClassName, BSTR ValueNamesCSV, SAFEARRAY ** ppValue)
{
PSL_BEGIN
*ppValue = NULL;
if(!ClassName || !ValueNamesCSV)
return MakeException(exInvalidParameter);
_bstr_t names(ValueNamesCSV);
vector<tstring> Columns;
if(!GetWQLColumnsFromCSV(names, Columns))
return MakeException(exInvalidParameter);
tstring sQuery;
long l = 64 + names.length();
sQuery.resize(l);
::wsprintf((LPTSTR)sQuery.c_str(), _T("SELECT %s FROM %s"), (LPCTSTR)names, (LPCTSTR)_bstr_t(ClassName));
long lErrorCode = 0;
CComPtr<IEnumWbemClassObject> pEnumObject = ExecuteQuery(NameSpace, _bstr_t(sQuery.c_str()), lErrorCode);
if(!pEnumObject)
return RaiseWMIException(lErrorCode);
ULONG uCount = 1, uReturned;
CComPtr<IWbemClassObject> pClassObject;
lErrorCode = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
if(lErrorCode == WBEM_S_FALSE) // no record found;
{
SAFEARRAYBOUND rgsabound[1]; // Safe Array boundaries;
rgsabound[0].lLbound = 0; // Low bound of the dimension is 0;
rgsabound[0].cElements = 0; // Number of elements in the array;
*ppValue = ::SafeArrayCreate(VT_VARIANT, 1, rgsabound); // Create Safe Array;
if(!*ppValue)
return MakeException(exLowMemory);
return S_OK;
}
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
SAFEARRAYBOUND rgsabound[1];
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = (ULONG)Columns.size();
*ppValue = ::SafeArrayCreate(VT_VARIANT, 1, rgsabound);
if(!*ppValue)
return MakeException(exLowMemory);
long Idx = 0;
for(vector<tstring>::iterator i = Columns.begin();i != Columns.end();i ++)
{
_variant_t v;
lErrorCode = pClassObject->Get(i->c_str(), 0, &v, 0, 0);
if(lErrorCode != WBEM_S_NO_ERROR)
{
::SafeArrayDestroy(*ppValue);
*ppValue = NULL;
return RaiseWMIException(lErrorCode);
}
::SafeArrayPutElement(*ppValue, &Idx, &v); // Put string into array;
Idx ++;
v.Clear();
}
PSL_END
}
STDMETHODIMP CPSLWMI::GetColNames(BSTR NameSpace, BSTR ClassName, SAFEARRAY ** ppValue)
{
PSL_BEGIN
*ppValue = NULL;
if(!ClassName)
return MakeException(exInvalidParameter);
long lErrorCode = 0;
CComPtr<IWbemServices> pServices = GetServices(NameSpace, lErrorCode);
if(!pServices)
return RaiseWMIException(lErrorCode);
CComPtr<IWbemClassObject> pClass;
lErrorCode = pServices->GetObject(ClassName, WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, &pClass, NULL);
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
lErrorCode = pClass->GetNames(NULL, WBEM_FLAG_ALWAYS|WBEM_FLAG_NONSYSTEM_ONLY, NULL, ppValue);
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
PSL_END
}
STDMETHODIMP CPSLWMI::GetValue(BSTR NameSpace, BSTR ClassName, BSTR ValueName, VARIANT * pValue)
{
PSL_BEGIN
if(!ClassName || !ValueName)
return MakeException(exInvalidParameter);
tstring sQuery;
sQuery.resize(64);
::wsprintf((LPTSTR)sQuery.c_str(), _T("SELECT %s FROM %s"), (LPCTSTR)_bstr_t(ValueName), (LPCTSTR)_bstr_t(ClassName));
long lErrorCode = 0;
CComPtr<IEnumWbemClassObject> pEnumObject = ExecuteQuery(NameSpace, _bstr_t(sQuery.c_str()), lErrorCode);
if(!pEnumObject)
return RaiseWMIException(lErrorCode);
ULONG uCount = 1, uReturned;
CComPtr<IWbemClassObject> pClassObject;
lErrorCode = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
if(lErrorCode == WBEM_S_FALSE)
{
VARIANT v;
::VariantInit(&v);
v.vt = VT_NULL;
::VariantCopy(pValue, &v);
lErrorCode = WBEM_S_NO_ERROR;
}
else
{
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
lErrorCode = pClassObject->Get(_bstr_t(ValueName), 0, pValue, 0, 0);
}
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
PSL_END
}
STDMETHODIMP CPSLWMI::GetData(BSTR NameSpace, BSTR WQL, IPSLTable ** ppValue)
{
PSL_BEGIN
*ppValue = NULL;
if(!WQL)
return MakeException(exInvalidParameter);
tstring ClassName;
vector<tstring> Columns;
vector<tstring> * pColumns = &Columns;
bool bAllCols = false, bHasSpecials = false;
_bstr_t sQuery = WQL;
if(!ExtractSELECTDetails(sQuery, ClassName, Columns, bAllCols, bHasSpecials))
return MakeException(exInvalidParameter);
if(bAllCols)
pColumns = NULL;
long lErrorCode = 0;
CComPtr<IEnumWbemClassObject> pEnumObject = ExecuteQuery(NameSpace, sQuery, lErrorCode);
if(!pEnumObject)
return RaiseWMIException(lErrorCode);
ULONG uCount = 1, uReturned;
CComPtr<IWbemClassObject> pClassObject;
lErrorCode = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
if(lErrorCode == WBEM_S_FALSE) // No records found;
{
// We need to get list of columns in a different way now;
CComPtr<IWbemServices> pServices = GetServices(NameSpace, lErrorCode);
if(pServices)
{
CComPtr<IWbemClassObject> pClass;
lErrorCode = pServices->GetObject(_bstr_t(ClassName.c_str()), WBEM_FLAG_RETURN_WBEM_COMPLETE, NULL, &pClass, NULL);
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
CComObject<CPSLTable> * pTable = NULL;
CComObject<CPSLTable>::CreateInstance(&pTable);
pTable->AddRef();
if(pTable->Initialize(pClass, lErrorCode, pColumns, bHasSpecials))
{
long nCols;
pTable->get_nCols(&nCols);
if(nCols < 1)
{
pTable->Release();
return MakeException(exInvalidParameter);
}
*ppValue = CComPtr<IPSLTable>(pTable);
}
else
pTable->Release();
}
else
return RaiseWMIException(lErrorCode);
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
return S_OK;
}
if(lErrorCode != WBEM_S_NO_ERROR)
return RaiseWMIException(lErrorCode);
CComObject<CPSLTable> * pTable = NULL;
CComObject<CPSLTable>::CreateInstance(&pTable);
pTable->AddRef();
bool bInit = pTable->Initialize(pClassObject, lErrorCode, pColumns, bHasSpecials);
long lCols = 0;
if(bInit)
pTable->get_nCols(&lCols);
if(!bInit || !lCols)
{
pTable->Release();
if(!bInit)
return RaiseWMIException(lErrorCode);
return MakeException(exInvalidParameter);
}
while(lErrorCode == WBEM_S_NO_ERROR)
{
if(!pTable->AddRow(pClassObject, lErrorCode))
{
pTable->Release();
return RaiseWMIException(lErrorCode);
}
pClassObject.Release();
lErrorCode = pEnumObject->Next(WBEM_INFINITE, uCount, &pClassObject, &uReturned);
}
if(lErrorCode == WBEM_S_FALSE) // Finished because no more elements left;
*ppValue = CComPtr<IPSLTable>(pTable);
else
pTable->Release(); // Terminated because of an error;
if(lErrorCode != WBEM_S_FALSE) // Something went wrong there!
return RaiseWMIException(lErrorCode);
PSL_END
}