Accessing HTML source on event DownloadComplete? - c++

I am working on a ads/popup blocker BHO and I am trying to access the html of a website from the event "downloadcomplete", so I can filter all the ads and malicious uris.
My code looks something like this:
case DISPID_DOWNLOADCOMPLETE:
{
if(iBrowser) //IWebBrowser2*
{
HRESULT hr;
IUnknown *pUnkBrowser = NULL;
hr = iBrowser->QueryInterface(IID_IUnknown, (void**)&pUnkBrowser);
if( SUCCEEDED(hr) && pUnkBrowser!=NULL)
{
if( SUCCEEDED(hr) )
{
IDispatch* pHtmlDocDispatch = NULL;
IHTMLDocument2 * pHtmlDoc = NULL;
hr = iBrowser->get_Document (&pHtmlDocDispatch);
if (SUCCEEDED (hr) && (pHtmlDocDispatch != NULL))
{
hr = pHtmlDocDispatch->QueryInterface (IID_IHTMLDocument2, (void**)&pHtmlDoc);
if (SUCCEEDED (hr) && (pHtmlDoc != NULL))
{
IHTMLElement *pBody = 0;
pHtmlDoc->get_body( &pBody );
// I want to get the html here and filter out the ads but pBody is always null
if(pHtmlDoc) pHtmlDoc->Release();
}
if(pHtmlDocDispatch) pHtmlDocDispatch->Release();
}
}
if(pUnkBrowser) pUnkBrowser->Release();
}
}
return S_OK;
}
break;
How could I access and modify the html from this event?

Wrong event, you can "play" with the dom on the DocumentComplete, not the DownloadComplete.
Also I would advise you to use CComPtr, that way you don't need to call the release() on every interface.

Related

How to host a preview handler in a dialog

I'm attempting to host a file preview handler within a dialog. I've set up an event sink for selection changes in Explorer. When the selection changes, I feed the selected shell item to the dialog, which in turn feeds it to a function that prepares the preview frame.
In general, it successfully loads the correct handler and displays the contents of the file, but for certain file types (namely, Excel and Word files), it runs into various problems like focus-loss or flashing. Here's a demo of the Excel preview handler messing up focus (and by mess up, I mean it wrongfully steals the focus from Explorer, which I'd like to maintain focus):
Word files may load successfully once, but they'll subsequently fail, especially if Word is opened.
As for the code:
For starters, here's my function for obtaining the preview handler from the file extension. This seems to work fine:
HRESULT PreviewHandlerFromExt(LPCWSTR pszExt, IPreviewHandler** ppph)
{
WCHAR szCLSID[CLSID_LEN] = { 0 };
DWORD cchOut = CLSID_LEN;
HRESULT hr = AssocQueryString( ASSOCF_INIT_DEFAULTTOSTAR,
ASSOCSTR_SHELLEXTENSION,
pszExt,
L"{8895b1c6-b41f-4c1c-a562-0d564250836f}",
szCLSID,
&cchOut );
if (FAILED(hr))
{
return hr;
}
CLSID clsid;
hr = CLSIDFromString(szCLSID, &clsid);
if (FAILED(hr))
{
return hr;
}
CComPtr<IUnknown> punk;
hr = punk.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER);
if (FAILED(hr))
{
return hr;
}
CComPtr<IPreviewHandler> pPrevHandler;
hr = punk->QueryInterface(&pPrevHandler);
if (FAILED(hr) || !pPrevHandler)
{
return hr;
}
return pPrevHandler.CopyTo(ppph);
}
And now here's the function in my dialog that prepares the preview, given a shell item (m_pPreviewHandler is the active preview handler, IDC_PREVIEWFRAME is a placeholder in the dialog for the preview pane, and m_mapExtsToPreviewHandlers is just a map for storing preview handlers as the user comes across them):
void CMyDialog::ShowPreview(IShellItem* pShItem)
{
HRESULT hr;
if (m_pPreviewHandler)
{
m_pPreviewHandler->Unload();
m_pPreviewHandler.Release();
}
CComHeapPtr<WCHAR> pszPath;
hr = pShItem->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (FAILED(hr))
{
return;
}
LPWSTR pszExt = CharLower(PathFindExtension(pszPath));
auto it = m_mapExtsToPreviewHandlers.find(pszExt);
if (it == m_mapExtsToPreviewHandlers.end())
{
hr = PreviewHandlerFromExt(pszExt, &m_pPreviewHandler);
if (FAILED(hr) || !m_pPreviewHandler)
{
return;
}
m_mapExtsToPreviewHandlers[pszExt] = m_pPreviewHandler;
}
else
{
m_pPreviewHandler = m_mapExtsToPreviewHandlers[pszExt];
}
CComPtr<IInitializeWithFile> pInitWithFile;
hr = m_pPreviewHandler->QueryInterface(&pInitWithFile);
if (SUCCEEDED(hr))
{
hr = pInitWithFile->Initialize(pszPath, STGM_READ);
if (FAILED(hr))
{
return;
}
}
else
{
CComPtr<IInitializeWithStream> pInitWithStream;
hr = m_pPreviewHandler->QueryInterface(&pInitWithStream);
if (SUCCEEDED(hr))
{
CComPtr<IStream> pStream;
hr = SHCreateStreamOnFile(pszPath, STGM_READ, &pStream);
if (FAILED(hr) || !pStream)
{
return;
}
hr = pInitWithStream->Initialize(pStream, STGM_READ);
if (FAILED(hr))
{
return;
}
}
}
CWindow wndPreviewFrame( GetDlgItem(IDC_PREVIEWFRAME) );
CRect rectPreviewFrame;
wndPreviewFrame.GetClientRect(&rectPreviewFrame);
hr = m_pPreviewHandler->SetWindow(wndPreviewFrame, &rectPreviewFrame);
if (FAILED(hr))
{
return;
}
hr = m_pPreviewHandler->DoPreview();
if (FAILED(hr))
{
return;
}
hr = m_pPreviewHandler->SetRect(&rectPreviewFrame);
if (FAILED(hr))
{
return;
}
}
Does anyone know what I'm doing wrong or what might fix these focus problems?
I've also tried placing LockSetForegroundWindow at various places, but no lock.
Also, this is what the dialog resource looks like:

