Thursday, March 11, 2010

SWT: Drop an Outlook Email

I wanted to drop an outlook email to my RCP application and as always I asked google to help me. You don't find much on this, an older post was
http://www.eclipse.org/forums/index.php?t=msg&&th=147516&goto=464661

That got me startet. The damn it didn't work, I asked on the forum again and the answer made confusion even greater:

http://www.eclipse.org/forums/index.php?t=rview&goto=519397#msg_519397

gladly my wife gladly does some more windows thingy's with c# and with her help and a c# example i found out that i have to make an os call to get the type of the transfer that may change with every boot.

public static int registerType() {
// Look name up in the registry
// If name is not in registry, add it and return assigned value.
// If name already exists in registry, return its assigned value
TCHAR chFormatName = new TCHAR(0, "FileContents", true);
return OS.RegisterClipboardFormat(chFormatName);
}
Then at least the stuff from the first post started to do something, i could get an IStorage and enumerate over the STATSTG's

public Object nativeToJava(TransferData transferData) {

IDataObject idata = new IDataObject(transferData.pIDataObject);
idata.AddRef();
FORMATETC formatetc = new FORMATETC();

STGMEDIUM stgmedium = new STGMEDIUM();
formatetc.cfFormat = type;
formatetc.lindex = 0;
formatetc.ptd = 0;
formatetc.tymed = 4 | 8 | 1;
formatetc.dwAspect = COM.DVASPECT_CONTENT;
transferData.result = getData(idata, formatetc, stgmedium);
idata.Release();
if (transferData.result != COM.S_OK) {
System.out.println("Fehler" + transferData.result);
return null;
}

if ((stgmedium.tymed & 8) == 8) {
// IStorage
IStorage storage = new IStorage(stgmedium.unionField);
storage.AddRef();
long[] x = new long[1];
long ret = storage.EnumElements(0, 0, 0, x);

IEnumSTATSTG enumSTATSTG = new IEnumSTATSTG(x[0]);
enumSTATSTG.AddRef();
STATSTG[] data = new STATSTG[0];
// Loop over enumerator
long rgelt = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, STATSTG.sizeof);
int[] pceltFetched = new int[1];
enumSTATSTG.Reset();
while (enumSTATSTG.Next(1, rgelt, pceltFetched) == COM.S_OK && pceltFetched[0] == 1) {
STATSTG statstg = new STATSTG();
COM.MoveMemory(statstg, rgelt, STATSTG.sizeof);
STATSTG[] newData = new STATSTG[data.length + 1];
System.arraycopy(data, 0, newData, 0, data.length);
newData[data.length] = statstg;
data = newData;
}
OS.GlobalFree(rgelt);

enumSTATSTG.Release();

// do something with STATSTG[]

but i still did not know what the heck to do with these objects. I started to search again and finally found an example in the eclipse OleClient:

private byte[] readStream(IStorage storage, String stream) {
boolean success = false;

long[] address = new long[1];
// Look for a CONTENTS stream
if (storage.OpenStream(stream, 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, address) == COM.S_OK) {
IStream tempContents = new IStream(address[0]);
tempContents.AddRef();

try {
ByteArrayOutputStream w = new ByteArrayOutputStream();

int increment = 1024 * 4;
long pv = COM.CoTaskMemAlloc(increment);
int[] pcbWritten = new int[1];
while (tempContents.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
byte[] buffer = new byte[pcbWritten[0]];
OS.MoveMemory(buffer, pv, pcbWritten[0]);
w.write(buffer);
success = true;
}
COM.CoTaskMemFree(pv);

w.close();
return w.toByteArray();
} catch (IOException err) {
} finally {

tempContents.Release();
}

}
return null;
}

now with that I can read Streams from the IStorage. What streams there are is in the STATSTG's

// do something with STATSTG[]
Map result = new HashMap();
for (STATSTG s : data) {
String stream = getString(s.pwcsName);
result.put(stream, readStream(storage, stream));
}

storage.Release();
return result;
There we are. The IStorage contains the data in the .msg Format that I return in some strange way via the Map. I don't know how to get the .msg in one piece, but at least with some informations from

http://www.fileformat.info/format/outlookmsg/

I can now extract the contents of the message and push it to jackrabbit ;) The code in these examples is for x86_64, you may need some ints for the adresses on 32bit windows.

5 comments:

Joerg said...

Hy

It's realy a hard work that you have done. Thanks.

Can I provider the code for the method getString in the line:
String stream = getString(s.pwcsName);

Another thing is, I have now the map with data, but I which format are the data und how can I extract the e-Mail from the map?

I'm looking to hear from you. Thanks.

Kind regards
Joerg

Thomas Kratz said...

getstring does the following
public static String getString(long addr) {
String str = null;
TCHAR buffer = new TCHAR(0, 256);
OS.MoveMemory(buffer, addr, 256);
str = buffer.toString(0, buffer.strlen());
return str;
}

the map is somehow in the format described in the last link. you have to look for the right strems you are interestest in. i found that on my outlook 2007 i get extra bytes for each character that i simply removed, i don't understand why, just happy that it works. drop me a mail at eiswind at gmail dot com

Jörg said...

Hi Thomas

Thanks for getString.

The code works. The data in the map are a little wirred...

Until know I was unable to figure out who attachments are represented in the map. Do you have any idea?

Jörg

Thomas Kratz said...

Sorry Jörg, I didn't give a try on the attachments, but they should be in the map too in some way.

Alexis said...

There are many tools on my computer. Yesterday I had unpleasant difficult problem,which I didn't know how to restore it. But for luck I entered the Internet and observed there an extraordinary software - microsoft exchange moving outlook ost file. I used the utility and it determined my issue for a minute and free of charge as I kept in mind. The software restored my crashed emails completely easy.

Using Mapstruct with Protobuf3