Custom Download Manager IWebbrowser2 - c++

Sorry if my question is previously answered here but I have, for days, searched the internet including SO with no solution.
Basically I want to Implement Download Manager for IE webbrowser control (Not IDE itself).
I have read a lot on MSDN and among them is this link which shows how to create it.
The Problem with this example (and my problem in that case) is where do I register/apply the IServiceProvider to my web browser. The Article does not say. However searching I found this question and it say I quote
Use CAxWindow::QueryHost to get IObjectWithSite pointer. Call SetSite
passing your IServiceProvider implementation.
Unfortunately I don't use or know anything about ATL as I use wxWidgets. So where do I get that in wxWebview or "vanilla" MS COM?
here is what I have so far
HRESULT wxDownloadMgr::Download(IMoniker *pmk, IBindCtx *pbc,DWORD dwBindVerb,
LONG grfBINDF,BINDINFO *pBindInfo, LPCOLESTR pszHeaders,LPCOLESTR pszRedir,UINT uiCP )
{
// Get URL
LPOLESTR urlToFile;
HRESULT result = pmk->GetDisplayName( pbc, NULL, &urlToFile );
//OLECHAR is simply a wchar_t and an LPOLESTR is a wide character string (e.g. wchar_t*).
wxString url(urlToFile);
wxWebViewEvent event(wxEVT_COMMAND_WEB_VIEW_DOWNLOAD_BEGINS,GetId(), url, "");
event.SetEventObject(this);//WHICH OBJECT TO SET HERE????????
HandleWindowEvent(event);
::MessageBox(NULL,"Download","Download Manager",MB_OK);
return S_OK;
}
STDMETHODIMP wxServiceProvider::QueryService(REFGUID guidService,
REFIID riid,
void **ppv)
{
HRESULT hr = E_NOINTERFACE;
if (guidService == SID_SDownloadManager && riid == IID_IDownloadManager)
{
// Create new DownloadMgr object using ATL.
CComObject<wxDownloadMgr>* pDownloadMgr;
hr = CComObject<wxDownloadMgr>::CreateInstance(&pDownloadMgr);
// Query the new CDownloadMgr object for IDownloadManager interface.
hr = pDownloadMgr->QueryInterface(IID_IDownloadManager, ppv);
}
return hr;
}

You can override wxActiveXContainer::QueryClientSiteInterface to add your own interface implementation like IServiceProvider or IDocHostUIHandler to the client site. An example can be found in the wxIEContainer class.

Related

How may I bind a Toast progress bar with my C++ Win32 application?

I'm creating a c++ Win32 application which should show some Toast notifications. One of them contains a progress bar. I need to bind its value with my application in order to update it while the status changes in the currently running operation.
The Toast interface containing the progress bar is defined as follow:
<toast>
<visual>
<binding template='ToastGeneric'>
<text>Backup in progress</text>
<progress title='Working folder' value='0' status='Starting backup...' valueStringOverride='0/1 files' />
</binding>
</visual>
</toast>
To show the Toast notification I call the following function, by passing the above interface in the toastContent string:
bool ShowToastNotification(const std::wstring toastContent)
{
if (!toastContent.length())
return false;
// build XML
::CComPtr<ABI::Windows::Data::Xml::Dom::IXmlDocument> pDoc;
HRESULT hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(toastContent.c_str(), &pDoc);
if (FAILED(hr))
return false;
// create the notifier. Classic Win32 apps MUST use the Compat method to create the notifier
::CComPtr<ABI::Windows::UI::Notifications::IToastNotifier> pNotifier;
hr = DesktopNotificationManagerCompat::CreateToastNotifier(&pNotifier);
if (FAILED(hr))
return false;
// create the Toast notification (using helper method from Compat library)
::CComPtr<ABI::Windows::UI::Notifications::IToastNotification> pToast;
hr = DesktopNotificationManagerCompat::CreateToastNotification(pDoc, &pToast);
if (FAILED(hr))
return false;
// get access to IToastNotification4, which should contain the binding methods
::CComPtr<ABI::Windows::UI::Notifications::IToastNotification4> pToastData;
pToast->QueryInterface(ABI::Windows::UI::Notifications::IID_IToastNotification4, (void**)&pToastData);
ABI::Windows::UI::Notifications::INotificationData* pData = nullptr;
// FIXME how may I create a valid INotificationData here?
// bind the data with the interface
hr = pToastData->put_Data(pData);
if (FAILED(hr))
return false;
// show it
hr = pNotifier->Show(pToast);
if (FAILED(hr))
return false;
return true;
}
As you can see in the FIXME part above, there is a missing part to achieve the binding before showing the Toast notification, and unfortunately there is absolutely NO documentation from Microsoft which explain how to perform that in a simple C++ Win32 application. The below one explain clearly how to perform a such binding, but only in C#:
https://learn.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/toast-progress-bar?tabs=builder-syntax
Even without documentation I could find that the IToastNotification4 seems to be a valid interface to attach a INotificationData container to my Toast, similar to what is done in the above mentioned documentation. However I don't know how to create a valid INotificationData instance. I saw that a CreateNotificationDataWithValuesAndSequenceNumber() function exists in the INotificationDataFactory interface, but same thing, I face a cruel miss of documentation about how to obtain a such interface.
Can someone explain me how I may bind my C++ Win32 application with my progress bar on my Toast notification, in order to send it messages to update its interface? Or can someone explain me how to get the above mentioned interfaces, in order to perform something similar to the above mentioned C# documentation? Or at least can someone point me a documentation which may give information about the INotificationData and INotificationDataFactory interfaces, and how to use them to perform the binding with my Toast progress bar?
If a WinRT class can be created from C#, it means it's activatable (or it's associated with another "statics" class which is), which you can check if you go to NotificationData documentation:
[Windows.Foundation.Metadata.Activatable(262144, "Windows.Foundation.UniversalApiContract")]
[Windows.Foundation.Metadata.Activatable(typeof(Windows.UI.Notifications.INotificationDataFactory), 262144, "Windows.Foundation.UniversalApiContract")]
[Windows.Foundation.Metadata.ContractVersion(typeof(Windows.Foundation.UniversalApiContract), 262144)]
[Windows.Foundation.Metadata.MarshalingBehavior(Windows.Foundation.Metadata.MarshalingType.Agile)]
[Windows.Foundation.Metadata.Threading(Windows.Foundation.Metadata.ThreadingModel.Both)]
public sealed class NotificationData
{...}
If it's activatable, then you just have to find it's activation class id, and call RoActivateInstance . The class id is visible in the corresponding .h file (here windows.ui.notifications.h) and it's usually very straightforward: <namespace> + '.' + <class name>:
...
extern const __declspec(selectany) _Null_terminated_ WCHAR RuntimeClass_Windows_UI_Notifications_NotificationData[] = L"Windows.UI.Notifications.NotificationData";
...
So here is how you can create it:
HRESULT CreateNotificationData(INotificationData** data)
{
if (!data)
return E_INVALIDARG;
*data = nullptr;
IInspectable* instance;
auto hr = RoActivateInstance(HStringReference(RuntimeClass_Windows_UI_Notifications_NotificationData).Get(), &instance);
if (FAILED(hr))
return hr;
hr = instance->QueryInterface(data);
instance->Release();
return hr;
}
PS: using C++/WinRT is usually easier to use than ATL, WRL or WIL for WinRT types.

Excel Automation - Filter COM events using FindConnectionPoint

