PdhEnumObjectsW issue - c++

I have an issue with enumerating performance counter objects. My code worked well for a few years, but recently I have found it started to fail to get counter objects with the following error:
PDH_CSTATUS_NO_MACHINE The path did not contain a computer name, and the function was unable to retrieve the local computer name.
DWORD bufLength = 0;
const DWORD detailLevel = PERF_DETAIL_WIZARD;
PDH_STATUS objStatus = PdhEnumObjectsW(nullptr, nullptr, nullptr, &bufLength, detailLevel, TRUE);
qDebug() << ManageApp::getPdhStatusMsg(objStatus);
qDebug() << "bufLength: " << bufLength;
std::wstring namebuf(bufLength, '\0');
PDH_STATUS status = PdhEnumObjectsW(nullptr, nullptr, &namebuf[0], &bufLength, detailLevel, FALSE);
qDebug() << ManageApp::getPdhStatusMsg(status);
I have tried to get the computer name and set it in the PdhEnumObjectsW() call:
PDH_STATUS objStatus = PdhEnumObjectsW(nullptr, machineName.toStdWString().c_str(), nullptr, &bufLength, detailLevel, TRUE);
WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1];
DWORD computerNameSize = _countof(computerName);
bool isComputerName = GetComputerNameExW(ComputerNameDnsFullyQualified, computerName, &computerNameSize);
qDebug() << isComputerName;
QString machineName = "";
I have removed the Windows 10 update and switched back to 19044.1645, but it still displays the PDH_CSTATUS_NO_MACHINE error. Also, I have checked it in a VM - Windows 10 build 19043, Windows 11 and Windows 11 Insider Preview, so it works well there.
From the docs, I get following error description:
PDH_CSTATUS_NO_MACHINE
Unable to connect to the specified computer. Could be caused by the computer not being on, not supporting PDH, not being connected to the network, or having the permissions set on the registry that prevent remote connections or remote performance monitoring by the user.
So, I think it's somehow related to registry permissions. Any ideas how to verify that all registry permissions for PDH are properly set on my machine?
I have checked out the registry permissions for Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib, and all permissions are set properly. So, I do not think, it is related to the registry permissions.
Any ideas what could cause such PDH_CSTATUS_NO_MACHINE issue?

I have fixed it by rebuilding the performance counters.
Instructions:
Set "Disable Performance Counters" to 0 using this command:
Reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PerfProc\Performance /v "Disable Performance Counters" /t REG_DWORD /d 0
Rebuild all performance counters:
%windir%\system32\lodctr /R
%windir%\sysWOW64\lodctr /R
When running the first command - %windir%\system32\lodctr /R, you will get:
Error: Unable to rebuild performance counter setting from system
backup store, error code is 2
In such case, feel free run this command first - %windir%\sysWOW64\lodctr /R and then run %windir%\system32\lodctr /R. The second time after this command: %windir%\sysWOW64\lodctr /R it will complete successfully for %windir%\system32\lodctr /R:
Info: Successfully rebuilt performance counter setting from system
backup store
Resync the counters with Windows Management Instrumentation (WMI): %windir%\system32\wbem\winmgmt.exe /resyncperf
Stop and restart the Performance Logs and Alerts service with the following commands:
net stop pla
net start pla
Stop and restart the Windows Management Instrumentation (WMI) service by using these commands:
net stop winmgmt
net start winmgmt
So, the issue is resolved. Thanks.

Related

How to use the WinRM C++ API in a simple example

