0x80070002 when trying to set default browser - c++

I'm trying to set the default browser on Windows. I did have it working, and then for some reason it stopped when I refactored my code and I can't work out why (and I can't get the old code back unfortunately).
Here is a test I wrote which I believe should work:
#include <windows.h>
#include <Shobjidl.h>
void test()
{
// Included because they aren't defined in mingw for some reason.
const GUID CLSID_ApplicationAssociationRegistration =
{ 0x591209c7, 0x767b, 0x42b2, {0x9f,0xba,0x44,0xee,0x46,0x15,0xf2,0xc7} };
const IID IID_ApplicationAssociationRegistration =
{ 0x4e530b0a, 0xe611, 0x4c77, {0xa3,0xac,0x90,0x31,0xd0,0x22,0x28,0x1b} };
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL,
CLSCTX_INPROC,
IID_ApplicationAssociationRegistration,
(void**)&pAAR);
if (FAILED(hr))
{
qDebug() << "CoCreateInstance failed: " << hr;
return;
}
hr = pAAR->SetAppAsDefault(L"IE.HTTP", L"http", AT_URLPROTOCOL);
if (FAILED(hr))
{
qDebug() << "SetAppAsDefault failed: " << hr;
pAAR->Release();
return;
}
pAAR->Release();
qDebug() << "Success";
return;
}
I get "SetAppAsDefault failed: -2147024894" (0x80070002). Note that CoCreateInstance does work (or at least claims to).
Any ideas? I've tried changing COINT_APARTMENTTHREADED to various other values, and CLSCTX_INPROC too. None of them work. Please explain your answer as if I have never used COM before and have no desire to fill my brain with it! I.e. I have no idea what marshalling, apartments, etc. are.
I'm 80% sure something very similar to this did work...
Update: Sorry I previously said it was giving E_NOINTERFACE which was totally wrong - not sure why I thought that. 0x80070002 doesn't appear to be anything though...
Update 2: 0x80070002 means "The system cannot find the file specified." according to FormatMessage. I have no idea how that makes sense in this context though.

Follow Microsoft's guidelines when working with the default browser as you will encounter many issues on many os versions.
http://msdn.microsoft.com/en-us/library/windows/desktop/cc144154(v=vs.85).aspx#browser

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());
}
}
}
}

get_accChildCount returns 0 when it shouldn't

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.

I2C error when using the Windows Monitor Configuration Functions

