/////////////////////////////////////////////////////
//
// 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;
}