My program (C++, Windows) implements a window, receiving OLE drag-and-drop object (a message dragged from MS Outlook), enumerates formats of dragged objects, reads a stream when TYMED_ISTREAM is received. But I am confused - how can I extract an Outlook letter and it's components (letter body, attachments, attributes - From:, To:, Subject etc.)
The attached screenshot shows a listing - what I have read, enumerated FORMATETC records (it's main fields are shown).
For example, I have read 302 bytes from a stream, format code = 49814.
Question1:
But how can I extract Outlook
letter from these 302 bytes ?
Could you please tell me the plan of my actions to do that ?
Question2:
Should I or not to call RegisterClipboardormat
(I did not) ?
If yes, (2.1) which code should I register and (2.2) why should this be done (why my enumeration is not enough) ?
Additionally, I have got system name of several format codes (via GetClipboardFormatName): 49968 - RenPrivateLatestMessages, 49735 - RenPrivateMessages, 49708 - RenPrivateItem, 49395 - FileGroupDescriptor, 49159 - FileNameW etc. Which of them are usefull and allow to get Outlook message ?
The code, used to enumerate dropped objects/formats:
HRESULT __stdcall MyDropTarget_CLASS::Drop(IDataObject* pDataObj, DWORD grfKeyState,
POINTL pt,
DWORD* pdwEffect) {
bool ok_get_enum = false, ok_reset = false;
DragWindowPtr->SetNormalBrush();
IEnumFORMATETC* ppenumFormatEtc;
HRESULT res = pDataObj->EnumFormatEtc(DATADIR_GET, &ppenumFormatEtc);
ok_get_enum = (res == S_OK);
if (!ok_get_enum)
std::cout << "EnumFormatEtc ERROR = " << res << std::endl;
if (ok_get_enum) {
res = ppenumFormatEtc->Reset();
ok_reset = (res == S_OK);
if (!ok_reset)
std::cout << "EnumFormatEtc.Reset ERROR = " << res << std::endl;
}
if (ok_reset) {
std::cout << "------ Enumerating ---------" << std::endl;
const ULONG ElementsInPortions = 5; // сколько записей читаем
FORMATETC ItemsArray[ElementsInPortions];
ULONG FetchedNumber = 0;
bool DoNext = true;
while (DoNext) {
DoNext = (res = ppenumFormatEtc->Next(ElementsInPortions, &(ItemsArray[0]), &FetchedNumber)) == S_OK;
std::cout << "Requested = " << ElementsInPortions << " Fetched = " << FetchedNumber << std::endl;
for (ULONG i = 0; i < FetchedNumber; i++) {
CLIPFORMAT CurFormatCode = ItemsArray[i].cfFormat;
DWORD CurAspect = ItemsArray[i].dwAspect;
DWORD CurTymed = ItemsArray[i].tymed;
LONG LIndex = ItemsArray[i].lindex;
std::cout << " Format = " << CurFormatCode << "(" << GetFormatName(CurFormatCode) << ")" <<
" Aspect = " << CurAspect <<
" Tymed = " << CurTymed << "(" << GetTymedName(CurTymed) << ")" <<
" LIndex = " << LIndex << std::endl;
if (CurTymed == TYMED_ISTREAM) {
std::cout << "--------- Reading from a stream ---------" << std::endl;
GetData_IStream(pDataObj, &(ItemsArray[i]));
std::cout << "-----------------------------------------" << std::endl << std::endl;
}
}
}
std::cout << "------ FINISHED ---------" << std::endl;
ppenumFormatEtc->Release();
}
*pdwEffect = DROPEFFECT_COPY;
return S_OK;
}
// The code that reads ISTREAM
bool GetData_IStream(IDataObject* pDataObj, const FORMATETC* in_FormatEtc) {
// ---------------- QueryGetData ----------------
FORMATETC ReadFormat;
ZeroMemory(&ReadFormat, sizeof(ReadFormat));
ReadFormat.cfFormat = in_FormatEtc->cfFormat; // CLIPFORMAT
ReadFormat.tymed = TYMED_ISTREAM; // DWORD
ReadFormat.ptd = NULL; // [unique] DVTARGETDEVICE*
ReadFormat.dwAspect = DVASPECT_CONTENT; // DWORD
ReadFormat.lindex = -1; // LONG
HRESULT res = pDataObj->QueryGetData(&ReadFormat);
bool ok_query = (res == S_OK);
if (ok_query)
std::cout << "QueryGetData - OK" << std::endl;
else {
std::cout << "QueryGetData - ERROR = " << res << std::endl;
return false;
}
// ---------------- GetData ----------------
ZeroMemory(&ReadFormat, sizeof(ReadFormat));
ReadFormat.cfFormat = in_FormatEtc->cfFormat; // CLIPFORMAT
ReadFormat.tymed = TYMED_ISTREAM; // DWORD
ReadFormat.ptd = NULL; // [unique] DVTARGETDEVICE*
ReadFormat.dwAspect = DVASPECT_CONTENT; // DWORD
ReadFormat.lindex = -1; // LONG
STGMEDIUM MediumInfo;
res = pDataObj->GetData(&ReadFormat, &MediumInfo);
bool ok_getdata = (res == S_OK);
if (ok_getdata)
std::cout << "GetData - OK" << std::endl;
else {
std::cout << "GetData - ERROR = " << res << std::endl;
return false;
}
// MediumInfo должен содержать актуальную ссылку на IStream
if (MediumInfo.tymed != ReadFormat.tymed) {
std::cout << "GetData ERROR - invalid _MediumInfo.tymed_ = " << MediumInfo.tymed << std::endl;
return false;
}
IStream* Stream = MediumInfo.pstm;
if (Stream == NULL) {
std::cout << "GetData ERROR - Stream == NULL." << std::endl;
return false;
}
bool ReadResult = false;
// ---------------- allocate a buffer to read stream ----------------
const int BufferSize = 100000;
char* BufferPtr = (char* ) malloc(BufferSize);
if (BufferPtr != nullptr) {
// ---------------- Read the Stream ------------------------
ULONG ActuallyRead = 0;
ULONG TotalRead = 0;
bool StreamEof = false;
bool ReadError = false;
// --- позицинируем в начало ---
ULARGE_INTEGER NewPos = { 0, 0 };
LARGE_INTEGER SetPos = { 0, 0 };
HRESULT res_seek = Stream->Seek(SetPos, STREAM_SEEK_SET, &NewPos);
ReadError = (res_seek != S_OK);
if (ReadError) {
bool pending = res_seek == E_PENDING;
if (pending)
std::cout << "Stream.Seek is pending :((" << std::endl;
else
std::cout << "Stream.Seek ERROR = " << res_seek << std::endl;
}
else
std::cout << "Stream.Seek OK. New pos = LO=" << NewPos.LowPart << " HI=" << NewPos.HighPart << std::endl;
while (!StreamEof && !ReadError) {
HRESULT res_read = Stream->Read(BufferPtr, (ULONG) BufferSize, &ActuallyRead);
switch (res_read) {
case S_OK:
case S_FALSE: {
// --- we have read something ---
StreamEof = (ActuallyRead < BufferSize);
TotalRead = TotalRead + ActuallyRead;
break;
} // S_OK, S_FALSE
case E_PENDING: {
// pending... - not processed
break;
} // E_PENDING
default: {
ReadError = true;
std::cout << "Stream reading ERROR = " <<res_read << std::endl;
} // default
} // switch
} // while
ReadResult = !ReadError;
if (!ReadError) {
std::cout << "Stream reading OK. TotalRead = " << TotalRead << std::endl;
}
}
else {
std::cout << "Malloc ERROR - cannot alloc memory for a stream data." << std::endl;
ReadResult = false;
}
Stream->Release();
return ReadResult;
}
The stream you get is really an MSG file (which is an IStorage file). You can either