C++ CHTMLView element page loading and then disappearing - c++

In my C++ application, a CHTMLView is used to load some HTML into a web page. It works by writing the HTML to a temporary file, and then calling Navigate2() on the CHTMLView to navigate to the file location.
What we are finding happens is that the navigation occurs, the file is written, the completely correct contents of the page appears, but then it quickly disappears and becomes blank. But it's a visual thing; right-clicking and saying "View Source" shows the correct source, and hovering over elements on the page that react to hovering make them appear again (but everything else stays white). Resizing the window or scrolling is the only way to make everything appear.
I have tried navigating first to about:blank and then triggering a navigation to the correct place with a OnDocumentComplete() event. I have even tried navigating first to blank dummy page and then going from there. Nothing changes.
Any advice?!
The derived class is ScriptViewer.
ScriptViewer.h
class CScriptViewer : public CHtmlView
{
protected:
CScriptViewer(); // protected constructor used by dynamic creation
DECLARE_DYNCREATE(CScriptViewer)
// html Data
public:
//{{AFX_DATA(CScriptViewer)
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// Attributes
public:
CAMAgentDesktopDoc* m_pDoc;
CScriptDlg* m_pDlg;
CString strScriptLocation;
BOOL m_bInitialLoad;
// Operations
public:
void GetAllValues( map<CString,CString>& mValues );
void GetValuesIn( IHTMLDocument2* pHTMLDoc, map<CString,CString>& mValues );
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CScriptViewer)
public:
virtual void OnInitialUpdate();
virtual void OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel);
virtual void OnDocumentComplete(LPCTSTR lpszURL);
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
virtual ~CScriptViewer();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
CAMTrace m_trace;
// Generated message map functions
//{{AFX_MSG(CScriptViewer)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
ScriptViewer.cpp
CScriptViewer::CScriptViewer()
{
//{{AFX_DATA_INIT(CScriptViewer)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_trace.SetEXEName( _T("CScriptViewer") );
m_trace.Trace( _T("constructor"), FALSE, 0 );
strScriptLocation = _T("");
m_bInitialLoad = FALSE;
m_pDoc = NULL;
m_pDlg = NULL;
}
CScriptViewer::~CScriptViewer()
{
/*
map<CString,CString> mValues;
GetAllValues( mValues );
m_pDlg->UpdateUserEnteredValues( mValues );
*/
m_trace.Trace( _T("destructor"), FALSE, 0 );
}
void CScriptViewer::DoDataExchange(CDataExchange* pDX)
{
CHtmlView::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CScriptViewer)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CScriptViewer, CHtmlView)
//{{AFX_MSG_MAP(CScriptViewer)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CScriptViewer diagnostics
#ifdef _DEBUG
void CScriptViewer::AssertValid() const
{
CHtmlView::AssertValid();
}
void CScriptViewer::Dump(CDumpContext& dc) const
{
CHtmlView::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CScriptViewer message handlers
void CScriptViewer::OnInitialUpdate()
{
try
{
m_trace.Trace( _T("OnInitialUpdate") );
ASSERT( m_pDoc );
ASSERT( m_pDlg );
}
catch(...)
{
}
}
void CScriptViewer::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel)
{
try
{
map<CString,CString> mValues;
GetAllValues( mValues );
ASSERT( m_pDlg );
// GJS
if (!m_pDlg) return;
m_pDlg->UpdateUserEnteredValues( mValues );
CString strURL = lpszURL;
int nPosClose = strURL.Find( URL_INSTRUCTION_TO_ADAPTIVE_DESKTOP );
if ( nPosClose > 0 )
{
*pbCancel = TRUE;
CHtmlView::OnBeforeNavigate2(lpszURL, nFlags, lpszTargetFrameName, baPostedData, lpszHeaders, pbCancel);
m_pDlg->OnScriptInstructionToDesktop( strURL.Mid( nPosClose + _tcslen(URL_INSTRUCTION_TO_ADAPTIVE_DESKTOP) ), mValues );
}
else
{
CHtmlView::OnBeforeNavigate2(lpszURL, nFlags, lpszTargetFrameName, baPostedData, lpszHeaders, pbCancel);
}
}
catch(...)
{
}
}
void CScriptViewer::OnDocumentComplete(LPCTSTR lpszURL) {
if (!m_bInitialLoad) {
//Navigate2(strScriptLocation);
//m_bInitialLoad = TRUE;
}
}
///////////////////////////////////////////////////////////////////////////////////
// accessing data values from the HTML pages, after the user has fiddled with them
void CScriptViewer::GetValuesIn( IHTMLDocument2* pHTMLDoc, map<CString,CString>& mValues )
{
try
{
if ( pHTMLDoc != NULL )
{
BSTR bsURL;
VERIFY( SUCCEEDED( pHTMLDoc->get_URL( &bsURL ) ) );
// TRACE( _T("GetValuesIn(%s)\r\n"), CString(bsURL) );
IHTMLFramesCollection2* pFrames = NULL;
if ( SUCCEEDED( pHTMLDoc->get_frames( &pFrames ) ) )
{
long lNumFrames = 0;
VERIFY( SUCCEEDED( pFrames->get_length( &lNumFrames ) ) );
for( long l = 0; l < lNumFrames; l++ )
{
COleVariant v1(l);
VARIANT vDispFrame;
if ( SUCCEEDED( pFrames->item( v1, &vDispFrame ) ) )
{
if ( vDispFrame.vt == VT_DISPATCH )
{
IHTMLWindow2* pWindow = NULL;
VERIFY( SUCCEEDED( (vDispFrame.pdispVal)->QueryInterface( IID_IHTMLWindow2, (LPVOID*)&pWindow ) ) );
ASSERT( pWindow );
IHTMLDocument2* pSubDoc = NULL;
if ( SUCCEEDED( pWindow->get_document( &pSubDoc ) ) )
{
GetValuesIn( pSubDoc, mValues );
pSubDoc->Release();
}
pWindow->Release();
}
}
}
pFrames->Release();
}
IHTMLElementCollection* pElemColl = NULL;
HRESULT hr = pHTMLDoc->get_all(&pElemColl);
if (SUCCEEDED(hr) && pElemColl)
{
long lNumElements = 0;
VERIFY( SUCCEEDED( pElemColl->get_length( &lNumElements ) ) );
for( long l = 0; l < lNumElements; l++ )
{
COleVariant v1(l);
COleVariant vzero((long)0);
LPDISPATCH pDispTemp = NULL;
VERIFY( SUCCEEDED( pElemColl->item( v1, vzero, &pDispTemp ) ) );
ASSERT( pDispTemp != NULL );
IHTMLElement* pel = NULL;
VERIFY( SUCCEEDED( pDispTemp->QueryInterface( IID_IHTMLElement, (LPVOID*)&pel ) ) );
CString str;
BSTR bsid;
pel->get_id( &bsid );
VARIANT vValue;
pel->getAttribute( CString("value").AllocSysString(), 0, &vValue );
CString strID = CString(bsid);
if ( !strID.IsEmpty() )
{
CString strValue = _T("");
if ( vValue.vt == VT_BSTR ) {
strValue = CString(vValue.bstrVal);
} else if ( vValue.vt == VT_I2 || vValue.vt == VT_I4 ) {
strValue.Format( _T("%d"), vValue.intVal );
}
mValues[strID] = strValue;
// str.Format( _T("ID %s, value %s\r\n"),
// strID, strValue );
// strRetval += str;
}
pel->Release();
}
pElemColl->Release();
}
else
ASSERT(FALSE);
}
else
ASSERT(FALSE); // passed null object doc
}
catch(...)
{
}
}
void CScriptViewer::GetAllValues( map<CString,CString>& mValues )
{
try
{
mValues.clear();
LPDISPATCH pDisp = GetHtmlDocument();
if ( pDisp )
{
IHTMLDocument2* p = NULL;
if ( SUCCEEDED( pDisp->QueryInterface( IID_IHTMLDocument2, (LPVOID*)&p ) ) && p != NULL )
{
GetValuesIn( p, mValues );
p->Release();
}
else
ASSERT(FALSE); // unable to QI for IHTMLDocument2?
pDisp->Release();
}
}
catch(...)
{
}
}
Here is the code that handles the navigation:
CString strFilePath = CAMMiscSharedFilePaths::GetFullPathToWindowsTempDir() + _T("\\") + m_call->m_camCampaignSettings.m_scriptView.m_strName+_T("_temp.htm");
HRESULT hr = WriteStringToTextFile( strFilePath, strRedirect ); //intentionally left for html settings, as it stores data in windows temp
if ( SUCCEEDED(hr) ) {
pView->strScriptLocation = strFilePath;
CString str = strFilePath;
pView->Navigate2(str);
}

