Found my first bug in Windows 7 APIs: PdhBrowseCounters requires Elevation
I just found out the following, which does not seem to be documented anywhere:
The Windows API has a function for displaying a dialog that lets the user select a specific performance counter from all counters available on the local or a remote computer. The dialog is used by perfmon.exe, for example, and looks like this:
This dialog is instantiated by calling the API function PdhBrowseCounters. This works well enough – the dialog displays all objects, counters and instances, the user is able to select a specific counter and clicks on “OK”.
The bad thing is that PdhBrowseCounters always returns an empty string instead of the counter path selected by the user. This happens if the application does not run elevated (i.e. with admin rights).
I have two problems with that:
- It is documented nowhere that elevation is required for PdhBrowseCounters.
- Why is elevation necessary? The user can see all objects, counters and their instances without elevation, so why not return the path selected?
For theses reasons I think this is a bug.
By the way, how does perfmon.exe work around this? It doesn’t. It requires elevation…
I have tested this on Windows 7 x64 German RTM with all patches till 10/27/2009.
And here is the code I used for testing (from my free tool DiskLED):
void CDialogConfig::OnBnClickedCounterpathSelect()
{
PDH_STATUS pdhStatus;
PDH_BROWSE_DLG_CONFIG oPDHBrowseDialogCfg;
TCHAR sBuffer[PDH_MAX_COUNTER_PATH + 1];
// Zero memory structures
ZeroMemory (&oPDHBrowseDialogCfg, sizeof (PDH_BROWSE_DLG_CONFIG));
// Initialize the path buffer
ZeroMemory (&sBuffer, sizeof (sBuffer));
//_tcscpy_s (sBuffer, PDH_MAX_COUNTER_PATH + 1, m_sCounterPath);
// Initialize the browser dialog window settings
oPDHBrowseDialogCfg.bIncludeInstanceIndex = FALSE;
oPDHBrowseDialogCfg.bSingleCounterPerAdd = TRUE;
oPDHBrowseDialogCfg.bSingleCounterPerDialog = TRUE;
oPDHBrowseDialogCfg.bLocalCountersOnly = FALSE;
oPDHBrowseDialogCfg.bWildCardInstances = TRUE;
oPDHBrowseDialogCfg.bHideDetailBox = TRUE;
oPDHBrowseDialogCfg.bInitializePath = FALSE;
oPDHBrowseDialogCfg.bDisableMachineSelection = FALSE;
oPDHBrowseDialogCfg.bIncludeCostlyObjects = FALSE;
oPDHBrowseDialogCfg.bShowObjectBrowser = FALSE;
oPDHBrowseDialogCfg.hWndOwner = m_hWnd;
oPDHBrowseDialogCfg.szReturnPathBuffer = sBuffer;
oPDHBrowseDialogCfg.cchReturnPathLength = sizeof (sBuffer) / sizeof (TCHAR);
oPDHBrowseDialogCfg.pCallBack = NULL;
oPDHBrowseDialogCfg.dwCallBackArg = 0;
oPDHBrowseDialogCfg.CallBackStatus = ERROR_SUCCESS;
oPDHBrowseDialogCfg.dwDefaultDetailLevel = PERF_DETAIL_WIZARD;
oPDHBrowseDialogCfg.szDialogBoxCaption = TEXT ("Select a counter for DiskLED");
// Display the counter browser window. The dialog is configured
// to return a single selection from the counter list.
pdhStatus = PdhBrowseCounters (&oPDHBrowseDialogCfg);
if (pdhStatus != ERROR_SUCCESS)
{
if (pdhStatus != PDH_DIALOG_CANCELLED)
{
m_pMainFrame->LogError (TEXT ("OnBnClickedCounterpathSelect"), TEXT ("PdhBrowseCounters"), pdhStatus);
}
}
else
{
m_sCounterPath = sBuffer;
// Update the dialog with the new data
UpdateData (FALSE);
}
}
1 Comment
This article was published on CodeProject (http://www.codeproject.com/Articles/104531/Found-my-first-bug-in-Windows-7-APIs-PdhBrowseCoun.aspx) where it got an interesting comment from Ascend4nt1:
PDH.DLL on Vista & Win 7 is rife with bugs, this is just one of them
The scenario you outline is just one of a few bugs with the PDH.DLL.
You don’t need elevation, you can just set this flag to false and it should work:
bSingleCounterPerAdd = FALSE
Also, the flag ‘bWildCardInstances’ must ALWAYS be set to TRUE, otherwise it causes crashes in many cases. The first one would be nice to be able to use (unelevated), but you’ll just have to ignore any secondary counters that are selected if a user selects more than one.
Other bugs:
– You need to manually set the title, it no longer offers a default (NULL pointer).
– You can’t set an initial performance counter to be highlighted.
– Sometimes the Instances won’t show up on the bottom, depending on where you click on the top portion of the dialog box. This causes an invalid Counter path to be returned.
– PDH.DLL and 40 other DLL’s are loaded, and STAY loaded once PdhBrowseCounters is called. Manually unloading the DLL’s often causes crashes. This may or may not have something to do with .NET 4.0 being present.
Other general PDH bugs:
– PdhExpandWildCardPath will not recognize any new Processes (and probably other instances for other object types), unless PDH.DLL is unloaded and reloaded. This also poses a problem if you use PdhBrowseCounters, as it increases the PDH.DLL load-count by 2. Forcing an un-load may be dangerous, but seems the only option.
My system: Win 7 x64, .Net 4.0, all updates as of 6/1/2011
See my own PDH Performance Counters code on the AutoIt forums at: http://www.autoitscript.com/forum/topic/90736-performance-counters-in-windows/