I'm attempting to get/set the brightness level of the monitor through the Windows API. I've tried both the Low-Level Monitor Configuration Functions and the High-Level Monitor Configuration Functions, but they both seem to be breaking in the same place. In both cases I have no problem getting the HMONITOR handle and getting the physical monitor handle from the HMONITOR, but once I try to query the DDC/CI capabilities, I get an error saying "An error occurred while transmitting data to the device on the I2C bus."
The particular functions that cause this error are GetMonitorCapabilities for the high-level functions and GetCapabilitiesStringLength for the low-level functions. They both cause the same error.
This leads me to believe that maybe my monitor doesn't support DDC/CI, but I know my laptop's monitor brightness can be changed through the control panel, so it must be controlled through software somehow. Also I can successfully use the WMI classes in a PowerShell script to get/set the brightness as described on this page. Most things I've read suggest that most modern laptop screens do support DDC/CI.
Is there any way to find out what is causing this error or to get more information about it? I'm currently working in C++ in Visual Studio 2013 on Windows 7. I could probably use WMI in my C++ program if I can't get this current method working, but I thought I would ask here first.
Here's the code I currently have:
#include "stdafx.h"
#include <windows.h>
#include <highlevelmonitorconfigurationapi.h>
#include <lowlevelmonitorconfigurationapi.h>
#include <physicalmonitorenumerationapi.h>
#include <iostream>
#include <string>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD minBrightness, curBrightness, maxBrightness;
HWND curWin = GetConsoleWindow();
if (curWin == NULL) {
std::cout << "Problem getting a handle to the window." << std::endl;
return 1;
}
// Call MonitorFromWindow to get the HMONITOR handle
HMONITOR curMon = MonitorFromWindow(curWin, MONITOR_DEFAULTTONULL);
if (curMon == NULL) {
std::cout << "Problem getting the display monitor" << std::endl;
return 1;
}
// Call GetNumberOfPhysicalMonitorsFromHMONITOR to get the needed array size
DWORD monitorCount;
if (!GetNumberOfPhysicalMonitorsFromHMONITOR(curMon, &monitorCount)) {
std::cout << "Problem getting the number of physical monitors" << std::endl;
return 1;
}
// Call GetPhysicalMonitorsFromHMONITOR to get a handle to the physical monitor
LPPHYSICAL_MONITOR physicalMonitors = (LPPHYSICAL_MONITOR)malloc(monitorCount*sizeof(PHYSICAL_MONITOR));
if (physicalMonitors == NULL) {
std::cout << "Unable to malloc the physical monitor array." << std::endl;
return 1;
}
if (!GetPhysicalMonitorsFromHMONITOR(curMon, monitorCount, physicalMonitors)) {
std::cout << "Problem getting the physical monitors." << std::endl;
return 1;
}
std::cout << "Num Monitors: " << monitorCount << std::endl; // This prints '1' as expected.
wprintf(L"%s\n", physicalMonitors[0].szPhysicalMonitorDescription); // This prints "Generic PnP Monitor" as expected
// Call GetMonitorCapabilities to find out which functions it supports
DWORD monCaps;
DWORD monColorTemps;
// The following function call fails with the error "An error occurred while transmitting data to the device on the I2C bus."
if (!GetMonitorCapabilities(physicalMonitors[0].hPhysicalMonitor, &monCaps, &monColorTemps)) {
std::cout << "Problem getting the monitor's capabilities." << std::endl;
DWORD errNum = GetLastError();
DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
LPVOID buffer;
FormatMessage(flags, NULL, errNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, NULL);
wprintf(L"%s\n", buffer);
return 1;
}
// Same error when I use GetCapabilitiesStringLength(...) here.
// More code that is currently never reached...
return 0;
}
Edit: Also I should note that physicalMonitors[0].hPhysicalMonitor is 0, even though the monitor count and text description are valid and the GetPhysicalMonitorsFromHMONITOR function returns successfully. Any thoughts on why this might be?
This is a "wonky hardware" problem, the I2C bus it talks about is the logical interconnect between the video adapter and the display monitor. Primarily useful for plug & play. Underlying error code is 0xC01E0582, STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA. It is generated by a the DxgkDdiI2CTransmitDataToDisplay() helper function in the video miniport driver. It is the vendor's video driver job to configure it, providing the functions that tickle the bus and to implement the IOCTL underlying GetMonitorCapabilities().
Clearly you are device driver land here, there isn't anything you can do about this failure in your C++ program. You can randomly spin the wheel of fortune by looking for a driver update from the video adapter manufacturer. But non-zero odds that the monitor is at fault here. Try another one first.
I know its bad time to reply but i thought you should know.
The problem you are facing is because of the DDC/CI disabled on your monitor so you should go to the monitor settings and check if DDC/CI is disabled and if it is, then you have to enable it and run your code again. It would work. If you were not able to find DDC/CI option ( some of the monitor have a separate button for enabling/disabling the DDC/CI like the Benq's T71W monitor has a separate down arrow button to enable/disable DDC/CI ) then you should refer to your monitor's manual or contact the manufacturer.
Hope that helps. and sorry for late reply.
Best of luck. :)
As I read the original question, the poster wanted to control a laptop display using DDC/CI. Laptop displays do not support DDC/CI. They provide a stripped down I2C bus sufficient to read the EDID at slave address x50, but that's it.

What is the different between system and Administrator account when call SHGetSpecialFolderLocation() function?

