get_accChildCount returns 0 when it shouldn't - c++

I'm trying to enumerate tabs of IE from an extension and from a standalone application. For one of the MSAA nodes get_accChildCount returns 0 when called from extension, while it should return 1 according to inspect and a call from standalone application.
The problem was described previously at StackOverflow, yet it was solved via a hack that doesn't work for me. /clr and /MT are incompatible.
Also there was a topic on MSDN with the same issue. There's no single answer there.
If you run IE with administrator privileges, it works properly.
API Monitor shows several thousand calls in a minimal example, and it's unclear which of these are related. The minimal example is attached below.
What are the undocumented cases when get_accChildCount returns wrong child count?
What other method could I use to activate the tab by URL in most versions of IE?
#include <atlbase.h>
#include <atlcom.h>
#include <atlctl.h>
#include <atltypes.h>
#include <atlsafe.h>
#include <io.h>
#include <fcntl.h>
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
#include <boost/format.hpp>
#include <fstream>
using namespace std;
CComPtr<IAccessible> get_acc_by_hwnd(HWND hwnd) {
CComPtr<IAccessible> ret;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &ret);
if (FAILED(hr) || !ret) {
wcout << L"Accessible::Accessible invalid hwnd" << endl;
}
return ret;
}
std::vector<CComPtr<IAccessible>> get_acc_children(CComPtr<IAccessible> acc) {
std::vector<CComPtr<IAccessible>> ret;
long count;
if (FAILED(acc->get_accChildCount(&count))) return ret;
long count_obtained = 0;
if (!count) return ret;
std::vector<CComVariant> accessors(count);
if (FAILED(::AccessibleChildren(acc, 0, count, &*accessors.begin(), &count_obtained))) return ret;
accessors.resize(count_obtained);
for (auto vtChild : accessors) {
if (vtChild.vt != VT_DISPATCH) continue;
CComQIPtr<IAccessible> pChild = vtChild.pdispVal;
if (pChild) ret.push_back(pChild);
}
return ret;
}
bool is_client(CComPtr<IAccessible> acc) {
CComVariant var;
HRESULT hr = acc->get_accRole(CComVariant(CHILDID_SELF), &var);
return SUCCEEDED(hr) && var.vt == VT_I4 && var.lVal == 0xA;
}
std::wstring get_descr(CComPtr<IAccessible> acc) {
CComBSTR str;
HRESULT hr = acc->get_accDescription(CComVariant(CHILDID_SELF), &str);
return SUCCEEDED(hr) && str ? std::wstring(str) : L"";
}
int main() {
::CoInitialize(nullptr);
_setmode(_fileno(stdout), _O_U16TEXT);
// put HWND of the window that contains tab labels
// it's hardcoded to minimize quantity of API calls
HWND hwnd = reinterpret_cast<HWND>(0x002D0696);
CComPtr<IAccessible> iaccessible;
HRESULT hr = ::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible, (void**) &iaccessible);
if (FAILED(hr) || !iaccessible) {
wcout << L"AccessibleBrowser::activate_tab " L"failed to get IAccessible for IE" << endl;
return EXIT_FAILURE;
}
wstring const sentinel = L"\r\n";
for (auto child : get_acc_children(iaccessible)) if (is_client(child)) {
for (auto child1 : get_acc_children(child)) { // fails here in extension
for (auto child2 : get_acc_children(child1)) {
std::wstring descr = get_descr(child2);
auto pos = descr.find(sentinel);
if (pos == string::npos) continue;
auto tab_url = descr.substr(pos + sentinel.size());
wcout << tab_url << endl;
}
}
}
}

