Click here to Skip to main content
15,888,610 members
Articles / Desktop Programming / WTL

Form Designer

26 Jul 2021CPOL24 min read 351.5K   82.5K   230  
Component for adding scriptable forms capabilities to an application.
// FormViewer.cpp : Implementation of CFormViewer
//
// Author : David Shepherd
//			Copyright (c) 2002, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DDForms.h"
#include "FormViewer.h"

#include "FormViewerItemCollection.h"
#include "FormViewerItemDetails.h"

// number of horizontal and vertical pixels to scroll per line
#define HSCROLL_LINE_SIZE		10
#define VSCROLL_LINE_SIZE		10

// storage stream name
#define STREAM_NAME				L"DaeDoeForm"

namespace AxFormViewer {

/////////////////////////////////////////////////////////////////////////////
// CItemInfo

CItemInfo::CItemInfo()
{
	// initialise everything
}

CItemInfo::CItemInfo(const CItemInfo &ItemInfo)
{
	ATLASSERT(FALSE);	// not allowed
}

CItemInfo::~CItemInfo()
{
	// clean up
	if(m_HostWindow.IsWindow())		// host window
	{
		ATLASSERT(FALSE);
		(void)m_HostWindow.DestroyWindow();
	}
}

const CItemInfo &CItemInfo::operator=(const CItemInfo &ItemInfo)
{
	ATLASSERT(FALSE);	// not allowed
	return *this;
}

CItemInfo *CItemInfo::CreateObject()
{
	// create a new object
	return new CItemInfo;
}

void CItemInfo::DeleteObject(const CItemInfo *pItemInfo)
{
	// delete an existing object
	ATLASSERT(pItemInfo!=NULL);
	delete pItemInfo;
}

/////////////////////////////////////////////////////////////////////////////
// CItemInfo predicates

bool IsItemIDispatchEqual(
	CItemInfo *p1,const IDispatch *p2)
{
	// check the item info pointer
	ATLASSERT(p1!=NULL);
	// check the IDispatch pointer
	ATLASSERT(p2!=NULL);

	// get the items IDispatch interface
	CComPtr<IDispatch> spDispatch;
	(void)p1->m_HostWindow.QueryControl(&spDispatch);
	// compare the interfaces
	return (spDispatch==p2) ? true : false;
}

/////////////////////////////////////////////////////////////////////////////
// CFormViewer

CFormViewer::CFormViewer()
{
	// initialise everything
	m_bWindowOnly=TRUE;
	m_pForm=NULL;
	m_BorderVisible=VARIANT_TRUE;
	m_BackColor=MAKE_OLE_COLOR(COLOR_BTNFACE);
}

CFormViewer::~CFormViewer()
{
	// clean up
}

CItemInfo *CFormViewer::GetFocusItem()
{
	// determine which item has the focus
	for(CItemInfoPtrList::const_iterator iter=
		m_ItemInfoPtrList.begin(); iter!=m_ItemInfoPtrList.end(); iter++)
	{
		CItemInfo &ItemInfo=**iter;
		// if the item has the focus
		CWindow FocusWindow=GetFocus();
		if(ItemInfo.m_HostWindow==FocusWindow or
			ItemInfo.m_HostWindow.IsChild(FocusWindow))
		{
			return &ItemInfo;
		}
	}
	return NULL;	// not found
}

void CFormViewer::Reset()
{
	// hide the form
	(void)m_pForm->ShowWindow(SW_HIDE);
	// reset the form size and position
	(void)m_pForm->SetWindowPos(NULL,0,0,0,0,SWP_NOZORDER);
	// delete all items
	CItemInfoPtrList::iterator iter_item=m_ItemInfoPtrList.begin();
	while(iter_item!=m_ItemInfoPtrList.end())
	{
		CItemInfo &ItemInfo=**iter_item;
		// remove item info from the list
		iter_item=m_ItemInfoPtrList.erase(iter_item);
		// destroy the host window
		(void)ItemInfo.m_HostWindow.DestroyWindow();
		// delete item info
		CItemInfo::DeleteObject(&ItemInfo);
	}
	// delete all event sinks
	CEventSinkPtrList::iterator iter_sink=m_EventSinkPtrList.begin();
	while(iter_sink!=m_EventSinkPtrList.end())
	{
		CEventSink *pEventSink=*iter_sink;
		// disconnect
		pEventSink->Disconnect();
		// delete the event sink
		delete pEventSink;
		// remove the event sink from the list
		iter_sink=m_EventSinkPtrList.erase(iter_sink);
	}
	// reset the script text
	m_ScriptText.clear();
	// reset the script control
	if(!SUCCEEDED(m_spScriptControl->Reset()))
	{
		throw std::exception();
	}
	// set the scrollbars
	SetScrollbars();
}

void CFormViewer::SetScrollbars()
{
	// get the available area in which to display the form
	CRect Rect(0,0,0,0);
	(void)GetWindowRect(Rect);
	if(m_BorderVisible)	// allow for the border
	{
		Rect.InflateRect(
			-GetSystemMetrics(SM_CXEDGE),-GetSystemMetrics(SM_CYEDGE));
	}
	long AvailableWidth=Rect.Width();
	long AvailableHeight=Rect.Height();
	// get the required area in which to display the form
	(void)m_pForm->GetWindowRect(Rect);
	long RequiredWidth=Rect.Width();
	long RequiredHeight=Rect.Height();
	// determine the range of the scrollbars
	DWORD HScrollRange=0;
	DWORD VScrollRange=0;
	for(long l=0; l<2; l++)
	{
		// if a horizontal scrollbar is required
		if(RequiredWidth > AvailableWidth)
		{
			// adjust the available height
			if(HScrollRange==0)
			{
				AvailableHeight-=GetSystemMetrics(SM_CYHSCROLL);
			}
			// update the horizontal scrollbar range
			HScrollRange=RequiredWidth-AvailableWidth;
		}
		// if a vertical scrollbar is required
		if(RequiredHeight > AvailableHeight)
		{
			// adjust the available width
			if(VScrollRange==0)
			{
				AvailableWidth-=GetSystemMetrics(SM_CXVSCROLL);
			}
			// update the vertical scrollbar range
			VScrollRange=RequiredHeight-AvailableHeight;
		}
	}
	// set the range and visibility of the scrollbars
	(void)SetScrollRange(SB_HORZ,0,HScrollRange);
	(void)SetScrollRange(SB_VERT,0,VScrollRange);
	// update the form position
	BOOL Dummy=FALSE;
	(void)OnHScroll(0,SB_ENDSCROLL,0,Dummy);
	(void)OnVScroll(0,SB_ENDSCROLL,0,Dummy);
}

void CFormViewer::LoadScriptConstants()
{
	// obtain a pointer to the script constants
	HRSRC hRes=FindResource(_Module.GetResourceInstance(),
		MAKEINTRESOURCE(IDR_SCRIPT_CONSTANTS),_T("SCRIPT"));
	if(hRes==NULL)
	{
		throw std::exception();
	}
	HGLOBAL hGlobal=LoadResource(_Module.GetResourceInstance(),hRes);
	if(hGlobal==NULL)
	{
		throw std::exception();
	}
	LPVOID pRes=LockResource(hGlobal);
	if(pRes==NULL)
	{
		throw std::exception();
	}
	// load the script constants into the script control
	DWORD Size=SizeofResource(_Module.GetResourceInstance(),hRes);
	if(Size==0)
	{
		throw std::exception();
	}
	if(!SUCCEEDED(m_spScriptControl->AddCode(CComBSTR(Size,(LPOLESTR)pRes))))
	{
		throw std::exception();
	}
	// windows will clean up on module termination
}

void CFormViewer::LoadExposedObjects()
{
	// load the exposed objects into the script control
	for(CObjectNameToIDispatchMap::const_iterator
			iter=m_ExposedObjects.begin();
		iter!=m_ExposedObjects.end(); iter++)
	{
		// object name
		std::wstring Name=iter->first;
		// object IDispatch interface
		CComPtr<IDispatch> spDispatch=iter->second;
		// add the object to the script control
		if(!SUCCEEDED(m_spScriptControl->AddObject(
			CComBSTR(Name.c_str()),spDispatch,VARIANT_FALSE)))
		{
			throw std::exception();
		}
		// create the event sink
		CreateEventSink(Name,spDispatch);
	}
}

void CFormViewer::InitialiseFormFromStream(
	const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
	// read the form block from the passed stream
	CStreamFormBlock FormBlock;
	FormBlock.ReadFromStream(spStream,StreamVersion);
	// set the form dimensions
	(void)m_pForm->SetWindowPos(NULL,0,0,
		FormBlock.m_Size.cx,FormBlock.m_Size.cy,SWP_NOZORDER|SWP_NOMOVE);
	// set the form colors
	m_pForm->SetColors(FormBlock.m_BackColor,FormBlock.m_ForeColor);
	// get the form IDispatch interface
	CComQIPtr<IDispatch> spDispatch(m_pForm->GetUnknown());
	if(spDispatch==NULL)
	{
		throw std::exception();
	}
	// add the form to the script control
	if(!SUCCEEDED(m_spScriptControl->AddObject(
		CComBSTR(FormBlock.m_Name.c_str()),spDispatch,VARIANT_FALSE)))
	{
		throw std::exception();
	}
	// create the event sink
	CreateEventSink(FormBlock.m_Name,spDispatch.p);
}

void CFormViewer::CreateItemsFromStream(
	const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
	// read the item list header from the passed stream
	CStreamItemListHeader ItemListHeader;
	ItemListHeader.ReadFromStream(spStream,StreamVersion);
	// for each item
	for(long l=0; l<(long)ItemListHeader.m_Count; l++)
	{
		// read the item block from the passed stream
		CStreamItemBlock ItemBlock;
		ItemBlock.ReadFromStream(spStream,StreamVersion);
		// create item info
		CAutoItemInfoPtr pItemInfo(CItemInfo::CreateObject());
		// set the name
		pItemInfo->m_Name=ItemBlock.m_Name;
		// set the tag
		pItemInfo->m_Tag=ItemBlock.m_Tag;
		// create the host window
		if(pItemInfo->m_HostWindow.Create(*m_pForm,CRect(0,0,0,0),_T(""),
			WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS)==NULL)
		{
			throw std::exception();
		}
		// get the host window IAxWinAmbientDispatch interface
		CComPtr<IAxWinAmbientDispatch> spAxWinAmbientDispatch;
		if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryHost(&spAxWinAmbientDispatch)))
		{
			throw std::exception();
		}
		// set the host window ambient properties
		OLE_COLOR BackColor=RGB(0,0,0);
		OLE_COLOR ForeColor=RGB(0,0,0);
		m_pForm->GetColors(BackColor,ForeColor);
		// background color
		if(!SUCCEEDED(spAxWinAmbientDispatch->put_BackColor(BackColor)))
		{
			throw std::exception();
		}
		// foreground color
		if(!SUCCEEDED(spAxWinAmbientDispatch->put_ForeColor(ForeColor)))
		{
			throw std::exception();
		}
		// windowless activation
		if(!SUCCEEDED(spAxWinAmbientDispatch->
			put_AllowWindowlessActivation(VARIANT_FALSE)))
		{
			throw std::exception();
		}
		// get the host window IAxWinHostWindow interface
		CComPtr<IAxWinHostWindow> spAxWinHostWindow;
		if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryHost(&spAxWinHostWindow)))
		{
			throw std::exception();
		}
		// get the item class id as text
		WCHAR ClassIdText[64]=L"";
		if(StringFromGUID2(ItemBlock.m_ClassId,
			ClassIdText,NUM_ELEMENTS(ClassIdText,WCHAR))==0)
		{
			throw std::exception();
		}
		// for early stream versions the item data will be read directly from
		// the passed stream, for later stream versions the item data will first
		// be copied to a temporary data stream and then read from there
		CComPtr<IStream> spDataStream=spStream;
		if(ItemBlock.m_HasPersistantData and StreamVersion > 2)
		{
			// create the data stream
			spDataStream=NULL;
			if(!SUCCEEDED(CreateStreamOnHGlobal(NULL,TRUE,&spDataStream)))
			{
				throw std::exception();
			}
			// initialise the size
			ULARGE_INTEGER Size;
			Size.QuadPart=0;
			if(!SUCCEEDED(spDataStream->SetSize(Size)))
			{
				throw std::exception();
			}
			// read the item persistant data header from the passed stream
			CStreamItemPersistantDataHeader ItemPersistantDataHeader;
			ItemPersistantDataHeader.ReadFromStream(spStream,StreamVersion);
			// copy the item data to the data stream
			if(!SUCCEEDED(spStream->CopyTo(
				spDataStream,ItemPersistantDataHeader.m_Length,NULL,NULL)))
			{
				throw std::exception();
			}
			// seek back to the start of the data stream
			LARGE_INTEGER Offset;
			Offset.QuadPart=0;
			if(!SUCCEEDED(spDataStream->Seek(Offset,STREAM_SEEK_SET,NULL)))
			{
				throw std::exception();
			}
		}
		// create the item
		if(!SUCCEEDED(spAxWinHostWindow->CreateControl(
			ClassIdText,pItemInfo->m_HostWindow,
				ItemBlock.m_HasPersistantData ? spDataStream : NULL)))
		{
			throw std::exception();
		}
		// get the item misc status
		CComPtr<IOleObject> spOleObject;
		if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryControl(&spOleObject)))
		{
			throw std::exception();
		}
		DWORD MiscStatus=0;
		if(!SUCCEEDED(spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
		{
			throw std::exception();
		}
		// hide the item if invisible at runtime
		if(MiscStatus & OLEMISC_INVISIBLEATRUNTIME)
		{
			(void)pItemInfo->m_HostWindow.ShowWindow(SW_HIDE);
		}
		// set the host window size and position
		// this is required to correctly size some items
		if(pItemInfo->m_HostWindow.SetWindowPos(
			NULL,ItemBlock.m_Rect,SWP_NOZORDER|SWP_FRAMECHANGED)==FALSE)
		{
			throw std::exception();
		}
		// get the item IDispatch interface
		CComPtr<IDispatch> spDispatch;
		if(!SUCCEEDED(pItemInfo->m_HostWindow.QueryControl(&spDispatch)))
		{
			throw std::exception();
		}
		// add the item to the script control
		if(!SUCCEEDED(m_spScriptControl->AddObject(
			CComBSTR(ItemBlock.m_Name.c_str()),spDispatch,VARIANT_FALSE)))
		{
			throw std::exception();
		}
		// create the event sink
		CreateEventSink(ItemBlock.m_Name,spDispatch);
		// add item info to the list
		m_ItemInfoPtrList.push_back(pItemInfo);
		// release auto resources
		(void)pItemInfo.Release();
	}
	// reverse the item zorder
	HWND InsertAfter=m_ItemRootWindow;
	for(CItemInfoPtrList::reverse_iterator iter=m_ItemInfoPtrList.rbegin();
		iter!=m_ItemInfoPtrList.rend(); iter++)
	{
		CItemInfo &ItemInfo=**iter;
		// update the zorder
		(void)ItemInfo.m_HostWindow.SetWindowPos(
			InsertAfter,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE);
		// insert the next item after this one
		InsertAfter=ItemInfo.m_HostWindow;
	}
}