I write two program, one is windows service run in system account(named xxService) and the other one is common application run in Administrator account(named xx);
They use the same code to get CSIDL_COMMON_DOCUMENTS directory. In most machine , they run well.
But some machine, the xxService can get the correct directory, the xx have failure in SHGetSpecialFolderLocation();
edit:
the program only run on Windows XP(sp3).
edit2:
Use SHGetFolderPathA() function to solve this problem.
My english is poor, everybody excuse me!
log:
[2964] [db](tid=1108)(pid=2964): SHGetSpecialFolderLocation() fail.hr=0x80070057, ierr=122
the detail error info:
//
// MessageId: E_INVALIDARG
//
// MessageText:
//
// One or more arguments are invalid
//
#define E_INVALIDARG _HRESULT_TYPEDEF_(0x80070057L)
ERROR_INSUFFICIENT_BUFFER
122 (0x7A)
The data area passed to a system call is too small.
code:
//C:\Users\Public\Documents
LPITEMIDLIST pidl;
LPMALLOC pShellMalloc;
HRESULT hr = S_FALSE;
hr = SHGetMalloc(&pShellMalloc);
if(SUCCEEDED(hr))
{
hr = SHGetSpecialFolderLocation(NULL,CSIDL_COMMON_DOCUMENTS,&pidl);
if(SUCCEEDED(hr))
{
if(!SHGetPathFromIDListW(pidl, strDbFilePath))
{
int ierr=GetLastError();
DebugMsgW((L"SHGetPathFromIDListW() fail., ierr=%d"), ierr);
}
DebugMsgW(L"DBpath=%s",strDbFilePath);
pShellMalloc->Free(pidl);
}
else
{
int ierr=GetLastError();
DebugMsgW((L"SHGetSpecialFolderLocation() fail.hr=0x%x, ierr=%d"), hr, ierr);
}
pShellMalloc->Release();
}
else
{
int ierr=GetLastError();
DebugMsgW((L"SHGetMalloc() fail.hr=0x%x, ierr=%d"), hr, ierr);
}
SHGetSpecialFolderLocation() (and any other function that returns an HRESULT) does not use GetLastError() to report error codes, since the HRESULT is the error code. Even SHGetPathFromIDList() is not documented as using GetLastError(), either. So the return value of GetLastError() is irrelevant in your example and needs to be removed to avoid confusion.
As for the E_INVALIDARG error, you are using a legacy function. CSIDL_COMMON_DOCUMENTS is known to fail in SHGetSpecialFolderLocation() on some systems. You need to use a newer function, such as SHGetFolderPath(), or SHGetKnownFolderPath() on Vista+.

HRESULT exception not caught in VS 2008

I've got a stange situation in visual studio 2008 C++. I work on code that was originally written for visual studio 2003, where everything works well. Now, ported to VS 2008, the exception handling, which unfortuantely exists widely in the code, does not work anymore. standard code example:
try
{
HRESULT hr = S_OK;
// do stuff...
if( FAILED( hr ) )
throw hr;
}
catch( HRESULT hr )
{
// error handling, but we never get here
}
catch( ... )
{
// ... not even here
}
Under VS 2008, no exception is encountered, but I get a crash somewhere else, indicating that the stack pointer must be screwed up. Did anybody come across this behaviour? Any help is appreciated.
After starting the debugger, go to Debug / Exceptions, and select for which exceptions the debugger should stop when the exception is thrown.
I get a crash somewhere else, indicating that the stack pointer must be screwed up.
How so? The fact of a crash has nothing to do with the stack. The bigger issue here is that we don't know what "hr" was declared as. If it was declared anything other than HRESULT, the compiler does not need to catch there.
Specifically, I believe HRESULT's definition changed with VS2005 in order to support 64 bit windows. If hr is declared as something else that happened to be the same as HRESULT before, but is not after you installed the new Windows SDK, then that's a likely cause.
Can't say more without seeing more code.
EDIT: The following works correctly:
#include <iostream>
#include <iomanip>
#include <windows.h>
int main()
{
try
{
HRESULT hr = E_FAIL;
std::cout << "Inside try\r\n";
if( FAILED( hr ) )
throw hr;
}
catch( HRESULT hr )
{
std::cout << "Error:" << std::hex << (unsigned int)hr;
}
system("pause > nul");
}
We need to see more code.