why is my code failing to run a simple executable using WinRM's C++ API?
//main.cpp
int main()
{
ShellClient *shellClient = new ShellClient();
//Set up the shell client here and connect to the localhost.
//This seems to be working fine because I'm handling every
//possible error code, and none of them are being triggered
PCWSTR commandLine = L"\"MyExampleExecutable.exe\"";
isOk = shellClient->RunCommand(commandLine);
if (!isOk)
return 1;
return 0;
}
//ShellClient.cpp
bool ShellClient::RunCommand(PCWSTR command)
{
WSMAN_SHELL_ASYNC createCommandAsync;
ZeroMemory(&createCommandAsync, sizeof(createCommandAsync));
createCommandAsync.operationContext = this;
createCommandAsync.completionFunction = (WSMAN_SHELL_COMPLETION_FUNCTION)CommandCreatedCallback;
WSManRunShellCommand(shellHandle, 0, command, NULL, NULL, &createCommandAsync, &commandHandle);
if (commandHandle == NULL)//It is *always* NULL
{
std::cout << "command handle null" << std::endl;
system("pause");
return false;
}
return true;
}
One possible clue is that my C++ code thinks the shell gets created fine, but in the Event Viewer for my machine, there is this:
WSMan operation CreateShell failed, error code 2150859250
At the time of writing, this lovely error code gives precisely zero results when put into Google, making it rather difficult to know what it means.
Background and common solutions which I have already checked
As documented here and explaned in this video by the same author, most WinRM issues boil down to either connection or authentication problems. In my case, if I deliberately enter incorrect user credentials, I get an authentication error, so I know that my program is connecting and authenticating fine when the correct username and password are supplied. Also:
From the command line, I can connect to my local machine and pretend it's a remote server, for example the following command works fine:
winrs -r:http://localhost:5985 -u:COMPUTERNAME\Jeremy "dir"
winrm quickconfig shows the service is working (which we already know otherwise the winrs command wouldn't work)
winrm get winrm/config shows TrustedHosts = localhost, AllowUnencrypted = true, and all authentication methods are set to true
Following this advice, I have set the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\LocalAccountTokenFilterPolicy = 1
Working in Windows 10
Thank you in advance!
I wasn't aware of this so can't comment on whether it's common knowledge but Microsoft have a nifty error lookup tool where you can enter an error code (after converting it from a normal number to hexadecimal) and it tells you what it means.
In this case, 2150859250 (803381F2 in hex) corresponds to:
ERROR_WINRS_IDLETIMEOUT_OUTOFBOUNDS wsmerror.h
# The WS-Management service cannot process the request. The
# requested IdleTimeout is outside the allowed range.
When setting up the WinRM shell, I was doing the following:
WSMAN_SHELL_STARTUP_INFO startupInfo;
ZeroMemory(&startupInfo, sizeof(startupInfo));
startupInfo.idleTimeoutMs = 1000;//not large enough!
startupInfo.workingDirectory = L"C:\\";
//other parameters of startupInfo set here
WSManCreateShell(session, 0, shellUri, &startupInfo, NULL, NULL, &createShellAsync, &shellHandle);
Changing idleTimeoutMs from 1000 to a much larger number like 100000 solved the error and my program now works fine.
Since the official docs for this parameter say anything between 0 and 0xFFFFFFFF are valid, it remains a mystery why a value of 1000 is throwing this error. I leave this for somebody more knowledgable than myself to answer, on the off chance that they come across this question.

Mount NTFS device in C++ on linux?

I'm attempting to mount an external drive in my C++ application. I originally tried to use mount(2) but this fails:
int ret = mount(deviceName.c_str(), mountPoint.c_str(), fsType.c_str(), 0, NULL);
errno is 19, ENODEV (filesystem type not configured in kernel)
However, if I switch to using mount(8) it works fine:
std::string cmd = "mount -t " + fsType + " " + deviceName + " " + mountPoint;
int ret = system(cmd.c_str());
Does mount(2) have a different list of acceptable filesystem types? This is an ntfs device, so I was using ntfs-3g as the fstype. I checked /proc/filesystems and saw that this was not listed, so I tried fuseblk but that just changes the error to 22, EINVAL.
What is the correct way to mount NTFS devices using mount(2)?
mount.2 is just a kernel call. mount.8 is a complete external tool which is extended beyond what kernel does.
I think you may be looking for libmount which is a library implementing the whole mounting magic done by mount.8. Newer mount versions use it as well. It's provided in util-linux.
Have you tried running mount(8) using the strace command? It will print out the system calls made by the program, including mount(2). When I do such a mount, it spawns mount.ntfs (which is NTFS-3g) which then does a mount for fuseblk and then spins off into the background to support that mount point.
FUSE-based filesystems are handled differently because the user-space daemon must be started. Mounting with fuseblk doesn't provide enough information for the kernel to start the daemon (and the kernel doesn't even really have the information to start the daemon). For ntfs-3g, one would normally do something like ntfs-3g /dev/sda1 /mnt/windows (from the help). There isn't a programmatic way to tell the kernel to do this because it happens in user-space.

