Im working on an application that uses a CHtmlView. New requirements mean I would like to be able to get the HTML source from the class to parse for a specific tag (or if possible just get the information in the tag). This would be fine if we were using a newer system and I could use CHtmlView::GetSource but it doesn't exist.
I've had a pretty extensive search online but am pretty new to most of Windows programming and haven't been able to achieve anything useful yet.
So if anyone has an example of how to extract the HTML from a CHtmlView without using GetSource I would appreciate seeing it. I've tried
BSTR bstr;
_bstr_t * bstrContainer;
HRESULT hr;
IHTMLDocument2 * pDoc;
IDispatch * pDocDisp = NULL;
pDocDisp = this->GetHtmlDocument();
if (pDocDisp != NULL) {
hr = pDocDisp->QueryInterface (IID_IHTMLDocument2, (void**)&pDoc);
if (SUCCEEDED(hr)) {
if (pDoc->toString(&bstr) != S_OK) {
//error...
} else {
bstrContainer = new _bstr_t(bstr);
size = (bstrContainer->length()+1)*2;
realString = new char[size];
strncpy(realString, (char*)(*bstrContainer), size);
}
} else {
//error
}
pDocDisp->Release();
}
but it mostly just gives me "[object]" in realString. Like I said, new to Windows.
Any help appreciated.
Add this helper function into your CHtmlView-derived class to retrieve the html source. Remember to check the returned boolean state from this function as com-interface can be quite unreliable when system resources are low.
/* ============================================== */
BOOL CTest1View::GetHtmlText(CString &strHtmlText)
{
BOOL bState = FALSE;
// get IDispatch interface of the active document object
IDispatch *pDisp = this->GetHtmlDocument();
if (pDisp != NULL)
{ // get the IHTMLDocument3 interface
IHTMLDocument3 *pDoc = NULL;
HRESULT hr = pDisp->QueryInterface(IID_IHTMLDocument3, (void**) &pDoc);
if (SUCCEEDED(hr))
{ // get root element
IHTMLElement *pRootElement = NULL;
hr = pDoc->get_documentElement(&pRootElement);
if (SUCCEEDED(hr))
{ // get html text into bstr
BSTR bstrHtmlText;
hr = pRootElement->get_outerHTML(&bstrHtmlText);
if (SUCCEEDED(hr))
{ // convert bstr to CString
strHtmlText = bstrHtmlText;
bState = TRUE;
SysFreeString(bstrHtmlText);
}
pRootElement->Release();
}
pDoc->Release();
}
pDisp->Release();
}
return bState;
}
Related
I use filter DS LAME for compressing audio. I loaded it from file "lame.ax" as follows:
// pPath - path to LAME "lame.ax"
HRESULT CMyFilter::CreateObjectFromPath(wchar_t *pPath, REFCLSID clsid, IUnknown **ppUnk)
{
// load the target DLL directly
if (!m_hLibFilter) m_hLibFilter = LoadLibrary(pPath);
if (!m_hLibFilter)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// the entry point is an exported function
FN_DLLGETCLASSOBJECT fn = (FN_DLLGETCLASSOBJECT)GetProcAddress(m_hLibFilter, "DllGetClassObject");
if (fn == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
// create a class factory
IUnknownPtr pUnk;
HRESULT hr = fn(clsid, IID_IUnknown, (void**)(IUnknown**)&pUnk);
if (SUCCEEDED(hr))
{
IClassFactoryPtr pCF = pUnk;
if (pCF == NULL)
{
hr = E_NOINTERFACE;
}
else
{
// ask the class factory to create the object
hr = pCF->CreateInstance(NULL, IID_IUnknown, (void**)ppUnk);
}
}
return hr;
}
further
HRESULT hr = 0;
IUnknown *ppUnk = 0;
ULONG lRef = 0;
hr = CreateObjectFromPath(L"lame.ax", CLSID_LAMEDShowFilter, (IUnknown **)&ppUnk);
hr = ppUnk->QueryInterface(&m_pFilter);
lRef = ppUnk->Release();
It works perfectly. LAME encoding audio.
I want to show the filter settings - property page, but this code failed
bool ShowConfigWindow(HWND hParent)
{
ISpecifyPropertyPages *pProp;
HRESULT hr = m_pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
if (SUCCEEDED(hr))
{
// Get the filter's name and IUnknown pointer.
FILTER_INFO FilterInfo;
hr = m_pFilter->QueryFilterInfo(&FilterInfo);
IUnknown *pFilterUnk;
m_pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);
// Show the page.
CAUUID caGUID;
pProp->GetPages(&caGUID);
pProp->Release();
HRESULT hr = OleCreatePropertyFrame(
hParent, // Parent window
0, 0, // Reserved
FilterInfo.achName, // Caption for the dialog box
1, // Number of objects (just the filter)
&pFilterUnk, // Array of object pointers.
caGUID.cElems, // Number of property pages
caGUID.pElems, // Array of property page CLSIDs
0, // Locale identifier
0, NULL // Reserved
);
// Clean up.
pFilterUnk->Release();
FilterInfo.pGraph->Release();
CoTaskMemFree(caGUID.pElems);
}
return true;
}
I find https://groups.google.com/forum/#!topic/microsoft.public.win32.programmer.directx.video/jknSbMenWeM
I should call CoRegisterClassObject for each property page, but how to do it?
Or what the right way?
OleCreatePropertyFrame takes property page class identifiers (CLSIDs) so you need to find a way to make them "visible" for the API.
Use of CoRegisterClassObject is one of the ways to achieve the mentioned task (perhaps the easiest, another method would be reg-free COM). You need to retrieve IClassFactory pointers for property page CLSIDs the same way as you do it in the first snippet. Then instead of calling IClassFactory::CreateInstance you use the interface pointers as arguments for CoRegisterClassObject API. Make sure you do it on the same thread as the following OleCreatePropertyFrame call. CoRevokeClassObject will clean things up afterwards.
Edit: Are there any tutorials on how to use WIA or TWAIN in c++, that explain how to scan pages, adjust settings (DPI, using automatic feeder etc.) and save them as PNG files?
I'd like to use WIA to scan pages and store them as png files. If the scanner supports automatic feeding I'd also like to use that feature. Currently I am following the steps of this tutorial and am stuck at the section Transferring Image Data in WIA 2.0.
So far my scanner has been found and I am able to create the device, and an IWiaItem2* has been created. How can use it to scan at 300dpi and store the result as png file?
The tutorial is not clear about how to start the scan process or how to set dpi for scanning, so I hope someone can help me with the code.
This is essentially the code for getting all local devices:
bool init(IWiaDevMgr2* devMgr)
{
//creating the device manager
*devMgr = 0;
CoCreateInstance( CLSID_WiaDevMgr2, 0, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr2, (void**)&devMgr);
//enumerating wia devices
IEnumWIA_DEV_INFO* enumDevInfo = 0;
HRESULT hr = devMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &enumDevInfo);
if(SUCCEEDED(hr))
{
//loop until an error occurs or end of list
while(hr == S_OK)
{
IWiaPropertyStorage* storage = 0;
hr = enumDevInfo->Next( 1, &storage, 0);
if(hr == S_OK)
{
readProperties(storage);
storage->Release();
storage = 0;
}
}
//set hr to ok, so no error code is returned
if(hr == S_FALSE) hr = S_OK;
enumDevInfo->Release();
enumDevInfo = 0;
}
return SUCCEEDED(hr);
}
void readProperties(IWiaPropertyStorage* storage)
{
PROPSPEC propSpec[2] = {0};
PROPVARIANT propVar[2] = {0};
const ULONG propCount = sizeof(propSpec) / sizeof(propSpec[0]);
propSpec[0].ulKind = PRSPEC_PROPID;
propSpec[0].propid = WIA_DIP_DEV_ID;
propSpec[1].ulKind = PRSPEC_PROPID;
propSpec[1].propid = WIA_DIP_DEV_NAME;
HRESULT hr = storage->ReadMultiple(propCount, propSpec, propVar);
if(SUCCEEDED(hr))
{
Device* dev = new Device(propVar[0].bstrVal, propVar[1].bstrVal);
devices.push_back( dev );
FreePropVariantArray( propCount, propVar );
}
}
Afterwards a device is initialized like this:
bool createDevice(BSTR id, IWiaItem2** item)
{
*item = 0;
HRESULT hr = devMgr->CreateDevice( 0, deviceId, item);
return SUCCEEDED(hr);
}
Then the items are enumerated:
bool enumerateItems(IWiaItem2* item)
{
LONG itemType = 0;
HRESULT hr = item->GetItemType(&itemType);
if(SUCCEEDED(hr))
{
if(itemType & WiaItemTypeFolder || itemType & WiaItemTypeHasAttachments)
{
IEnumWiaItem2* enumItem = 0;
hr = item->EnumChildItems(0, &enumItem );
while(hr == S_OK)
{
IWiaItem2* child = 0;
hr = enumItem->Next( 1, &child, 0 );
if(hr == S_OK)
{
hr = enumerateItems( child );
child->Release();
child = 0;
}
}
if(hr == S_FALSE) hr = S_OK;
enumItem->Release();
enumItem = 0;
}
}
return SUCCEEDED(hr);
}
Now that everything has been initialized I'd like to implement a scan function. However, the code provided at the tutorial is for transferring files and folders and not for scanning images.
void scanAndSaveAsPNG(IWiaItem2* item, unsigned int dpi, std::string targetPath)
{
}
EDIT:
I installed the latest version available of the scanner driver (WIA and TWAIN) and after checking the supported commands using this code
void printCommands(IWiaItem2* i)
{
IEnumWIA_DEV_CAPS* caps = 0;
HRESULT h = item->EnumDeviceCapabilities(WIA_DEVICE_COMMANDS, &caps);
if(SUCCEEDED(h))
{
ULONG count = 0;
caps->GetCount(&count);
if(count > 0)
{
WIA_DEV_CAP* cap = new WIA_DEV_CAP[ count ];
ULONG fetched;
caps->Next(count, cap, &fetched);
for(int i = 0; i < fetched; i++)
{
std::cout << bstr_t( cap[i].bstrName ) << "\n";
}
}
caps->Release();
}
}
I noticed it only lists WIA Synchronize command. I am not sure if I didn't initialize the device correctly, or if the device doesn't support all WIA commands although the driver is installed.
So unless this problem is solved I am alternatively also looking for the same code based on TWAIN.
You want to use IWiaItem2::DeviceCommand which sends a command to the image capture device. The list of commands you can send are listed here.
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)));
We have customers asking for VBScript functions to be called when particular actions occur within our product. I've been trying to research the Windows Scripting technologies but I'm having difficulty finding exactly what I need. Hopefully some of you can help.
Our product is a native C++ Windows product. The customer would specify a VBScript file, which we would load, and whenever a particular event occurs, we'd call a particular function in the VBScript and let it do its thing. We may provide objects within the script's namespace for it to access information about our product as well.
I found some information on MSDN about the IActiveScript interface, and some related things, but cannot find any examples of instantiating a COM object that implements this interface for VBScript.
I know that PowerShell would probably be a better option for this these days, but our customers are stuck in a lot of legacy systems and VBScript is what they know.
Any help you can provide (links or otherwise) would be appreciated!
I've put together a "Hello World" IActiveScript C++ ATL console application that:
Define CSimpleScriptSite class
Implement IActiveScriptSite interface (mandatory)
Implement IActiveScriptSiteWindow interface (optional)
Minimum implementation with most functions implemented with a dummy stub
Has no error handling. Consult MSDN IActiveScriptError.
Use CoCreateInstance a new IActiveSite object
Create instances of both VBScript and JScript
Link the IActiveSite to IActiveScriptSite using IActiveSite::SetScriptSite
Call QueryInterface to get an IActiveScriptParse interface
Use IActiveScriptParse to execute VBScript or JScript code
The sample:
Evaluates an expression in JScript
Evaluates an expression in VBScript
Runs a command in VBScript
Code:
#include "stdafx.h"
#include <atlbase.h>
#include <activscp.h>
class CSimpleScriptSite :
public IActiveScriptSite,
public IActiveScriptSiteWindow
{
public:
CSimpleScriptSite() : m_cRefCount(1), m_hWnd(NULL) { }
// IUnknown
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);
// IActiveScriptSite
STDMETHOD(GetLCID)(LCID *plcid){ *plcid = 0; return S_OK; }
STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti) { return TYPE_E_ELEMENTNOTFOUND; }
STDMETHOD(GetDocVersionString)(BSTR *pbstrVersion) { *pbstrVersion = SysAllocString(L"1.0"); return S_OK; }
STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) { return S_OK; }
STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) { return S_OK; }
STDMETHOD(OnScriptError)(IActiveScriptError *pIActiveScriptError) { return S_OK; }
STDMETHOD(OnEnterScript)(void) { return S_OK; }
STDMETHOD(OnLeaveScript)(void) { return S_OK; }
// IActiveScriptSiteWindow
STDMETHOD(GetWindow)(HWND *phWnd) { *phWnd = m_hWnd; return S_OK; }
STDMETHOD(EnableModeless)(BOOL fEnable) { return S_OK; }
// Miscellaneous
HRESULT SetWindow(HWND hWnd) { m_hWnd = hWnd; return S_OK; }
public:
LONG m_cRefCount;
HWND m_hWnd;
};
STDMETHODIMP_(ULONG) CSimpleScriptSite::AddRef()
{
return InterlockedIncrement(&m_cRefCount);
}
STDMETHODIMP_(ULONG) CSimpleScriptSite::Release()
{
if (!InterlockedDecrement(&m_cRefCount))
{
delete this;
return 0;
}
return m_cRefCount;
}
STDMETHODIMP CSimpleScriptSite::QueryInterface(REFIID riid, void **ppvObject)
{
if (riid == IID_IUnknown || riid == IID_IActiveScriptSiteWindow)
{
*ppvObject = (IActiveScriptSiteWindow *) this;
AddRef();
return NOERROR;
}
if (riid == IID_IActiveScriptSite)
{
*ppvObject = (IActiveScriptSite *) this;
AddRef();
return NOERROR;
}
return E_NOINTERFACE;
}
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Initialize
CSimpleScriptSite* pScriptSite = new CSimpleScriptSite();
CComPtr<IActiveScript> spJScript;
CComPtr<IActiveScriptParse> spJScriptParse;
hr = spJScript.CoCreateInstance(OLESTR("JScript"));
hr = spJScript->SetScriptSite(pScriptSite);
hr = spJScript->QueryInterface(&spJScriptParse);
hr = spJScriptParse->InitNew();
CComPtr<IActiveScript> spVBScript;
CComPtr<IActiveScriptParse> spVBScriptParse;
hr = spVBScript.CoCreateInstance(OLESTR("VBScript"));
hr = spVBScript->SetScriptSite(pScriptSite);
hr = spVBScript->QueryInterface(&spVBScriptParse);
hr = spVBScriptParse->InitNew();
// Run some scripts
CComVariant result;
EXCEPINFO ei = { };
hr = spJScriptParse->ParseScriptText(OLESTR("(new Date()).getTime()"), NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &result, &ei);
hr = spVBScriptParse->ParseScriptText(OLESTR("Now"), NULL, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, &result, &ei);
hr = spVBScriptParse->ParseScriptText(OLESTR("MsgBox \"Hello World! The current time is: \" & Now"), NULL, NULL, NULL, 0, 0, 0, &result, &ei);
// Cleanup
spVBScriptParse = NULL;
spVBScript = NULL;
spJScriptParse = NULL;
spJScript = NULL;
pScriptSite->Release();
pScriptSite = NULL;
::CoUninitialize();
return 0;
}
A version of the above code can be found here:
https://github.com/stephenquan/RunScriptDemo
IActiveScript and related interfaces work very well. I use them in my product exactly the same way you have described. Some of out customers write their own VBScript and JScript scripts to analyze and update application data before it gets posted to a database.
You use CoCreateInstance() to instantiate IActiveScript, like you would any other COM object. You would then call its QueryInterface() method to obtain an IActiveScriptParse interface for loading snippets of scripting code, and then you update the IActiveScript's state to execute the code.
You can add custom objects to the script by implementing IDispatch-derived classes and then passing them to the engine using IActiveScript::AddNamedItem() and an IActiveScriptSite::GetItemInfo() callback.
There are examples of IActiveScript usage available on MSDN.
I wrote an Active X plugin for IE7 which implements IObjectWithSite besides some other necessary interfaces (note no IOleClient). This interface is queried and called by IE7. During the SetSite() call I retrieve a pointer to IE7's site interface which I can use to retrieve the IHTMLDocument2 interface using the following approach:
IUnknown *site = pUnkSite; /* retrieved from IE7 during SetSite() call */
IServiceProvider *sp = NULL;
IHTMLWindow2 *win = NULL;
IHTMLDocument2 *doc = NULL;
if(site) {
site->QueryInterface(IID_IServiceProvider, (void **)&sp);
if(sp) {
sp->QueryService(IID_IHTMLWindow2, IID_IHTMLWindow2, (void **)&win);
if(win) {
win->get_document(&doc);
}
}
}
if(doc) {
/* found */
}
I tried a similiar approach on PIE as well using the following code, however, even the IPIEHTMLWindow2 interface cannot be acquired, so I'm stuck:
IUnknown *site = pUnkSite; /* retrieved from PIE during SetSite() call */
IPIEHTMLWindow2 *win = NULL;
IPIEHTMLDocument1 *tmp = NULL;
IPIEHTMLDocument2 *doc = NULL;
if(site) {
site->QueryInterface(__uuidof(*win), (void **)&win);
if(win) { /* never the case */
win->get_document(&tmp);
if(tmp) {
tmp->QueryInterface(__uuidof(*doc), (void **)&doc);
}
}
}
if(doc) {
/* found */
}
Using the IServiceProvider interface doesn't work either, so I already tested this.
Any ideas?
I found the following code in the Google Gears code, here. I copied the functions I think you need to here. The one you need is at the bottom (GetHtmlWindow2), but the other two are needed as well. Hopefully I didn't miss anything, but if I did the stuff you need is probably at the link.
#ifdef WINCE
// We can't get IWebBrowser2 for WinCE.
#else
HRESULT ActiveXUtils::GetWebBrowser2(IUnknown *site, IWebBrowser2 **browser2) {
CComQIPtr<IServiceProvider> service_provider = site;
if (!service_provider) { return E_FAIL; }
return service_provider->QueryService(SID_SWebBrowserApp,
IID_IWebBrowser2,
reinterpret_cast<void**>(browser2));
}
#endif
HRESULT ActiveXUtils::GetHtmlDocument2(IUnknown *site,
IHTMLDocument2 **document2) {
HRESULT hr;
#ifdef WINCE
// Follow path Window2 -> Window -> Document -> Document2
CComPtr<IPIEHTMLWindow2> window2;
hr = GetHtmlWindow2(site, &window2);
if (FAILED(hr) || !window2) { return false; }
CComQIPtr<IPIEHTMLWindow> window = window2;
CComPtr<IHTMLDocument> document;
hr = window->get_document(&document);
if (FAILED(hr) || !document) { return E_FAIL; }
return document->QueryInterface(__uuidof(*document2),
reinterpret_cast<void**>(document2));
#else
CComPtr<IWebBrowser2> web_browser2;
hr = GetWebBrowser2(site, &web_browser2);
if (FAILED(hr) || !web_browser2) { return E_FAIL; }
CComPtr<IDispatch> doc_dispatch;
hr = web_browser2->get_Document(&doc_dispatch);
if (FAILED(hr) || !doc_dispatch) { return E_FAIL; }
return doc_dispatch->QueryInterface(document2);
#endif
}
HRESULT ActiveXUtils::GetHtmlWindow2(IUnknown *site,
#ifdef WINCE
IPIEHTMLWindow2 **window2) {
// site is javascript IDispatch pointer.
return site->QueryInterface(__uuidof(*window2),
reinterpret_cast<void**>(window2));
#else
IHTMLWindow2 **window2) {
CComPtr<IHTMLDocument2> html_document2;
// To hook an event on a page's window object, follow the path
// IWebBrowser2->document->parentWindow->IHTMLWindow2
HRESULT hr = GetHtmlDocument2(site, &html_document2);
if (FAILED(hr) || !html_document2) { return E_FAIL; }
return html_document2->get_parentWindow(window2);
#endif
}
Well I was aware of the gears code already. The mechanism gears uses is based on a workaround through performing an explicit method call into the gears plugin from the gears loader to set the window object and use that as site interface instead of the IUnknown provided by IE Mobile in the SetSite call. Regarding to the gears code the Google engineers are aware of the same problem I'm asking and came up with this workaround I described.
However, I believe there must be another more "official" way of dealing with this issue since explicitely setting the site on an Active X control/plugin isn't very great. I'm going to ask the MS IE Mobile team directly now and will keep you informed once I get a solution. It might be a bug in IE Mobile which is the most likely thing I can imagine of, but who knows...
But thanks anyways for your response ;))