void CFormViewer::LoadScriptFromStream(
	const CComPtr<IStream> &spStream,DWORD StreamVersion)
{
	// read the script header from the passed stream
	CStreamScriptHeader ScriptHeader;
	ScriptHeader.ReadFromStream(spStream,StreamVersion);
	// read in the lines
	std::wstring Lines;
	for(long l=0; l<(long)ScriptHeader.m_LineCount; l++)
	{
		std::wstring Line;		// next line
		ReadFromStream(spStream,Line);
		Lines+=Line+L"\r\n";	// update the lines
	}
	// save the script text
	m_ScriptText=Lines;
	// load the script into the script control
	if(!SUCCEEDED(m_spScriptControl->AddCode(CComBSTR(Lines.c_str()))))
	{
		throw std::exception();
	}
}

BOOL CFormViewer::IsFormLoaded()
{
	// the form is loaded if visible
	return (m_pForm->GetWindowLong(GWL_STYLE) & WS_VISIBLE) ? TRUE : FALSE;
	// the window style is checked rather than calling IsWindowVisible()
	// since the form viewer or its parent etc may not be visible
}

void CFormViewer::InternalLoad(const CComPtr<IStream> &spStream)
{
	// unload the current form
	InternalUnload();
	// load the script constants
	LoadScriptConstants();
	// load the exposed objects
	LoadExposedObjects();
	// read in the header
	CStreamHeader StreamHeader;
	StreamHeader.ReadFromStream(spStream);
	// the stream version must be recognisable
	if(StreamHeader.m_Version > CStreamHeader().m_Version)
	{
		throw std::exception();
	}
	// read in the form
	InitialiseFormFromStream(spStream,StreamHeader.m_Version);
	// read in the item list
	CreateItemsFromStream(spStream,StreamHeader.m_Version);
	// read in the script
	LoadScriptFromStream(spStream,StreamHeader.m_Version);
	// read in the footer
	CStreamFooter StreamFooter;
	StreamFooter.ReadFromStream(spStream);
	// fire the load event
	(void)m_pForm->Fire_Load();
	// set the scrollbars
	SetScrollbars();
	// show the form
	(void)m_pForm->ShowWindow(SW_SHOW);
}