Programmatically pre-select using IFileDialog in C++

I am perplexed on whether IFileDialog has the capability of programmatically selecting an item inside the dialog even with out the user selecting.
Ex.
I was hoping to achieve opening IFileDialog then selecting a default item/folder inside the dialog.
Ex.
By the way, in the picture above. I did manually click/select the folder.
But I was hoping to implement a defaultly selected item inside the IFileDialog.
The procedure suggested by zett42 works. You need to implement your own version of IFileDialogEvents. After hooking with IFileDialog::Advise you can query your way to IShellView and that lets you change the selection.
This example is a little silly because I'm forcing the directory as well to be sure I have a file I can select.
struct MyIFileDialogEvents : public IFileDialogEvents {
bool forcedDir, forcedSel;
MyIFileDialogEvents() : forcedDir(false), forcedSel(false) {}
...
};
STDMETHODIMP MyIFileDialogEvents::OnFolderChanging( IFileDialog *pfd, IShellItem*psiFolder)
{
if (forcedDir) return S_OK; else forcedDir = true;
IShellItem*psiwindir;
HRESULT hr = SHGetKnownFolderItem(FOLDERID_Windows, KF_FLAG_DEFAULT, NULL, IID_IShellItem, (void**) &psiwindir);
if (!hr)
{
hr = pfd->SetFolder(psiwindir); // MSDN says it is OK to change the folder in OnFolderChanging with SetFolder
psiwindir->Release();
}
if (FAILED(hr)) forcedSel = true;
return S_OK;
}
STDMETHODIMP MyIFileDialogEvents::OnFolderChange(IFileDialog *pfd)
{
if (forcedSel || !forcedDir) return S_OK; else forcedSel = true;
IShellItem*psiwindir, *psiexp;
HRESULT hr = SHGetKnownFolderItem(FOLDERID_Windows, KF_FLAG_DEFAULT, NULL, IID_IShellItem, (void**) &psiwindir);
if (!hr)
{
hr = SHCreateItemFromRelativeName(psiwindir, L"Explorer.exe", NULL, IID_IShellItem, (void**) &psiexp);
psiwindir->Release();
if (!hr)
{
IServiceProvider*pSP;
IShellBrowser*pSB;
IShellView*pSV;
if (!pfd->QueryInterface(IID_IServiceProvider, (void**) &pSP))
{
if (!pSP->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void**)&pSB))
{
if (!pSB->QueryActiveShellView(&pSV))
{
PIDLIST_ABSOLUTE pidl;
if (!SHGetIDListFromObject(psiexp, &pidl))
{
pSV->SelectItem(ILFindLastID(pidl), SVSI_SELECT|SVSI_ENSUREVISIBLE|SVSI_FOCUSED|SVSI_DESELECTOTHERS);
CoTaskMemFree(pidl);
}
pSV->Release();
}
pSB->Release();
}
pSP->Release();
}
psiexp->Release();
}
}
return S_OK;
}

