Currently we are trying to start a Word process from our local machine on a Remote-Office-PC and the process is started on the Remote-Office-PC with the correct user, but the problem is that this process cannot access network directories from the domain to which this user belongs. For communication between our PC and the Remote-Office-PC we use DCOM objects. Our Code looks as follows:
void testSetup(const OLECHAR* filename)
{
// Call CoInitialize or CoInitializeEx
CoInitialize(NULL);
// Create an Instance of Word.Application on RemoteOfficePC with CoCreateInstanceEx
COAUTHINFO ca = {0};
ca.dwAuthnSvc = RPC_C_AUTHN_WINNT;
ca.dwAuthzSvc = RPC_C_AUTHZ_NONE;
ca.dwAuthnLevel = RPC_C_AUTHN_LEVEL_PKT_PRIVACY;
ca.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
COAUTHIDENTITY
authIdentity{(USHORT*)L"username",
8,
(USHORT*)L"domain",
8,
(USHORT*)L"************",
12,
SEC_WINNT_AUTH_IDENTITY_UNICODE};
ca.pAuthIdentityData = &authIdentity;
COSERVERINFO serverInfo = {0};
serverInfo.pwszName = (LPWSTR)L"RemoteOfficePC";
serverInfo.pAuthInfo = &ca;
serverInfo.dwReserved1 = 0;
serverInfo.dwReserved2 = 0;
MULTI_QI qi = {&IID_IDispatch, NULL, S_OK};
CLSID clsId;
CLSIDFromString(L"{000209FF-0000-0000-C000-000000000046}", &clsId); // CLSID from Word.Application
CoCreateInstanceEx(clsId, NULL, CLSCTX_REMOTE_SERVER, &serverInfo, 1, &qi);
IDispatch* pID = (IDispatch*)qi.pItf;
// Call CoSetProxyBlanket to set the authentication information
// Also tried it without the following call but with the same result
if (false)
CoSetProxyBlanket(pID,
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
&authIdentity,
EOAC_NONE); // proxy capabilities
// get Interface for Documents of Word.Application
DISPID dispId = 6; // Documents object (Word)
VARIANT vResult;
VariantInit(&vResult);
DISPPARAMS dpNoArgs = {NULL, NULL, 0, 0};
pID->Invoke(dispId,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET,
&dpNoArgs,
&vResult,
NULL,
NULL);
pID = vResult.pdispVal;
// Call CoSetProxyBlanket to set the authentication information
// Also tried it without the following call but with the same result
if (false)
CoSetProxyBlanket(pID,
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
&authIdentity,
EOAC_NONE); // proxy capabilities
// Call Documents.Open method of the above Documents-Object
// Documents.Open FileName:=filename, ReadOnly:=True
VARIANT vArgs[16];
DISPPARAMS dParams = {nullptr, nullptr, 0, 0};
for (int i = 0; i < 16; ++i)
{
VariantInit(&vArgs[i]);
}
vArgs[0].vt = VT_BOOL;
vArgs[2].vt = VT_BSTR;
vArgs[0].boolVal = VARIANT_TRUE;
vArgs[2].bstrVal = SysAllocString(filename);
dParams.cArgs = 3;
dParams.cNamedArgs = 0;
dParams.rgvarg = vArgs;
dispId = 19; // Documents.Open method (Word)
EXCEPINFO exInfo;
unsigned int mIndex;
VARIANT result;
pID->Invoke(dispId,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_METHOD,
&dParams,
&result,
&exInfo,
&mIndex);
// result.pdispVal should be a Document object
trAssertNotNull(result.pdispVal);
}
Our tests are as follows:
//passes
void openLocalFile() { testSetup(L"C:\\temp\\Test.docx"); }
//passes
void openRemoteFile() { testSetup(L"\\\\RemoteOfficePC\\temp\\Test.docx"); }
//fails
void openFileFromDifferentDomain()
{
// same domain to which the specified user belongs
testSetup(L"\\\\externalFolder\\Test.docx");
}
Note that the specified user has all start- and activate-permissions, all access-permissions and all configuration-permissions at the DCOM-configuration for "Microsoft Word 97-2003-Document" Component.
Moreover, the specified user can access the external files when he is logged on to the Remote-Office-PC manually.
If we use a local path of the Remote-Office-PC or a network path pointing to a local file of the Remote-Office-PC, we find a Document object in result.
If we use a path pointing to a location in the domain to which this user belongs, result is empty.
Should we call something differently?
Do we miss something?
Which security-settings may interfere with what we attempt to do?
Related
I am trying to query to Win32_Tpm class of WMI from remote machine.but It's failing with HRESULT 0x80041013 & Description: Provider load failure.
Is it possible to access MicrosoftTPM namespace remotely?
Following is the code,
hres = pSvc ->CreateInstanceEnum(
bstr_t(Class),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&Enumerator);
hres = CoSetProxyBlanket(
Enumerator, // Indicates the proxy to set
RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx
COLE_DEFAULT_PRINCIPAL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
mUserAcct, // client identity
EOAC_NONE // proxy capabilities
);
hres = Enumerator->Next(WBEM_INFINITE, 1, &TPMClass, &uReturn);
//Here I am failing with hres = 0x80041013 and uReturn = 0, value of TPMClass is NULL after execution of above statement also.
Bellow is the detailed code,
IEnumWbemClassObject* Enumerator;
IWbemClassObject* TPMClass = NULL;
ULONG uReturn = 0;
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
cout << "Failed to initialize COM library. Error code = 0x"
<< hex << hres << endl;
return 1; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IDENTIFY, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres))
{
cout << "Failed to initialize security. Error code = 0x"
<< hex << hres << endl;
CoUninitialize();
return 1; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
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;
CoUninitialize();
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(
_bstr_t(L"\\\\REMOTE\\root\\cimv2\\Security\\MicrosoftTpm"),
_bstr_t(useToken ? NULL : pszName), // User name
_bstr_t(useToken ? NULL : pszPwd), // User password
NULL, // Locale
NULL, // Security flags
_bstr_t(useNTLM ? NULL : pszAuthority),// Authority
NULL, // Context object
&pSvc // IWbemServices proxy
);
COAUTHIDENTITY *userAcct = NULL;
COAUTHIDENTITY authIdent;
//Created COAUTHIDENTITY with internal function
userAcct = &authIdent;
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx
COLE_DEFAULT_PRINCIPAL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
userAcct, // client identity
EOAC_NONE // proxy capabilities
);
hres = pSvc ->CreateInstanceEnum(
bstr_t(Class),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&Enumerator);
hres = CoSetProxyBlanket(
Enumerator, // Indicates the proxy to set
RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx
COLE_DEFAULT_PRINCIPAL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
mUserAcct, // client identity
EOAC_NONE // proxy capabilities
);
//Here I am failing , value of TPMClass is NULL after execution of following statement also.
hres = Enumerator->Next(WBEM_INFINITE, 1, &TPMClass, &uReturn);
Above class name is L"Win32_Tpm" in function CreateInstanceEnum().
I am able to get instance while accessing from local machine but it's failing while accessing from remote machine.
Note: I have assigned proper permissions to user on machine for remote.
I am wanting to learn how to get asset tag and serial number from the bios of a local machine using c++. I have been searching online for almost an hour now and all I have come up with so far is a few script written in vb.
I am working on a Windows 7 computer, but would like it to work with Windows 8 as well.
You can use WMI
An example function would be something like the following source which I put together to obtain the serial number for a PC and it seems to work quite nicely. There is a fall back to pull a serial number from the Windows Registry however just ignore that as it is for a specialized environment.
# pragma comment(lib, "wbemuuid.lib")
static SHORT CDeviceConfigCheckSerialNumber (TCHAR *tcsSerialNo)
{
HRESULT hres;
IWbemLocator *pLoc = NULL;
*tcsSerialNo = 0; // initialze return string to empty string
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
return 1; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
pLoc->Release();
return 2; // Program has failed.
}
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return 3; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
// bstr_t("SELECT * FROM Win32_SystemEnclosure"),
// bstr_t("SELECT * FROM Win32_BaseBoard"),
bstr_t("SELECT * FROM Win32_BIOS"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
pSvc->Release();
pLoc->Release();
return 4; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject *pclsObj;
ULONG uReturn = 0;
SHORT sRetStatus = -100;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
if (!FAILED(hres)) {
switch (vtProp.vt) {
case VT_BSTR:
_tcscpy (tcsSerialNo, vtProp.bstrVal);
sRetStatus = 0;
break;
}
}
VariantClear(&vtProp);
pclsObj->Release();
}
pEnumerator->Release();
// Cleanup
// ========
pSvc->Release();
pLoc->Release();
// if the search for a serial number in the BIOS did not provide
// something then lets see if the NCR Retail Systems Manager has
// put a serial number into the Windows Registry.
if (sRetStatus != 0) {
ULONG ulCount = 0;
TCHAR lpszValue[256] = {0};
ScfGetRsmValue (L"SerialNumber", &ulCount, lpszValue);
if (ulCount > 0) {
_tcscpy (tcsSerialNo, lpszValue);
sRetStatus = 0;
}
}
return sRetStatus;
}
I need to get all processes for current user. Currently I have a function implemented using WMI that gets all active processes by All Users. I need to somehow get all processes by Current User.
If you go to Task Manager in Windows, Details tab, you will see all processes by All Users. But say you are in Visual Studio and you go to Debug/Attach To Process, this shows all processes by Current User. That's the list that I need.
Here is my solution of All Processes by All Users:
CComPtr<IEnumWbemClassObject> pEnumerator;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_Process"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
Either a WMI or Win32 solution will be good enough. Thank you!!
OK, there is now short way. But here is the working solution if anyone is interested:
std::wstring GetProcessUserName(DWORD dwProcessId)
{
HANDLE processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
if (processHandle != NULL)
{
HANDLE tokenHandle;
if (OpenProcessToken(processHandle, TOKEN_READ, &tokenHandle))
{
TOKEN_USER tokenUser;
ZeroMemory(&tokenUser, sizeof(TOKEN_USER));
DWORD tokenUserLength = 0;
PTOKEN_USER pTokenUser;
GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS::TokenUser, NULL,
0, &tokenUserLength);
pTokenUser = (PTOKEN_USER) new BYTE[tokenUserLength];
if (GetTokenInformation(tokenHandle, TOKEN_INFORMATION_CLASS::TokenUser, pTokenUser, tokenUserLength, &tokenUserLength))
{
TCHAR szUserName[_MAX_PATH];
DWORD dwUserNameLength = _MAX_PATH;
TCHAR szDomainName[_MAX_PATH];
DWORD dwDomainNameLength = _MAX_PATH;
SID_NAME_USE sidNameUse;
LookupAccountSid(NULL, pTokenUser->User.Sid, szUserName, &dwUserNameLength, szDomainName, &dwDomainNameLength, &sidNameUse);
delete pTokenUser;
return std::wstring(szUserName);
}
}
}
return std::wstring();
}
std::unordered_map<std::wstring, std::vector<DWORD>> GetAllRunningProcessesForCurrentUser()
{
std::unordered_map<std::wstring, std::vector<DWORD>> processHash;
HRESULT hres;
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
return processHash;
}
do {
// Obtain the initial locator to WMI -------------------------
CComPtr<IWbemLocator> pLoc;
hres = pLoc.CoCreateInstance(CLSID_WbemLocator);
if (FAILED(hres))
{
break;
}
// Connect to WMI through the IWbemLocator::ConnectServer method
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
CComPtr<IWbemServices> pSvc;
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres))
{
break;
}
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
break;
}
// Use the IWbemServices pointer to make requests of WMI ----
CComPtr<IEnumWbemClassObject> pEnumerator;
hres = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("SELECT * FROM Win32_Process"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hres))
{
break;
}
// Get the data from the query in step 6 -------------------
CComPtr<IWbemClassObject> pclsObj;
while (pEnumerator)
{
ULONG uReturn = 0;
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn)
{
break;
}
VARIANT vtProp;
VARIANT vtProp2;
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
hr = pclsObj->Get(L"ProcessId", 0, &vtProp2, NULL, NULL);
auto userName = GetProcessUserName(vtProp2.intVal);
TCHAR szActiveUserName[_MAX_PATH];
DWORD dwActiveUserNameLength = _MAX_PATH;
GetUserName(szActiveUserName, &dwActiveUserNameLength);
if (_tcscmp(userName.c_str(), szActiveUserName) == 0)
{
processHash[vtProp.bstrVal].push_back(vtProp2.intVal);
}
VariantClear(&vtProp2);
VariantClear(&vtProp);
pclsObj.Release();
}
} while (false);
CoUninitialize();
return processHash;
}
I have the following serial ports listed in my devicemanager:
COM3
COM4 (BT)
COM5 (BT)
COM6 (GlobeTrotter MO67xx - Control Interface)
COM7 (GlobeTrotter MO67xx - GPS Control Interface)
COM8 (GlobeTrotter MO67xx - GPS Data Interface)
COM9 (GlobeTrotter MO67xx - Diagnostics Interface)
COM11 (USB Serial Port)
COM12 (USB Serial Port)
COM45 (SUNIX COM Port)
COM46 (SUNIX COM Port)
The SUNIX COM ports are connected via an internal PCI-Card.
The USB Serial Port is connected via USB (FDTI-chip)
The GlobeTrotter ports are from a GlobeTrotter device connected via USB. There are also a modem, a USB-device and a network device listed for this modem.
So I have several different sources of serial ports.
All I want to do is to get a list containing all those ports using WMI.
For my tests I am using WMI Code Creator
Test 1:
root\CIMV2; Query: SELECT * FROM Win32_SerialPort only returns the following serial ports:
COM3
COM4
COM5
Test 2:
root\WMI; Query: SELECT * FROM MSSerial_PortName only returns the following serial ports:
COM3
COM11
COM12
COM45
COM45
How can I get a complete list of serial ports?
I found the solution.
The following query (root\CIMV2) gets the requested results:
SELECT * FROM Win32_PnPEntity WHERE ClassGuid="{4d36e978-e325-11ce-bfc1-08002be10318}"
Update
This answer is pretty old now. Ehen I asked it I still had to consider WinXP and was using Windows7.
Since I don't deal with serial ports any more, I can't give any new information on that issue. At that time this solution reported all ports that the devicemanager was showing. But I know listing serial ports is not that easy so this answer might not be correct in all scenarios.
In my case, I have physical serial ports, USB serial ports, and com0com virtual serial ports. I need both the full names and COM port addresses.
The query suggested in this answer does not find com0com ports. The query suggested in this answer requires Administrator priviledges.
SELECT * FROM Win32_PnPEntity find all devices. It returns physical devices like this, and address can be parsed from Caption:
Serial Port for Barcode Scanner (COM13)
However, for com0com ports Caption is like this (no address):
com0com - serial port emulator
SELECT * FROM Win32_SerialPort returns addresses (DeviceID), as well as full names (Name). However, it only finds physical serial ports and com0com ports, not USB serial ports.
So in the end, I need two WMI calls: SELECT * FROM Win32_SerialPort (address is DeviceID) and SELECT * FROM Win32_PnPEntity WHERE Name LIKE '%(COM%' (address can be parsed from Caption). I have narrowed down the Win32_PnPEntity call, because it only needs to find devices that were not found in the first call.
This C++ code can be used to find all serial ports:
// Return list of serial ports as (number, name)
std::map<int, std::wstring> enumerateSerialPorts()
{
std::map<int, std::wstring> result;
HRESULT hres;
hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
if (SUCCEEDED(hres) || hres == RPC_E_CHANGED_MODE) {
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (SUCCEEDED(hres) || hres == RPC_E_TOO_LATE) {
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (SUCCEEDED(hres)) {
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(
bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (SUCCEEDED(hres)) {
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (SUCCEEDED(hres)) {
// Use Win32_PnPEntity to find actual serial ports and USB-SerialPort devices
// This is done first, because it also finds some com0com devices, but names are worse
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT Name FROM Win32_PnPEntity WHERE Name LIKE '%(COM%'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp;
pclsObj[jj]->Get(L"Name", 0, &vtProp, 0, 0);
// Name should be for example "Serial Port for Barcode Scanner (COM13)"
const std::wstring deviceName = vtProp.bstrVal;
const std::wstring prefix = L"(COM";
size_t ind = deviceName.find(prefix);
if (ind != std::wstring::npos) {
std::wstring nbr;
for (size_t i = ind + prefix.length();
i < deviceName.length() && isdigit(deviceName[i]); i++)
{
nbr += deviceName[i];
}
try {
const int portNumber = boost::lexical_cast<int>(nbr);
result[portNumber] = deviceName;
}
catch (...) {}
}
VariantClear(&vtProp);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
// Use Win32_SerialPort to find physical ports and com0com virtual ports
// This is more reliable, because address doesn't have to be parsed from the name
pEnumerator = NULL;
hres = pSvc->ExecQuery(
bstr_t(L"WQL"),
bstr_t(L"SELECT DeviceID, Name FROM Win32_SerialPort"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (SUCCEEDED(hres)) {
constexpr size_t max_ports = 30;
IWbemClassObject *pclsObj[max_ports] = {};
ULONG uReturn = 0;
do {
hres = pEnumerator->Next(WBEM_INFINITE, max_ports, pclsObj, &uReturn);
if (SUCCEEDED(hres)) {
for (ULONG jj = 0; jj < uReturn; jj++) {
VARIANT vtProp1, vtProp2;
pclsObj[jj]->Get(L"DeviceID", 0, &vtProp1, 0, 0);
pclsObj[jj]->Get(L"Name", 0, &vtProp2, 0, 0);
const std::wstring deviceID = vtProp1.bstrVal;
if (deviceID.substr(0, 3) == L"COM") {
const int portNumber = boost::lexical_cast<int>(deviceID.substr(3));
const std::wstring deviceName = vtProp2.bstrVal;
result[portNumber] = deviceName;
}
VariantClear(&vtProp1);
VariantClear(&vtProp2);
pclsObj[jj]->Release();
}
}
} while (hres == WBEM_S_NO_ERROR);
pEnumerator->Release();
}
}
pSvc->Release();
}
pLoc->Release();
}
}
CoUninitialize();
}
if (FAILED(hres)) {
std::stringstream ss;
ss << "Enumerating serial ports failed. Error code: " << int(hres);
throw std::runtime_error(ss.str());
}
return result;
}
The Win32_SerialPort class used in this article reports the physical com ports, if you wanna enumerate all the serial ports including the USB-Serial/COM ports, you must use the MSSerial_PortName class located in the root\wmi namespace.
Also try these classes located in the same namespace
MSSerial_CommInfo
MSSerial_CommProperties
MSSerial_HardwareConfiguration
MSSerial_PerformanceInformation
Note : If you want to know the properties and methods of this class you can use the WMI Delphi Code Creator.
I had a similar issues trying to have an application locate the COM port for a USB Serial device.
By using the scope \\localhost\root\CIMV2 for the query SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0, the application was able to find COM ports via each returned object's caption or locate the exact port by checking the caption for the device name.
ManagementObjectSearcher comPortSearcher = new ManagementObjectSearcher(#"\\localhost\root\CIMV2", "SELECT * FROM Win32_PnPEntity WHERE ConfigManagerErrorCode = 0");
using (comPortSearcher)
{
string caption = null;
foreach (ManagementObject obj in comPortSearcher.Get())
{
if (obj != null)
{
object captionObj = obj["Caption"];
if (captionObj != null)
{
caption = captionObj.ToString();
if (caption.Contains("CH340"))
{
_currentSerialSettings.PortName = caption.Substring(caption.LastIndexOf("(COM")).Replace("(", string.Empty).Replace(")", string.Empty);
}
}
}
}
}
The parsing code was found at [C#] How to programmatically find a COM port by friendly name
I'm not sure why am I getting this strange result. Can someone please shed some light on this?
I'm using WMI calls from a C++ program to export the 'Application' part of the Windows Event Log.
This is done from a local service and the code works fine under Windows 7. The problem happens when I run it on Windows XP. For some weird reason the ExecMethod() of the WMI interface returns HRESULT=0x80041003, which is Access denied.
But if I put the exact same code into a simple user process and run it from there, everything works great. How could that be, the code fails to run from a more privileged local service but works from a simple user process?
PS. I'd appreciate any ideas because I've been working on this for several days to no avail....
PS2. I enabled the following privileges (like if I need to do this for the local service) and that still didn't help:
SE_SECURITY_NAME
SE_BACKUP_NAME
EDIT: I guess adding a sample code wouldn't hurt. (Sorry for the long chunk, but this darn WMI/COM isn't a beauty either....) I marked the spot where I get an error below:
// Initialize COM. ------------------------------------------
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if(SUCCEEDED(hr))
{
// Set general COM security levels --------------------------
// Note: If you are using Windows 2000, you need to specify -
// the default authentication credentials for a user by using
// a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----
// parameter of CoInitializeSecurity ------------------------
hr = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if(SUCCEEDED(hr))
{
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hr = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if(SUCCEEDED(hr))
{
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hr = pLoc->ConnectServer(
_bstr_t(L"\\\\.\\ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if(SUCCEEDED(hr))
{
// Set security levels on the proxy -------------------------
hr = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if(SUCCEEDED(hr))
{
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
hr = pSvc->ExecQuery(
bstr_t("WQL"),
bstr_t("Select * from Win32_NTEventLogFile Where LogFileName='Application'"),
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if(SUCCEEDED(hr))
{
IWbemClassObject *pclsObj = NULL;
int nCnt = -1;
//Go through all results
while (pEnumerator)
{
ULONG uReturn = 0;
hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
//Go to next iteration
nCnt++;
// Get a reference to the Win32_Printer class so we can find
// the RenamePrinter method. This lets us create an object
// representing the input parameter block to be passed to the
// method when we call it.
IWbemClassObject *pNTEventLogFile = NULL;
IWbemClassObject *params = NULL;
IWbemClassObject *paramsInst = NULL;
hr = pSvc->GetObject( _bstr_t( L"Win32_NTEventLogFile" ), 0, NULL,
&pNTEventLogFile, NULL );
if(SUCCEEDED(hr))
{
hr = pNTEventLogFile->GetMethod( _bstr_t( "BackupEventLog" ), 0, ¶ms,
NULL );
if(SUCCEEDED(hr))
{
hr = params->SpawnInstance( 0, ¶msInst );
if(SUCCEEDED(hr))
{
// Now that we've got an instance representing the input
// parameters, we can fill in the parameter values
_bstr_t paramValue( L"C:\\Users\\UserName\\Documents\\application.evt" );
VARIANT paramVt;
paramVt.vt = VT_BSTR;
paramVt.bstrVal = paramValue;
hr = paramsInst->Put( L"ArchiveFileName", 0, ¶mVt, NULL );
if(SUCCEEDED(hr))
{
// Get the "this" pointer to our object instance so that we
// can call the RenamePrinter method on it
CIMTYPE type;
LONG flavor;
VARIANT var;
hr = pclsObj->Get( L"__PATH", 0, &var, &type, &flavor );
if(SUCCEEDED(hr))
{
// Execute the RenamePrinter method on our object instance
IWbemClassObject *results = NULL;
hr = pSvc->ExecMethod( var.bstrVal, _bstr_t( L"BackupEventLog" ), 0,
NULL, paramsInst, &results, NULL );
**///////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////// THIS IS WHERE hr = 0x80041003 or wbemErrAccessDenied
//////////////////////////////////////////////// only when this code is run from a local service
//////////////////////////////////////////////// on a Windows XP machine, but if I run the exact same
//////////////////////////////////////////////// code from a user process on XP machine, it works!
//////////////////////////////////////////////// Note that this works fine on Windows Vista/7 in any configuration.
///////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////**
if(SUCCEEDED(hr))
{
//Get result code from the BackupEventLog method
VARIANT vtProp;
hr = results->Get(L"ReturnValue", 0, &vtProp, 0, 0);
if(SUCCEEDED(hr))
{
if(vtProp.vt == VT_I4)
{
//Check
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384808(v=vs.85).aspx
//0 = Success
//8 = Privilege missing
//21 = Invalid parameter
//80 = Archive file name already exists. This value is returned starting with Windows Vista.
int nResV = vtProp.intVal;
//_tprintf(_T("result2 : %d\n"), nResV);
}
else
{
//Error
}
//Free
VariantClear(&vtProp);
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(results)
{
results->Release();
results = NULL;
}
//Clear
VariantClear(&var);
}
else
{
//Error
}
//Clear
VariantClear(¶mVt);
}
else
{
//Error
}
}
else
{
//Error
}
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(pNTEventLogFile)
{
pNTEventLogFile->Release();
pNTEventLogFile = NULL;
}
if(params)
{
params->Release();
params = NULL;
}
if(paramsInst)
{
paramsInst->Release();
paramsInst = NULL;
}
if(pclsObj)
{
pclsObj->Release();
pclsObj = NULL;
}
}
//Free
if(pclsObj)
{
pclsObj->Release();
pclsObj = NULL;
}
}
else
{
//Error
}
//Free
if(pEnumerator)
{
pEnumerator->Release();
pEnumerator = NULL;
}
}
else
{
//Error
}
}
else
{
//Error
}
//Free
if(pSvc)
{
pSvc->Release();
pSvc = NULL;
}
}
else
{
//Error
}
//Free
if(pLoc)
{
pLoc->Release();
pLoc = NULL;
}
}
else
{
//Error
}
//Uninit
CoUninitialize();
}
else
{
//Error
}
I think I got it... For anyone else who doesn't want to waste 3 days looking for an answer here it is: on Windows XP replace RPC_C_IMP_LEVEL_IMPERSONATE with RPC_C_IMP_LEVEL_DELEGATE in the calls to CoInitializeSecurity() and CoSetProxyBlanket(). I don't know what exactly it does, but it makes the code above work! Yay!!!