void CFormViewer::InternalUnload()
{
	// fire the unload event
	(void)m_pForm->Fire_Unload();
	// reset the form viewer
	Reset();
}

void CFormViewer::ExposeObject(
	const std::wstring &Name,const CComPtr<IDispatch> &spDispatch)
{
	// add the object to the exposed objects map
	m_ExposedObjects[Name]=spDispatch;
}

void CFormViewer::CreateEventSink(
	const std::wstring &Name,const CComPtr<IDispatch> &spDispatch)
{
	// create the event sink
	std::auto_ptr<CEventSink> pEventSink(new CEventSink);
	// connect
	try { pEventSink->Connect(Name,spDispatch,this); }
	catch(...)
	{
		// this may fail for valid reasons
		return;
	}
	// add the event sink to the list
	m_EventSinkPtrList.push_back(pEventSink.get());
	(void)pEventSink.release();
}

void CFormViewer::TabForwardToNextItem()
{
	// if the form contains one or more items
	if(m_ItemInfoPtrList.size())
	{
		// find the item which has the focus
		CItemInfoPtrList::const_iterator iter_focus=
			std::find(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
				GetFocusItem()/*will be NULL if no item has the focus*/);
		// if not found
		if(iter_focus==m_ItemInfoPtrList.end())
		{
			// start from the last item
			iter_focus--;
		}
		// tab forward to the next item
		for(long l=0; l<(long)m_ItemInfoPtrList.size(); l++)
		{
			// get item info for the next item
			if(++iter_focus==m_ItemInfoPtrList.end())
			{
				iter_focus=m_ItemInfoPtrList.begin();	// wrap around
			}
			CItemInfo &ItemInfo=**iter_focus;
			// get the item misc status
			CComPtr<IOleObject> spOleObject;
			if(!SUCCEEDED(
				ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
			{
				throw std::exception();
			}
			DWORD MiscStatus=0;
			if(!SUCCEEDED(
				spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
			{
				throw std::exception();
			}
			// if the item can be tabbed to
			if((MiscStatus & (
				OLEMISC_ACTSLIKELABEL		|
				OLEMISC_INVISIBLEATRUNTIME	|
				OLEMISC_NOUIACTIVATE))==0)
			{
				// give focus to the item
				(void)ItemInfo.m_HostWindow.SetFocus();
				break;	// done
			}
		}
	}
}

void CFormViewer::TabBackwardToNextItem()
{
	// if the form contains one or more items
	if(m_ItemInfoPtrList.size())
	{
		// find the item which has the focus
		CItemInfoPtrList::const_iterator iter_focus=
			std::find(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
				GetFocusItem()/*will be NULL if no item has the focus*/);
		// if not found
		if(iter_focus==m_ItemInfoPtrList.end())
		{
			// start from the first item
			iter_focus=m_ItemInfoPtrList.begin();
		}
		// tab backward to the next item
		for(long l=0; l<(long)m_ItemInfoPtrList.size(); l++)
		{
			// get item info for the next item
			if(iter_focus==m_ItemInfoPtrList.begin())
			{
				iter_focus=m_ItemInfoPtrList.end();	// wrap around
			}
			CItemInfo &ItemInfo=**(--iter_focus);
			// get the item misc status
			CComPtr<IOleObject> spOleObject;
			if(!SUCCEEDED(
				ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
			{
				throw std::exception();
			}
			DWORD MiscStatus=0;
			if(!SUCCEEDED(
				spOleObject->GetMiscStatus(DVASPECT_CONTENT,&MiscStatus)))
			{
				throw std::exception();
			}
			// if the item can be tabbed to
			if((MiscStatus & (
				OLEMISC_ACTSLIKELABEL		|
				OLEMISC_INVISIBLEATRUNTIME	|
				OLEMISC_NOUIACTIVATE))==0)
			{
				// give focus to the item
				(void)ItemInfo.m_HostWindow.SetFocus();
				break;	// done
			}
		}
	}
}

void CFormViewer::TabToNextItem(TabDirection Direction)
{
	// tab to the next item
	switch(Direction)
	{
	// forward
	case TabDirectionForward:
		TabForwardToNextItem();
		break;
	// backward
	case TabDirectionBackward:
		TabBackwardToNextItem();
		break;
	// unknown direction
	default:
		ATLASSERT(FALSE);
		break;
	}
}

void CFormViewer::ESNS_ProcessEvent(
	const std::wstring &SourceName,const std::wstring &EventName,
	SAFEARRAY *pParams,VARIANT *pResult)
{
	// create the event handler name
	std::wstring HandlerName=SourceName+L"_"+EventName;
	// check the event handler exists in the script
	// this ensures all references to objects exposed to the script control
	// are released on Reset()
	std::wstring ScriptText=m_ScriptText;
	// remove white space
	ScriptText.erase(
		std::remove_if(ScriptText.begin(),ScriptText.end(),iswspace),
			ScriptText.end());
	// convert to lower case
	std::transform(
		ScriptText.begin(),ScriptText.end(),ScriptText.begin(),towlower);
	// search for sub followed by the event handler name
	std::wstring SearchText=L"sub"+HandlerName;
	// convert to lower case
	std::transform(
		SearchText.begin(),SearchText.end(),SearchText.begin(),towlower);
	// search
	if(ScriptText.find(SearchText)!=std::wstring::npos)
	{
		// run the event handler
		(void)m_spScriptControl->Run(
			CComBSTR(HandlerName.c_str()),&pParams,pResult);
		// this may fail for valid reasons
	}
}

HRESULT CFormViewer::FinalConstruct()
{
IMP_BEGIN
	// call the base class
	HRESULT hr=CComObjectRootBase::FinalConstruct();
	if(!SUCCEEDED(hr))
	{
		throw CHResult(hr);
	}
	// create the form
	if(!SUCCEEDED(CComObject<CViewableForm>::CreateInstance(&m_pForm)))
	{
		throw CHResult(E_FAIL);
	}
	(void)m_pForm->AddRef();
	// create the script control
	if(!SUCCEEDED(m_spScriptControl.CoCreateInstance(
		__uuidof(MSScriptControl::ScriptControl))))
	{
		throw CHResult(E_FAIL);
	}
	// set the language
	// this is required in order for most other methods to succeed
	if(!SUCCEEDED(m_spScriptControl->put_Language(CComBSTR(L"VBScript"))))
	{
		throw CHResult(E_FAIL);
	}
	// allow the script control to display a user interface
	if(!SUCCEEDED(m_spScriptControl->put_AllowUI(VARIANT_TRUE)))
	{
		throw CHResult(E_FAIL);
	}
IMP_END
	return RetVal;
}

void CFormViewer::FinalRelease()
{
IMP_BEGIN
	// call the base class
	CComObjectRootBase::FinalRelease();
	// release the form
	if(m_pForm!=NULL)
	{
		(void)m_pForm->Release();
	}
IMP_END
}

BOOL CFormViewer::PreTranslateAccelerator(LPMSG pMsg,HRESULT &hRet)
{
	// this will be set TRUE if the message was translated
	BOOL Handled=FALSE;
TRY
	// find the item which has the focus
	CItemInfo *pItemInfo=GetFocusItem();
	// if found
	if(pItemInfo!=NULL)
	{
		// get the item IOleInPlaceActiveObject interface
		CComPtr<IOleInPlaceActiveObject> spOleInPlaceActiveObject;
		if(!SUCCEEDED(
			pItemInfo->m_HostWindow.QueryControl(&spOleInPlaceActiveObject)))
		{
			throw std::exception();
		}
		// translate the message
		hRet=spOleInPlaceActiveObject->TranslateAccelerator(pMsg);
		// if a translation was attempted
		if(hRet!=S_FALSE)
		{
			// mark the message as translated
			Handled=TRUE;
		}
	}
	// if the message was not translated
	if(Handled==FALSE and
		// and this is a tab key down message
		pMsg->message==WM_KEYDOWN and pMsg->wParam==VK_TAB)
	{
		// get shift key modifiers
		BOOL Ctrl  = (GetKeyState(VK_CONTROL) & 0x80000000) ? TRUE : FALSE;
		BOOL Shift = (GetKeyState(VK_SHIFT  ) & 0x80000000) ? TRUE : FALSE;
		BOOL Alt   = (GetKeyState(VK_MENU   ) & 0x80000000) ? TRUE : FALSE;

		// tab to the next item
		TabToNextItem(Shift ? TabDirectionBackward : TabDirectionForward);

		// signal success
		hRet=S_OK;
		// mark the message as translated
		Handled=TRUE;
	}
CATCH_ALL
	return Handled;
}

HRESULT CFormViewer::OnDraw(ATL_DRAWINFO &di)
{
	// ATL wizard generated
IMP_BEGIN
	// dc wrapper
	CDCHandle dc(di.hdcDraw);
	// bounding rectangle
	CRect Rect=*(RECT*)di.prcBounds;

	// some containers will only provide a window for us to draw on using
	// this function - ie the control is not created

	if(IsWindow()==FALSE)	// skip if we are created
	{
		// draw rectangle
		(void)dc.Rectangle(Rect);
		// show text
		LPCTSTR pszText=_T("FormViewer");
		(void)dc.SetTextAlign(TA_CENTER|TA_BASELINE);
		(void)dc.TextOut(
			(Rect.left+Rect.right)/2,(Rect.top+Rect.bottom)/2,
				pszText,lstrlen(pszText));
	}
	else
	{
		// fill in the background
		COLORREF RGBBackColor=RGB(0,0,0);
		if(!SUCCEEDED(OleTranslateColor(m_BackColor,NULL,&RGBBackColor)))
		{
			throw std::exception();
		}
		dc.FillSolidRect(Rect,RGBBackColor);
	}
IMP_END
	return RetVal;
}

LRESULT CFormViewer::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// set the window style
	if(m_BorderVisible==VARIANT_FALSE)
	{
		(void)ModifyStyleEx(WS_EX_CLIENTEDGE,0);
	}
	// enable control containment
	if(AtlAxWinInit()==FALSE)
	{
		throw std::exception();
	}
	// create the form
	if(m_pForm->Create(*this,CRect(0,0,0,0),_T("ViewableForm"),
		WS_CHILD|WS_CLIPCHILDREN,WS_EX_CONTROLPARENT)==NULL)
	{
		throw std::exception();
	}
	// create the item root window
	m_ItemRootWindow.m_hWnd=NULL;	// allow the wrapper to be reused
	if(m_ItemRootWindow.Create(_T("STATIC"),
		*m_pForm,CRect(0,0,0,0),_T("ItemRootWindow"),WS_CHILD|WS_DISABLED,0)==NULL)
	{
		throw std::exception();
	}
CATCH_ALL
	return Caught ? -1 : 0;	// fail creation if an exception was thrown
}

LRESULT CFormViewer::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// unload the form
	InternalUnload();
CATCH_ALL
	return 0;
}

LRESULT CFormViewer::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// set the scrollbars
	SetScrollbars();
CATCH_ALL
	return 0;
}

LRESULT CFormViewer::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	// to prevent flicker do not erase the background
	return TRUE;
}

LRESULT CFormViewer::OnHScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// get our client rect
	CRect ClientRect(0,0,0,0);
	(void)GetClientRect(ClientRect);

	// get the minimum and maximum scroll positions
	int MinPos=0;
	int MaxPos=0;
	(void)GetScrollRange(SB_HORZ,&MinPos,&MaxPos);

	// get the new scroll position
	long ScrollPos=GetScrollPos(SB_HORZ);
	switch(LOWORD(wParam))
	{
	case SB_ENDSCROLL:	// ignored
		break;
	case SB_LEFT:
		ScrollPos=MinPos;
		break;
	case SB_RIGHT:
		ScrollPos=MaxPos;
		break;
	case SB_LINELEFT:
		ScrollPos-=HSCROLL_LINE_SIZE;
		break;
	case SB_LINERIGHT:
		ScrollPos+=HSCROLL_LINE_SIZE;
		break;
	case SB_PAGELEFT:
		ScrollPos-=ClientRect.Width();
		break;
	case SB_PAGERIGHT:
		ScrollPos+=ClientRect.Width();
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		ScrollPos=HIWORD(wParam);
		break;
	default:
		ATLASSERT(FALSE);	// unexpected
		break;
	}

	// check the new scroll position is in range
	ScrollPos=min(ScrollPos,MaxPos);
	ScrollPos=max(ScrollPos,MinPos);
	// update the scrollbar
	(void)SetScrollPos(SB_HORZ,ScrollPos);

	// scroll the form horizontally
	CRect FormRect(0,0,0,0);
	(void)m_pForm->GetWindowRect(FormRect);	// window rect
	(void)ScreenToClient(FormRect);			// reletive to our client
	(void)m_pForm->SetWindowPos(NULL,
		-ScrollPos,FormRect.top,0,0,SWP_NOZORDER|SWP_NOSIZE);
CATCH_ALL
	return 0;
}

LRESULT CFormViewer::OnVScroll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
TRY
	// get our client rect
	CRect ClientRect(0,0,0,0);
	(void)GetClientRect(ClientRect);

	// get the minimum and maximum scroll positions
	int MinPos=0;
	int MaxPos=0;
	(void)GetScrollRange(SB_VERT,&MinPos,&MaxPos);

	// get the new scroll position
	long ScrollPos=GetScrollPos(SB_VERT);
	switch(LOWORD(wParam))
	{
	case SB_ENDSCROLL:	// ignored
		break;
	case SB_TOP:
		ScrollPos=MinPos;
		break;
	case SB_BOTTOM:
		ScrollPos=MaxPos;
		break;
	case SB_LINEUP:
		ScrollPos-=VSCROLL_LINE_SIZE;
		break;
	case SB_LINEDOWN:
		ScrollPos+=VSCROLL_LINE_SIZE;
		break;
	case SB_PAGEUP:
		ScrollPos-=ClientRect.Height();
		break;
	case SB_PAGEDOWN:
		ScrollPos+=ClientRect.Height();
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		ScrollPos=HIWORD(wParam);
		break;
	default:
		ATLASSERT(FALSE);	// unexpected
		break;
	}

	// check the new scroll position is in range
	ScrollPos=min(ScrollPos,MaxPos);
	ScrollPos=max(ScrollPos,MinPos);
	// update the scrollbar
	(void)SetScrollPos(SB_VERT,ScrollPos);

	// scroll the form vertically
	CRect FormRect(0,0,0,0);
	(void)m_pForm->GetWindowRect(FormRect);	// window rect
	(void)ScreenToClient(FormRect);			// reletive to our client
	(void)m_pForm->SetWindowPos(NULL,
		FormRect.left,-ScrollPos,0,0,SWP_NOZORDER|SWP_NOSIZE);
CATCH_ALL
	return 0;
}

