Click here to Skip to main content
15,895,011 members
Articles / Desktop Programming / ATL

ATL PersistXML implementation

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
28 Jul 20032 min read 127.2K   1.3K   38  
Easiest way to add XML persistence to an existing IDispatch based ATL COM object.
/////////////////////////////////////////////////////
//											
// This is a part of the ATL PersistXML add-on
// 
// Copyright (c) VitalyKatasonov.  All rights reserved.
//
// free to use everywhere!
//
// any comments send to:  vkatasonov@yahoo.com
//
/////////////////////////////////////////////////////
#include "stdafx.h"
#include "IPersistVarXML.h"

#include "atlbase.h"
extern CComModule _Module;
#include "atlcom.h"
#include "atlsax.h"

HRESULT AtlIPersistVarXML_Load(ISAXAttributes * pAttributes, ATL_PROPMAP_ENTRY* pMap, void* pThis, IUnknown* pUnk)
{
	USES_CONVERSION;
	HRESULT hr;
	wchar_t*	wchValue;
	int			chValue;
	CComPtr<IDispatch> pDispatch;
	const IID* piidOld = NULL;
	for (int i = 0; pMap[i].pclsidPropPage != NULL; i++)
	{
		if (pMap[i].szDesc == NULL)
			continue;

		wchar_t* wchPropName = OLE2W((LPOLESTR)(pMap[i].szDesc));
		CComVariant var;
		var.vt = pMap[i].vt;
		// If raw entry -  skip it, - we don't handle it
		if (pMap[i].dwSizeData != 0)
		{
			void* pData = (void*) (pMap[i].dwOffsetData + (DWORD)pThis);

			if(SUCCEEDED(hr = pAttributes->getValueFromQName(
							wchPropName,
							wcslen(wchPropName),
							&wchValue,
							&chValue)))
							{
								//bstrPanelType = wchValue;
								var = wchValue;
								hr = var.ChangeType(pMap[i].vt);
							}


			if (SUCCEEDED(hr))
			{
				// check the type - we only have deal with the limited set
				switch (pMap[i].vt)
				{
				case VT_UI1:
				case VT_I1:
					*((BYTE*)pData) = var.bVal;
					break;
				case VT_BOOL:
					*((VARIANT_BOOL*)pData) = var.boolVal;
					break;
				case VT_UI2:
					*((short*)pData) = var.iVal;
					break;
				case VT_UI4:
				case VT_INT:
				case VT_UINT:
					*((long*)pData) = var.lVal;
					break;
				}
			}
			continue;
		}

		if (pMap[i].piidDispatch != piidOld)
		{
			pDispatch.Release();
			if (FAILED(pUnk->QueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
			{
				ATLTRACE2(atlTraceCOM, 0, _T("Failed to get a dispatch pointer for property #%i\n"), i);
				return E_FAIL;
			}
			piidOld = pMap[i].piidDispatch;
		}

		if(SUCCEEDED(hr = pAttributes->getValueFromQName(
							wchPropName,
							wcslen(wchPropName),
							&wchValue,
							&chValue)))
							{
								var = wchValue;
							}

		if (FAILED(hr))
		{
			ATLTRACE2(atlTraceCOM, 0, _T("Property %s not in XML Attributes\n"), OLE2CT(pMap[i].szDesc));
			continue;
		}

		if (FAILED(CComDispatchDriver::PutProperty(pDispatch, pMap[i].dispid, &var)))
		{
			ATLTRACE2(atlTraceCOM, 0, _T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
			return E_FAIL;
		}
	}
	return S_OK;
}

HRESULT AtlIPersistVarXML_Save(IMXAttributes*	pIMXAttributes, ATL_PROPMAP_ENTRY* pMap, void* pThis, IUnknown* pUnk)
{
	HRESULT hr;
	if (pIMXAttributes == NULL)
	{
		ATLTRACE2(atlTraceCOM, 0, _T("pIMXAttributes pointer passed in was invalid\n"));
		return E_POINTER;
	}

	CComPtr<IDispatch> pDispatch;
	const IID* piidOld = NULL;
	for (int i = 0; pMap[i].pclsidPropPage != NULL; i++)
	{
		if (pMap[i].szDesc == NULL)
			continue;

		CComBSTR bstrPropName(pMap[i].szDesc);
		CComVariant var;

		// If raw entry skip it - we don't handle it
		if (pMap[i].dwSizeData != 0)
		{
			void* pData = (void*) (pMap[i].dwOffsetData + (DWORD)pThis);
			// check the type - we only have deal with the limited set
			bool bTypeOK = false;
			switch (pMap[i].vt)
			{
			case VT_UI1:
			case VT_I1:
				var.bVal = *((BYTE*)pData);
				bTypeOK = true;
				break;
			case VT_BOOL:
				var.boolVal = *((VARIANT_BOOL*)pData);
				bTypeOK = true;
				break;
			case VT_UI2:
				var.iVal = *((short*)pData);
				bTypeOK = true;
				break;
			case VT_UI4:
			case VT_INT:
			case VT_UINT:
				var.lVal = *((long*)pData);
				bTypeOK = true;
				break;
			}
			if (bTypeOK)
			{
				var.vt = pMap[i].vt;

				//Convert to String and Save
				if(SUCCEEDED(hr = var.ChangeType(VT_BSTR)))
					hr = pIMXAttributes->addAttribute(CComBSTR(""),bstrPropName,bstrPropName,CComBSTR("CDATA"),V_BSTR(&var));

				if (FAILED(hr))
					return hr;
			}
			continue;
		}

		if (pMap[i].piidDispatch != piidOld)
		{
			pDispatch.Release();
			if (FAILED(pUnk->QueryInterface(*pMap[i].piidDispatch, (void**)&pDispatch)))
			{
				ATLTRACE2(atlTraceCOM, 0, _T("Failed to get a dispatch pointer for property #%i\n"), i);
				return E_FAIL;
			}
			piidOld = pMap[i].piidDispatch;
		}

		if (FAILED(CComDispatchDriver::GetProperty(pDispatch, pMap[i].dispid, &var)))
		{
			ATLTRACE2(atlTraceCOM, 0, _T("Invoked failed on DISPID %x\n"), pMap[i].dispid);
			return E_FAIL;
		}

		if (var.vt == VT_UNKNOWN || var.vt == VT_DISPATCH)
		{
			if (var.punkVal == NULL)
			{
				ATLTRACE2(atlTraceCOM, 0, _T("Warning skipping empty IUnknown in Save\n"));
				continue;
			}
		}

		//Convert to String and Save
		if(SUCCEEDED(hr = var.ChangeType(VT_BSTR)))
		{	
			hr = pIMXAttributes->addAttribute(CComBSTR(""),bstrPropName,bstrPropName,CComBSTR("CDATA"),V_BSTR(&var));
		}

		if (FAILED(hr))
			return hr;
	}
	return S_OK;
}

// IPersistFile Help functions! :)
HRESULT ATLIPersistFile_Load(LPCOLESTR pszFileName, IUnknown* pUnk)
{
	USES_CONVERSION;
	HRESULT hRes = E_FAIL;

	LPCTSTR strFileName= OLE2CT(pszFileName);
	HANDLE hFile = CreateFile(strFileName, GENERIC_READ, 0, NULL,
							  OPEN_EXISTING,
							  FILE_ATTRIBUTE_ARCHIVE,
							  NULL);

	if (INVALID_HANDLE_VALUE == hFile)
		return HRESULT_FROM_WIN32(GetLastError());

	bool bXMLFormat = false;
	DWORD dwHiFileSize;
	DWORD dwSize = GetFileSize(hFile,&dwHiFileSize);
	if(dwSize)
	{
		HGLOBAL hGlob= GlobalAlloc(GHND, dwSize);
		LPVOID pString  = GlobalLock(hGlob);
		DWORD dwReaded;
		BOOL bRet= ReadFile(hFile,pString,dwSize,&dwReaded,NULL);

		//auto recognition XML format (can be improved)
		if( ((char*)pString)[0] == '<' || ( ((byte*)pString)[0] == 0xFF && ((byte*)pString)[1] == 0xFE && ((char*)pString)[2] =='<' ))
		{
			bXMLFormat = true;
		}
		GlobalUnlock(hGlob);
		CloseHandle(hFile);

		DWORD dwError;
		if(!bRet)
			hRes = HRESULT_FROM_WIN32(dwError = GetLastError());

		CComPtr<IStream> spStream;
		hRes = CreateStreamOnHGlobal(hGlob, TRUE, &spStream);
		if (spStream)
		{
			if(bXMLFormat)
			{
				//load XML
				CComQIPtr<IPersistVarXML> pIPersistVarXML(pUnk);
				if(pIPersistVarXML)
					hRes = pIPersistVarXML->LoadXML(CComVariant(spStream));
				else
				{
					ATLASSERT(0);
					hRes = E_FAIL;
				}
			}
			else
			{
				//load STREAM
				CComQIPtr<IPersistStreamInit> pIPersistStream(pUnk);
				if(pIPersistStream)
					hRes = pIPersistStream->Load(spStream);
				else
				{
					ATLASSERT(0);
					hRes = E_FAIL;
				}
			}
			ATLASSERT(SUCCEEDED(hRes));
			spStream.Release();
		}
	}else
		CloseHandle(hFile);
	return hRes;
}

HRESULT ATLIPersistFile_Save( LPCOLESTR pszFileName, IUnknown* pUnk)
{
	USES_CONVERSION;
	HRESULT hRes = S_OK;

	LPCTSTR strFileName= OLE2CT(pszFileName);
	HANDLE hFile = CreateFile(strFileName, GENERIC_WRITE, 0, NULL,
							  CREATE_ALWAYS,
							  FILE_ATTRIBUTE_ARCHIVE,
							  NULL);
	if (INVALID_HANDLE_VALUE == hFile)
		return HRESULT_FROM_WIN32(GetLastError());

	//Determinate Format to Save from File Name!
	int	bTryToSaveAsXML=true;
	long Size = _tcsclen(strFileName);
	if(!_tcsncicmp(&strFileName[Size-4],_T(".bin"),4))
			bTryToSaveAsXML = false;

	DWORD dwWrited;

	HGLOBAL hGlob;// = GlobalAlloc(GHND, l.LowPart);
	CComPtr<IStream> spStream;
	hRes = CreateStreamOnHGlobal(NULL/*hGlob*/, TRUE, &spStream);
	if (spStream)
	{
		if(bTryToSaveAsXML)
		{
			//try to save XML
			CComQIPtr<IPersistVarXML> pIPersistVarXML(pUnk);
			if(pIPersistVarXML)
				hRes = pIPersistVarXML->SaveXML(CComVariant(spStream));
			else
			{
				ATLASSERT(0);
				hRes = E_FAIL;
			}
		}
		
		if(FAILED(hRes) || !bTryToSaveAsXML)
		{
			//try to save STREAM
			CComQIPtr<IPersistStreamInit> pIPersistStream(pUnk);
			if(pIPersistStream)
				hRes = pIPersistStream->Save(spStream,TRUE);
			else
			{
				ATLASSERT(0);
				hRes = E_FAIL;
			}
		}
		ATLASSERT(SUCCEEDED(hRes));

		if(SUCCEEDED(hRes))
		{
			hRes = GetHGlobalFromStream(spStream,&hGlob);
			DWORD dwSize = GlobalSize(hGlob);
			LPVOID pString  = GlobalLock(hGlob);

			BOOL bRet= WriteFile(hFile,pString,dwSize,&dwWrited,NULL);

			ATLASSERT(bRet || dwWrited!=dwSize);

			GlobalUnlock(hGlob);

			DWORD dwError;
			if(!bRet)
				hRes = HRESULT_FROM_WIN32(dwError = GetLastError());
		}
		spStream.Release();
	}
	CloseHandle(hFile);
	return hRes;
}

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


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

Comments and Discussions