Here is my derived class of a CHTMLView. I load the HTML directly into the browser. Works for me, maybe you can use it:
#include "stdafx.h"
#include "GMHtmlView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CGMHtmlView, CHtmlView)
CGMHtmlView::CGMHtmlView(IHtmlEventNotifier* pHEN)
{
EnableToolTips(FALSE);
m_pBrowser = NULL;
m_pBrowserDispatch = NULL;
m_pHEN = pHEN;
m_strPrefix = "http://";
}
void CGMHtmlView::DoDataExchange(CDataExchange* pDX)
{
CHtmlView::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CGMHtmlView, CHtmlView)
END_MESSAGE_MAP()
BOOL CGMHtmlView::Create(const RECT &rect,CWnd* pParentWnd)
{
BOOL bRet = CHtmlView::Create(NULL,NULL,WS_VISIBLE,rect,pParentWnd,AFX_IDW_PANE_FIRST);
// Pointer auf Browser herausfinden
if(bRet)
{
LPUNKNOWN unknown = GetDlgItem(0)->GetControlUnknown();
HRESULT hr = unknown->QueryInterface(IID_IWebBrowser2,(void **)&m_pBrowser);
if (SUCCEEDED(hr))
hr = unknown->QueryInterface(IID_IDispatch,(void **)&m_pBrowserDispatch);
}
return bRet;
}
void CGMHtmlView::SetPrefix(const CString& prefix)
{
m_strPrefix = prefix;
}
void CGMHtmlView::OnBeforeNavigate2(LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel)
{
CString url(lpszURL);
url.MakeLower();
// Sperre: alles andere als die leere Seite
// und unser Inhalt wird gesperrt
if(url == "about:blank")
{
CHtmlView::OnBeforeNavigate2(lpszURL,nFlags,lpszTargetFrameName,baPostedData,lpszHeaders,pbCancel);
return;
}
if(url.Find(m_strPrefix) != 0)
{
*pbCancel = TRUE;
return;
}
// jetzt die Adresse nach aussen weiterleiten
if(m_pHEN)
{
url = url.Right(url.GetLength() - m_strPrefix.GetLength());
m_pHEN->UrlNotify(url);
}
}
void CGMHtmlView::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 CGMHtmlView::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* CGMHtmlView::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;
}
void CGMHtmlView::AssertValid() const
{
//CHtmlView::AssertValid();
}
void CGMHtmlView::PostNcDestroy()
{
//CHtmlView::PostNcDestroy();
}

Related

Memory leak when using IShellItem2.GetString()