// begin standard method implementation block
#define METHOD_BEGIN			\
	if(IsWindow()==FALSE)		\
	{							\
		throw CHResult(E_FAIL);	\
	}

// end standard method implementation block
#define METHOD_END

// begin standard property put implementation block
#define PROPPUT_BEGIN

// end standard property put implementation block
#define PROPPUT_END

// begin standard property get implementation block
#define PROPGET_BEGIN

// end standard property get implementation block
#define PROPGET_END

STDMETHODIMP CFormViewer::About()
{
// this is allowed not matter what state the viewer is in
IMP_BEGIN
	// show the about box
	CSimpleDialog<IDD_ABOUT_FORMVIEWER> AboutDlg;
	(void)AboutDlg.DoModal();
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::LoadFromFile(BSTR FileName)
{
IMP_BEGIN
METHOD_BEGIN
	// open the storage
	CComPtr<IStorage> spStorage;
	if(!SUCCEEDED(StgOpenStorage(BSTR2W(FileName),NULL,
		STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE,
			NULL,0,&spStorage)))
	{
		throw CHResult(E_FAIL);
	}
	// open the stream
	CComPtr<IStream> spStream;
	if(!SUCCEEDED(spStorage->OpenStream(STREAM_NAME,NULL,
		STGM_DIRECT|STGM_READ|STGM_SHARE_EXCLUSIVE,
			0,&spStream)))
	{
		throw CHResult(E_FAIL);
	}
	// load the form
	InternalLoad(spStream);
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::Unload()
{
IMP_BEGIN
METHOD_BEGIN
	// unload the form
	InternalUnload();
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::Expose(BSTR Name, IDispatch *pDispatch)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// expose the object
	ExposeObject(BSTR2W(Name),pDispatch);
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::get_Form(IViewableForm **ppViewableForm)
{
IMP_BEGIN
PROPGET_BEGIN
	// check parameters
	if(ppViewableForm==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// get the form
	CComQIPtr<IViewableForm> spViewableForm(m_pForm->GetUnknown());
	if(spViewableForm==NULL)
	{
		throw CHResult(E_FAIL);
	}
	*ppViewableForm=spViewableForm.Detach();
PROPGET_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::get_BorderVisible(VARIANT_BOOL *pVal)
{
IMP_BEGIN
PROPGET_BEGIN
	// check parameters
	if(pVal==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// get the border visible flag
	*pVal=m_BorderVisible;
PROPGET_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::put_BorderVisible(VARIANT_BOOL newVal)
{
IMP_BEGIN
PROPPUT_BEGIN
	if(IsWindow())	// refresh
	{
		if(newVal)
		{
			// show the border
			(void)ModifyStyleEx(0,WS_EX_CLIENTEDGE);
		}
		else
		{
			// hide the border
			(void)ModifyStyleEx(WS_EX_CLIENTEDGE,0);
		}
		(void)SetWindowPos(NULL,0,0,0,0,
			SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
	}
	// set the border visible flag
	m_BorderVisible=newVal;
	PROPERTY_CHANGED(DISPID_BORDERVISIBLE);
PROPPUT_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::get_BackColor(OLE_COLOR *pVal)
{
IMP_BEGIN
PROPGET_BEGIN
	// check parameters
	if(pVal==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// get the background color
	*pVal=m_BackColor;
PROPGET_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::put_BackColor(OLE_COLOR newVal)
{
IMP_BEGIN
PROPPUT_BEGIN
	if(IsWindow())	// refresh
	{
		(void)Invalidate();
	}
	// set the background color
	m_BackColor=newVal;
	PROPERTY_CHANGED(DISPID_BACKCOLOR);
PROPPUT_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::GetItemDetails(
	IDispatch *pDispatch, IFormViewerItemDetails **ppFormViewerItemDetails)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(ppFormViewerItemDetails==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// check the item exists
	if(std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
		std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch))==
			m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// create the item details
	CComObject<CFormViewerItemDetails> *pItemDetails=NULL;
	if(!SUCCEEDED(
		CComObject<CFormViewerItemDetails>::CreateInstance(&pItemDetails)))
	{
		throw CHResult(E_FAIL);
	}
	CComPtr<IUnknown> spUnknown(pItemDetails->GetUnknown());
	// set the item
	pItemDetails->m_spItem=pDispatch;
	// set the form viewer
	CComQIPtr<IFormViewer2> spFormViewer2(GetUnknown());
	if(spFormViewer2==NULL)
	{
		throw CHResult(E_FAIL);
	}
	pItemDetails->m_spFormViewer2=spFormViewer2;
	// get the item details
	CComQIPtr<IFormViewerItemDetails> spFormViewerItemDetails(spUnknown);
	if(spFormViewerItemDetails==NULL)
	{
		throw CHResult(E_FAIL);
	}
	*ppFormViewerItemDetails=spFormViewerItemDetails.Detach();
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::GetItemClassId(IDispatch *pDispatch,BSTR *pClassId)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pClassId==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// check the item exists
	CItemInfoPtrList::const_iterator iter=
		std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
			std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
	if(iter==m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	CItemInfo &ItemInfo=**iter;
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// get the item class id
	CComPtr<IOleObject> spOleObject;
	if(!SUCCEEDED(ItemInfo.m_HostWindow.QueryControl(&spOleObject)))
	{
		throw CHResult(E_FAIL);
	}
	CLSID ClassId=CLSID_NULL;
	if(!SUCCEEDED(spOleObject->GetUserClassID(&ClassId)))
	{
		throw CHResult(E_FAIL);
	}
	// convert to text
	WCHAR ClassIdText[64]=L"";
	if(StringFromGUID2(ClassId,
		ClassIdText,NUM_ELEMENTS(ClassIdText,WCHAR))==0)
	{
		throw CHResult(E_FAIL);
	}
	// get the item class id
	*pClassId=CComBSTR(ClassIdText).Detach();
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::GetItemName(IDispatch *pDispatch,BSTR *pName)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pName==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// check the item exists
	CItemInfoPtrList::const_iterator iter=
		std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
			std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
	if(iter==m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	CItemInfo &ItemInfo=**iter;
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// get the item name
	*pName=CComBSTR(ItemInfo.m_Name.c_str()).Detach();
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::GetItemTag(IDispatch *pDispatch,BSTR *pTag)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pTag==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// check the item exists
	CItemInfoPtrList::const_iterator iter=
		std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
			std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
	if(iter==m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	CItemInfo &ItemInfo=**iter;
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// get the item tag
	*pTag=CComBSTR(ItemInfo.m_Tag.c_str()).Detach();
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::GetItemPosition(IDispatch *pDispatch,
	long *pLeft,long *pTop,long *pWidth,long *pHeight,long *pTabNumber)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pLeft==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pTop==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pWidth==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pHeight==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(pTabNumber==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// check the item exists
	CItemInfoPtrList::const_iterator iter=
		std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
			std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
	if(iter==m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	CItemInfo &ItemInfo=**iter;
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// get the item rect reletive to the screen
	CRect ItemRect(0,0,0,0);
	(void)ItemInfo.m_HostWindow.GetWindowRect(ItemRect);
	// convert to form coordinates
	(void)m_pForm->ScreenToClient(ItemRect);
	// get the item position
	*pLeft=ItemRect.left;
	*pTop=ItemRect.top;
	*pWidth=ItemRect.Width();
	*pHeight=ItemRect.Height();
	// the item tab number is the same as the item index but 1 based
	*pTabNumber=std::distance(
		(CItemInfoPtrList::const_iterator)m_ItemInfoPtrList.begin(),iter)+1;
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::SetItemPosition(IDispatch *pDispatch,
	long Left,long Top,long Width,long Height,long TabNumber)
{
IMP_BEGIN
METHOD_BEGIN
	// check parameters
	if(pDispatch==NULL)
	{
		throw CHResult(E_POINTER);
	}
	if(Width < 0)
	{
		throw CHResult(E_INVALIDARG);
	}
	if(Height < 0)
	{
		throw CHResult(E_INVALIDARG);
	}
	// get the form rect
	CRect FormRect(0,0,0,0);
	(void)m_pForm->GetClientRect(FormRect);
	// get the item rect
	CRect ItemRect(Left,Top,Left+Width,Top+Height);
	// check the item fits within the form
	CRect IntersectRect(0,0,0,0);
	(void)IntersectRect.IntersectRect(FormRect,ItemRect);
	if(IntersectRect!=ItemRect)
	{
		throw CHResult(E_INVALIDARG);
	}
	if(TabNumber < 1 or TabNumber > (long)m_ItemInfoPtrList.size())
	{
		throw CHResult(E_INVALIDARG);
	}
	// check the item exists
	CItemInfoPtrList::const_iterator iter=
		std::find_if(m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),
			std::bind2nd(std::ptr_fun(IsItemIDispatchEqual),pDispatch));
	if(iter==m_ItemInfoPtrList.end())
	{
		throw CHResult(E_INVALIDARG);
	}
	CItemInfo &ItemInfo=**iter;
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// remove item info from the list
	CItemInfoPtrList::iterator iter_delete=std::find(
		m_ItemInfoPtrList.begin(),m_ItemInfoPtrList.end(),&ItemInfo);
	ATLASSERT(iter_delete!=m_ItemInfoPtrList.end());	// should always exist
	m_ItemInfoPtrList.erase(iter_delete);				// remove item info
	CAutoItemInfoPtr ItemInfoPtr(&ItemInfo);
	// insert item info back into the list at the correct position
	CItemInfoPtrList::iterator iter_insert=m_ItemInfoPtrList.begin();
	std::advance(iter_insert,TabNumber-1);
	m_ItemInfoPtrList.insert(iter_insert,&ItemInfo);
	(void)ItemInfoPtr.Release();
	// determine the window to insert the item after
	HWND InsertAfter=m_ItemRootWindow;
	if(TabNumber < (long)m_ItemInfoPtrList.size())
	{
		// advance to the next item
		CItemInfoPtrList::const_iterator iter_next=m_ItemInfoPtrList.begin();
		std::advance(iter_next,TabNumber);
		// get the item host window
		InsertAfter=(**iter_next).m_HostWindow;
	}
	// set the item position
	(void)ItemInfo.m_HostWindow.SetWindowPos(
		InsertAfter,Left,Top,Width,Height,SWP_NOACTIVATE|SWP_NOCOPYBITS);
METHOD_END
IMP_END
	return RetVal;
}

STDMETHODIMP CFormViewer::get_Items(
	IFormViewerItemCollection **ppFormViewerItemCollection)
{
IMP_BEGIN
PROPGET_BEGIN
	// check parameters
	if(ppFormViewerItemCollection==NULL)
	{
		throw CHResult(E_POINTER);
	}
	// the form should be loaded
	if(IsFormLoaded()==FALSE)
	{
		throw CHResult(E_FAIL);
	}
	// create the item collection
	CComObject<CFormViewerItemCollection> *pItems=NULL;
	if(!SUCCEEDED(
		CComObject<CFormViewerItemCollection>::CreateInstance(&pItems)))
	{
		throw CHResult(E_FAIL);
	}
	CComPtr<IUnknown> spUnknown(pItems->GetUnknown());
	// add all items to the item collection
	for(CItemInfoPtrList::const_iterator iter=
		m_ItemInfoPtrList.begin(); iter!=m_ItemInfoPtrList.end(); iter++)
	{
		CItemInfo &ItemInfo=**iter;
		// get the items IDispatch interface
		CComPtr<IDispatch> spDispatch;
		if(!SUCCEEDED(ItemInfo.m_HostWindow.QueryControl(&spDispatch)))
		{
			throw CHResult(E_FAIL);
		}
		// update the item collection
		pItems->m_coll.push_back(spDispatch);
	}
	// get the item collection
	CComQIPtr<IFormViewerItemCollection> spFormViewerItemCollection(spUnknown);
	if(spFormViewerItemCollection==NULL)
	{
		throw CHResult(E_FAIL);
	}
	*ppFormViewerItemCollection=spFormViewerItemCollection.Detach();
PROPGET_END
IMP_END
	return RetVal;
}

} // namespace AxFormViewer

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions