Menu

mapiSendMail throws EXCEPTION_ACCESS_VIOLATION

Help
Jo Wouters
2014-08-14
2014-08-19
  • Jo Wouters

    Jo Wouters - 2014-08-14

    Hi,

    I'm a .NET-developer but recently I had to switch to Java for supporting one of our partners.
    So I'm pretty new to Java. I'm extending the functionality of an existing application and I'm having an issue with my custom code to open the default email application and add attachments and recipients using JNative. I receive the following message in Eclipse after an application crash:
    A fatal error has been detected by the Java Runtime Environment:
    EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x77e8e823, pid=7240, tid=8268

    It would be handy if you had some code:

    package com.company.control;

    import java.io.File;
    import java.util.ArrayList;
    import java.util.List;

    import javax.swing.JOptionPane;

    import org.xvolks.jnative.JNative;
    import org.xvolks.jnative.exceptions.NativeException;
    import org.xvolks.jnative.misc.basicStructures.HANDLE;
    import org.xvolks.jnative.misc.basicStructures.LONG;
    import org.xvolks.jnative.pointers.Pointer;
    import org.xvolks.jnative.util.mapi.MAPIConstants;
    import org.xvolks.jnative.util.mapi.SimpleMAPI;
    import org.xvolks.jnative.util.mapi.structs.MapiFileDesc;
    import org.xvolks.jnative.util.mapi.structs.MapiMessage;
    import org.xvolks.jnative.util.mapi.structs.MapiRecipDesc;

    public class MailController {

    //TODO: change email to yours for testing
    private final static String TO_ADDRESS = "me@company.com"; //not mine of course
    private ArrayList<mapifiledesc> validAttachments = null;
    private ArrayList<mapirecipdesc> validRecipients = null;
    private Pointer ptrAttachments = null;
    private Pointer ptrRecipients = null;
    private HANDLE session = new HANDLE(0);
    private MapiMessage mapiMessage = null;</mapirecipdesc></mapifiledesc>

    //  private transient Logger logger = Logger.getLogger(MailController.class);
    
    
    public static void test() {
    
    //TODO: change path to a location with some text files
        String path = "D:\\logs\\";
    
        ArrayList<String> attachments = new ArrayList<>();
    
        //TODO: add some text files as attachments
        attachments.add(path + "1406900592265_auditLog.log");
        attachments.add(path + "1406900592265_debugLog.log");
        attachments.add(path + "1406900592265_errorLog.log");
        attachments.add(path + "DebugAndHigher.log");
        attachments.add(path + "WarnAndHigher.log");
        attachments.add(path + "DebugAndHigher.log.1");
    
        MailController mail = new MailController();
    
        ArrayList<String> to = new ArrayList<>();
        to.add(TO_ADDRESS);
    
        ArrayList<String> cc = new ArrayList<>();
        //      cc.add(TO_ADDRESS);
    
        ArrayList<String> bcc = new ArrayList<>();
        //      bcc.add(TO_ADDRESS);
    
        mail.openEmailWithAttachmentsAndRecepients(to, cc, bcc, "Test", "Some text\nand a second line\nattachments should be included", attachments);
    
    }
    
    private void openEmailWithAttachmentsAndRecepients(List<String> to, List<String> cc,List<String> bcc, String subject, String body, List<String> attachments){
    
        boolean addAttachments = false;
        boolean addRecipients = false;
    
        if(JOptionPane.showConfirmDialog(null, "Add attachments?", "Mail test", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION){
            addAttachments = true;
        }
        if(JOptionPane.showConfirmDialog(null, "Add recipients?", "Mail test", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION){
            addRecipients = true;
        }
    
    
        Pointer lpszSubject = null;
        Pointer lpszNoteText = null;
        Pointer filePath = null;
    
        try
        {
            // create a MapiMessage structure
            mapiMessage = new MapiMessage();
    
            //***************************************************************************************************
            // Subject and body
            //***************************************************************************************************
            // set subject and message pointers
            lpszSubject = Pointer.createPointerFromString(subject);
            lpszNoteText = Pointer.createPointerFromString(body);
    
            // add the pointers to the MapiMessage structure for subject and body
            mapiMessage.lpszSubject = lpszSubject.getPointer();
            mapiMessage.lpszNoteText = lpszNoteText.getPointer();
    
            //***************************************************************************************************
            // Attachments
            //***************************************************************************************************
            if(addAttachments)
                processAttachments(attachments);
    
            //***************************************************************************************************
            // Set the recipients
            //***************************************************************************************************
            if(addRecipients)
                processRecipients(to, cc, bcc);
    
    
            //***************************************************************************************************
            // Open the email and show to the user
            //***************************************************************************************************
    
            // send the message
            String returnError = SimpleMAPI.MAPISendMail(session, new LONG(0), mapiMessage, MAPIConstants.MAPI_DIALOG );
            System.out.println("MAPISendMail: "+ returnError);
            System.out.println("If not 0, see value at http://msdn.microsoft.com/en-us/library/windows/desktop/hh707275(v=vs.85).aspx");
    
    
    
        } catch(IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch(NativeException e){
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            //log off the session if possible
            try {
                SimpleMAPI.MAPILogoff(session, new LONG(0), 0);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NativeException e) {
                e.printStackTrace();
            }
    
            //clean up attachment pointer
            if(ptrAttachments != null){
                try {
                    ptrAttachments.dispose();
                    ptrAttachments = null;
                } catch (NativeException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            attachmentCleanup();
    
            //clean up recipients pointer
            if(ptrRecipients != null){
                try {
                    ptrRecipients.dispose();
                    ptrRecipients = null;
                } catch (NativeException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            recipientsCleanup(validRecipients);
    
            //clean up body pointer
            if(lpszNoteText != null)
                try {
                    lpszNoteText.dispose();
                } catch (NativeException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            //clean up subject pointer
            if(lpszSubject != null)
                try {
                    lpszSubject.dispose();
                } catch (NativeException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            //clean up message
            if(mapiMessage != null)
                try {
                    mapiMessage.getPointer().dispose();
                } catch (NativeException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            //clean up file pointer
            if(filePath != null)
                try {
                    filePath.dispose();
                } catch (NativeException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
    
            //unload all libraries
            JNative.unLoadAllLibraries();
        }
    }
    
    private void processRecipients(List<String> to, List<String> cc, List<String> bcc) {
    
        //making sure that all arrays are instantiated
        if(to == null)
            to = new ArrayList<String>();
        if(cc == null)
            cc = new ArrayList<String>();
        if(bcc == null)
            bcc = new ArrayList<String>();
    
        //Depending on the email program, the user must allow access for this routine
        //This array contains the recipients that are valid
        if(validRecipients == null){
            validRecipients = new ArrayList<>();
        }
    
        try {
    
            //process each list and put the valid results in validRecipients
            processOneListOfRecipients(to, MAPIConstants.MAPI_TO);
            processOneListOfRecipients(cc, MAPIConstants.MAPI_CC);
            processOneListOfRecipients(bcc, MAPIConstants.MAPI_BCC);
    
            //if there are valid recipients, add them to the message. if no, show a message with no recipients
            if(validRecipients.size() > 0){
                //allocate memory with the size of MapiRecipDesc-object for each recipient (to, cc, bcc), basically an array pointer
                //which holds all the data
                ptrRecipients = Pointer.createPointer( validRecipients.size() * MapiRecipDesc.sizeOf());
    
                //for each recipient, we need to set the memory in the right place in the memory (offset)
                int offset = 0;
    
                //loop through each valid recipient
                for(MapiRecipDesc tmp: validRecipients){
                    //copy the bytes from each recipient in the right place in the array
                    JNative.setMemory(ptrRecipients.getPointer(), tmp.createPointer().getMemory(), offset, 2*MapiRecipDesc.sizeOf()-1);
                    //increase offset for the next recipient
                    offset += MapiRecipDesc.sizeOf();
                }
    
                //set the recipients to the message
                mapiMessage.nRecipCount = new LONG(validRecipients.size());
                mapiMessage.lpRecips = ptrRecipients.getPointer();
            }
    
        } catch (NativeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
    }
    
    private void processOneListOfRecipients(List<String> toList, int typeOfRecipient) throws NativeException, IllegalAccessException{
        LONG holder;
        int ret;
        Pointer ptrToAddress;
        MapiRecipDesc mapiRecipDesc;
    
        //loop through all recipients in the list
        for(String singleTo: toList){
            //new long with 0 -> null pointer, and will be filled in during MAPIResolveName
            holder = new LONG(0);
    
            //create a pointer to the string
            ptrToAddress = Pointer.createPointerFromString(singleTo);
    
            //The next line creates a MapiRecipDesc-object from the string pointer ptrToAddress and returns a pointer-value (LONG) 
            //which will be stored in holder.
            //To do this, Outlook (or other program), might ask for permissions to the user. 
            //If denied or in case of error, this recipient is skipped
            ret = SimpleMAPI.MAPIResolveName(session, new LONG(0), ptrToAddress, 0, new LONG(0), holder);
    
            //if and only if a creation was successful, then modify the type and add it to the array of valid recipients
            if(ret == MAPIConstants.SUCCESS_SUCCESS)
            {
                //change type to the requested type (TO, CC, BCC)
                mapiRecipDesc = new MapiRecipDesc(holder.getValueFromPointer());
                mapiRecipDesc.ulRecipClass = new LONG(typeOfRecipient);
                //add to the valid recipients array
                validRecipients.add(mapiRecipDesc);
            }
    
            mapiRecipDesc = null;
            ptrToAddress.dispose();
        }
    }
    
    private void recipientsCleanup(ArrayList<MapiRecipDesc> validRecipients){
        if(validRecipients != null){
            //clean up
            while(validRecipients.size()>0) {
                try
                {
                    validRecipients.get(0).getPointer().dispose();
                } catch (NativeException e) {
                    //failed to free the block
                    e.printStackTrace();
                }
                validRecipients.remove(0);
            }
        }
    }
    
    private void processAttachments(List<String> attachments) {
    
        //making sure that all arrays are instantiated
        if(attachments == null)
            attachments = new ArrayList<String>();
    
        //This array contains the attachments that are valid
        if(validAttachments == null) {
            validAttachments = new ArrayList<>();
        }
    
        try {
    
            //process each list and put the valid results in validAttachments
            Pointer ptrAttachment;
            MapiFileDesc mapiFileDesc;
    
            //loop through all attachments in the list
            for(String attachment: attachments){
                if((new File(attachment).exists())){
                    ptrAttachment = Pointer.createPointerFromString(attachment);
                    mapiFileDesc = new MapiFileDesc();
                    mapiFileDesc.lpszPathName = ptrAttachment.getPointer();
                    mapiFileDesc.nPosition = new LONG(-1);
    
                    mapiFileDesc.createPointer();
    
                    validAttachments.add(mapiFileDesc);
    
                }
            }
    
            //if there are valid attachments, add them to the message. if no, show a message with no attachments
            if(validAttachments.size() > 0){
                //allocate memory with the size of MapiFileDesc-object for each attachment, basically an array pointer
                //which holds all the data
                ptrAttachments = Pointer.createPointer( validAttachments.size() * MapiFileDesc.sizeOf());
    
                //for each attachment, we need to set the memory in the right place in the memory (offset)
                int offset = 0;
    
                //loop through each valid attachment
                for(MapiFileDesc tmp: validAttachments){
                    //copy the bytes from each attachment in the right place in the array
                    JNative.setMemory(ptrAttachments.getPointer(), tmp.createPointer().getMemory(), offset, 2*MapiFileDesc.sizeOf()-1);
                    //increase offset for the next recipient
                    offset += MapiFileDesc.sizeOf();
                }
    
                //set the attachment to the message
                mapiMessage.nFileCount = new LONG(validAttachments.size());
                mapiMessage.lpFiles = ptrAttachments.getPointer();
    
            }
    
        } catch (NativeException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
    
    private void attachmentCleanup(){
        if(validAttachments != null){
            //clean up
            while(validAttachments.size()>0) {
                try
                {
                    validAttachments.get(0).getPointer().dispose();
                } catch (NativeException e) {
                    //failed to free the block
                    e.printStackTrace();
                }
    
                validAttachments.remove(0);
            }
        }
    }
    

    }

    When I run the code, No attachments, No recipients, then it always works.
    From the moment I add attachments, it sometimes works +-80%. I cannot find the cause for the crash.
    If I only add recipient, the application crashes 90% of the time on the first try.
    If I add both, the application crashes always.

    Does anyone have an idea what it could be?

    Thanks in advance
    Jo

     
  • Thubb

    Thubb - 2014-08-19

    Well, I wrote this long long time ago and can't really remember too much.
    Actually this looks strange to me:

    //copy the bytes from each attachment in the right place in the array
    JNative.setMemory(ptrAttachments.getPointer(), tmp.createPointer().getMemory(), offset, 2 * MapiFileDesc.sizeOf() - 1);
    

    Why is the length paramter "2 * MapiFileDesc.sizeOf() - 1"? I also see this in my SimpleMAPI example but honestly I don't know why and why this works at all. I would expect simply "MapiFileDesc.sizeOf()". You are only allocating "validAttachments.size() * MapiFileDesc.sizeOf()" bytes of memory:

    ptrAttachments = Pointer.createPointer(validAttachments.size() * MapiFileDesc.sizeOf());
    

    So I think that using the above JNative.setMemory() will result in a buffer overrun.

    Please check the output of JNative.getMemoryAsString() on tmp.createPointer() with different length to see what is the proper size.

     
  • Thubb

    Thubb - 2014-08-19

    Ok, I was right. My example is not working with "2 * MapiRecipDesc.sizeOf() - 1", I get an EXCEPTION_ACCESS_VIOLATION like you do. Now I changed it to "MapiRecipDesc.sizeOf()" and it is working fine. So please do the same for recipients (MapiRecipDesc.sizeOf()) and attachments (MapiFileDesc.sizeOf()) and it should work for you, too.

     

Log in to post a comment.

MongoDB Logo MongoDB