Control existing Internet Explorer with c++ - c++

Use case: Someone asked me to automate his internet explorer. Every day, he has to navigate to the same URL, enter the same credentials und log in. He'd like the computer to do that automatically: With an application that navigates to the URL, enters the post data and logs in automatically. He then can continue to navigate manually through the page.
So, if I want to control directly an existing internet explorer instance, how would I do that with C++?

After hours of research, I managed to open a new instance of the IE and navigating to a specific URL.
The steps I undertook:
Link the following libraries in the project options: libole32.a, liboleaut32.a, liboleacc.a, libuuid.a
Include cassert and exdisp.h at the beginning of the main cpp-file.
Insert the following code in the main cpp-file:
int main(void) {
HRESULT hret;
hret=CoInitialize(NULL);
assert(SUCCEEDED(hret));
CLSID clsid; // Get IE CLSID
hret=CLSIDFromProgID(L"InternetExplorer.Application",&clsid);
assert(SUCCEEDED(hret));
IUnknown *p; // Get IUnknown Interface
hret=CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IUnknown,reinterpret_cast<void**>(&p));
assert(SUCCEEDED(hret));
IDispatch *q; // Get IDispatch Interface from IUnknown
hret=p->QueryInterface(IID_IDispatch,reinterpret_cast<void**>(&q));
assert(SUCCEEDED(hret));
IWebBrowser2 *r; // Get IWebBrowser2 Interface from IDispatch
hret=q->QueryInterface(IID_IWebBrowser2,reinterpret_cast<void**>(&r));
assert(SUCCEEDED(hret));
IUnknown *s; // Get IUnknown from IWebBrowser2
hret=r->QueryInterface(IID_IUnknown,reinterpret_cast<void**>(&s));
assert(SUCCEEDED(hret));
///// Transitive //////////////////////////
assert(p==s);
////////////////////////////////////////
VARIANT vEmpty;
VariantInit(&vEmpty);
VARIANT vFlags;
V_VT(&vFlags) = VT_I4;
V_I4(&vFlags) = navOpenInNewWindow;
BSTR bstrURL = SysAllocString(L"http://www.google.com");
r->Navigate(bstrURL, &vFlags, &vEmpty, &vEmpty, &vEmpty);
r->Quit();
SysFreeString(bstrURL);
p->Release(); q->Release(); r->Release(); s->Release();
CoUninitialize(); return 0;
}

Related

Implement IDispatch Interface for MFC automation

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.

How to use the IVirtualDesktopManager interface in C++/Win32

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

Call IConnectionPoint::Advise cause a crash