I poked at your program for a while without having much to show for it. Perhaps I realized too late that it was not supposed to reproduce the problem :( I can only provide some possibly helpful hints to get you to look under the right kind of rock.
yet it was solved via a hack that doesn't work for me
These programmers made a simple mistake, they forgot to call CoInitialize/Ex(). A very common oversight. Using the /clr build option works around that mistake because it is now the CLR that calls it. You can easily repro this mishap, just comment out the CoInitialize() call. Unfortunately it works for a while without any loud errors being produced, but you do get 0 for certain accobjects. You'll notice your program no longer finds the tabs.
Not so sure I can cleanly explain this, certain COM-style object models don't actually use the COM infrastructure. DirectX is the best example, we can add UIAutomation to that list. I assume it will silently fail like this when the client app's component is COM-based. Unclear if it is, DirectUIHWnd is quite undocumented.
So stop looking for that as a workaround, you did not forget to call CoInitialize() and it will be taken care of by IE before it activates your extension.
If you run IE with administrator privileges, it works properly.
That's a better rock. Many programmers run VS elevated all the time, the roles might be reversed in the case of UIA. Do keep in mind that your extension runs in a sandbox inside IE. No real idea how elevating IE affects this. One thing you cannot do from code running in the low-integrity sandbox is poke at UI components owned by a code that runs in a higher integrity mode. Google "disable IE enhanced protected mode" and follow the guideline to see what effect that has on your addin.

Related

How do you identify "File Explorer" instances in an `IShellWindows` collection?

I'm trying to get a list of all "File Explorer" instances currently running. It's fairly straight forward getting a list that includes all instances, but I find myself running into a brick wall filtering that list to only "File Explorer" instances.
The following piece of code retrieves all Explorer instances, meaning both "File Explorer" and "Internet Explorer":
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
using _com_util::CheckError;
using std::puts;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
}
}
The obvious attempt
My first take at the problem was to just get rid of everything that isn't "File Explorer". Asking for the IWebBrowser2 interface would certainly only get an affirmative response from objects that actually are web browsers. Adding the following to the code above:
_COM_SMARTPTR_TYPEDEF(IWebBrowser2, __uuidof(IWebBrowser2));
// ...
int main()
{
// ...
IWebBrowser2Ptr spWebBrowser{};
hr = spDisp.QueryInterface(__uuidof(spWebBrowser), &spWebBrowser);
if (SUCCEEDED(hr)) puts("Implements IWebBrowser2");
// ...
After making the changes and running the code while an "Internet Explorer" instance is running produces the desired output. However, running the code while a "File Explorer" instance is running produces the same output! That's a surprise and a disappointment, all at the same time.
More robust, less useful
Exluding objects that can be identified as "not File Explorer" didn't work out. Let's try to only include objects that can be identified as "File Explorer" instead. That sounds even more obvious, but as we've learned, "obvious" and "not" go hand in hand when it comes to the Windows Shell.
I haven't actually implemented this, but the IShellWindows interface provides an Item method that can return only objects that match a particular ShellWindowTypeConstants (e.g. SWC_EXPLORER or SWC_BROWSER). Or return an object at a particular index in the window collection. But not BOTH!
So, yes, (potentially) more robust, but also less useful as it doesn't meet my requirements as soon as more than one instances of "File Explorer" are running. Bummer.
Circumstantial evidence
While neither of the above led anywhere, I started over and went on a full-blown investigation looking for hints. Since "File Explorer" browses the Shell namespace, there may be something to that account. The following outlines the approach, based on an article by Raymond Chen titled A big little program: Monitoring Internet Explorer and Explorer windows, part 1: Enumeration:
Starting from the IDispatch interface above, ask for a service with ID SID_STopLevelBrowser to get an IShellBrowser interface.
Call IShellBrowser::QueryActiveShellView to get an IShellView interface.
Ask the IShellView whether it implements something Shell-namespace-y, e.g. IPersistIDList.
If it does, conclude that we're holding a reference to a "File Explorer" instance.
This appears to produce the desired result, though it's not clear to me future-proof this is or when it stops working. Leaving aside how overly convoluted this appears, I'm concerned about its reliability.
Question
What's the recommended/robust/reliable way to identify all "File Explorer" instances in an IShellWindows collection? I will favor solutions based on official documentation, though I understand that this is the Windows Shell and there's next to no documentation at all.
Here's a possibility ...
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
#include <atlbase.h>
#include <string>
using _com_util::CheckError;
using std::puts;
using std::string;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
//nicked from StackOverflow
std::string ProcessIdToName(DWORD_PTR processId)
{
std::string ret;
HANDLE handle = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
processId /* This is the PID, you can find one from windows task manager */
);
if (handle)
{
DWORD buffSize = 1024;
CHAR buffer[1024];
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
{
ret = buffer;
}
else
{
printf("Error GetModuleBaseNameA : %lu", GetLastError());
}
CloseHandle(handle);
}
else
{
printf("Error OpenProcess : %lu", GetLastError());
}
return ret;
}
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
CComPtr<IWebBrowser2> lpWB;
spDisp->QueryInterface(&lpWB);
SHANDLE_PTR hWnd{ 0 };
lpWB->get_HWND(&hWnd);
if (hWnd)
{
DWORD pid = 0;
GetWindowThreadProcessId((HWND)hWnd, &pid);
if (pid != 0)
{
puts("pid");
auto s = ProcessIdToName((DWORD_PTR)pid);
puts(s.c_str());
}
}
}
}