enumerate forms in BeforeNavigate2 event

I'm writing an IE BHO, I'd like to know how to enumerate forms in event callback.
here's the code that enumerates forms in BeforeNavigate2 event, but the length is always 0.
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
HRESULT hr;
char bf[1024];
if(!IsEqualIID(riid, IID_NULL))
return DISP_E_UNKNOWNINTERFACE;
if(dispIdMember == DISPID_BEFORENAVIGATE2) {
IWebBrowser2* pSite = (IWebBrowser2*)pDispParams->rgvarg[6].pdispVal;
IDispatch* pHtmlDocDispatch;
hr = pSite->get_Document(&pHtmlDocDispatch);
if (FAILED(hr) || !pHtmlDocDispatch)
return S_OK;
IHTMLDocument2* pHtmlDoc = 0;
hr = pHtmlDocDispatch->QueryInterface(IID_IHTMLDocument2, (void**)&pHtmlDoc);
if(SUCCEEDED(hr) && pHtmlDoc) {
CComPtr<IHTMLElementCollection> pColl=NULL;
hr = pHtmlDoc->get_forms(&pColl);
if (SUCCEEDED (hr) && (pColl != NULL))
{
long nLength = 0;
hr = pColl->get_length (&nLength);
if(SUCCEEDED(hr)) {
sprintf(bf, "len = %d", nLength);
OutputDebugString(bf); // always 0
}
}
}
}
return S_OK;
}
Why it always output 0?
Thanks.
I just copy/pasted your code in my BHO and got a non-zero length collection.
Try that URL: http://linuxfr.org/ Use random user/password values and try to connect. That will trigger a DISPID_BEFORENAVIGATE2 and you will get 3 forms.
So, it seems there is no form in the page your are navigating away.
Also, your code leaks memory and is not quite correct in using COM interfaces (ag: you should QueryInterface for obtaining an IWebBrowser2 from a IDispatch.
Rewritten:
CComPtr<IDispatch> spIDispatch( pDispParams->rgvarg[6].pdispVal );
CComPtr<IWebBrowser2> spIWebBrowser2;
HRESULT hr = spIDispatch.QueryInterface<IWebBrowser2>( &spIWebBrowser2 );
if ( SUCCEEDED( hr ) && spIWebBrowser2 ) {
CComPtr<IDispatch> spIDispatchDoc;
hr = spIWebBrowser2->get_Document( &spIDispatchDoc );
if ( SUCCEEDED( hr ) && spIDispatchDoc ) {
CComPtr<IHTMLDocument2> spIHTMLDocument2;
hr = spIDispatchDoc.QueryInterface<IHTMLDocument2>( &spIHTMLDocument2 );
if ( SUCCEEDED( hr ) && spIHTMLDocument2 ) {
CComPtr<IHTMLElementCollection> spIHTMLElementCollection;
hr = spIHTMLDocument2->get_forms( &spIHTMLElementCollection );
if ( SUCCEEDED( hr ) && spIHTMLElementCollection ) {
[...]
}
}
}
}

How can I load an existing HTML file into a CHtmlEditCtrl or navigate to the HTML file?

How can I load an existing HTML file into a CHtmlEditCtrl or navigate to the HTML file?
Here is an example on how to load a html string into a CHtmlView:
void CMyHtmlView::Clear()
{
if(!IsWindow(m_hWnd))
return;
IHTMLDocument2* pDoc = GetDocument();
if(!pDoc)
{
Navigate2("about:blank");
return;
}
pDoc->close();
VARIANT open_name;
VARIANT open_features;
VARIANT open_replace;
IDispatch *open_window = NULL;
::VariantInit(&open_name);
open_name.vt = VT_BSTR;
open_name.bstrVal = ::SysAllocString(L"_self");
::VariantInit(&open_features);
::VariantInit(&open_replace);
HRESULT hr = pDoc->open(::SysAllocString(L"text/html"),open_name,open_features,
open_replace,&open_window);
if (hr == S_OK)
Refresh();
if (open_window != NULL)
open_window->Release();
}
void CMyHtmlView::LoadHTML(const CString& html)
{
if(!IsWindow(m_hWnd))
return;
Clear();
IHTMLDocument2* pDoc = GetDocument();
if(!pDoc)
return;
SAFEARRAY* sa = SafeArrayCreateVector(VT_VARIANT,0,1);
VARIANT* var;
SafeArrayAccessData(sa,(LPVOID*) &var);
var->vt = VT_BSTR;
var->bstrVal = html.AllocSysString();
SafeArrayUnaccessData(sa);
pDoc->write(sa);
pDoc->Release();
}
IHTMLDocument2* CMyHtmlView::GetDocument()
{
IHTMLDocument2* document = NULL;
if (m_pBrowser != NULL)
{
IDispatch *document_dispatch = NULL;
HRESULT hr = m_pBrowser->get_Document(&document_dispatch);
if (SUCCEEDED(hr) && (document_dispatch != NULL))
{
hr = document_dispatch->QueryInterface(IID_IHTMLDocument2,(void **)&document);
document_dispatch->Release();
}
}
return document;
}
I haven't tried it, but I guess that loading a local file with file://... instead of http://... will work.
Use the one of the Navigate or Navigate2 methods;
To access an online page:
pHtmlView->Navigate("http://www.google.com");
To access a local HTML file:
pHtmlView->Navigate("c:\\mypath\\myfile.html");
Depending on your implementation, the HTML control might require COM type access, or if you need to set additional info such as headers, then ;
CString strHeader = "User Agent:Mozilla/5.0...";
m_browserCtrl.Navigate2(&_variant_t(_T("file://C:\\mypath\\myfile.htm")), NULL, NULL, NULL, &_variant_t(_T(strHeader)));

multiple web cams in AMCap

I built the AMCap sample under direct show in SDK. It is able to handle 2 or more web cam, how do I modify the program for me to use them simultaneously… like press one button that says ‘start capture’ and make sure that all the cameras start capturing and one button that says ‘stop capture’ to stop all the cameras. i want the frames from different cameras to be save in different files. I am new to C++ and any kind of help is appreciated ! Thanks for your time!
Use GraphEdit tool. There you can build you own graph with all video input devices connected. Please see http://en.wikipedia.org/wiki/GraphEdit
If you're just starting out with DirectShow programming, this might be a bit of a hill climb, but I hope it's of some help, or points you in the right direction.
M$DN has a page describing how to select a capture device. It's a little thin on examples, so I've provided a simple implementation below.
The theory is that you need to enumerate all devices in the CLSID_VideoInputDeviceCategory, and attempt to create a render graph for each valid item in the category.
First I'll show you the code to iterate the names of the devices in the category. Below are 3 static functions you can use to iterate the devices in a category. Once you've added these functions to your project, you can list the devices in the Video Input Device category by calling the following function:
ListDevicesInCategory(CLSID_VideoInputDeviceCategory);
Okay, here are the three functions. ListDevicesInCategory() is where the work happens. It depends on other two functions, FindDeviceInCategory() and CountDevicesInCategory()
#define RFAIL(x) { HRESULT hr = x; if(FAILED(hr)) {return hr;} }
static HRESULT FindDeviceInCategory(IBaseFilter** pSrc, const IID& cls, wstring& wFilterName,int devNum)
{
CComPtr<ICreateDevEnum> spDevEnum;
CComPtr<IEnumMoniker> spEnum;
int i;
RFAIL( spDevEnum.CoCreateInstance(CLSID_SystemDeviceEnum) );
RFAIL( spDevEnum->CreateClassEnumerator(cls, &spEnum, 0) );
if(spEnum == 0)
return E_FAIL;
for(i = 0; i >= 0; i++)
{
CComPtr<IMoniker> spiMoniker;
if( spEnum->Next(1, &spiMoniker, 0) != S_OK )
return E_FAIL;
if( devNum == i)
{
CComVariant varName;
CComPtr<IPropertyBag> spiPropBag;
RFAIL(spiMoniker->BindToStorage(0, 0, IID_IPropertyBag,reinterpret_cast<void**>(&spiPropBag)));
RFAIL(spiPropBag->Read(L"FriendlyName", &varName, 0));
RFAIL(spiMoniker->BindToObject(0, 0, IID_IBaseFilter, reinterpret_cast<void**>(pSrc)));
wFilterName = V_BSTR(&varName);
varName.Clear();
return S_OK;
}
}
return E_FAIL;
}
static HRESULT CountDevicesInCategory( int *pCount, const IID& categoryClass )
{
// pass in a category class like CLSID_VideoInputDeviceCategory, writes the count of the number of filters in that category
// available on the local machine
HRESULT hr = S_OK;
CComPtr<ICreateDevEnum> spIDevEnum;
CComPtr<IEnumMoniker> spIEnum;
CComPtr<IMoniker> spIMoniker;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, 0, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, reinterpret_cast<void**>(&spIDevEnum));
if( ! SUCCEEDED(hr) || hr == S_FALSE )
{
*pCount = 0;
return hr;
}
hr = spIDevEnum->CreateClassEnumerator( categoryClass, &spIEnum, 0 );
if( ! SUCCEEDED(hr) || hr == S_FALSE )
{
*pCount = 0;
return hr;
}
if( spIEnum )
{
while( spIEnum->Next(1, &spIMoniker, 0) == S_OK )
{
(*pCount) ++;
spIMoniker.Detach();
}
}
return S_OK;
}
static HRESULT ListDevicesInCategory( const GUID & cls )
{
CComPtr<IBaseFilter> spSource;
wstring * psNextFilter = NULL;
int nDeviceNum = 0;
int nTotalNumDevices = 0;
HRESULT hr = S_OK;
bool bComplete = false;
DeviceNames CaptureDeviceNames;
if( FAILED(CountDevicesInCategory( &nTotalNumDevices, (IID)cls )) )
bComplete = TRUE;
if( nTotalNumDevices == 0 )
bComplete = TRUE;
while( ! bComplete )
{
psNextFilter = new std::wstring;
hr = FindDeviceInCategory( &spSource, (IID)cls, *psNextFilter, nDeviceNum++ );
if( SUCCEEDED(hr) && spSource )
{
if ( *psNextFilter )
{
wcout << *psNextFilter << endl;
delete *psNextFilter;
psNextFilter = NULL;
}
spSource.Release();
spSource = NULL;
}
else
bComplete = TRUE;
}
return S_OK;
}
Once you've identified an item in a category that you're interested in, you can add it to a graph with the IGraphBuilder::AddFilter call.
To add a filter to your graph, you'll first need to get an IBaseFilter* to that filter. I've got one more function for you to do this with.
Define an IBaseFilter smart pointer:
CComPtr<IBaseFilter> spSource;
Attach to the filter:
m_spSource.Attach(
GetFilter(CLSID_VideoInputDeviceCategory, CComBSTR(L"Osprey-450e Video Device 1A"))
);
Here's that last function - GetFilter:
static IBaseFilter * GetFilter( REFCLSID clsidDeviceClass, CComBSTR & sName )
{
HRESULT hr;
IBaseFilter * pRetFilter = NULL;
ICreateDevEnum * pSysDevEnum = NULL;
IEnumMoniker * pEnum = NULL;
IMoniker * pMoniker = NULL;
int nSameSrcCounter = 0;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pSysDevEnum);
if( pSysDevEnum )
hr = pSysDevEnum->CreateClassEnumerator(clsidDeviceClass, &pEnum, 0);
if (hr != S_OK)
return NULL;
USES_CONVERSION;
while ( pEnum->Next(1, &pMoniker, NULL) == S_OK )
{
IPropertyBag *pPropBag = NULL;
VARIANT var;
VariantInit(&var);
pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
hr = pPropBag->Read(L"FriendlyName", &var, 0);
if (SUCCEEDED(hr))
{
if(sName == OLE2T(var.bstrVal))
{
hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (void**)&pRetFilter);
if (FAILED(hr))
pRetFilter = NULL;
VariantClear(&var);
pPropBag->Release();
pMoniker->Release();
break;
}
}
VariantClear(&var);
pPropBag->Release();
pMoniker->Release();
}
pSysDevEnum->Release();
pEnum->Release();
return pRetFilter;
}