In my project, I wrote a component which will be called in a service(COM server). There is another process will get this component's interface and register a callback interface through connection point. So the service could use the callback interface to do some feedback.
But I found that when I use the IConnectionPoint::Advise to register my callback interface cause a crash.
I implement the connection point manually. Here are part of my code:
class ATL_NO_VTABLE CUploadManager :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CUploadManager, &CLSID_UploadManager>,
public IUploadManager,
public IConnectionPointContainer,
public IConnectionPoint
...
// IConnectionPointContainer Functions
STDMETHODIMP EnumConnectionPoints(IEnumConnectionPoints **ppEnum);
STDMETHODIMP FindConnectionPoint(REFIID riid, IConnectionPoint **ppCP);
// IConnectionPoint Functions
STDMETHODIMP GetConnectionInterface(IID *pIID);
STDMETHODIMP GetConnectionPointContainer(IConnectionPointContainer **ppCPC);
STDMETHODIMP Advise(IUnknown *pUnkSink, DWORD *pdwCookie);
STDMETHODIMP Unadvise(DWORD dwCookie);
STDMETHODIMP EnumConnections(IEnumConnections **ppEnum);
In the client, I have get the component interface already, then I get the connection point and try to call function Advise:
...
CComPtr<IConnectionPointContainer> pCPC;
hr = pUnknown->QueryInterface(IID_IConnectionPointContainer, (LPVOID *)&pCPC);
if (FAILED(hr))
{
return ;
}
CComPtr<IConnectionPoint> pConnectionPoint;
hr = pCPC->FindConnectionPoint(__uuidof(_IEvents), &pConnectionPoint);
if (SUCCEEDED(hr))
{
DWORD dwCookie = 0;
CDataCallback* pCallback = new CDataCallback();
IUnknown* pUnknown = NULL;
pCallback->QueryInterface(IID_IUnknown, (void**)&pUnknown);
hr = pConnectionPoint->Advise(pUnknown, &dwCookie);
...
All interface pointers could be got successfully, but when I call Advise, the crash occurs. The strange thing is the crash is not happened in the Advise implementation, it seems occurs in the RPC call process, the call stack is like this:
003d0070() <----crash here
combase.dll!76eea3ab()
[Frames below may be incorrect and/or missing, no symbols loaded for combase.dll]
combase.dll!76ee9d00()
rpcrt4.dll!7612a53d()
mfc90ud.dll!CDialog::OnCmdMsg
...
But if the first parameter of Advise is NULL, the function Advise could be executed.
Here is the Sink code implemented in client:
class CDataCallback : public _IEvents
{
public:
CDataCallback() {}
~CDataCallback() {}
HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
{
*ppInterface = this;
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef()
{
return 1;
}
ULONG STDMETHODCALLTYPE Release()
{
return 0;
}
HRESULT __stdcall TestFunc()
{
...
return S_OK;
}
};
The interface _IEvents is generated in the component which will be used as a callback in the connection point. I just use this class to test the connection point, so the implementation is very simple.
I have no idea about how this crash occurs.
Thanks for all your help!
The problem is likely to be in your CDataCallback class. It's QueryInterface implementation is unsafe and returns interface pointer incorrectly.
class CDataCallback : public _IEvents
HRESULT __stdcall QueryInterface(REFIID iid, LPVOID* ppInterface)
{
*ppInterface = this;
return S_OK;
}
You have to check iid and return E_NOINTERFACE in case requested interface is not _IEvents. Basically you can set a breakpoint there and see if you have a call there prior to crash and check the arguments.
Your sink might be queried for other interfaces (IMarhsal etc) and you have to properly indicate that you are not implementing them, instead of returning unmatching pointer, use of which leads to undefined behavior.

Providing an IDispatch implementation for a connection point client

I've written a simple COM DLL inproc server with a single simple COM object. The COM object implements a connection point.
I know how to create an ATL client that derives from IDispEventImpl, and uses a sink map to simplify this process.
But, for the purposes of demonstration, I'd like to create a win32 console application that uses a class that calls my simple COM object, then acts as a connection point sink.
I've got no idea how to provide an implementation of IDispatch - can someone recommend documentation on this, as I can't find any (I've got ATL Internals, but this doesn't seem to cover what I need ).
Here's the class I've already got:
#pragma once
#include <iostream>
using namespace std;
// Because we're implementing a connection points sink (_IPogFarmEvents)
// in a non-ATL class, we must provide implementations for IUnknown and IDispatch.
class KidWithAPogFarm : public _IPogFarmEvents
{
private:
DWORD m_dwRefCount;
LONG m_lNumPogs;
public:
KidWithAPogFarm() :
m_dwRefCount (0),
m_lNumPogs (0)
{
}
~KidWithAPogFarm()
{
}
// -- IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
{
if (iid == DIID__IPogFarmEvents)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
if (iid == IID_IUnknown)
{
m_dwRefCount++;
*ppvObject = (void *)this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE AddRef()
{
m_dwRefCount++;
return m_dwRefCount;
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG l;
l = m_dwRefCount--;
if ( 0 == m_dwRefCount)
delete this;
return l;
}
// -- IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
{
return E_NOTIMPL;
}
STDMETHODIMP GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo )
{
return E_NOTIMPL;
}
STDMETHODIMP GetIDsOfNames(const IID &riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId )
{
return E_NOTIMPL;
}
STDMETHODIMP Invoke(DISPID dispIdMember, const IID &riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr )
{
return E_NOT_IMPL;
}
// -- IAntFarmEvents
STDMETHODIMP OnFarmCreated(LONG lInitialPopulation)
{
m_lNumPogs = lInitialPopulation;
cout << "The kid has a pog farm with " << m_lNumPogs << " pogs " << endl;
return S_OK;
}
};
Since you already have ATL you can examine its sources and see how IDispatchImpl does all that stuff. IDispatch methods are implemented therehby reading data from the type library in the same module since it's the easiest and most reliable way when a type library is already present.
It's also worth noting that it's a rather hard topic for making a demonstration on it - you'll need to write a lot of code that doesn't really bring any insight. IMO you'll be much better off if you implement an events interface that doesn't inherit from IDispatch but rather inherits directly from IUnknown - this will demonstrate how events work without dragging too much of attention to IDispatch inner workings.
I think the easiest way of doing this is through CreateStdDispatch
You can use this IDispatch implementation.
It's not exactly what you're looking for, but FireBreath uses IDispatchEx and connection points to provide an ActiveX control that runs in IE. Because FireBreath is an abstraction to allow plugins to be written once and used in all major browsers, the IDispatch interface needed to be written by hand -- including connection points.
The code may be a little confusing, since there is a templated mixin class used to provide IDispatch and ConnectionPoints to two different COM object classes, but it may help.
Mixin class
ActiveX Control
Second COM control (not OLE)
You've already accepted an answer, but perhaps it will still help. Sorry I didn't see the question sooner.

How to handle IHttpSecurity::OnSecurityProblem having a IWebBrowser2 object

I can't seem to understand how i give my implementation of the IHttpSecurity::OnSecurityProblem to my IWebBrowser2 object.
I know that i need to implement a class something like this:
class CServiceProvider : public IServiceProvider
{
public:
CServiceProvider();
~CServiceProvider();
// IUnknown
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject);
//QueryService
STDMETHODIMP QueryService(REFGUID guidService,REFIID riid,void **ppv);
private:
ULONG m_ulRefCnt;
};
And in the QueryService function when it requests the IID_IHttpSecurity i return my implementation of the IHttpSecurity interface.
But my problem is how i set the my service provider implementation on the IWebBrowser2 object and when?
My code is something like this:
IWebBrowser2 *_Browser;
IServiceProvider* pServiceProvider = NULL;
_Browser->QueryInterface(
IID_IServiceProvider,
(void**)&pServiceProvider);
IHttpSecurity* pi;
pServiceProvider->QueryService(IID_IHttpSecurity, &pi);
_Browser->Navigate(url.AllocSysString(),
&flags,
&target_frame_name,
&post_data,
&headers);
The question this works like i'm thinking if yes how i do this then, and if not can you explain how this works and can be setted?
PS: i only whant to implement the IID_IHttpSecurity interface, all other interfaces requested on the QueryService should do the default implementation provided by the system...
Thanks
I already figure out how this is done.
Using MFC we only need to implement CCustomOccManager that implements the COccManager in witch the implementation of CreateSite function returns an implementation of our COleControlSite (example CCustomControlSite). In this class you will need to override at least the QueryService function of IServiceProvider interface and in this implementation supply yours IHttpSecurity implementation (when required by the interface).
In the end the we register all this in the App InitInstance using the MFC function AfxEnableControlContainer.
Code:
// declare our custom control site to serve as the client site
class CCustomControlSite:public COleControlSite
{
public:
// constructor associates this site with the container
CCustomControlSite(COleControlContainer *pCnt):COleControlSite(pCnt){}
protected:
DECLARE_INTERFACE_MAP();
BEGIN_INTERFACE_PART(ServiceProvider, IServiceProvider)
// declare the interface method(s)
STDMETHOD(QueryService) (
/* [in] */ REFGUID guidService,
/* [in] */ REFIID riid,
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
END_INTERFACE_PART(ServiceProvider)
};
// declare our control container manager
class CCustomOccManager :public COccManager
{
public:
CCustomOccManager(){}
// creates an instance of our custom control site and associates it with the container
COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
{
CCustomControlSite *pSite = new CCustomControlSite(pCtrlCont);
return pSite;
}
};
In the App InitInstance simple call AfxEnableControlContainer on our implementation:
// Create a custom control container manager class so we can overide the client site
CCustomOccManager *pMgr = new CCustomOccManager;
// Set our control containment up but using our control container
// management class instead of MFC's default
AfxEnableControlContainer(pMgr);
If someone has the knowledge on how this is done without using MFC please let me know.
Thanks
Judging by the remarks in the documentation for IServiceProvider, it seems like your IOleClientSite object needs to implement IServiceProvider.