How to know if a device has been explicitly been disabled by user?

Using device manager a user can explicitly enable/disable a device, as can be seen in the following image.
For a given device I want to know if it's currently in a user disabled/enabled state.
I have tried the following approaches
CM_Get_DevNode_Status(&status, &problem, data.DevInst, 0); I was hoping that presence of DN_STARTED, or DN_DRIVER_LOADED would tell me that. But these can be zero even when a driver is being loaded/unloaded by the OS, when the device connects/disconnects. For example, a device which is enabled, and for which driver is loaded. DN_STARTED will be 1, but when we disconnect device it is set to zero before the device's entry is removed from device manager.
SetupDiGetDeviceRegistryProperty(..., SPDRP_INSTALL_STATE, ...) I though a state of CM_INSTALL_STATE_INSTALLED should mean that the device is enabled. But the function returns this state even for disabled devices.
Using WMI I was able to get the required information, but I used wmi in PowerShell. I do not want to use wmi, as it is quite difficult to implement in native c++. I used the following query.
Select Name, Availability, ConfigManagerErrorCode, ConfigManagerUserConfig from Win32_PnPEntity where Name = 'NVIDIA Quadro M1000M'
ConfigManagerErrorCode in above query, if set to 22, means that device has been disabled, 21 means that windows is removing the device
I am looking for a non wmi solution.
The information can be obtained from a device's problem code. There are two ways which I could find to get it.
Use SetupDiGetDeviceProperty() to query DEVPKEY_Device_ProblemCode.
Use CM_Get_DevNode_Status() the problem code will be present in the second argument after the call.
A problem code of 22 (CM_PROB_DISABLED) means that the device has been explicitly disabled by a user by either using device manager, or other such utility.
Sample code
#include "stdafx.h"
#include <Windows.h>
#include <SetupAPI.h>
#include <Cfgmgr32.h>
#include <devguid.h>
#include <initguid.h>
#include "devpkey.h"
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
HDEVINFO hDevInfo = ::SetupDiGetClassDevs(&GUID_DEVCLASS_DISPLAY, NULL, NULL, 0); //only getting for GPUs on the machine
if (INVALID_HANDLE_VALUE != hDevInfo)
{
SP_DEVINFO_DATA data;
data.cbSize = (DWORD)sizeof(data);
for (unsigned int nIndex = 0; ::SetupDiEnumDeviceInfo(hDevInfo, nIndex, &data); nIndex++)
{
ULONG status = 0, problem = 0;
CONFIGRET cr = ::CM_Get_DevNode_Status(&status, &problem, data.DevInst, 0); //after the call 'problem' variable will have the problem code
if (CR_SUCCESS == cr)
{
cout << " problem " << problem <<endl;
if(problem == CM_PROB_DISABLED)
{ /*Do Something*/ }
DEVPROPTYPE propertyType;
const DWORD propertyBufferSize = 100;
BYTE propertyBuffer[propertyBufferSize];
std::fill(begin(propertyBuffer), end(propertyBuffer), BYTE(0));
DWORD requiredSize = 0;
if (SetupDiGetDeviceProperty(hDevInfo, &data, &DEVPKEY_Device_ProblemCode, &propertyType, propertyBuffer, propertyBufferSize, &requiredSize, 0)) //after the call 'propertyBuffer' will have error codes
{
unsigned long deviceProblemCode = *((unsigned long*)propertyBuffer);
cout << " deviceProblemCode " << deviceProblemCode << endl;
if(problem == CM_PROB_DISABLED)
{ /*Do Something*/ }
}
}
}
}
return 0;
}
Sample Output
problem 0
deviceProblemCode 0
problem 22
deviceProblemCode 22
In the question it can be seen that Intel(R) HD Graphics 530 was enabled, and NVIDIA Quadro M1000M was disabled. Hence in the output we got a problem code of 0, and a problem code of 22 (CM_PROB_DISABLED).

Getting BLE Beacons in C++ Windows 10 Desktop Application