I'm using the following code to enumerate the contents of the Recyclebin Shell folder and get the file type of each item.
The code gives the expected results but if I call the function in a loop it looks like there's some memory leak when using the IshellItem2 GetString function (see attached screenshot at the end).
Am I cleaning up everything properly?
Am I misinterpreting the results?
void Test1()
{
// Get recyclebin ishellitem
IShellItem* psiRecycleBin;
if (SUCCEEDED(SHGetKnownFolderItem(FOLDERID_RecycleBinFolder, KF_FLAG_DEFAULT,
NULL, IID_PPV_ARGS(&psiRecycleBin))))
{
// Get ishellitem contents enumerator
IEnumShellItems* pesi;
if (SUCCEEDED(psiRecycleBin->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&pesi))))
{
IShellItem* psi;
while (pesi->Next(1, &psi, NULL) == S_OK)
{
// Get ishellitem2 from ishellitem
IShellItem2* psi2;
if (SUCCEEDED(psi->QueryInterface(IID_PPV_ARGS(&psi2))))
{
// Get the item file type string
LPWSTR fileType = NULL;
if (SUCCEEDED(psi2->GetString(PKEY_ItemTypeText, &fileType)))
{
CoTaskMemFree(fileType);
}
psi2->Release();
}
psi->Release();
}
pesi->Release();
}
psiRecycleBin->Release();
}
}
And I'm calling it in loop like this:
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <propkey.h>
#include <iostream>
void Test1();
int main()
{
(void)CoInitialize(NULL);
std::cout << "Enumerating recyclebin items..\n";
for (int ii = 0; ii < 5000; ii++)
{
Test1();
}
CoUninitialize();
return 0;
}
When debugging this console program in VS in the memory diagnostics window this is what I get:
Thanks for the help
yes, here really exist memory leak, related to HIDDENRECYCLEBINDATAV2 structure from shell32.dll
partial definition of it:
struct HIDDENRECYCLEBINDATAV2
{
//... some mebers
FILETIME time;
PWSTR pszLocationBeforeDelete = 0; // !!! not released
PWSTR pszLocationInRecycleBin = 0; // !!! not released
HRESULT Serialize(PBYTE *, PUSHORT);
static HRESULT Deserialize(
_In_reads_bytes_opt_(cbStream) const BYTE *pbStream ,
_In_ USHORT cbStream,
_Out_ HIDDENRECYCLEBINDATAV2 ** pphrbd);
static HRESULT Initialize(HIDDENRECYCLEBINDATAV1 const *, HIDDENRECYCLEBINDATAV2**);
};
this structure hold 2 strings - file path from where it deleted ( pszLocationBeforeDelete - this is my name, i don't know original) and current file path in Recycle Bin ( pszLocationInRecycleBin - again my name)
this names allocated inside Deserialize method, by call IStream_ReadStrLong and must be freed with CoTaskMemFree. but how i found - CoTaskMemFree never called for this two strings.
pseudo code for Deserialize :
static HRESULT HIDDENRECYCLEBINDATAV2::Deserialize(
_In_reads_bytes_opt_(cbInit) const BYTE *pbStream ,
_In_ USHORT cbStream,
_Out_ HIDDENRECYCLEBINDATAV2 ** pphrbd)
{
HRESULT hr = E_FAIL;
if (HIDDENRECYCLEBINDATAV2 *phrbd = new HIDDENRECYCLEBINDATAV2)
{
if (IStream *pStream = SHCreateMemStream(pbStream, cbStream))
{
if (0 <= (hr = IStream_ReadStrLong(pStream, &phrbd->pszLocationBeforeDelete)) &&
0 <= (hr = IStream_ReadStrLong(pStream, &phrbd->pszLocationInRecycleBin)))
{
*pphrbd = phrbd, phrbd = 0;
}
pStream->Release();
}
CoTaskMemFree(phrbd); // !! error, need delete phrbd
}
return hr;
}
and it called from CBitBucket::_ValidateItem :
HRESULT InitDeletedItem(PCWSTR pszLocationBeforeDelete, PCWSTR pszLocationBeforeDelete, DELETEDITEM *);
static HRESULT CBitBucket::_ValidateItem(_ITEMIDLIST_RELATIVE const *, DELETEDITEM ** ppdi)
{
HIDDENRECYCLEBINDATAV2 * phrbd;
if (0 <= HIDDENRECYCLEBINDATAV2::Deserialize(pbStream, cbStream, &phrbd))
{
if (DELETEDITEM * pdi = new DELETEDITEM)
{
if (0 <= InitDeletedItem( phrbd->pszLocationBeforeDelete,
phrbd->pszLocationInRecycleBin, pdi))
{
*ppdi = pdi, pdi = 0;
}
if (pdi) delete pdi;
}
CoTaskMemFree(phrbd); // !! error, need delete phrbd
}
}
in both functions - memory for HIDDENRECYCLEBINDATAV2 simply released with CoTaskMemFree api, but memory for strings inside this structure not released. i think need add
HIDDENRECYCLEBINDATAV2::~HIDDENRECYCLEBINDATAV2()
{
CoTaskMemFree(pszLocationInRecycleBin);
CoTaskMemFree(pszLocationBeforeDelete);
}
to this structure and call delete instead CoTaskMemFree
how possible found this ? i hook RtlAllocateHeap and RtlFreeHeap before second call to Test1() (important do this not on first call, because during first call may be additional libs load, some differed initialization, etc.. - all this can distort the real result)and log all alloc/free calls in current thread. also i replace while (pesi->Next..) to if (pesi->Next..) (usually one iteration is enough ). and i found that count of alloc on 2 more than count of free. so i easy found from where this 2 allocations- inside IStream_ReadStrLong. then i set breakpoint here and easy view from where this called :
CBitBucket::_ValidateItem
HIDDENRECYCLEBINDATAV2::Deserialize
IStream_ReadStrLong
CoTaskMemAlloc
partial demo code for log:
struct AI
{
PVOID BaseAddress;
PVOID From;
ULONG Size;
ULONG Flags;
};
struct TID
{
AI *pi;
ULONG nAlloc, nFree, nCells, MaxAllocDelta;
BOOL bNotLog;
TID()
{
RtlZeroMemory(this, sizeof(*this));
}
};
BOOLEAN NTAPI hook_RtlFreeHeap(PVOID HeapHandle, ULONG Flags, PVOID BaseAddress )
{
TID* p = RTL_FRAME<TID>::get();
if (!p || p->bNotLog)
{
return RtlFreeHeap(HeapHandle, Flags, BaseAddress) != 0;
}
p->bNotLog = TRUE;
if (!RtlFreeHeap(HeapHandle, Flags, BaseAddress))
{
__debugbreak();
}
if (BaseAddress)
{
AI* pi = p->pi;
ULONG n = p->nCells;
do
{
if (pi->BaseAddress == BaseAddress)
{
pi->BaseAddress = 0;
p->nFree++;
break;
}
} while (pi++, --n);
if (!n)
{
__debugbreak();
}
}
p->bNotLog = FALSE;
return TRUE;
}
PVOID NTAPI hook_RtlAllocateHeap( PVOID HeapHandle, ULONG Flags, SIZE_T Size )
{
TID* p = RTL_FRAME<TID>::get();
if (!p || p->bNotLog)
{
return RtlAllocateHeap(HeapHandle, Flags, Size);
}
p->bNotLog = TRUE;
if (PVOID BaseAddress = RtlAllocateHeap(HeapHandle, Flags, Size))
{
AI* pi = p->pi;
ULONG n = p->nCells;
do
{
if (!pi->BaseAddress)
{
pi->BaseAddress = BaseAddress;
pi->From = _ReturnAddress();
pi->Size = (ULONG)Size;
pi->Flags = Flags;
p->nAlloc++;
ULONG k = p->nAlloc - p->nFree;
if (k > p->MaxAllocDelta)
{
p->MaxAllocDelta = k;
}
break;
}
} while (pi++, --n);
if (!n)
{
__debugbreak();
}
p->bNotLog = FALSE;
return BaseAddress;
}
return 0;
}
void TestEx()
{
enum { cell_count = 0x1000 };
if (AI* pi = new AI[cell_count])
{
Test1();// first call
// hook RtlAllocateHeap + RtlFreeHeap
{
RtlZeroMemory(pi, cell_count * sizeof(AI));
RTL_FRAME<TID> f;
f.pi = pi;
f.nCells = cell_count;
Test1();// second call
DbgPrint("%x(%x) %x\n", f.nAlloc, f.nFree, f.MaxAllocDelta);
if (f.nAlloc - f.nFree)
{
ULONG n = cell_count;
AI* qi = pi;
do
{
if (qi->BaseAddress)
{
DbgPrint("%p> %x %x\n", qi->From, qi->Size, qi->Flags);
}
} while (qi++, --n);
}
}
delete [] pi;
}
}

IWiaDataTransfer::idtGetData fails with STG_E_MEDIUMFULL

Followed Microsoft tutorial to implement an MFC GUI application, XP compatible, to read images from Canon MF6540 printer/scanner.
With lpszFileName in STGMEDIUM left at NULL, idtGetData is failing with STG_E_MEDIUMFULL.
With lpszFileName set to L"c:\tmp\foo.bmp", where "c:\tmp" is an existing folder, idtGetData is failing with 0xC0000005.
How might I please idtGetData? Thanks.
// WIADlg.cpp : implementation file
//
#include "stdafx.h"
#include "WIA.h"
#include "WIADlg.h"
#include "WIADataCallback.h"
#include "afxdialogex.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CWIADlg dialog
CWIADlg::CWIADlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CWIADlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
m_root = NULL;
m_scanner = NULL;
}
void CWIADlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CWIADlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDOK, &CWIADlg::OnBnClickedOk)
ON_BN_CLICKED(IDC_BUTTON_SCAN, &CWIADlg::OnBnClickedButtonScan)
END_MESSAGE_MAP()
// CWIADlg message handlers
BOOL CWIADlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(CLSID_WiaDevMgr, NULL, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr, (void**)&m_pWiaDevMgr);
if (FAILED(hr))
{
CString str;
str.Format("CoCreateInstance CLSID_WiaDevMsg failure %08X", hr);
MessageBox(str);
}
IEnumWIA_DEV_INFO *pWiaEnumDevInfo = NULL;
hr = m_pWiaDevMgr->EnumDeviceInfo(WIA_DEVINFO_ENUM_LOCAL, &pWiaEnumDevInfo);
if (SUCCEEDED(hr))
{
// Loop until you get an error or pWiaEnumDevInfo->Next returns S_FALSE to signal the end of the list.
while (S_OK == hr)
{
// Get the next device's property storage interface pointer
IWiaPropertyStorage *pWiaPropertyStorage = NULL;
hr = pWiaEnumDevInfo->Next(1, &pWiaPropertyStorage, NULL);
// pWiaEnumDevInfo->Next will return S_FALSE when the list is exhausted, so check for S_OK before using the returned value.
if (hr == S_OK)
{
// Do something with the device's IWiaPropertyStorage*
ReadSomeWiaProperties(pWiaPropertyStorage);
// Release the device's IWiaPropertyStorage*
pWiaPropertyStorage->Release();
pWiaPropertyStorage = NULL;
}
}
// If the result of the enumeration is S_FALSE (which is normal), change it to S_OK.
if (S_FALSE == hr)
{
hr = S_OK;
}
// Release the enumerator
pWiaEnumDevInfo->Release();
pWiaEnumDevInfo = NULL;
}
return TRUE; // return TRUE unless you set the focus to a control
}
void CWIADlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CWIADlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CWIADlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
HRESULT CWIADlg::ReadSomeWiaProperties(IWiaPropertyStorage *pWiaPropertyStorage)
{
// Declare PROPSPECs and PROPVARIANTs, and initialize them to zero.
PROPSPEC PropSpec[3] = {0};
PROPVARIANT PropVar[3] = {0};
// How many properties are you querying for?
const ULONG c_nPropertyCount = sizeof(PropSpec)/sizeof(PropSpec[0]);
// Define which properties you want to read:
// Device ID. This is what you would use to create the device.
PropSpec[0].ulKind = PRSPEC_PROPID;
PropSpec[0].propid = WIA_DIP_DEV_ID;
// Device Name
PropSpec[1].ulKind = PRSPEC_PROPID;
PropSpec[1].propid = WIA_DIP_DEV_NAME;
// Device description
PropSpec[2].ulKind = PRSPEC_PROPID;
PropSpec[2].propid = WIA_DIP_DEV_DESC;
// Ask for the property values
HRESULT hr = pWiaPropertyStorage->ReadMultiple(c_nPropertyCount, PropSpec, PropVar);
if (SUCCEEDED(hr))
{
// IWiaPropertyStorage::ReadMultiple will return S_FALSE if some
// properties could not be read, so you have to check the return
// types for each requested item.
CString str;
WIA_DEVICE *device = new WIA_DEVICE;
// Check the return type for the device ID
if (VT_BSTR == PropVar[0].vt)
{
// Do something with the device ID
TRACE(TEXT("WIA_DIP_DEV_ID: %ws\n"), PropVar[0].bstrVal);
device->deviceID.Format("%ws", PropVar[0].bstrVal);
device->bstrDeviceID = SysAllocString(PropVar[0].bstrVal);
}
// Check the return type for the device name
if (VT_BSTR == PropVar[1].vt)
{
// Do something with the device name
TRACE(TEXT("WIA_DIP_DEV_NAME: %ws\n"), PropVar[1].bstrVal);
device->deviceName.Format("%ws", PropVar[1].bstrVal);
}
// Check the return type for the device description
if (VT_BSTR == PropVar[2].vt)
{
// Do something with the device description
TRACE(TEXT("WIA_DIP_DEV_DESC: %ws\n"), PropVar[2].bstrVal);
device->deviceDesc.Format("%ws", PropVar[2].bstrVal);
}
CListBox *list = (CListBox*)GetDlgItem(IDC_LIST_WIA_DEVICES);
int nIndex = list->AddString((LPCTSTR)device->deviceName);
list->SetItemDataPtr(nIndex, device);
// Free the returned PROPVARIANTs
FreePropVariantArray(c_nPropertyCount, PropVar);
}
// Return the result of reading the properties
return hr;
}
void CWIADlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
CDialogEx::OnOK();
}
void CWIADlg::OnBnClickedButtonScan()
{
// TODO: Add your control notification handler code here
CListBox *list = (CListBox*)GetDlgItem(IDC_LIST_WIA_DEVICES);
int nIndex = list->GetCurSel();
if (nIndex < 0)
{
MessageBox("No device selected");
return;
}
WIA_DEVICE *device = (WIA_DEVICE*)list->GetItemDataPtr(nIndex);
MessageBox(device->deviceName);
HRESULT hr = m_pWiaDevMgr->CreateDevice(device->bstrDeviceID, &m_root);
if (FAILED(hr))
{
CString str;
str.Format("CreateDevice failure %08X", hr);
MessageBox(str);
}
EnumerateItems(m_root);
TransferWiaItem(m_root, m_scanner);
}
HRESULT CWIADlg::EnumerateItems(IWiaItem *pWiaItem)
{
CString str;
// Get the item type for this item.
LONG lItemType = 0;
HRESULT hr = pWiaItem->GetItemType(&lItemType);
if (SUCCEEDED(hr))
{
str.Format("pWiaItem=%08X", pWiaItem);
if (lItemType & WiaItemTypeAnalyze)
str += " WiaItemTypeAnalyze";
if (lItemType & WiaItemTypeAudio)
str += " WiaItemTypeAudio";
if (lItemType & WiaItemTypeBurst)
str += " WiaItemTypeBurst";
if (lItemType & WiaItemTypeDeleted)
str += " WiaItemTypeDeleted";
if (lItemType & WiaItemTypeDevice)
str += " WiaItemTypeDevice";
if (lItemType & WiaItemTypeDisconnected)
str += " WiaItemTypeDisconnected";
if (lItemType & WiaItemTypeDocument)
str += " WiaItemTypeDocument";
if (lItemType & WiaItemTypeFile)
str += " WiaItemTypeFile";
if (lItemType & WiaItemTypeFolder)
str += " WiaItemTypeFolder";
if (lItemType & WiaItemTypeFree)
str += " WiaItemTypeFree";
if (lItemType & WiaItemTypeGenerated)
str += " WiaITemTypeGenerated";
if (lItemType & WiaItemTypeHasAttachments)
str += " WiaItemTypeHasAttachments";
if (lItemType & WiaItemTypeHPanorama)
str += " WiaItemTypeHPanorama";
if (lItemType & WiaItemTypeImage)
{
str += " WiaItemTypeImage";
m_scanner = pWiaItem;
}
if (lItemType & WiaItemTypeProgrammableDataSource)
str += " WiaItemTypeProgrammableDataSource";
if (lItemType & WiaItemTypeRemoved)
str += " WiaItemTypeRemoved";
if (lItemType & WiaItemTypeRoot)
str += " WiaItemTypeRoot";
if (lItemType & WiaItemTypeStorage)
str += " WiaItemTypeStorage";
if (lItemType & WiaItemTypeTransfer)
str += " WiaItemTypeTransfer";
// if (lItemType & WiaItemTypeTwainCapabilityPassThrough)
// str += " WiaItemTypeTwainCapabilityPassThrough";
if (lItemType & WiaItemTypeVideo)
str += " WiaItemTypeVideo";
if (lItemType & WiaItemTypeVPanorama)
str += " WiaItemTypeVPanorama";
MessageBox(str);
// If it is a folder, or it has attachments, enumerate its children.
if (lItemType & WiaItemTypeFolder || lItemType & WiaItemTypeHasAttachments)
{
// Get the child item enumerator for this item.
IEnumWiaItem *pEnumWiaItem = NULL;
hr = pWiaItem->EnumChildItems(&pEnumWiaItem);
if (SUCCEEDED(hr))
{
// Loop until you get an error or pEnumWiaItem->Next returns S_FALSE to signal the end of the list.
while (S_OK == hr)
{
str.Format("pEnumWiaItem=%08X", pEnumWiaItem);
MessageBox(str);
// Get the next child item.
IWiaItem *pChildWiaItem = NULL;
hr = pEnumWiaItem->Next(1, &pChildWiaItem, NULL);
// pEnumWiaItem->Next will return S_FALSE when the list is
// exhausted, so check for S_OK before using the returned
// value.
if (S_OK == hr)
{
str.Format("pChildWiaItem=%08X", pChildWiaItem);
MessageBox(str);
// Recurse into this item.
hr = EnumerateItems(pChildWiaItem);
#if 0
// Release this item.
str.Format("Releasing %08X", pChildWiaItem);
MessageBox(str);
pChildWiaItem->Release();
pChildWiaItem = NULL;
#endif
}
}
// If the result of the enumeration is S_FALSE (which is normal), change it to S_OK.
if (S_FALSE == hr)
{
hr = S_OK;
}
// Release the enumerator.
pEnumWiaItem->Release();
pEnumWiaItem = NULL;
}
}
}
return hr;
}
HRESULT CWIADlg::TransferWiaItem(IWiaItem *root, IWiaItem *scanner)
{
CString str;
// Get the IWiaPropertyStorage interface so you can set required properties.
IWiaPropertyStorage *pWiaPropertyStorage = NULL;
HRESULT hr = root->QueryInterface(IID_IWiaPropertyStorage, (void**)&pWiaPropertyStorage);
if (SUCCEEDED(hr))
{
// Prepare PROPSPECs and PROPVARIANTs for setting the media type and format
PROPSPEC PropSpec[2] = {0};
PROPVARIANT PropVariant[2] = {0};
const ULONG c_nPropCount = sizeof(PropVariant) / sizeof(PropVariant[0]);
// Use BMP as the output format
GUID guidOutputFormat = WiaImgFmt_BMP;
// Initialize the PROPSPECs
PropSpec[0].ulKind = PRSPEC_PROPID;
PropSpec[0].propid = WIA_IPA_FORMAT;
PropSpec[1].ulKind = PRSPEC_PROPID;
PropSpec[1].propid = WIA_IPA_TYMED;
// Initialize the PROPVARIANTs
PropVariant[0].vt = VT_CLSID;
PropVariant[0].puuid = &guidOutputFormat;
PropVariant[1].vt = VT_I4;
PropVariant[1].lVal = TYMED_FILE;
// Set the properties
hr = pWiaPropertyStorage->WriteMultiple(c_nPropCount, PropSpec, PropVariant, WIA_IPA_FIRST);
if (SUCCEEDED(hr))
{
// Get the IWiaDataTransfer interface
IWiaDataTransfer *pWiaDataTransfer = NULL;
hr = scanner->QueryInterface(IID_IWiaDataTransfer, (void**)&pWiaDataTransfer);
if (SUCCEEDED(hr))
{
#if 1
// Create our callback class
CWiaDataCallback *pCallback = new CWiaDataCallback;
if (pCallback)
{
// Get the IWiaDataCallback interface from our callback class.
IWiaDataCallback *pWiaDataCallback = NULL;
hr = pCallback->QueryInterface(IID_IWiaDataCallback, (void**)&pWiaDataCallback);
if (SUCCEEDED(hr))
{
AfxMessageBox("calling idtGetData");
// Perform the transfer using default settings
STGMEDIUM stgMedium = {0};
stgMedium.tymed = TYMED_FILE;
stgMedium.lpszFileName = L"c:\\tmp\\foo.bmp";
hr = pWiaDataTransfer->idtGetData(&stgMedium, pWiaDataCallback);
if (S_OK == hr)
{
// Print the filename (note that this filename is always a WCHAR string, not TCHAR).
str.Format("Transferred filename: %ws", stgMedium.lpszFileName);
AfxMessageBox(str);
// Release any memory associated with the stgmedium This will delete the file stgMedium.lpszFileName.
ReleaseStgMedium(&stgMedium);
}
else
{
str.Format("idtGetData failure=%08X", hr);
AfxMessageBox(str);
}
// Release the callback interface
pWiaDataCallback->Release();
pWiaDataCallback = NULL;
}
// Release our callback. It should now delete itself.
pCallback->Release();
pCallback = NULL;
}
#else
// Perform the transfer using default settings
STGMEDIUM stgMedium = {0};
stgMedium.tymed = TYMED_FILE;
// stgMedium.lpszFileName = L"c:\\tmp\\foo.bmp";
hr = pWiaDataTransfer->idtGetData(&stgMedium, NULL);
if (S_OK == hr)
{
// Print the filename (note that this filename is always a WCHAR string, not TCHAR).
str.Format(TEXT("Transferred filename: %ws"), stgMedium.lpszFileName);
MessageBox(str);
// Release any memory associated with the stgmedium This will delete the file stgMedium.lpszFileName.
ReleaseStgMedium(&stgMedium);
}
else
{
str.Format("idtGetData failure=%08X", hr);
MessageBox(str);
}
#endif
// Release the IWiaDataTransfer
pWiaDataTransfer->Release();
pWiaDataTransfer = NULL;
}
else
{
str.Format("TransferWiaItem::QueryInterface IID_IWiaDataTransfer failure=%08X", hr);
MessageBox(str);
}
}
else
{
str.Format("TransferWiaItem::WriteMultiple failure=%08X", hr);
MessageBox(str);
}
// Release the IWiaPropertyStorage
pWiaPropertyStorage->Release();
pWiaPropertyStorage = NULL;
}
else
{
str.Format("TransferWiaItem::QueryInterface IID_IWiaPropertyStorage failure=%08X", hr);
MessageBox(str);
}
return hr;
}

