I need to get fullDescription property of a UI element using get_CurrentFullDescription method of UIAutomation library of c++ windows.
Issue is I have element as IUIAutomationElement instead of IUIAutomationElement6, get_CurrentFullDescription can only be invoked on element with IUIAutomationElement6 type.
How can I convert IUIAutomationElement to IUIAutomationElement6?
I am using HandlePropertyChangedEvent method to listen on changes in UI, which returns:
HRESULT HandlePropertyChangedEvent(
[in] IUIAutomationElement *sender,
[in] PROPERTYID propertyId,
[in] VARIANT newValue
);
https://learn.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-uiautomationclient-iuiautomationpropertychangedeventhandler-handlepropertychangedevent
Here, I need to access FullDescription property of sender element coming from HandlePropertyChangedEvent function.
#IInspectable mentioned QueryInterface hint in comments, based on that here is answer:
CComPtr<IUIAutomationElement> pElement;
// Code to initialize pElement goes here
CComPtr<IUIAutomationElement6> pElement6;
HRESULT hr = pElement->QueryInterface(&pElement6);
if (SUCCEEDED(hr))
{
// Use the pElement6 object here
}
Related
I have a COM project which does not support MFC and needs to communicate with another App through a MFC interface.
I cannot use directly in MFC interface with my COM project as it is not an MFC project.
A solution was to create an automation interface for the Of the MFC interface which I would be able to use as a layer through which my COM project can communicate to the MFC project.
As I understood, the automation app will run in a different thread. My COM project will invoke methods from the automation interface to call the MFC interface.
The Automation project and its IDL file were provided to me and provides me with a Pure Dispinterface.
Before integrating the implementation of the IDispatch interface to my COM project I decided to create a new C++ console project in order to focus on simply using the automation interface.
Using the documentation provided by microsoft (https://learn.microsoft.com/en-us/previous-versions/windows/desktop/automat/implementing-the-idispatch-interface) I implemented the IDispatch interface as follows:
#include "TTAutomate.h"
HRESULT hresult;
DISPID dispid;
DISPPARAMS dispparamsNoArgs = { NULL, NULL, 0, 0 };
EXCEPINFO excepinfo;
UINT nArgErr;
ClassFromIDL* m_ClassFromIDL;
_DTTAutomateEvents* m_DTTAutomateEvents;
_DTTAutomate* m_DTTAutomate;
hresult = OleInitialize(NULL);
// OLE function CoCreateInstance starts application using GUID.
hresult = CoCreateInstance(CLSID_TTfromIDL, NULL, CLSCTX_INPROC_SERVER, DIID__DTTAutomateEvents, (LPVOID*)m_Automate);
// Call QueryInterface to see if object supports IDispatch.
hresult = m_ClassFromIDL->QueryInterface(DIID__DTTfromIDL, (LPVOID*)pdisp);
// Retrieve the dispatch identifier for the SayHello method.
// Use defaults where possible.
OLECHAR* szMember = "SayHello";
DISPID dispid_Item;
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szFunction,
1,
LOCALE_USER_DEFAULT,
&dispid_Item);
First of all the implementation is a bit different as the one in the documentation because with the line :
hresult = CoCreateInstance(CLSID_Hello, NULL, CLSCTX_SERVER, IID_IUnknown, (void **)&punk);
I would get an error "amp undefined".
With the code above I currently get an error stating a const char * cannot initialize a OLECHAR *.
On my COM project I have a line:
LPOLESTR szFunction = OLESTR("SayHello");
which compiles but the same line on my test project does not work.
Did I miss some includes to add?
Did I not create the right kind of test project?
So I found a way to fix my code so that it compiles but not exactly sure if it is a proper solution:
#include "TTAutomate.h"
#include <string>
HRESULT hresult;
DISPID dispid;
DISPPARAMS dispparamsNoArgs = { NULL, NULL, 0, 0 };
EXCEPINFO excepinfo;
UINT nArgErr;
ClassFromIDL* m_ClassFromIDL;
IDispatch* m_Automate; // fixed
hresult = OleInitialize(NULL);
// OLE function CoCreateInstance starts application using GUID.
hresult = CoCreateInstance(CLSID_TTfromIDL, NULL, CLSCTX_INPROC_SERVER, DIID__DTTAutomateEvents, (void**)&m_Automate); // fixed
// Call QueryInterface to see if object supports IDispatch.
hresult = m_ClassFromIDL->QueryInterface(DIID__DTTfromIDL, (void**)&pdisp); // fixed
// Retrieve the dispatch identifier for the SayHello method.
// Use defaults where possible.
// here I create a wchar_t* variable as LPOESTR is casted from that.
std::string functionName = "SayHello";
int wchars_num = MultiByteToWideChar(CP_UTF8, 0, functionName.c_str(), -1, NULL, 0);
wchar_t* wstr = new wchar_t[wchars_num];
LPOLESTR szMember = wstr;
DISPID dispid_Item;
hresult = pdisp->GetIDsOfNames(
IID_NULL,
&szFunction,
1,
LOCALE_USER_DEFAULT,
&dispid_Item);
I now get a E_NOINTERFACE No such interface supported but it is probably unrelated to this topic.
I need to search for maximized windows from Win32 (by using EnumWindows) but I also want to filter for windows which are on the current virtual desktop. On MSDN, I found a page about the IVirtualDesktopManager interface but there seems to be no information on how to use this interface.
IVirtualDesktopManager::IsWindowOnCurrentVirtualDesktop(/*args...*/);
Throws the following error:
Non static member reference must be relative to a specific object
VirtualDesktopManager mVirtualDeskManager;
mVirtualDesktopManager.IsWindowOnCurrentVirtualDesktop(/args...*/)
Throws this error:
Incomplete type is not allowed
I have only found solutions on using the IVirtualDesktopManager interface in C# yet.
IVirtualDesktopManager is a COM interface. You need to instantiate the COM object that implements the interface.
Based on code from this blog, you can use IServiceProvider to access IVirtualDesktopManager (and IVirtualDesktopManagerInternal, which has much more functionality than IVirtualDesktopManager has), eg:
IServiceProvider* pServiceProvider = NULL;
HRESULT hr = ::CoCreateInstance(
CLSID_ImmersiveShell, NULL, CLSCTX_LOCAL_SERVER,
__uuidof(IServiceProvider), (PVOID*)&pServiceProvider);
if (SUCCEEDED(hr))
{
IVirtualDesktopManager *pDesktopManager = NULL;
hr = pServiceProvider->QueryService(__uuidof(IVirtualDesktopManager), &pDesktopManager);
if (SUCCEEDED(hr))
{
BOOL bIsOnCurrentDesktop = FALSE;
hr = pDesktopManager->IsWindowOnCurrentVirtualDesktop(hWnd, &bIsOnCurrentDesktop);
if (SUCCEEDED(hr))
{
// use bIsOnCurrentDesktop as needed...
}
pDesktopManager->Release();
}
pServiceProvider->Release();
}
I hope this falls within the realm of this forum:
I want to use the windows shell(?) to allow users to select a number of files before allowing my programme to do a few things to them. For this I found the MSDN sample "CommonFileDialogModes" - http://msdn.microsoft.com/en-us/library/windows/desktop/dd940350%28v=vs.85%29.aspx
In the sample under this class:
class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
they have this function:
// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
// if this button is in the "Add" mode then do this, otherwise return S_OK
IFileOpenDialog *pfod;
HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
IShellItemArray *psia;
hr = pfod->GetSelectedItems(&psia);
if (SUCCEEDED(hr))
{
ReportSelectedItems(pfd, psia);
psia->Release();
}
pfod->Release();
}
return S_FALSE; // S_FALSE keeps the dialog up; return S_OK to allow it to dismiss.
}
which calls:
void ReportSelectedItems(IUnknown *punkSite, IShellItemArray *psia)
{
DWORD cItems;
HRESULT hr = psia->GetCount(&cItems);
for (DWORD i = 0; SUCCEEDED(hr) && (i < cItems); i++)
{
IShellItem *psi;
hr = psia->GetItemAt(i, &psi);
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = GetIDListName(psi, &pszName);
// .. I've cut some of this out for the example
CoTaskMemFree(pszName);
}
psi->Release();
}
}
}
Now I know pszName contains the names of the files selected. So I can add some extra code in to write this to disk. That works fine. But I dont want to write it to disk. I want to pass it back to the original functions that called this. The arguments for ReportSelectedItems can be altered, but IFACEMETHODIMP OnFileOk(IFileDialog *pfd) cannot as it is inherited. Adding a vector& file_names to the argument will stop it compiling.
So how should I deal with this? I could use a global variable for file_names, but everything I am learning about programming is telling me not to. It would be a quick fix, but I worry that would encourage me to be lazy in the future. I find it difficult to read the windows code and I don't really want to delve too much into the details of it. I can't even find what is calling the OnFileOk function, even though I know it is from one of the two base classes.
Do I really need to work at understanding all the library code just to get this one function doing what I'd like? Is there an faster way of going about this?
So to summarize, how would I get information from this inherited function without using a global variable or writing to disk? As I mentined before, I don't have much of a grasp of the code I am working with. And for future reference, how should I deal with this type of situation? I use c++ and would like to avoid c# and c as much as possible.
Thanks as always.
It seems a fairly big omission for Microsoft to have left out any sort of user data associated with the IFileDialog callbacks, but that does seem to be the case.
I'm assuming that simply calling GetSelectedItems() once the dialog returns is something you don't want to do for some reason - because that would obviously be the simplest solution.
From a quick look at the docs one way you may be able to pass data back from the event callback is using the owner window that you pass to IFileDialog::Show() (which is actually IModalWindow::Show()).
In the event handler, you get given the IFileDialog* pointer. From this, you can QI the address of the IOleWindow interface which will give you the dialog's window:
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
CComPtr<IOleWindow> pWindow;
if (SUCCEEDED(pfd->QueryInterface(IID_IOleWindow, reinterpret_cast<void**>(&pWindow))))
{
HWND hwndDlg;
if (SUCCEEDED(pWindow->GetWindow(&hwndDlg)))
{
HWND hwndOwner;
if (hwndOwner = GetWindow(hwndDlg, GW_OWNER))
{
// hwndOwner is the owner window of the dialog
}
}
}
// more code
}
Now assuming that hwndOwner is your own window, you can associate any data you like with it using SetProp()/GetProp() - so you could use this as a mechanism to pass data back from within the callback.
A simple solution was to add member data inside the inherited class and link it from the constructor:
class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
CFileOpenBasketPickerCallback(vector<wstring>& files) : files_(files)
{
}
// functions
private:
vector<wstring>& files_;
};
When constructing the object
vector<std::wstring> files
CFileOpenBasketPickerCallback foacb(files);
And in IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
ReportSelectedItems(pfd, psia, files_);
ReportSelectedItems is not a member so you can alter the arguments.
Since the function's return value is utilized for error message, how do the functions return the necessary information back to the caller?
For example:
IDirect3D9::CreateDevice method
So if you take a look at that link you'll notice that it has some parameters marked Out this is important because this denotes what will be returned to the caller.
HRESULT CreateDevice(
[in] UINT Adapter,
[in] D3DDEVTYPE DeviceType,
[in] HWND hFocusWindow,
[in] DWORD BehaviorFlags,
[in, out] D3DPRESENT_PARAMETERS *pPresentationParameters,
[out, retval] IDirect3DDevice9 **ppReturnedDeviceInterface
);
In the above sample (copied and pasted from the MSDN link), you'll notice a parameter ppReturnedDeviceInterface is marked as being ** or a pointer to a pointer, the caller would pass in a the address of their pointer and would be returned a pointer at that address. Also the D3DPRESENT_PARAMETERS structure passed in to pPresentationParameters will be updated on return, as noted by the out annotation.
Ex:
IDirect3DDevice9 *pDevice = NULL;
HRESULT hr = pD3D->CreateDevice(
D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hwnd,
pPresentationParams,
&pDevice);
if(SUCCEEDED(hr))
{
//pDevice should be non null at this point
}
I want my BHO to listen to onmousedown event of some element in a certain webpage.
I have all the code that find the specific element, and in msdn its says that I need to use the get_onmousedown event. I came up with this code.
CComQIPtr<IHTMLElement> someElement;
VARIANT mouse_eve;
someElement->get_onmousedown(&mouse_eve);
The question is, how do I tell it to run some function when this event occurs?
v - VARIANT of type VT_DISPATCH that specifies the IDispatch interface of an object with a default method that is invoked when the event occurs.
Event handlers in this context are COM instances that implement IDispatch - so you need to pass a pointer to an IDispatch that your event handler object implements:
CComQIPtr<IDispatch> spDisp(spMyHandler); // something like that
someElement->put_onmousedown(CComVariant(spDisp));
Note: put_ instead of get_ - you want to register a handler.
On this, IDispatch::Invoke() gets called with:
wFlags containing DISPATCH_METHOD ("a method is getting invoked")
dispIdMember being 0 / DISPID_VALUE ("the default method")
Put together this should become something like:
HRESULT MyHandler::Invoke(DISPID dispIdMember, REFIID, LCID, WORD wFlags,
DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT*)
{
// ...
if((wFlags & DISPATCH_METHOD) && (dispIdMember == DISPID_VALUE))
{
// ...
}
}