Has someone already figured out how to get BLE Beacons into a c++ desktop apps?
I have some code from the following websites to get it done in c#:
msdn sozial site
and
codefest.at post. Sorry, it's in german but the code is code
but that'for C# and not c++
I also have the example from MS (msdn.microsoft.com/en-us/library/hh973459.aspx) how to use the WinRL
For now I have the following code:
#include "stdafx.h"
#include <iostream>
#include <Windows.Foundation.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <stdio.h>
#include <../winrt/windows.devices.bluetooth.h>
#include <../winrt/windows.devices.bluetooth.advertisement.h>
using namespace std;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Devices::Bluetooth::Advertisement;
/* http://www.codefest.at/post/2015/09/07/Bluetooth-Beacons-Windows-10.aspx
private async void WatcherOnReceived(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs eventArgs)
{
_beaconManager.ReceivedAdvertisement(eventArgs);
}
var watcher = new BluetoothLEAdvertisementWatcher { ScanningMode = BluetoothLEScanningMode.Active };
watcher.Received += WatcherOnReceived;
watcher.Stopped += WatcherOnStopped;
watcher.Start();
*/
// Prints an error string for the provided source code line and HRESULT
// value and returns the HRESULT value as an int.
int PrintError(unsigned int line, HRESULT hr)
{
wprintf_s(L"ERROR: Line:%d HRESULT: 0x%X\n", line, hr);
return hr;
}
EventRegistrationToken watcherToken;
int main()
{
// Initialize the Windows Runtime.
RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
if (FAILED(initialize))
{
return PrintError(__LINE__, initialize);
}
// Get the activation factory for the IBluetoothLEAdvertisementWatcherFactory interface.
ComPtr<IBluetoothLEAdvertisementWatcherFactory> bleAdvWatcherFactory;
HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementWatcher).Get(), &bleAdvWatcherFactory);
if (FAILED(hr))
{
return PrintError(__LINE__, hr);
}
IBluetoothLEAdvertisementWatcher* bleWatcher;
IBluetoothLEAdvertisementFilter* bleFilter;
hr = bleAdvWatcherFactory->Create(bleFilter, &bleWatcher);
if (bleWatcher == NULL)
{
cout << "bleWatcher is NULL, err is " << hex << hr;
}
else
{
bleWatcher->Start();
while (1)
Sleep(1000);
}
return 0;
}
my problem is that the Watcher Factory complains ( hr = E_INVALIDARG "One or more arguments are not valid" 0x80070057) that one of the variables is not valid (I suspect the filter because it has no valid content).
And even on the same level of severity, I have no idea how to register the event handler for the incoming beacons.
msdn.microsoft.com/en-us/library/windows/apps/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher.received?cs-save-lang=1&cs-lang=cpp
is telling me something about the Received event. But I don't have it in my autocomplete from VS and also not by manually looking into the header file.
The next best thing I have is the "add_Received" but I can't find any documentation about it how to use. And I don't get that much wiser from the header file.
Thanks in advance for tips and tricks or even a working solution.
Markus
You have few problems in your code. First of all All I* classes should be wrapped around ComPtr<I*>
ComPtr<IBluetoothLEAdvertisementWatcher> bleWatcher;
ComPtr<IBluetoothLEAdvertisementFilter> bleFilter;
In function bleAdvWatcherFactory->Create(bleFilter, &bleWatcher); parameter bleFilter is undefined and it will lead to undefined behaviour (In Debug version it might be initialized as nullptr).
You can create it by using:
Wrappers::HStringReference
Wrappers::HStringReference class_id_filter(RuntimeClass_Windows_Devices_Bluetooth_Advertisement_BluetoothLEAdvertisementFilter);
hr = RoActivateInstance(class_id_filter.Get(), reinterpret_cast<IInspectable**>(bleFilter.GetAddressOf()));
Then you are allowed to call:
hr = bleAdvWatcherFactory->Create(bleFilter.Get(), &bleWatcher.GetAddressOf());

Visual C++, Windows Update Interface (IUpdate) <wuapi.h>, get_MsrcSeverity