I use C++ COM to listen to events IID_ApplicationEvents of an existing Excel application. My code is placed in a separate EXE file.
The Connection point is initialized with the following code:
IConnectionPointContainer* pCPC = NULL;
hr = pEventSource->QueryInterface(IID_IConnectionPointContainer,
(void**)&pCPC);
if (SUCCEEDED(hr)){
hr = pCPC->FindConnectionPoint(IID_ApplicationEvents, &m_pConnectionPoint);
if (SUCCEEDED(hr)){
hr = m_pConnectionPoint->Advise(this, &m_dwConnection);
if (FAILED(hr))
LOG_ERROR(L"m_pConnectionPoint->Advise - FAIL");
}
else
LOG_ERROR(L"FindConnectionPoint - IID_ApplicationEvents: FAIL");
pCPC->Release();
}
else
LOG_ERROR(L"IID_IConnectionPointContainer: FAIL");
return hr;
I am receiving the Excel events via the Invoke function:
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams,
VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
UINT* puArgErr)
My question is following: In order to improve the performance of my app and not slow down the running Excel, I am interested only in OnBeforeSave and OnClose events. Is there a way to filter out other Excel events in my process (I dont want my Invoke function to be called at all)?
As Igor mentioned in a comment above, you cannot simply filter events. However, you can build something yourself to do this.
Here's one way:
Create a COM Add-in that will be loaded in the Excel process. (See IDTExtensibility2).
When OnConnection is called:
Create an instance of your own homegrown event filter class (implement IDispatch to handle events and IConnectionPointContainer to raise events).
Make that event filter object "public" by assigning its IUnknown pointer to the Object property of the AddIn object supplied as the AddInIst parameter to OnConnection.
When your event filter object receives an event, filter it according to your rules. If the event isn't filtered out, raise it from the filter object.
In your external process, find your COM Add-In in the AddIns2 collection and read its Object property. You can then treat that as your event source instead of the Application object.

IE BHO - DISPID_FILEDOWNLOAD being called for page loads?

I am implementing an Internet Explorer Browser Helper Object that should catch the DISPID_FILEDOWNLOAD event.
I first implemented this in C# which worked great except that I also need the cookies that go with the URL so need to call InternetGetCookiesEx. As .NET runs in it's own process it does not return me the session cookies so is no good.
I then wrote a quick test DLL in C++ so that it is loaded into the same process as IE which works great for the cookies but I now have a new problem:
I am getting calls to DISPID_FILEDOWNLOAD in my Invoke function for each page load when I only want them for an actual download.
In the C# version I only got a call to WebBrowser.FileDownload for an actual download but it seems that the C++ version is sending a DISPID_FILEDOWNLOAD even for each page load.
STDMETHODIMP CIEHlprObj::Invoke(
DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pvarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr
)
{
USES_CONVERSION;
if (!pDispParams)
return E_INVALIDARG;
LPOLESTR lpURL = NULL;
m_spWebBrowser2->get_LocationURL(&lpURL);
int i = 0;
switch (dispidMember)
{
case DISPID_BEFORENAVIGATE2:
case DISPID_BEFORENAVIGATE:
sCurrentFile=NULL;
if (pDispParams->cArgs >= 5 && pDispParams->rgvarg[5].vt == (VT_BYREF | VT_VARIANT))
{
CComVariant varURL(*pDispParams->rgvarg[5].pvarVal);
varURL.ChangeType(VT_BSTR);
char* myStr = OLE2T(varURL.bstrVal);
if (myStr)
{
sCurrentFile = AllocateString(myStr);
sCurrentFileW = varURL.bstrVal;
}
}
break;
case DISPID_FILEDOWNLOAD:
// CALLED FOR EACH PAGE LOAD!
if(sCurrentFile)
{
TCHAR cookies[8192];
DWORD size = 8192;
BOOL ret = InternetGetCookieEx(sCurrentFile,
0,
cookies,
&size,
INTERNET_COOKIE_HTTPONLY,
0);
::MessageBox(0, sCurrentFile, "Downloading called multiple times!!", MB_OK);
}
break;
default:
break;
}
return S_OK;
}
Is there some filter that needs checking somewhere to know if a DISPID_FILEDOWNLOAD event relates to a file load or an actual file download?
Many thanks
UPDATE:
On closer inspection it seems that the C# managed code version is actually doing the same, I just didn't notice it the firs time around.
It seems that the FileDownload event is being called in the following circumstances:
New window / tab opened
New domain connected to (maybe a new keep-alive connection?)
An actual download
Obviously I only want the even on the actual download event.
As a possible solution to this I notice MS provide a ActiveDocument (BOOL) argument along with the event.. according to the Microsoft documentation the ActiveDocument argument means:
A Boolean that specifies whether the file is an Active Document
http://msdn.microsoft.com/en-us/library/bb268220(v=vs.85).aspx
Not very helpful but if I log the DISPID_FILEDOWNLOAD events to a text file and look at them later it appears that I should ignore all events where ActiveDocument = true
Getting the params is quite easy:
BOOL cancel = *pDispParams->rgvarg[0].pboolVal;
BOOL active = pDispParams->rgvarg[1].boolVal;
So the code would just need updating to something like:
if (active)
return S_OK;
I can only assume that this is some event the IE is sending telling the BHO that a page is downloading.. not a file, although that should mean that I get a DISPID_FILEDOWNLOAD event for each page load, which I don't.. only new tabs/browser instances and some new connections.
Hopefully someone else will be able to contribute to this and clarify what is the best way of handling this as it feels like a bit of a hack.

Visual C++: InvokeHelper() function

I am deciphering a huge project that uses COM, which I am completely new to. It's quite confusing and I can't figure out how everything interacts. All I see is InvokeHelper(...) where I would expect to see a big amount of code. What is InvokeHelper()? What does it do?
Thank you for any help.
Even though it's a late answer, I'd like to post it here as I have spent a couple of days to figure out how it's working. It may be interesting for someone else.
Below is the path how to get to the real code from InvokeHelper() call:
InvokeHelper() should be called for an object of a class, inherited from CWnd with DISPID specified, where DISPID is something like 0x00000261
The class should have inside a call to a method CreateControl() with a GUID of a COM class
The COM class with the GUID should be COM coclass with at least one IDL interface
The IDL interface should implement a method with the attribute [id(DISPID)]. This is the same DISPID as in item 1
Look for implementation of the interface and find the method with this id attribute
VoilĂ !
Sure, if you don't have a source code of the COM class with the CLSID you cannot take a look inside the method, but at least, you can find its name as follows:
DISPID dispidCommand = 0x1; /// This is the dispid, you're looking for
COleDispatchDriver driver;
BOOL bRes = driver.CreateDispatch(GetClsid());
ASSERT(bRes);
HRESULT hr;
CComPtr<ITypeInfo> pti;
hr = driver.m_lpDispatch->GetTypeInfo(0, GetUserDefaultLCID(), &pti);
ASSERT(SUCCEEDED(hr));
UINT nCount = 0;
CComBSTR bstrName; // Name of the method, which is called via DISPID
hr = pti->GetNames(dispidCommand, &bstrName, 1, &nCount);
ASSERT(SUCCEEDED(hr));
I hope it helps someone.
Take care.

How to prevent crashing if com dll isnt registered

From some old c++ code im trying to use a com dll, it works fine when the dll is registered, but it crahses if the dll isnt registered.
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IGetTestPtr ptest(__uuidof(tester));
"Use method from the dll"
// Uninitialize COM.
CoUninitialize();
Is it anyway to check if the dll has been registered, before calling IGetTestPtr ptest(__uuidof(tester))?
Or what is the correct way to prevent the crash?
Calling CreateInstance on your object will return an HRESULT that can be tested for success:
IGetTestPtr p = null;
HRESULT hRes = p.CreateInstance( __uuidof(tester) );
bool bSuccess = SUCCEEDED(hRes);
This assumes you've created an interface wrapper around your type library using Visual Studio, where COM Smart Pointers are used in the interface (this gives you the CreateInstance method).
If the DLL is registered, there is a record in HKCR/CLSID/{uuidof(tester)} (curly brackets do matter).
Actually, if it's not, then CoCreateInstance will return an error. Check for this error before using the pointer.
If the COM class is not registered then CoCreateInstance will return REGDB_E_CLASSNOTREG. You should check for general success or failure by using the SUCCEEDED() or FAILED() macros. You also need to create the object properly - see MDSN for an introduction to COM. For example:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IGetTestPtr ptr = NULL;
hr = CoCreateInstance(CLSID_MyObjectNULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, ptr)
if (SUCCEEDED(hr))
{
Do something with your COM object
...
// Don't forget to release your interface pointer or the object will leak
ptr->Release();
}
hr = CoUninitialize();
}
return hr;
If I recall correctly this "#import" styled COM framework throws exceptions. Add a try-catch block around the code. Google tells me the exceptions are of type _com_error.
Unless you twiddle the params on #import to prevent this, all failing COM calls are going to throw exceptions. You're going to have to turn this off (#import "raw" interfaces I think does it - look at the #import docs) or get real familiar with these exceptions.