How to send a email in VC++? - email

I am new to VC++ and programming.
I have to write code to send a email in VC++.
How do I go about it? Please help!!

Here's how I do it with the ATL classes. I think you need one of the paid versions of VC++ to get ATL. You will need the name of your email server.
CSMTPConnection smtp;
if (!smtp.Connect(m_strEmailServer))
return false;
// start generating the email message; remember to call CoInitialize somewhere in the app before this
CMimeMessage msg;
msg.SetSubject(m_strSubject);
msg.SetSender(m_strSender);
// repeat the following as necessary
msg.AddRecipient(strSingleRecipient);
msg.AddText(m_strBody);
if (!smtp.SendMessage(msg))
return false;
return true;

Can your software require MAPI? That's an interface that provides a fairly simple interface to whatever the user's installed as default email program is.
If so, then you can use something like the following:
/////////////////////////////////////////////////////////
// CMapiSendMessage
// Allows for simplified message generation and transmission using Simple MAPI
class CMapiSendMessage
{
public:
// constant data
enum RecipientType { FROM = MAPI_ORIG, TO = MAPI_TO, CC = MAPI_CC, BCC = MAPI_BCC };
// ctors
CMapiSendMessage() { }
// accessors
CString GetSubject() const { return m_subject; }
CString & GetMessage() { return m_message; }
const CString & GetMessage() const { return m_message; }
// setters
void AddRecipient(RecipientType type, const CStringA & strName, const CStringA & strAddress = "")
{
m_recipients.push_back(Recipient(type, strName, strAddress));
}
void SetSubject(const CString & strSubject)
{
m_subject = strSubject;
}
void SetMessage(const CString & strMessage)
{
m_message = strMessage;
}
void AddAttachment(const CFilename & filename)
{
m_attachments.push_back(filename.cstring());
}
// actions
ULONG Send(bool bShowDialog, HWND hwndParent); // send this message, show or don't show the user the message dialog
// returns SUCCESS_SUCCESS if all went well
// returns MAPI_USER_ABORT if user aborted the send
// returns MAPI_E_LOGIN_FAILURE if unable to login to MAPI service (user login failed)
ULONG AfxSend(bool bShowDialog, bool bShowError); // send message with special processing for an MFC application
// set bShowError if you wish to automatically show standard MFC error messages if the send message failed
protected:
// types
struct Recipient
{
RecipientType type;
CStringA name;
CStringA address;
Recipient(RecipientType _type, const CStringA & _name, const CStringA & _address = "") :
type(_type), name(_name), address(_address)
{
}
// force default ctor to set type to zero (not random)
Recipient() : type(FROM) { }
};
typedef std::vector<Recipient> RecipientVector;
typedef std::vector<CString> AttachmentVector;
CMailAPI32 m_api; // MAPI interface
RecipientVector m_recipients; // recipients (TO:, CC:, and BCC:)
CString m_subject; // message subject
CString m_message; // message body text
AttachmentVector m_attachments; // file attachments
};
And the CPP half:
typedef std::vector<CMapiRecipDesc> MapiRecipDescVector;
typedef std::vector<CMapiFileDesc> MapiFileDescVector;
//////////////////////////////////////////////////////////////////////
// CMailAPI32
//////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// CMapiSendMessage
ULONG CMapiSendMessage::Send(bool bShowDialog, HWND hwndParent) // send, show user interface - let them edit the message and have final choice in send/cancel
{
// save the cwd
CPathname cwd(CPathname::GetCurrent());
// build the recipients array
const size_t total_recipients = m_recipients.size();
MapiRecipDescVector recipients(total_recipients);
for (size_t i = 0; i < total_recipients; ++i)
{
CMapiRecipDesc recipient;
recipient.ulReserved = 0L;
recipient.ulRecipClass = m_recipients[i].type;
recipient.lpszName = const_cast<LPSTR>(GetPtrOrNull(m_recipients[i].name));
recipient.lpszAddress = const_cast<LPSTR>(GetPtrOrNull(m_recipients[i].address));
recipient.ulEIDSize = 0;
recipient.lpEntryID = NULL;
recipients[i] = recipient;
}
// build attachments array
const size_t total_attachments = m_attachments.size();
MapiFileDescVector attachments(total_attachments);
for (size_t j = 0; j < total_attachments; ++j)
{
CFilename filename(m_attachments[j]);
if (!filename.Exists())
ThrowLabeledException(FString(_T("File does not exist: %s"), filename.c_str()));
// get the fully specified path
size_t size = filename.cstring().GetLength() + 1;
attachments[j].lpszPathName = new char[size];
#ifdef _UNICODE
_wcstombsz(attachments[j].lpszPathName, filename, size);
#else
lstrcpy(attachments[j].lpszPathName, filename);
#endif
// build an appropriate title for the attachment
CString strTitle = filename.GetFullName();
size = strTitle.GetLength() + 1;
attachments[j].lpszFileName = new char[size];
#ifdef _UNICODE
_wcstombsz(attachments[j].lpszFileName, strTitle, size);
#else
lstrcpy(attachments[j].lpszFileName, strTitle);
#endif
// attachment not embedded in the mapi_msg text;
attachments[j].nPosition = (ULONG)-1;
}
// prepare the mapi_msg
MapiMessage mapi_msg =
{
0, // reserved, must be 0
(LPSTR)GetPtrOrNull(m_subject), // subject
(LPSTR)GetPtrOrNull(m_message), // body
NULL, // NULL = interpersonal mapi_msg
NULL, // no date; MAPISendMail ignores it
NULL, // no conversation ID
0L, // no flags, MAPISendMail ignores it
NULL, // no originator, this is ignored too
total_recipients, // no. recipients
total_recipients ? &recipients[0] : NULL, // array of recipients
total_attachments, // no. attachments
total_attachments ? &attachments[0] : NULL // array of attachments
};
// send mail
FLAGS flags = MAPI_LOGON_UI;
BitSetIf(flags, MAPI_DIALOG, bShowDialog);
ULONG nError = m_api.SendMail(
NULL, // temporary session
(ULONG)hwndParent,
&mapi_msg,
flags,
0
);
// delete the attachment filenames
for (int k = total_attachments; k ; --k)
{
delete [] attachments[k-1].lpszPathName;
delete [] attachments[k-1].lpszFileName;
}
// restore CWD
cwd.SetCurrent();
// indicate if we succeeded or not
return nError;
}
ULONG CMapiSendMessage::AfxSend(bool bShowDialog, bool bReportError)
{
// prepare for modal dialog box
AfxGetApp()->EnableModeless(FALSE);
HWND hWndTop;
CWnd * pParentWnd = CWnd::GetSafeOwner(NULL, &hWndTop);
// some extra precautions are required to use MAPISendMail as it
// tends to enable the parent window in between dialogs (after
// the login dialog, but before the send note dialog).
pParentWnd->SetCapture();
::SetFocus(NULL);
pParentWnd->m_nFlags |= WF_STAYDISABLED;
// attempt to send the message
ULONG nError = Send(bShowDialog, pParentWnd->GetSafeHwnd());
// display error to user
if (nError != SUCCESS_SUCCESS &&
nError != MAPI_USER_ABORT &&
nError != MAPI_E_LOGIN_FAILURE)
{
AfxMessageBox(AFX_IDP_FAILED_MAPI_SEND);
}
// after returning from the MAPISendMail call, the window must
// be re-enabled and focus returned to the frame to undo the workaround
// done before the MAPI call.
::ReleaseCapture();
pParentWnd->m_nFlags &= ~WF_STAYDISABLED;
pParentWnd->EnableWindow(TRUE);
::SetActiveWindow(NULL);
pParentWnd->SetActiveWindow();
pParentWnd->SetFocus();
if (hWndTop != NULL)
::EnableWindow(hWndTop, TRUE);
AfxGetApp()->EnableModeless(TRUE);
// report success or not
return nError;
}
This is from my codebase - and I'm not making an effort to unravel it from my own libraries. But you should be able to "fill in the blanks".
Good luck!