I'm probably just blind, but I cannot see any errors here (and I am looking on this issue already for days now...)
I am trying to get the Patch Priority (Severity) from the Windows Update Interface using the following piece of code in Visual Studio:
#include "stdafx.h"
#include <wuapi.h>
#include <iostream>
#include <ATLComTime.h>
#include <wuerror.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr;
hr = CoInitialize(NULL);
IUpdateSession* iUpdate;
IUpdateSearcher* searcher;
ISearchResult* results;
BSTR criteria = SysAllocString(L"IsInstalled=0");
hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&iUpdate);
hr = iUpdate->CreateUpdateSearcher(&searcher);
wcout << L"Searching for updates ..."<<endl;
hr = searcher->Search(criteria, &results);
SysFreeString(criteria);
switch(hr)
{
case S_OK:
wcout<<L"List of applicable items on the machine:"<<endl;
break;
case WU_E_LEGACYSERVER:
wcout<<L"No server selection enabled"<<endl;
return 0;
case WU_E_INVALID_CRITERIA:
wcout<<L"Invalid search criteria"<<endl;
return 0;
}
IUpdateCollection *updateList;
IUpdateCollection *bundledUpdates;
IUpdate *updateItem;
IUpdate *bundledUpdateItem;
LONG updateSize;
LONG bundledUpdateSize;
BSTR updateName;
BSTR severity;
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
wcout << L"No updates found"<<endl;
}
for (LONG i = 0; i < updateSize; i++)
{
updateList->get_Item(i,&updateItem);
updateItem->get_Title(&updateName);
severity = NULL;
updateItem->get_MsrcSeverity(&severity);
if (severity != NULL)
{
wcout << L"update severity: " << severity << endl;
}
wcout<<i+1<<" - " << updateName << endl;
// bundled updates
updateItem->get_BundledUpdates(&bundledUpdates);
bundledUpdates->get_Count(&bundledUpdateSize);
if (bundledUpdateSize != 0)
{
// iterate through bundled updates
for (LONG ii = 0; ii < bundledUpdateSize; ii++)
{
bundledUpdates->get_Item(ii, &bundledUpdateItem);
severity = NULL;
bundledUpdateItem->get_MsrcSeverity(&severity);
if (severity != NULL)
{
wcout << L" bundled update severity: " << severity << endl;
}
}
}
}
::CoUninitialize();
wcin.get();
return 0;
}
So here's the issue: updateItem->get_MsrcSeverity(&severity); is not returning anything. If I catch the result code with an HRESULT it always returns S_OK.
Link to MSDN IUpdate MsrcSeverity: http://msdn.microsoft.com/en-us/library/windows/desktop/aa386906(v=vs.85).aspx
Can you see what I am doing obviously wrong or is the get_MsrcSeverity function currently broken?
#EDIT: Changed the code to iterate through "BundledUpdates" as suggested.
#EDIT2: the code now outputs the severity value of the updateItem as well as the bundledUpdatesItem, but it's always NULL.
I also know there is one important update waiting for my computer - regarding to Windows Update in the control panel. It is KB2858725.
#EDIT3: Okay, it turns out, KB2858725 is no security update and therefore has no severity rating by Microsoft. But how does Microsoft Windows Update now categorize the updates in "important" and "optional" like you can see it in control panel's update?
Thank you for any hints!
// Markus
I've been struggling with the exact same problem for hours now... I finally figured out that Microsoft only seems to set the MsrcSeverity value on some updates. For general Windows updates, it's usually null. For most security updates it's set to one of:
"Critical"
"Moderate"
"Important"
"Low"
It seems as though the "Unspecified" value is never used, though it is documented in MSDN (http://msdn.microsoft.com/en-us/library/microsoft.updateservices.administration.msrcseverity(v=vs.85).aspx).
I wrote a small C# program to list all available updates and their reported severity. It requires a reference to WUApiLib:
using System;
using WUApiLib;
namespace EnumerateUpdates
{
class Program
{
static void Main(string[] args)
{
var session = new UpdateSession();
var searcher = session.CreateUpdateSearcher();
searcher.Online = false;
// Find all updates that have not yet been installed
var result = searcher.Search("IsInstalled=0 And IsHidden=0");
foreach (dynamic update in result.Updates)
{
Console.WriteLine(update.Title + ": " + update.Description);
Console.WriteLine("Severity is " + update.MsrcSeverity);
Console.WriteLine();
}
Console.ReadLine();
}
}
}
Hope this helps someone!
Look for bundled updates in the list. The bundled updates does not have some properties or methods as described on this page.
Remarks
If the BundledUpdates property contains an IUpdateCollection, some
properties and methods of the update may only be available on the
bundled updates, for example, DownloadContents or CopyFromCache.
The update you are processing in is a bundled update. Extract the individual updates from this bundle, it will have the property you are looking out for.
The IUpdate interface has a method get_BundledUpdates which gets you a IUpdateCollection if the size of this collection is greater then 0, this is a bundled update.

Windows HOME - SHGet(Known)FolderPath

I'm trying to write a function to get the Windows-equivalent of HOME. My C skills are rusty, so don't mind that my example code does not compile. I'm trying to use SHGetKnownFolderPath on Windows Vista and newer, and SHGetFolderPath on Server 2003 and older. Since I expect to encounter users running Windows XP (as it is still the number one deployed version of Windows), I am going to avoid having a reference to SHGetKnownFolderPath in the symbol table (as this would lead to a binary that won't even load on XP). I know to LoadLibrary() shell32 and to GetProcAddress() from there, but my skills on doing function pointers are, well, crap, to say the least.
When I write features, and they are difficult to handle, I isolate them into an separate example file. The broken example I have so far is:
#include <windows.h>
#include <stdio.h>
// Pointerizing this Vista-and-later call for XP/2000 compat, etc.
typedef HRESULT (WINAPI* lpSHGetKnownFolderPath)(
REFKNOWNFOLDERID rfid,
DWORD dwFlags,
HANDLE hToken,
PWSTR *ppszPath
) lpSHGetKnownFolderPath;
int main(int argc, char *argv[])
{
// SHGet(Known)FolderPath() method.
HMODULE hndl_shell32;
lpSHGetKnownFolderPath pSHGetKnownFolderPath;
hndl_shell32 = LoadLibrary("shell32");
pSHGetKnownFolderPath = GetProcAddress(hndl_shell32, "SHGetKnownFolderPathW");
if(pSHGetKnownFolderPath != NULL) {
} else {
}
}
My question is this: Knowing that I'm doing this wrong, how would I go about doing this right? And an explanation as to how to do it right in the future would be appreciated. Thanks.
Here is a small application that shows how to use LoadLibrary() and GetProcAddress() with advice provided in comments:
#include <windows.h>
#include <stdio.h>
#include <shlobj.h>
/* The name of the function pointer type is
'lpSHGetKnownFolderPath', no need for
additional token after ')'. */
typedef HRESULT (WINAPI* lpSHGetKnownFolderPath)(
REFKNOWNFOLDERID rfid,
DWORD dwFlags,
HANDLE hToken,
PWSTR *ppszPath
);
int main()
{
HMODULE hndl_shell32;
lpSHGetKnownFolderPath pSHGetKnownFolderPath;
/* Always check the return value of LoadLibrary. */
hndl_shell32 = LoadLibrary("shell32");
if (NULL != hndl_shell32)
{
/* There is no 'SHGetKnownFolderPathW()'.
You need to cast return value of 'GetProcAddress()'. */
pSHGetKnownFolderPath = (lpSHGetKnownFolderPath)
GetProcAddress(hndl_shell32, "SHGetKnownFolderPath");
if(pSHGetKnownFolderPath != NULL)
{
PWSTR user_dir = 0;
if (SUCCEEDED(pSHGetKnownFolderPath(
FOLDERID_Profile,
0,
NULL,
&user_dir)))
{
/* Use 'user_dir' - remember to:
CoTaskMemFree(user_dir);
when no longer required.
*/
}
}
else
{
fprintf(stderr, "Failed to locate function: %d\n",
GetLastError());
}
/* Always match LoadLibrary with FreeLibrary.
If FreeLibrary() results in the shell32.dll
being unloaded 'pSHGetKnownFolderPath' is
no longer valid.
*/
FreeLibrary(hndl_shell32);
}
else
{
fprintf(stderr, "Failed to load shell32.dll: %d\n", GetLastError());
}
return 0;
}
This was compiled on Windows XP.
Output on Windows XP:
Failed to locate function: 127
where 127 means The specified procedure could not be found.
Output on Windows Vista:
C:\Users\admin
You can always use getenv("HOMEDRIVE") and getenv("HOMEPATH") and concatenate the results.
std::string home = std::string(getenv("HOMEDRIVE")) + getenv("HOMEPATH");
The Windows equivalent of HOME is USERPROFILE. It is an ordinary environment variable just like in Linux. You can make the following call to retrieve it:
char *profilepath = getenv("USERPROFILE");