Catastrophic failure calling ActiveX method from c++ console application

I need to replace an ActiveX control with a new one for use in an existing application, without modifying the existing application.
I have the original ocx file and the code for the wrapper class that creates and uses the ActiveX control in the existing application.
I have created a test console app in VS 2005, using the wrapper class from the existing application, which can create and use the old ActiveX control.
I have created a new ActiveX control with the same interface as the old, modified all the Class Ids to match and registered the new ocx. The new control can be created, but on calling any of the methods I get a Catastrophic failure with error code 7fff0001.
I took a step back and created another new ActiveX control with the same interface, but didn't modify the Class Ids. I then built a new test console app using the wrapper class and compiled it for this new control. But I still get a Catastrophic failure when calling the control's methods.
I have searched for an answer, but most refer to problems calling ActiveX methods from managed c# applications and don't provide any help for my situation.
Please see the code for the wrapper and ActiveX control import files below, any help would be appreciated thanks.
ClientWrapper.h
#if !defined(AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_)
#define AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#import <Msxml3.dll>
#import "ACDCLIENTX.OCX"
#include <atlbase.h>
#include <atlconv.h>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <map>
#include <assert.h>
#ifdef ASSERT
#undef ASSERT
#endif
#define ASSERT assert
class CParsedResponse
{
public:
CParsedResponse():m_bIsApproved(false),m_bIsCaptured(false),
m_bIsSuccess(false),m_bIsError(false){;}
virtual ~CParsedResponse(){;};
CParsedResponse(const CParsedResponse& copy){*this = copy;}
CParsedResponse& operator = (const CParsedResponse& that)
{
if(this == &that ) return *this;
m_strErrorNo = that.m_strErrorNo;
m_strVerbage = that.m_strVerbage;
m_strBalance = that.m_strBalance;
m_strPrePaidExp = that.m_strPrePaidExp;
m_strAuthCode = that.m_strAuthCode;
m_strReferenceNo = that.m_strReferenceNo;
m_strAcctNo = that.m_strAcctNo;
m_strExpDate = that.m_strExpDate;
m_strPurchaseAmount = that.m_strPurchaseAmount;
m_strInvoiceNo = that.m_strInvoiceNo;
m_strOpId = that.m_strOpId;
m_strAcqRefData = that.m_strAcqRefData;
m_strOrigin = that.m_strOrigin;
m_bIsApproved = that.m_bIsApproved;
m_bIsCaptured = that.m_bIsCaptured;
m_bIsSuccess = that.m_bIsSuccess;
m_bIsError = that.m_bIsError;
return *this;
}
void Empty()
{
m_strErrorNo =
m_strVerbage =
m_strBalance =
m_strPrePaidExp =
m_strAuthCode =
m_strReferenceNo =
m_strAcctNo =
m_strExpDate =
m_strPurchaseAmount =
m_strInvoiceNo =
m_strOpId =
m_strAcqRefData =
m_strOrigin = "";
m_bIsApproved =
m_bIsCaptured =
m_bIsSuccess =
m_bIsError = false;
}
std::string m_strErrorNo,
m_strVerbage,
m_strBalance,
m_strPrePaidExp,
m_strAuthCode,
m_strReferenceNo,
m_strAcctNo,
m_strExpDate,
m_strPurchaseAmount,
m_strInvoiceNo,
m_strOpId,
m_strAcqRefData,
m_strOrigin;
bool m_bIsApproved,
m_bIsCaptured,
m_bIsSuccess,
m_bIsError;
};
class CClientWrapper
{
std::string m_strName;
bool LooksLikeIp(const std::string IPorHostName);
std::string GetIPAddressFromHostName(const std::string& Ip);
bool GetIPFromXML(const std::string XML,std::string& Ip);
void AddToStore(std::string & s,const std::string & ip);
int GetNumIPStoredIPFromXML(const std::string XML);
std::string BuildErrorResponse(const int ErrNo, const std::string strErrText);
std::string BuildErrorResponse(const std::string ErrNo, const std::string strErrText);
std::string CClientWrapper::BuildErrorResponse(const _com_error ComError);
public:
std::string m_strError;
std::string m_strServerPassword;
CParsedResponse ExtractTransResponse(const std::string XmlResponse);
std::string SendRequestReturnXML(const std::string strXML);
CParsedResponse SendRequest(const std::string strXML);
bool DoServerIp(const std::vector<std::string>& IpList);
std::string PingStoredServerListReturnXML();
private:
ACDCLIENTXLib::_DACDCLientXPtr m_pClient;
bool m_bComInit;
bool m_bControlInit;
public:
void Destroy();
bool Create();
void Cancel();
CClientWrapper();
virtual ~CClientWrapper();
int SetConnectTimeout(const int t);
int SetResponseTimeout(const int t);
CParsedResponse PingStoredServerList();
// 0 dialog // 1 no dialog // 2 not currently supported in wrapper
SHORT m_nDialogControl;
};
#endif // !defined(AFX_CLIENTWRAPPER_H__D8C23AC2_6F22_11D6_9F45_0003B3008F24__INCLUDED_)
ClientWrapper.cpp
#include "StdAfx.h"
#include "ClientWrapper.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
#define WRAP_ERROR "900999"
CClientWrapper::CClientWrapper():m_bComInit(false),m_bControlInit(false),
m_nDialogControl(0),m_strServerPassword("")
{
}
CClientWrapper::~CClientWrapper()
{
Destroy();
}
void CClientWrapper::Destroy()
{
if(m_bControlInit)
{
m_pClient.Release();
m_bControlInit = false;
}
if(m_bComInit)
{
::CoUninitialize();
m_bComInit = false;
}
}
bool CClientWrapper::Create()
{
USES_CONVERSION;
Destroy();
ASSERT(!m_bComInit);
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr))
{
m_strError = "CoInitialize Failed - Fatal Error!";
return false;
}
m_bComInit = true;
ASSERT(!m_bControlInit);
hr = m_pClient.CreateInstance(__uuidof(ACDCLIENTXLib::ACDCLientX));
if(FAILED(hr) )
{
m_strError = "Failed to Create ACDCLientX.ocx you may need to reinstall the product - Fatal Error!";
return false;
}
m_bControlInit = true;
// make sure xml parser is available
try
{
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
}
catch(_com_error e)
{
m_strError = "Msxml3.dll required for this application. You may need to reinstall the product - Fatal Error!";
return false;
}
return true;
}
#pragma warning(disable:4786)
std::string CClientWrapper::GetIPAddressFromHostName(const std::string& Ip)
{
std::string rv;
USES_CONVERSION;
_bstr_t strIp(Ip.c_str());
try
{
rv = W2T(m_pClient->GetIPAddressFromHostName(strIp,m_nDialogControl));
}
catch(const _com_error &e)
{
throw e;
}
return rv;
}
bool CClientWrapper::DoServerIp(const std::vector<std::string>& IpList)
{
std::vector<std::string>::const_iterator i;
USES_CONVERSION;
assert(m_bComInit && m_bControlInit);
////
////
try
{
std::string IpStore("");
i = IpList.begin();
while(i != IpList.end())
{
std::string Ip = *i;
if(!Ip.length() )
{
++ i;
continue;
}
if(!LooksLikeIp(Ip) )
{
std::string xml = GetIPAddressFromHostName(Ip);
if(GetIPFromXML(xml,Ip) )
{
AddToStore(IpStore,Ip);
}
}
else
{
AddToStore(IpStore,Ip);
}
++ i;
}
if(!IpStore.length())
{
m_strError = "No Resolved Ip Address.";
return false;
}
_bstr_t strIpStore(IpStore.c_str());
std::string xml = W2T(m_pClient->ServerIPConfig(strIpStore,m_nDialogControl));
if(GetNumIPStoredIPFromXML(xml) <= 0 )
{
m_strError = "No Resolved Ip Address.";
return false;
}
//defaults to 10 seconds connect - 300 seconds response
#ifdef _DEBUG
ASSERT(m_pClient->SetConnectTimeout(5) == 5);
#else
m_pClient->SetConnectTimeout(5);
#endif
// VERIFY(m_pClient->SetConnectTimeout(5) == 5);
//VERIFY(m_pClient->SetResponseTimeout(300) == 300);
// you may wish to 'ping' the servers at this point
// using PingStoredServerList(..)
// or you could ping each before you store the ServerIpConfig(..)
// using PingServer(..)
// it is up to you whether you want your
// app not to start or give some other message because a server is down
// if you decide not to ping AND the Server is DOWN when you attempt
// an authorization the control will return an appropriate error.
}
catch (const _com_error &ComError)
{
m_strError = ComError.ErrorMessage() + std::string(" Fatal Error!");
return false;
}
catch (...)
{
m_strError = "ATL/COM Error" + std::string(" Fatal Error!");
return false;
}
char nameBuffer[101] = {0};
DWORD size = 100;
if(::GetUserName(nameBuffer,&size) )
m_strName = nameBuffer;
return true;
}
bool CClientWrapper::LooksLikeIp(const std::string IPorHostName)
{
std::map<int,std::string> Map;
std::map<int,std::string>::const_iterator iter;
const std::string& s = IPorHostName;
int x(0),len = s.length();
char c,fsep = '.';
std::string fieldText;
int NumOctets (0);
while(x < len)
{
c = s[x];
if(c != fsep)
fieldText += c;
else
{
Map[NumOctets++]=fieldText;
fieldText = "";
}
x++;
}
if(fieldText.length() )
Map[NumOctets++]=fieldText;
if(NumOctets != 4)
return false;
for(x = 0; x < 4 ; x ++ )
{
std::string octect;
if((iter = Map.find(x))==Map.end() )
return false;
octect = (*iter).second;
int val = _ttoi(octect.c_str());
if(val == 0 )
{
//octect.TrimLeft();
int len = octect.length();
std::string temp("");
for(int xx= 0; xx < len ; xx ++ )
{
char c = octect[xx];
if(temp.length() || c != ' ' )
{
temp += c;
}
}
octect = temp;
if(!octect.length() || octect[0] < 0x30 || octect[0] > 0x39)
return false;
}
if(val < 0 || val > 255)
return false;
}
return true;
}
bool CClientWrapper::GetIPFromXML(const std::string XML,std::string& Ip)
{
USES_CONVERSION;
try
{
_bstr_t strXML (XML.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
// any error is bad
if (VARIANT_FALSE == varResult)
return false;
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(0 == pNode )
return false;
std::string Result = W2T(pNode->Gettext() );
if("Success" != Result)
return false;
pNode = pDOMDoc->selectSingleNode("//IpAddress");
if(0 == pNode )
return false;
Ip = W2T(pNode->Gettext());
return true;
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetIPFromXML") << std::endl;
#endif
}
catch(...)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetIPFromXML") << std::endl;
#endif
}
return false;
}
void CClientWrapper::AddToStore(std::string & s,const std::string& ip)
{
// if ip is empty ignore it
if(!ip.length() )
return;
// if the store is not empty it is ';' separated
if(s.length() )
s += std::string(";") + ip;
else
s = ip;
}
int CClientWrapper::GetNumIPStoredIPFromXML(const std::string XML)
{
USES_CONVERSION;
try
{
_bstr_t strXML (XML.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
// any error is bad
if (VARIANT_FALSE == varResult)
return 0;
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(0 == pNode )
return 0;
std::string Result = W2T(pNode->Gettext() );
if("Success" != Result)
return 0;
pNode = pDOMDoc->selectSingleNode("//TextResponse");
if(0 == pNode )
return 0;
return _ttoi(W2T(pNode->Gettext() ) );
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetNumIPStoredIPFromXML") << std::endl;
#endif
}
catch(...)
{
#ifdef _DEBUG
std::cerr << std::string("Caught Exception: GetNumIPStoredIPFromXML") << std::endl;
#endif
}
return 0;
}
std::string CClientWrapper::BuildErrorResponse(const _com_error ComError)
{
std::stringstream ssErrNo;
ssErrNo << ComError.Error();
return BuildErrorResponse(ssErrNo.str(), ComError.ErrorMessage());
}
std::string CClientWrapper::BuildErrorResponse(const std::string ErrNo, const std::string strErrText)
{
return BuildErrorResponse(_ttoi(ErrNo.c_str()), strErrText);
}
std::string CClientWrapper::BuildErrorResponse(const int ErrNo, const std::string strErrText)
{
#define TAB "\t"
#define CRLF "\r\n"
std::ostringstream oss;
oss << "<?xml version=\"1.0\"?>"CRLF
"<RStream>"CRLF
TAB "<CmdResponse>"CRLF
TAB TAB "<ACDXReturnCode>"
<< std::setw(6) << std::setfill('0') << ErrNo
<< "</ACDXReturnCode>"CRLF
TAB TAB "<CmdStatus>Error</CmdStatus>"CRLF
TAB TAB "<TextResponse>"
<< strErrText.c_str()
<< "</TextResponse>"CRLF
TAB "</CmdResponse>"CRLF
"</RStream>"CRLF
<< std::endl;
std::string rv(oss.str());
return rv;
}
std::string CClientWrapper::PingStoredServerListReturnXML()
{
USES_CONVERSION;
std::string rv;
try
{
rv = W2T(m_pClient->PingStoredServerList("",m_nDialogControl) );
}
catch (const _com_error &ComError)
{
rv = BuildErrorResponse(ComError);
}
catch (...)
{
rv = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
}
return rv;
}
void CClientWrapper::Cancel()
{
try
{
m_pClient->CancelRequest();
}
catch (const _com_error &ComError)
{
std::string s = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
#ifdef _DEBUG
std :: cerr << s << std::endl;
#endif
}
catch (...)
{
std::string s = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
#ifdef _DEBUG
std :: cerr << s << std::endl;
#endif
}
}
CParsedResponse CClientWrapper::SendRequest(const std::string strXML)
{
return ExtractTransResponse(SendRequestReturnXML(strXML) );
}
std::string CClientWrapper::SendRequestReturnXML(const std::string strXML)
{
USES_CONVERSION;
std::string rv;
try
{
_bstr_t strXml(strXML.c_str());
_bstr_t strPasswd(m_strServerPassword.c_str());
_bstr_t strName(m_strName.c_str());
rv = W2T(m_pClient->ProcessTransaction(
strXml,
m_nDialogControl,
strPasswd,
strName) );
}
catch (const _com_error &ComError)
{
rv = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
}
catch (...)
{
rv = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
}
return rv;
}
CParsedResponse CClientWrapper::ExtractTransResponse(const std::string XmlResponse)
{
CParsedResponse rv;
USES_CONVERSION;
try
{
_bstr_t strXML (XmlResponse.c_str());
MSXML2::IXMLDOMDocumentPtr pDOMDoc(__uuidof(MSXML2::DOMDocument));
VARIANT_BOOL varResult = pDOMDoc->loadXML(strXML);
if (VARIANT_FALSE == varResult)
{
MSXML2::IXMLDOMParseErrorPtr pParseError = pDOMDoc->GetparseError();
long dwError = pParseError->GeterrorCode();
_bstr_t bstrReason = pParseError->Getreason();
long num = pParseError->Getline();
std::ostringstream oss;
std::string strReason = W2T(bstrReason);
oss << "XML Parse Error at line#:" << num << "\r\n" << strReason << std::endl;
std::string error (oss.str() );
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = error;
rv.m_bIsError = true;
return rv;
}
MSXML2::IXMLDOMNodePtr pNode = pDOMDoc->selectSingleNode("//TextResponse");
if(pNode)
{
rv.m_strVerbage = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//Balance");
if(pNode)
{
rv.m_strBalance = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//PrePaidExp");
if(pNode)
{
rv.m_strPrePaidExp = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//ResponseOrigin");
if(pNode)
{
rv.m_strOrigin = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//ACDXReturnCode");
if(pNode)
{
rv.m_strErrorNo = W2T(pNode->Gettext() );
}
pNode = pDOMDoc->selectSingleNode("//CmdStatus");
if(pNode)
{
std::string strText;
strText = W2T(pNode->Gettext() );
if("Approved" == strText )
{
rv.m_bIsApproved = true;
rv.m_strVerbage = "APPROVED";
pNode = pDOMDoc->selectSingleNode("//AuthCode");
if(pNode)
{
rv.m_strAuthCode = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//CaptureStatus");
if(pNode)
{
#if defined(__USING__MFC)
if("Captured" == CString(W2T(pNode->Gettext()) ) )
#else
if("Captured" == std::string(W2T(pNode->Gettext()) ) )
#endif
{
rv.m_bIsCaptured = true;
}
}
pNode = pDOMDoc->selectSingleNode("//RefNo");
if(pNode)
{
rv.m_strReferenceNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//AccountNo");
if(pNode)
{
rv.m_strAcctNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//ExpDate");
if(pNode)
{
rv.m_strExpDate = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//Purchase");
if(pNode)
{
rv.m_strPurchaseAmount = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//InvoiceNo");
if(pNode)
{
rv.m_strInvoiceNo = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//OperatorId");
if(pNode)
{
rv.m_strOpId = W2T(pNode->Gettext());
}
pNode = pDOMDoc->selectSingleNode("//AcqRefData");
if(pNode)
{
rv.m_strAcqRefData = W2T(pNode->Gettext());
}
}
else if("Declined" == strText)
{
rv.m_bIsApproved = false;
rv.m_bIsCaptured = false;
}
else if("Success" == strText )
{
rv.m_bIsSuccess =true;
}
else if("Error" == strText )
{
rv.m_bIsError = true;
}
else
{
rv.m_strVerbage = "Received Invalid Result from ACDClientX Control.- \r\n"+ XmlResponse;
rv.m_strErrorNo = WRAP_ERROR;
}
}
}
catch(_com_error e)
{
#ifdef _DEBUG
std::cerr<< std::string("Caught Exception: ExtractTransResponse")<< std::endl;
#endif
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = e.ErrorMessage();
rv.m_bIsError = true;
}
catch(...)
{
#ifdef _DEBUG
std::cerr<< std::string("Caught Exception: ExtractTransResponse")<< std::endl;
#endif
rv.m_strErrorNo = WRAP_ERROR;
rv.m_strVerbage = "Msxml3 Open Error";
rv.m_bIsError = true;
}
return rv;
}
CParsedResponse CClientWrapper::PingStoredServerList()
{
return ExtractTransResponse(PingStoredServerListReturnXML() );
}
int CClientWrapper::SetResponseTimeout(const int t)
{
USES_CONVERSION;
int rv;
try
{
rv = m_pClient->SetResponseTimeout(t);
}
catch (const _com_error &ComError)
{
m_strError = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
rv = -1;
}
catch (...)
{
m_strError = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
rv = -1;
}
return rv;
}
int CClientWrapper::SetConnectTimeout(const int t)
{
USES_CONVERSION;
int rv;
try
{
rv = m_pClient->SetConnectTimeout(t);
}
catch (const _com_error &ComError)
{
m_strError = BuildErrorResponse(WRAP_ERROR,ComError.ErrorMessage() );
rv = -1;
}
catch (...)
{
m_strError = BuildErrorResponse(WRAP_ERROR,"ATLCOM ERROR");
rv = -1;
}
return rv;
}
acdclientx.tlh
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace ACDCLIENTXLib {
//
// Forward references and typedefs
//
struct __declspec(uuid("87476f1d-9a0f-4e4e-bd38-0378b35d71b8"))
/* LIBID */ __ACDCLIENTXLib;
struct __declspec(uuid("4ff86764-9448-4e1d-a160-4e55c7151c3d"))
/* dispinterface */ _DACDCLientX;
struct __declspec(uuid("fd6514db-1fa1-43c2-982a-9bf3a4f63c71"))
/* dispinterface */ _DACDCLientXEvents;
struct /* coclass */ ACDCLientX;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(_DACDCLientX, __uuidof(_DACDCLientX));
_COM_SMARTPTR_TYPEDEF(_DACDCLientXEvents, __uuidof(_DACDCLientXEvents));
//
// Type library items
//
struct __declspec(uuid("4ff86764-9448-4e1d-a160-4e55c7151c3d"))
_DACDCLientX : IDispatch
{
//
// Wrapper methods for error-handling
//
// Methods:
_bstr_t ProcessTransaction (
_bstr_t XMLCommand,
short ProcessControl,
_bstr_t ClientServerPassword,
_bstr_t UserTraceData );
_bstr_t ServerIPConfig (
_bstr_t HostList,
short ProcessControl );
_bstr_t PingStoredServerList (
_bstr_t IpPort,
short ProcessControl );
long CancelRequest ( );
_bstr_t GetIPAddressFromHostName (
_bstr_t HostName,
short ProcessControl );
_bstr_t PingServer (
_bstr_t IpAddress,
_bstr_t IpPort,
short ProcessControl );
short SetConnectTimeout (
short TimeoutSec );
short SetResponseTimeout (
short TimeoutSec );
_bstr_t ProcessCanadianTransaction (
_bstr_t XMLCommand,
short ProcessControl,
_bstr_t ClientServerPassword,
_bstr_t UserTraceData );
};
struct __declspec(uuid("fd6514db-1fa1-43c2-982a-9bf3a4f63c71"))
_DACDCLientXEvents : IDispatch
{};
struct __declspec(uuid("d76e402e-c138-4480-a8a3-bc840d6a2a4e"))
ACDCLientX;
// [ default ] dispinterface _DACDCLientX
// [ default, source ] dispinterface _DACDCLientXEvents
//
// Wrapper method implementations
//
#include "acdclientx.tli"
} // namespace ACDCLIENTXLib
#pragma pack(pop)
acdclientx.tli
#pragma once
//
// dispinterface _DACDCLientX wrapper method implementations
//
inline _bstr_t _DACDCLientX::ProcessTransaction ( _bstr_t XMLCommand, short ProcessControl, _bstr_t ClientServerPassword, _bstr_t UserTraceData ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x1, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002\x0008\x0008", (BSTR)XMLCommand, ProcessControl, (BSTR)ClientServerPassword, (BSTR)UserTraceData);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::ServerIPConfig ( _bstr_t HostList, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x2, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)HostList, ProcessControl);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::PingStoredServerList ( _bstr_t IpPort, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x3, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)IpPort, ProcessControl);
return _bstr_t(_result, false);
}
inline long _DACDCLientX::CancelRequest ( ) {
long _result = 0;
_com_dispatch_method(this, 0x4, DISPATCH_METHOD, VT_I4, (void*)&_result, NULL);
return _result;
}
inline _bstr_t _DACDCLientX::GetIPAddressFromHostName ( _bstr_t HostName, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x5, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002", (BSTR)HostName, ProcessControl);
return _bstr_t(_result, false);
}
inline _bstr_t _DACDCLientX::PingServer ( _bstr_t IpAddress, _bstr_t IpPort, short ProcessControl ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x6, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0008\x0002", (BSTR)IpAddress, (BSTR)IpPort, ProcessControl);
return _bstr_t(_result, false);
}
inline short _DACDCLientX::SetConnectTimeout ( short TimeoutSec ) {
short _result = 0;
_com_dispatch_method(this, 0x7, DISPATCH_METHOD, VT_I2, (void*)&_result,
L"\x0002", TimeoutSec);
return _result;
}
inline short _DACDCLientX::SetResponseTimeout ( short TimeoutSec ) {
short _result = 0;
_com_dispatch_method(this, 0x8, DISPATCH_METHOD, VT_I2, (void*)&_result,
L"\x0002", TimeoutSec);
return _result;
}
inline _bstr_t _DACDCLientX::ProcessCanadianTransaction ( _bstr_t XMLCommand, short ProcessControl, _bstr_t ClientServerPassword, _bstr_t UserTraceData ) {
BSTR _result = 0;
_com_dispatch_method(this, 0x9, DISPATCH_METHOD, VT_BSTR, (void*)&_result,
L"\x0008\x0002\x0008\x0008", (BSTR)XMLCommand, ProcessControl, (BSTR)ClientServerPassword, (BSTR)UserTraceData);
return _bstr_t(_result, false);
}
Found the answer in:
http://support.microsoft.com/kb/146120/EN-US
Needed to define this function in my ActiveX Control
BOOL CMyOleControl::IsInvokeAllowed (DISPID)
{
// You can check to see if COleControl::m_bInitialized is FALSE
// in your automation functions to limit access.
return TRUE;
}

How to get the most current IHTMLDocument2 object from IE DOM

Currently, I use MSAA to get an IHTMLDocument2 object from a IE HWND. However, with some complicated web applications, this IHTMLDocument2 object may contain several IHTMLDocument2 objects, some of them are not belong to the current displaying page, but the previous page.
It seems to me, IE sometimes doesn't refesh its DOM object, but keep adding more IHTMLDocument2 object into its DOM. My question is how can I get the current displaying IHTMLDocument2 object from the DOM object.
Thanks in advance
Update
Hi Remy,
Thanks for your answer.
Yes, you are right, I do use frames to get to other IHTMLDocument2 objects. My understanding is that the IHTMLDocument2 object that I get from a HWND is the top object in its DOM. IE sometimes puts the prevous IHTMLDocument2 objects inside one of the frames as well.
Here is part of my code.
BOOL IESpy::GetHTMLText( CComPtr<IHTMLDocument2> spDoc, int tagNo, int schNo)
{
USES_CONVERSION;
HRESULT hr = NULL;
BOOL res = TRUE;
BOOL doneSearch = FALSE;
// Extract the source code of the document
if (spDoc) {
IHTMLFramesCollection2* pFrames = NULL;
if (hr = (spDoc->get_frames(&pFrames)) == S_OK){
LONG framesCount;
pFrames->get_length(&framesCount);
if (framesCount > 0) {
for( long i=0; i < framesCount; i++) {
VARIANT varIdx;
varIdx.vt=VT_I4;
VARIANT varResult;
varIdx.lVal=i;
VariantInit(&varResult);
hr = pFrames->item(&varIdx, &varResult);
if (SUCCEEDED(hr) && (varResult.vt == VT_DISPATCH)){
CComQIPtr<IHTMLWindow2> pFrameWnd;
CComQIPtr<IHTMLDocument2> pFrameDoc;
CComBSTR description=NULL;
pFrameWnd = varResult.pdispVal;
VariantClear(&varResult);
if (pFrameWnd == 0) {
continue;
}
hr = pFrameWnd->get_document(&pFrameDoc);
if (SUCCEEDED(hr) && pFrameDoc){
GetHTMLText( pFrameDoc, tagNo, schNo );
if ( m_foundText ) {
break;
}
} else if ( hr == E_ACCESSDENIED ) {
CComQIPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(pFrameWnd);
if ( spBrws != NULL) {
// Get the document object from the IWebBrowser2 object.
CComQIPtr<IDispatch> spDisp;
hr = spBrws->get_Document(&spDisp);
if ( hr == S_OK ) {
pFrameDoc = spDisp;
if ( pFrameDoc ) {
GetHTMLText( pFrameDoc, tagNo, schNo );
if ( m_foundText ) {
break;
}
}
}
}
}
}
}
}
pFrames->Release();
if ( !m_foundText ) {
res = ReadSearchText(spDoc, tagNo, schNo );
doneSearch = TRUE;
}
}
if ( !m_foundText && doneSearch == FALSE ) {
res = ReadSearchText(spDoc, tagNo, schNo );
}
}
return res;
}
BOOL IESpy::ReadSearchText(CComPtr<IHTMLDocument2> spDoc, int tagNo, int schNo )
{
USES_CONVERSION;
HRESULT hr = NULL;
BOOL found = FALSE;
IHTMLElementCollection *pAll;
hr = spDoc->get_all(&pAll);
if (FAILED(hr)) {
return FALSE;
}
long items;
IDispatch *ppvDisp;
IHTMLElement *ppvElement;
pAll->get_length(&items);
std::wstring foundText = L"";
for ( long j = 0; j < items; j++ ) {
VARIANT index;
index.vt = VT_I4;
index.lVal = j;
hr = pAll->item( index, index, &ppvDisp );
if (FAILED(hr)) {
return FALSE;
}
if ( ppvDisp ) {
ppvDisp->QueryInterface(IID_IHTMLElement, (void **)&ppvElement);
if ( ppvElement ) {
CComBSTR bstrTag;
ppvElement->get_tagName(&bstrTag);
wchar_t *wtemp = OLE2W(bstrTag);
if ( wtemp ) {
std::wstring text = ReadSearchText(ppvElement, wtemp, tagNo, schNo, found);
if ( !text.empty() ) {
if ( !foundText.empty() ) {
foundText += concat_string;
}
foundText += text;
}
ppvElement->Release();
if ( found ) {
BOOL stop = FALSE;
for ( size_t i = 0; i < m_tagName[tagNo]->size(); i++ ) {
if ( wcscmp(m_tagName[tagNo]->at(i).c_str(), L"HTML") == 0
|| wcscmp(m_tagName[tagNo]->at(i).c_str(), L"HEAD") == 0
|| wcscmp(m_tagName[tagNo]->at(i).c_str(), L"BODY") == 0 ) {
stop = TRUE;
break;
}
}
if ( stop ) {
break;
}
}
} else {
ppvElement->Release();
}
}
}
}
if ( !foundText.empty() ) {
if ( m_screenCompare ) {
// long timeStamp = GetHPTimeStamp(spDoc);
// m_temp_results[timeStamp] = foundText;
m_temp_results.push_back(foundText);
} else {
m_result += foundText;
m_result += L" ";
m_foundText = TRUE;
}
}
return TRUE;
}
An IHTMLDocument2 cannot contain other IHTMLDocument2 objects (unless they belong to frames on the page), and certainly not from previous pages. How are you determining that exactly? Can you show some code?

Windows CD Burning API

We need to programatically burn files to CD in a C\C++ Windows XP/Vista application we are developing using Borlands Turbo C++.
What is the simplest and best way to do this? We would prefer a native windows API (that doesnt rely on MFC) so as not to rely on any third party software/drivers if one is available.
We used the following:
Store files in the directory returned by GetBurnPath, then write using Burn. GetCDRecordableInfo is used to check when the CD is ready.
#include <stdio.h>
#include <imapi.h>
#include <windows.h>
struct MEDIAINFO {
BYTE nSessions;
BYTE nLastTrack;
ULONG nStartAddress;
ULONG nNextWritable;
ULONG nFreeBlocks;
};
//==============================================================================
// Description: CD burning on Windows XP
//==============================================================================
#define CSIDL_CDBURN_AREA 0x003b
SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate);
SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);
#ifdef UNICODE
#define SHGetSpecialFolderPath SHGetSpecialFolderPathW
#else
#define SHGetSpecialFolderPath SHGetSpecialFolderPathA
#endif
//==============================================================================
// Interface IDiscMaster
const IID IID_IDiscMaster = {0x520CCA62,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
const CLSID CLSID_MSDiscMasterObj = {0x520CCA63,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
typedef interface ICDBurn ICDBurn;
// Interface ICDBurn
const IID IID_ICDBurn = {0x3d73a659,0xe5d0,0x4d42,{0xaf,0xc0,0x51,0x21,0xba,0x42,0x5c,0x8d}};
const CLSID CLSID_CDBurn = {0xfbeb8a05,0xbeee,0x4442,{0x80,0x4e,0x40,0x9d,0x6c,0x45,0x15,0xe9}};
MIDL_INTERFACE("3d73a659-e5d0-4d42-afc0-5121ba425c8d")
ICDBurn : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetRecorderDriveLetter(
/* [size_is][out] */ LPWSTR pszDrive,
/* [in] */ UINT cch) = 0;
virtual HRESULT STDMETHODCALLTYPE Burn(
/* [in] */ HWND hwnd) = 0;
virtual HRESULT STDMETHODCALLTYPE HasRecordableDrive(
/* [out] */ BOOL *pfHasRecorder) = 0;
};
//==============================================================================
// Description: Get burn pathname
// Parameters: pathname - must be at least MAX_PATH in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetBurnPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
if (pICDBurn->HasRecordableDrive(&flag) == S_OK) {
if (SHGetSpecialFolderPath(0, path, CSIDL_CDBURN_AREA, 0)) {
strcat(path, "\\");
}
else {
ret = 1;
}
}
else {
ret = 2;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Get CD pathname
// Parameters: pathname - must be at least 5 bytes in size
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int GetCDPath(char *path)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
BOOL flag;
WCHAR drive[5];
if (pICDBurn->GetRecorderDriveLetter(drive, 4) == S_OK) {
sprintf(path, "%S", drive);
}
else {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 3;
}
return ret;
}
//==============================================================================
// Description: Burn CD
// Parameters: None
// Returns: Non-zero for an error
// Notes: CoInitialize(0) must be called once in application
//==============================================================================
int Burn(void)
{
ICDBurn* pICDBurn;
int ret = 0;
if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
if (pICDBurn->Burn(NULL) != S_OK) {
ret = 1;
}
pICDBurn->Release();
}
else {
ret = 2;
}
return ret;
}
//==============================================================================
bool GetCDRecordableInfo(long *FreeSpaceSize)
{
bool Result = false;
IDiscMaster *idm = NULL;
IDiscRecorder *idr = NULL;
IEnumDiscRecorders *pEnumDiscRecorders = NULL;
ULONG cnt;
long type;
long mtype;
long mflags;
MEDIAINFO mi;
try {
CoCreateInstance(CLSID_MSDiscMasterObj, 0, CLSCTX_ALL, IID_IDiscMaster, (void**)&idm);
idm->Open();
idm->EnumDiscRecorders(&pEnumDiscRecorders);
pEnumDiscRecorders->Next(1, &idr, &cnt);
pEnumDiscRecorders->Release();
idr->OpenExclusive();
idr->GetRecorderType(&type);
idr->QueryMediaType(&mtype, &mflags);
idr->QueryMediaInfo(&mi.nSessions, &mi.nLastTrack, &mi.nStartAddress, &mi.nNextWritable, &mi.nFreeBlocks);
idr->Release();
idm->Close();
idm->Release();
Result = true;
}
catch (...) {
Result = false;
}
if (Result == true) {
Result = false;
if (mtype == 0) {
// No Media inserted
Result = false;
}
else {
if ((mflags & 0x04) == 0x04) {
// Writable Media
Result = true;
}
else {
Result = false;
}
if (Result == true) {
*FreeSpaceSize = (mi.nFreeBlocks * 2048);
}
else {
*FreeSpaceSize = 0;
}
}
}
return Result;
}
To complement the accepted answer, we added this helper function to programatically change the burn directory on the fly as this was a requirement of ours.
typedef HMODULE (WINAPI * SHSETFOLDERPATHA)( int , HANDLE , DWORD , LPCTSTR );
int SetBurnPath( char * cpPath )
{
SHSETFOLDERPATHA pSHSetFolderPath;
HANDLE hShell = LoadLibraryA( "shell32.dll" );
if( hShell == NULL )
return -2;
DWORD dwOrdinal = 0x00000000 + 231;
pSHSetFolderPath = (SHSETFOLDERPATHA)GetProcAddress( hShell, (LPCSTR)dwOrdinal );
if( pSHSetFolderPath == NULL )
return -3;
if( pSHSetFolderPath( CSIDL_CDBURN_AREA, NULL, 0, cpPath ) == S_OK )
return 0;
return -1;
}
This is the information for IMAPI in MSDN site http://msdn.microsoft.com/en-us/library/aa939967.aspx
You should be able to use the shell's ICDBurn interface. Back in the XP day MFC didn't even have any classes for cd burning. I'll see if I can find some examples for you, but it's been a while since I looked at this.