Why is a Remote WMI call to Win32_Printer Coming Back With An Empty Set For Some Machines?

I am using WMI (prototyping everything in VBScript first, as examples are more plentiful and it removes VBScript/Python impedence) to connect remotely to a fresh PC (we will call this PC2). Most Win32_* classes can be remotely read, yet Win32_Printer returns an empty set when queried, but only when I query remotely. The resulting SWbemObjectSet always has a .Count of zero. No error. I can connect to PC1 and receive a SWbemObjectSet with a non-zero .Count, can iterate through it, etc. If I run the script locally (after removing the superuser username and password from the .ConnectServer method, naturally), I get a non-zero .Count back and can iterate through it. Even if I foolishly use my own Domain Administrator account, the problem persists. The Script:
strComputer = "nnn.nnn.nnn.nnn"
username = "DOMAIN\superuser"
password = "thisisaverygoodpassword"
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
objSWbemLocator.Security_.ImpersonationLevel = 3
objSWbemLocator.Security_.AuthenticationLevel = 6
Set objSWbemServices = objSWbemLocator.ConnectServer(strComputer, "root\cimv2", username, password)
Set colSWBemObjectSet = objSWbemServices.ExecQuery("Select * From Win32_Printer")
WScript.Echo colSWBemObjectSet.Count & " Found."
For Each objPrinter in colSWBemObjectSet
For Each Property in objPrinter.Properties_
If TypeName(Property.Value) = "Variant()" Then
Wscript.Echo """" & Property.Name & """, """ & TypeName(Property.Value) & """, ""Skipping ..."""
Else
Wscript.Echo """" & Property.Name & """, """ & TypeName(Property.Value) & """, """ & Property.Value & """"
End If
Next
Next
Commenting and error checking have been omitted for brevity.
It does not appear to be a firewall problem.
Reason 1: Where a firewall blockage does exist, I receive an error
back from SWbemLocator, "The RPC server is unavailable."
Reason 2: I can access and run through the WMI class Win32_ComputerSystem
with ease.
It does not appear to be a username/password problem.
Reason 1: I can retrieve information from Win32_ComputerSystem.
Reason 2: I ought to get an error.
It does not appear to be an OS version problem:
Reason: PC2 and PC1 are both running Windows 7 Professional. PC1 is running the 64-bit version, PC2 the 32-bit.
Although I started trying to reach a 32-bit machine from a 64-bit server, it does not appear to be a 32-bit vs. 64-bit problem.
Reason 1: I added a value of 32 for __ProviderArchitecture in a SWbemNamedValueSet prior to my .ConnectServer attempt (with that SWbemNamedValueSet in the arguments to no avail), although I was unable to later add that same context to the .ExecQuery method of the connected server without a type mismatch operator.
Reason 2: I later ran the script from a 32-bit server with the same result.
It does not appear to be a corrupted WMI problem.
Reason: Once I stop using credentials, I can run the script from the target machine itself and receive a result set with more than zero items and can iterate through it.
It does not appear to be a credential/namespace mistake within my script.
Reason: Using WBemTest.exe from the same source machine and using identical username, password, authentication level, impersonation level, namespace, and so forth, I receive the same null set for an answer.
It does not appear to be an issue of WMI Namespace security on the target machine.
Reason 1: Logging in to the target machine with the same credentials as the script uses generates results.
Reason 2: Win32_Printer is in the same namespace as Win32_ComputerSystem. Win32_ComputerSystem works.
Reason 3: After using the Wmimgmt.msc Microsoft Management Console (MMC) to give the superuser full permissions, starting in the root namespace, propagating to "This namespace and subnamespaces," rebooting, and checking again, I still receive the same empty set.
It does not appear to be the respective OUs of PC2 and PC1 that are the problem.
Reason: I swapped the OUs each machine was in and rebooted. No change.
It does not appear to be the Local Computer Groups:
Reason: I made the membership of groups in PC2 look like PC1 and rebooted. No change.
It does not appear to be something magical about Win32_Printer in that remote access does not work.
Reason: I can read PC1's Win32_Printer class.
It does not appear that my WQL is unusual.
Reason: "SELECT * FROM Win32_Printer" is my test case.
It does not appear that my DCOM settings are off.
Reason: They appear identical when I go through PC1 and PC2.
I have even gone so far as to hit the Trace logs in WMI-Activity, print them out for both PC1 and PC2, then sort by GroupOperationID, OperationID (the TimeCreated SystemTime is not granular enough and EventID seems ... out of order. I can see events from the following actions:
IWbemServices::Connect
Start IWbemServices::ExecQuery - Select * from __ClassProviderRegistration
Start IWbemServices::GetObject - __Win32Provider.Name="WmiPerfClass"
Start IWbemServices::ExecQuery - references of {__Win32Provider.Name="WmiPerfClass"}
Start IWbemServices::GetObject - Win32_Printer
Start IWbemServices::ExecQuery - Select * From Win32_Printer
Provider::CreateInstanceEnum - Win32_Printer
in both sets of logs, and if I sort by GroupOperationID, OperationID they appear to happen in identical order. Sorting by EventID shows a somewhat different order. That's the closest I can see to a difference. I'm stumped at this point.
I know this verges perilously close to a system administration issue.
Found this link in the win32_printer spec page referring to this problem http://www.lansweeper.com/forum/yaf_postsm18178_WMI-Security-PowershellLansweeper.aspx#post18178 It appears only printers installes for this user are returned, not all printers on the system. So if you've never logged on to the remote system under the credentials of the user you are using to enumerate the printers then you get an empty result.
Looks like you've had a good shot at it. ServerFault might yield something more...
It's a long shot, but I once heard terminal services being disabled aparently caused issues when issuing WMI queries...
Edit:
This may not apply, but could be worth a look: AD Delegation

Permission issue getting uptime with performance counters

I'm trying to read the system uptime using performance counters in C++. I want to support both XP and Windows 7 at minimum.
The following code works fine on Windows XP...
HQUERY hQuery; HCOUNTER hCounter;
PDH_FMT_COUNTERVALUE Value;
int ret = 0;
if (PdhOpenQuery(NULL, 0, &hQuery) == ERROR_SUCCESS) {
if ((status = PdhAddCounter(hQuery, queryURI, 0, &hCounter)) == ERROR_SUCCESS) {
if ((status = PdhCollectQueryData(hQuery)) == ERROR_SUCCESS) {
if ((status = PdhGetFormattedCounterValue(hCounter, PDH_FMT_LARGE, NULL, &Value)) == ERROR_SUCCESS) {
ret = (DWORD)(Value.largeValue);
}
}
PdhRemoveCounter(hCounter);
}
PdhCloseQuery(hQuery);
}
return ret;
..but it fails on Windows 7. Specifically, PdhCollectQueryData returns PDH_NO_DATA regardless of whether or not i run as administrator.
How can i get the system uptime on both Windows 7 and XP? I expect the times to be much larger than the 49-day overflow of GetTickCount, and i would rather not have separate PDH versions for XP and GetTickCount64 versions for 7 if at all possible...
So the help for PdhCollectQueryData indicates that PDH_NO_DATA can be returned if the process doing the query lacks the appropriate elevated token to allow the query. See if you can check exactly what user permissions the process itself has been allocated, regardless of whether you are logged in as admin or not. Windows 7 has a lot of granularity to this concept, especially with UAC turned on. There can be a distinction also between the local Administrator account created with the OS & a member of the Administrators group in terms of what permissions the account ends up with, though I've not encountered a specific one on performance counters.
Try an explicit 'Run as administrator' on the process, for example, and ensure the administrator account you're using really does have that permission (I'm not sure from your question whether you have already tried this or not). Try a user account in the Performance Logs User Group. Try the account that was created when the OS was installed. Try with UAC off. These hopefully should help nail down the source of the problem.
From the Microsoft help on the subject:
Only the administrator of the computer or users in the Performance Logs User Group can log and view counter data. Users in the Administrator group can log and view counter data only if the tool they use to log and view counter data is started from a Command Prompt window that is opened with Run as administrator.... Users in the Performance Monitoring Users group can view counter data.