Related

how to make HTTPS request in esp8266?

I once wanted to get THERMOCOUPLE DATA and by making HTTP request I put it on API
it worked flawlessly , here is the code for esp32:
// this example is public domain. enjoy! https://learn.adafruit.com/thermocouple/
#include "max6675.h"
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <WiFiMulti.h>
int thermoDO = 19;
int thermoCS = 23;
int thermoCLK = 5;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
const char *AP_SSID = "ssid";
const char *AP_PWD = "pass";
WiFiMulti wifiMulti;
void setup() {
Serial.begin(9600);
delay(4000);
wifiMulti.addAP(AP_SSID, AP_PWD);
Serial.println("MAX6675 test");
// wait for MAX chip to stabilize
delay(500);
}
void loop() {
// basic readout test, just print the current temp
Serial.print("C = ");
Serial.println(thermocouple.readCelsius());
Serial.print("F = ");
Serial.println(thermocouple.readFahrenheit());
// For the MAX6675 to update, you must delay AT LEAST 250ms between reads!
delay(1000);
postDataToServer();
}
void postDataToServer() {
Serial.println("Posting JSON data to server...");
// Block until we are able to connect to the WiFi access point
if (wifiMulti.run() == WL_CONNECTED) {
HTTPClient http;
http.begin("https://retoolapi.dev/fyXalF/data/1");
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
// Add values in the document
//
doc["temp1"] = thermocouple.readCelsius();
doc["temp2"] = thermocouple.readFahrenheit();
String requestBody;
serializeJson(doc, requestBody);
int httpResponseCode = http.PUT(requestBody);
if(httpResponseCode>0){
String response = http.getString();
Serial.println(httpResponseCode);
Serial.println(response);
}
else {
Serial.printf("Error occurred while sending HTTP POST");
}
}
}
now I'm trying to do it by esp8266.
so the code is basically the same , you have to put "ESP8266" before library names and for making http.begin() you have to pass a WiFiClient beside API address :
// this example is public domain. enjoy! https://learn.adafruit.com/thermocouple/
#include "max6675.h"
#include <ArduinoJson.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFiMulti.h>
#include <WiFiClientSecure.h>
int thermoDO = 19;
int thermoCS = 23;
int thermoCLK = 5;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
const char *AP_SSID = "ssid";
const char *AP_PWD = "pass";
ESP8266WiFiMulti wifiMulti;
void setup() {
Serial.begin(9600);
delay(4000);
wifiMulti.addAP(AP_SSID, AP_PWD);
Serial.println("MAX6675 test");
// wait for MAX chip to stabilize
delay(500);
}
void loop() {
// basic readout test, just print the current temp
Serial.print("C = ");
Serial.println(thermocouple.readCelsius());
Serial.print("F = ");
Serial.println(thermocouple.readFahrenheit());
// For the MAX6675 to update, you must delay AT LEAST 250ms between reads!
delay(1000);
postDataToServer();
}
void postDataToServer() {
Serial.println("Posting JSON data to server...");
// Block until we are able to connect to the WiFi access point
if (wifiMulti.run() == WL_CONNECTED) {
WiFiClient client;
HTTPClient http;
http.begin(client,"https://retoolapi.dev/fyXalF/data/1");
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
// Add values in the document
//
doc["temp1"] = thermocouple.readCelsius();
doc["temp2"] = thermocouple.readFahrenheit();
String requestBody;
serializeJson(doc, requestBody);
int httpResponseCode = http.PUT(requestBody);
if(httpResponseCode>0){
String response = http.getString();
Serial.println(httpResponseCode);
Serial.println(response);
}
else {
Serial.printf("Error occurred while sending HTTP POST");
}
}
}
it compiled but I got "bad request-400" and "The plain HTTP request was sent to HTTPS port" as a response .
I think the esp8266 library cant send https requests ...
any ideas how to solve this ?
PS : I also use #include <WiFiClientSecure.h>
WiFiClientSecure client;
HTTPClient http;
http.begin(client,"https://retoolapi.dev/0tvafA/data/1");
it's not working either : Error occurred while sending HTTP POSTC = 0.00
:(

How do i setup BroadcastReceiver to only display sms received from specific numbers?

I need help modifying a broadcastReceiver I wrote so that it only displays incoming sms message from numbers that I can specify. right now the following code displays ALL received sms, it would be nice if the app only displayed sms from 1 specific number. can anyone provide any specifics on how I could accomplish this?
code is as follows:
public class IncomingSms extends BroadcastReceiver {
final SmsManager sms = SmsManager.getDefault();
public void onReceive(Context context, Intent intent) {
// Retrieves a map of extended data from the intent.
final Bundle bundle = intent.getExtras();
try {
if (bundle != null) {
final Object[] pdusObj = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdusObj.length; i++) {
SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
String phoneNumber = currentMessage.getDisplayOriginatingAddress();
String senderNum = phoneNumber;
String message = currentMessage.getDisplayMessageBody();
Log.i("SmsReceiver", "senderNum: "+ senderNum + "; message: " + message);
// Show Alert
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context,
"senderNum: "+ senderNum + ", message: " + message, duration);
toast.show();
} // end for loop
} // bundle is null
} catch (Exception e) {
Log.e("SmsReceiver", "Exception smsReceiver" +e);
}
}
}
thanks a bunch for any help guys.
Just put one if there :)
public class IncomingSms extends BroadcastReceiver {
private static final String MY_PHONE_NUMBER = "your number here";
final SmsManager sms = SmsManager.getDefault();
public void onReceive(Context context, Intent intent) {
// Retrieves a map of extended data from the intent.
final Bundle bundle = intent.getExtras();
try {
if (bundle != null) {
final Object[] pdusObj = (Object[]) bundle.get("pdus");
for (int i = 0; i < pdusObj.length; i++) {
SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) pdusObj[i]);
String phoneNumber = currentMessage.getDisplayOriginatingAddress();
if(!MY_PHONE_NUMBER.equals(phoneNumber)) {
return;
}
...

Frictionless requests are not working in FBUnitySDK

This is the code of my FB.cs file which is a by default code from unity fb sdk. If you see at line number 10 I have already marked frictionless request as true by default by removing the variable and just putting the value as true.
private static bool frictionlessRequests=true;
public static void Init(InitDelegate onInitComplete, HideUnityDelegate onHideUnity = null, string authResponse = null)
{
Init(
onInitComplete,
FBSettings.AppId,
FBSettings.Cookie,
FBSettings.Logging,
FBSettings.Status,
FBSettings.Xfbml,
true,//FBSettings.FrictionlessRequests,
onHideUnity,
authResponse);
}
/**
* If you need a more programmatic way to set the facebook app id and other setting call this function.
* Useful for a build pipeline that requires no human input.
*/
public static void Init(
InitDelegate onInitComplete,
string appId,
bool cookie = true,
bool logging = true,
bool status = true,
bool xfbml = false,
bool frictionlessRequests = true,
HideUnityDelegate onHideUnity = null,
string authResponse = null)
{
FB.appId = appId;
FB.cookie = cookie;
FB.logging = logging;
FB.status = status;
FB.xfbml = xfbml;
FB.frictionlessRequests = frictionlessRequests;
FB.authResponse = authResponse;
FB.OnInitComplete = onInitComplete;
FB.OnHideUnity = onHideUnity;
if (!isInitCalled)
{
FbDebug.Info(String.Format("Using SDK {0}, Build {1}", FBBuildVersionAttribute.SDKVersion, FBBuildVersionAttribute.GetBuildVersionOfType(typeof(IFacebook))));
#if UNITY_EDITOR
FBComponentFactory.GetComponent<EditorFacebookLoader>();
#elif UNITY_WEBPLAYER
FBComponentFactory.GetComponent<CanvasFacebookLoader>();
#elif UNITY_IOS
FBComponentFactory.GetComponent<IOSFacebookLoader>();
#elif UNITY_ANDROID
FBComponentFactory.GetComponent<AndroidFacebookLoader>();
#else
throw new NotImplementedException("Facebook API does not yet support this platform");
#endif
isInitCalled = true;
return;
}
No I am calling Init using this.
FB.Init(OnInitComplete, OnHideUnity,null);
And I then when I try to send request using this code
public static function SendLifeBack(){
FB.AppRequest
(
"Enjoy a free Potion",
["USER_ID"],
null,
null,
null,
"life",
"Free Life",
LogCallback
);
}
There is not sign of frictionless request as there is no checkbox of
"don't ask me again before sending requests to this user"
Even if I make the line number 10 as default as
FBSettings.FrictionlessRequests
even then it is not working. I have checked the frictionless checkbox in FBSettings too.

tool chain allowing two-way communication between a D app and a browser

I wish to have an app written in the D programming language update its display in a browser. The browser should also send input data back to the app.
I'm still quite new to programming and am confused with how sockets/websockets/servers all fit together. Can anyone suggest an approach?
Many thanks to gmfawcett for the link to his basic D server example which I've mated with a bare-bones websocket implementation of the version 8 spec that I found elsewhere (currently only works in Chrome 14/15, I believe). It's pretty much cut'n'paste but seems to work well enough and I expect it will be sufficient in serving my needs.
If anyone has the inclination to cast a quick eye over my code for any glaring no-nos, please feel free to do so - and thanks!
Bare-bones websocket impl: http://blog.vunie.com/implementing-websocket-draft-10
Websocket v8 spec (protocol-17): https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17
module wsserver;
import std.algorithm;
import std.base64;
import std.conv;
import std.stdio;
import std.socket;
import std.string;
//std.crypto: https://github.com/pszturmaj/phobos/tree/master/std/crypto
import crypto.hash.base;
import crypto.hash.sha;
struct WsServer
{
private
{
Socket s;
Socket conn;
string subProtocol;
}
this(string host, ushort port = 8080, string subProtocol = "null")
{
this.subProtocol = subProtocol;
s = new TcpSocket(AddressFamily.INET);
s.bind(new InternetAddress(host, port));
s.listen(8);
conn = s.accept();
writeln("point/refresh your browser to \"http://", host, "\" to intiate the websocket handshake");
try
{
initHandshake(conn);
}
catch (Throwable e)
{
stderr.writeln("thrown: ", e);
}
}
~this()
{
conn.shutdown(SocketShutdown.BOTH);
conn.close();
s.shutdown(SocketShutdown.BOTH);
s.close();
}
string data()
{
ubyte[8192] msgBuf;
auto msgBufLen = conn.receive(msgBuf);
auto firstByte = msgBuf[0];
auto secondByte = msgBuf[1];
// not sure these two checks are woking correctly!!!
enforce((firstByte & 0x81), "Fragments not supported"); // enforce FIN bit is present
enforce((secondByte & 0x80), "Masking bit not present"); // enforce masking bit is present
auto msgLen = secondByte & 0x7f;
ubyte[] mask, msg;
if(msgLen < 126)
{
mask = msgBuf[2..6];
msg = msgBuf[6..msgBufLen];
}
else if (msgLen == 126)
{
mask = msgBuf[4..8];
msg = msgBuf[8..msgBufLen];
}
foreach (i, ref e; msg)
e = msg[i] ^ mask[i%4];
debug writeln("Client: " ~ cast(string) msg);
return cast(string) msg;
}
void data(string msg)
{
ubyte[] newFrame;
if (msg.length > 125)
newFrame = new ubyte[4];
else
newFrame = new ubyte[2];
newFrame[0] = 0x81;
if (msg.length > 125)
{
newFrame[1] = 126;
newFrame[2] = cast(ubyte) msg.length >> 8;
newFrame[3] = msg.length & 0xFF;
}
else
newFrame[1] = cast(ubyte) msg.length;
conn.send(newFrame ~= msg);
debug writeln("Server: " ~ msg);
}
private void initHandshake(Socket conn)
{
ubyte[8192] buf; // big enough for some purposes...
size_t position, headerEnd, len, newpos;
// Receive the whole header before parsing it.
while (true)
{
len = conn.receive(buf[position..$]);
debug writeln(cast(string)buf);
if (len == 0) // empty request
return;
newpos = position + len;
headerEnd = countUntil(buf[position..newpos], "\r\n\r\n");
position = newpos;
if (headerEnd >= 0)
break;
}
// Now parse the header.
auto lines = splitter(buf[0..headerEnd], "\r\n");
string request_line = cast(string) lines.front;
lines.popFront;
// a very simple Header structure.
struct Pair
{
string key, value;
this(ubyte[] line)
{
auto tmp = countUntil(line, ": ");
key = cast(string) line[0..tmp]; // maybe down-case these?
value = cast(string) line[tmp+2..$];
}
}
Pair[] headers;
foreach(line; lines)
headers ~= Pair(line);
auto tmp = splitter(request_line, ' ');
string method = tmp.front; tmp.popFront;
string url = tmp.front; tmp.popFront;
string protocol = tmp.front; tmp.popFront;
enum GUID_v8 = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // version 8 spec... might change
auto sha1 = new SHA1;
sha1.put(strip(headers[5].value) ~ GUID_v8);
auto respKey = to!string(Base64.encode(sha1.finish()));
// Prepare a response, and send it
string resp = join(["HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: " ~ respKey,
"Sec-WebSocket-Protocol: " ~ subProtocol,
""],
"\r\n");
conn.send(cast(ubyte[]) (resp ~ "\r\n"));
debug writeln(resp);
}
}

Can I drag items from Outlook into my SWT application?

Background
Our Eclipse RCP 3.6-based application lets people drag files in for storage/processing. This works fine when the files are dragged from a filesystem, but not when people drag items (messages or attachments) directly from Outlook.
This appears to be because Outlook wants to feed our application the files via a FileGroupDescriptorW and FileContents, but SWT only includes a FileTransfer type. (In a FileTransfer, only the file paths are passed, with the assumption that the receiver can locate and read them. The FileGroupDescriptorW/FileContents approach can supply files directly application-to-application without writing temporary files out to disk.)
We have tried to produce a ByteArrayTransfer subclass that could accept FileGroupDescriptorW and FileContents. Based on some examples on the Web, we were able to receive and parse the FileGroupDescriptorW, which (as the name implies) describes the files available for transfer. (See code sketch below.) But we have been unable to accept the FileContents.
This seems to be because Outlook offers the FileContents data only as TYMED_ISTREAM or TYMED_ISTORAGE, but SWT only understands how to exchange data as TYMED_HGLOBAL. Of those, it appears that TYMED_ISTORAGE would be preferable, since it's not clear how TYMED_ISTREAM could provide access to multiple files' contents.
(We also have some concerns about SWT's desire to pick and convert only a single TransferData type, given that we need to process two, but we think we could probably hack around that in Java somehow: it seems that all the TransferDatas are available at other points of the process.)
Questions
Are we on the right track here? Has anyone managed to accept FileContents in SWT yet? Is there any chance that we could process the TYMED_ISTORAGE data without leaving Java (even if by creating a fragment-based patch to, or a derived version of, SWT), or would we have to build some new native support code too?
Relevant code snippets
Sketch code that extracts file names:
// THIS IS NOT PRODUCTION-QUALITY CODE - FOR ILLUSTRATION ONLY
final Transfer transfer = new ByteArrayTransfer() {
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
if (!isSupportedType(transferData))
return null;
final byte[] buffer = (byte[]) super.nativeToJava(transferData);
if (buffer == null)
return null;
try {
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(buffer));
long count = 0;
for (int i = 0; i < 4; i++) {
count += in.readUnsignedByte() << i;
}
for (int i = 0; i < count; i++) {
final byte[] filenameBytes = new byte[260 * 2];
in.skipBytes(72); // probable architecture assumption(s) - may be wrong outside standard 32-bit Win XP
in.read(filenameBytes);
final String fileNameIncludingTrailingNulls = new String(filenameBytes, "UTF-16LE");
int stringLength = fileNameIncludingTrailingNulls.indexOf('\0');
if (stringLength == -1)
stringLength = 260;
final String fileName = fileNameIncludingTrailingNulls.substring(0, stringLength);
System.out.println("File " + i + ": " + fileName);
}
in.close();
return buffer;
}
catch (final Exception e) {
return null;
}
}
};
In the debugger, we see that ByteArrayTransfer's isSupportedType() ultimately returns false for the FileContents because the following test is not passed (since its tymed is TYMED_ISTREAM | TYMED_ISTORAGE):
if (format.cfFormat == types[i] &&
(format.dwAspect & COM.DVASPECT_CONTENT) == COM.DVASPECT_CONTENT &&
(format.tymed & COM.TYMED_HGLOBAL) == COM.TYMED_HGLOBAL )
return true;
This excerpt from org.eclipse.swt.internal.ole.win32.COM leaves us feeling less hope for an easy solution:
public static final int TYMED_HGLOBAL = 1;
//public static final int TYMED_ISTORAGE = 8;
//public static final int TYMED_ISTREAM = 4;
Thanks.
even if
//public static final int TYMED_ISTREAM = 4;
Try below code.. it should work
package com.nagarro.jsag.poc.swtdrag;
imports ...
public class MyTransfer extends ByteArrayTransfer {
private static int BYTES_COUNT = 592;
private static int SKIP_BYTES = 72;
private final String[] typeNames = new String[] { "FileGroupDescriptorW", "FileContents" };
private final int[] typeIds = new int[] { registerType(typeNames[0]), registerType(typeNames[1]) };
#Override
protected String[] getTypeNames() {
return typeNames;
}
#Override
protected int[] getTypeIds() {
return typeIds;
}
#Override
protected Object nativeToJava(TransferData transferData) {
String[] result = null;
if (!isSupportedType(transferData) || transferData.pIDataObject == 0)
return null;
IDataObject data = new IDataObject(transferData.pIDataObject);
data.AddRef();
// Check for descriptor format type
try {
FORMATETC formatetcFD = transferData.formatetc;
STGMEDIUM stgmediumFD = new STGMEDIUM();
stgmediumFD.tymed = COM.TYMED_HGLOBAL;
transferData.result = data.GetData(formatetcFD, stgmediumFD);
if (transferData.result == COM.S_OK) {
// Check for contents format type
long hMem = stgmediumFD.unionField;
long fileDiscriptorPtr = OS.GlobalLock(hMem);
int[] fileCount = new int[1];
try {
OS.MoveMemory(fileCount, fileDiscriptorPtr, 4);
fileDiscriptorPtr += 4;
result = new String[fileCount[0]];
for (int i = 0; i < fileCount[0]; i++) {
String fileName = handleFile(fileDiscriptorPtr, data);
System.out.println("FileName : = " + fileName);
result[i] = fileName;
fileDiscriptorPtr += BYTES_COUNT;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
OS.GlobalFree(hMem);
}
}
} finally {
data.Release();
}
return result;
}
private String handleFile(long fileDiscriptorPtr, IDataObject data) throws Exception {
// GetFileName
char[] fileNameChars = new char[OS.MAX_PATH];
byte[] fileNameBytes = new byte[OS.MAX_PATH];
COM.MoveMemory(fileNameBytes, fileDiscriptorPtr, BYTES_COUNT);
// Skip some bytes.
fileNameBytes = Arrays.copyOfRange(fileNameBytes, SKIP_BYTES, fileNameBytes.length);
String fileNameIncludingTrailingNulls = new String(fileNameBytes, "UTF-16LE");
fileNameChars = fileNameIncludingTrailingNulls.toCharArray();
StringBuilder builder = new StringBuilder(OS.MAX_PATH);
for (int i = 0; fileNameChars[i] != 0 && i < fileNameChars.length; i++) {
builder.append(fileNameChars[i]);
}
String name = builder.toString();
try {
File file = saveFileContent(name, data);
if (file != null) {
System.out.println("File Saved # " + file.getAbsolutePath());
;
}
} catch (IOException e) {
System.out.println("Count not save file content");
;
}
return name;
}
private File saveFileContent(String fileName, IDataObject data) throws IOException {
File file = null;
FORMATETC formatetc = new FORMATETC();
formatetc.cfFormat = typeIds[1];
formatetc.dwAspect = COM.DVASPECT_CONTENT;
formatetc.lindex = 0;
formatetc.tymed = 4; // content.
STGMEDIUM stgmedium = new STGMEDIUM();
stgmedium.tymed = 4;
if (data.GetData(formatetc, stgmedium) == COM.S_OK) {
file = new File(fileName);
IStream iStream = new IStream(stgmedium.unionField);
iStream.AddRef();
try (FileOutputStream outputStream = new FileOutputStream(file)) {
int increment = 1024 * 4;
long pv = COM.CoTaskMemAlloc(increment);
int[] pcbWritten = new int[1];
while (iStream.Read(pv, increment, pcbWritten) == COM.S_OK && pcbWritten[0] > 0) {
byte[] buffer = new byte[pcbWritten[0]];
OS.MoveMemory(buffer, pv, pcbWritten[0]);
outputStream.write(buffer);
}
COM.CoTaskMemFree(pv);
} finally {
iStream.Release();
}
return file;
} else {
return null;
}
}
}
Have you looked at https://bugs.eclipse.org/bugs/show_bug.cgi?id=132514 ?
Attached to this bugzilla entry is an patch (against an rather old version of SWT) that might be of interest.
I had the same problem and created a small library providing a Drag'n Drop Transfer Class for JAVA SWT. It can be found here:
https://github.com/HendrikHoetker/OutlookItemTransfer
Currently it supports dropping Mail Items from Outlook to your Java SWT application and will provide a list of OutlookItems with the Filename and a byte array of the file contents.
All is pure Java and in-memory (no temp files).
Usage in your SWT java application:
if (OutlookItemTransfer.getInstance().isSupportedType(event.currentDataType)) {
Object o = OutlookItemTransfer.getInstance().nativeToJava(event.currentDataType);
if (o != null && o instanceof OutlookMessage[]) {
OutlookMessage[] outlookMessages = (OutlookMessage[])o;
for (OutlookMessage msg: outlookMessages) {
//...
}
}
}
The OutlookItem will then provide two elements: filename as String and file contents as array of byte.
From here on, one could write it to a file or further process the byte array.
To your question above:
- What you find in the file descriptor is the filename of the outlook item and a pointer to an IDataObject
- the IDataObject can be parsed and will provide an IStorage object
- The IStorageObject will be then a root container providing further sub-IStorageObjects or IStreams similar to a filesystem (directory = IStorage, file = IStream
You find those elements in the following lines of code:
Get File Contents, see OutlookItemTransfer.java, method nativeToJava:
FORMATETC format = new FORMATETC();
format.cfFormat = getTypeIds()[1];
format.dwAspect = COM.DVASPECT_CONTENT;
format.lindex = <fileIndex>;
format.ptd = 0;
format.tymed = TYMED_ISTORAGE | TYMED_ISTREAM | COM.TYMED_HGLOBAL;
STGMEDIUM medium = new STGMEDIUM();
if (data.GetData(format, medium) == COM.S_OK) {
// medium.tymed will now contain TYMED_ISTORAGE
// in medium.unionfield you will find the root IStorage
}
Read the root IStorage, see CompoundStorage, method readOutlookStorage:
// open IStorage object
IStorage storage = new IStorage(pIStorage);
storage.AddRef();
// walk through the content of the IStorage object
long[] pEnumStorage = new long[1];
if (storage.EnumElements(0, 0, 0, pEnumStorage) == COM.S_OK) {
// get storage iterator
IEnumSTATSTG enumStorage = new IEnumSTATSTG(pEnumStorage[0]);
enumStorage.AddRef();
enumStorage.Reset();
// prepare statstg structure which tells about the object found by the iterator
long pSTATSTG = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, STATSTG.sizeof);
int[] fetched = new int[1];
while (enumStorage.Next(1, pSTATSTG, fetched) == COM.S_OK && fetched[0] == 1) {
// get the description of the the object found
STATSTG statstg = new STATSTG();
COM.MoveMemory(statstg, pSTATSTG, STATSTG.sizeof);
// get the name of the object found
String name = readPWCSName(statstg);
// depending on type of object
switch (statstg.type) {
case COM.STGTY_STREAM: { // load an IStream (=File)
long[] pIStream = new long[1];
// get the pointer to the IStream
if (storage.OpenStream(name, 0, COM.STGM_DIRECT | COM.STGM_READ | COM.STGM_SHARE_EXCLUSIVE, 0, pIStream) == COM.S_OK) {
// load the IStream
}
}
case COM.STGTY_STORAGE: { // load an IStorage (=SubDirectory) - requires recursion to traverse the sub dies
}
}
}
}
// close the iterator
enumStorage.Release();
}
// close the IStorage object
storage.Release();