First call to Windows Performance Counters (PDH) sometimes fails

I'm having a problem where sometimes my code will function correctly, but other times it will fail.
This is the first bit of PDH related code that I run:
const std::wstring pidWildcardPath = L"\\Process(*)\\ID Process";
DWORD bufferSize = 0;
LPTSTR paths = NULL;
PDH_STATUS status = PdhExpandCounterPath(
pidWildcardPath.c_str(),
paths,
&bufferSize);
checkPDHStatus(status, PDH_MORE_DATA, L"Expected request for more data.");
The result of the PdhExpandCounterPath function call is 0x800007D0 (PDH_CSTATUS_NO_MACHINE). The checkPDHStatus function is a simple function that I wrote that asserts that the status is equal to the second parameter. In this case, I expect the result to be PDH_MORE_DATA because paths is NULL and bufferSize is 0. The goal of this call is to determine the size of the buffer I must allocate to store all of the results for a subsequent call to PdhExpandCounterPath. This is described in the PDH documentation under the Remarks section.
The list of PDH error codes describes PDH_MORE_DATA as "Unable to connect to the specified computer, or the computer is offline." As you can see by the performance counter path in the code above, I am not even trying to connect to a different computer than my own.
It is interesting the way that this code fails. Sometimes it works fine and then other times, it will fail on multiple back-to-back executions of my application. I have #include <pdh.h> in my header file and I have a section in my property sheet for this DLL that looks like this:
<Tool
Name="VCLinkerTool"
AdditionalDependencies="pdh.lib"
/>
I'm not sure if it matters, but this program is built by Visual Studio 2005 and run on Windows XP. Am I doing something incorrectly?
I'm a co-worker of Dave's and have discovered the following during my investigation:
the code above runs fine when run from a logged-in interactive session
the code runs fine when initiated as a Scheduled Task AND the user is logged in at the time the scheduled task is fired off
the code FAILS only when run as a Scheduled Task AND the user is NOT logged in at the time the task starts
the code continues to fail if the user logs in after the failing task has started but while it is still running (because it is looping "endlessly" until it gets a PDH_MORE_DATA status back).
In the failing instances, the following environment variables have not been established/set for the program: APPDATA, HOMEDRIVE and HOMEPATH ... I don't think this is a problem. However, the failing program also lacks the SeCreateGlobalPrivilege from its token; the passing programs all have this privilege in the token and PERFMON shows it as "Default Enabled". The other difference is that failing program has the NT_AUTH\BATCH user group in the token, while the passing program has NT_AUTH\INTERACTIVE instead ... all other user groups and privileges are the same for both cases. I think the global privilege is coming from the interactive login, but don't know if it has any bearing on PDH operation.
I cannot find anything in the Performance Counter/PDH documentation that talks about needing any special permissions or privileges for this functionality to succeed. Is the global privilege required to use Performance Counters ?
Or is there some other context/environment difference between running Scheduled Tasks (as a specific user) when that user is/isn't logged in at the time the task starts, that would account for the PDH call succeeding/failing respectively ?
Try this format, indicating the local computer:
const std::wstring pidWildcardPath = L"\\.\Process(*)\ID Process";