Bob Balaban's Blog

     
    alt

    Bob Balaban

     

    Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Bob Balaban  April 4 2011 05:45:00 AM
    Greetings, Geeks!

    Recently I posted some sample code showing you how to convert Notes documents to MIME format on disk, using the Notes Java APIs. Unfortunately, if you need to do this kind of conversion using some other platform (such as .net), the job is a bit harder. A key method on the Document class (doc.convertToMIME()) is missing in the COM classes for Notes. Why? No idea, someone should ask Lotus about that. Probably has something to do with fear (but that's just speculation on my part).

    No worries, though. the convertToMIME() method uses public C API calls to do its work, so we can do the same from a .net program. For starters, see my blog post on calling C API from C# (c-sharp) programs, here. That post talks about the basic techniques and syntax for setting up any kind of call-out from C# to C, so I won't repeat that here (the idea is just about the same if you're using vb). Here's a c-sharp file that provides a bunch of wrappers for Notes C API calls, bundled together in a class called NotesEntriess.NotesEntries.cs  

    The tedious bit about doing MIME conversion for a .net program is that there are several C calls you have to make for the 1 Java/LotusScript call. The tricky part is that, to do it without having to do everything at the C API level, you want to save typing by leveraging the COM classes, and only use the NotesEntries wrappers when you have to. The problem is that the COM classes do not expose the in-memory "handle" for a NotesDocument instance -- you can only get the Note ID -- while the C API calls require the handle for most things.

    So you'll see in the sample program "BlogMIME.cs" (BlogMIME.cs) that the first part of the work (converting a document to MIME format in memory) is done with C API wrappers (after using "regular" COM classes to access the NSF and find the document in the first place, of course). I could have continued to use the C API to iterate over MIMEParts in the converted document, force Base64 encoding, and write out the results, but it's really much easier to do all that using COM classes. Unfortunately, there's no way to take the NOTEHANDLE that I get using the C API and use it to get a NotesDocument COM object. I had to save() the document back to disk, get the Note ID, and use that to "get" the document again via COM. There's a slight performance hit for the extra save, but it was worth it in this case.

    You'll notice that the c# code that actually writes out the converted MIME document to disk is almost a line-for-line translation of the Java code I posted earlier. That's possible because the Notes COM classes are almost identical (on purpose) to the Java and LotusScript interfaces. The only reason we have to get tricky with using C API wrappers is that the Document.convertToMIME() function is missing in the COM interface.

    There are some other nice (reusable) features of the c# code, such as mapping various C constants to c# syntax, and so on. So, take the code, use it as a source of techniques for doing this kind of integration.  Enjoy!

    Geek ya later.

    (Need expert application development architecture/coding help?  Want me to help you invent directory services based on RDBMS? Need some Cloud-fu or some web services? Contact me at: bbalaban, gmail.com)
    Follow me on Twitter @LooseleafLLC
    This article ┬ęCopyright 2011 by Looseleaf Software LLC, all rights reserved. You may link to this page, but may not copy without prior approval.

    Comments

    1Scott Leis  4/4/2011 8:11:01 PM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Just a question about code samples on this blog:

    Do you have any specific rules about using this code in projects?

    Is it simply a case of acknowledging the source?

    2Bob Balaban  5/9/2011 6:32:50 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Attribution is certainly something anyone should do when using someone else's code.

    Beyond that, if you're making money from my samples, you should probably give me some.

    3chaminda  6/21/2011 9:28:55 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    I'm trying to your code to convert some Notes documents to MIME type so that I can extract HTML out of that. all my documents are returning null for GetMIMEEntity().

    One thing I noted is NotesEntries.NSFNoteOpenExt returns 690 as the status. I tried finding what this status on net but I failed to find any clue about that code.

    Can you please help me ?

    4Domingo Trujillo  8/6/2011 1:43:38 PM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Hi Bob, Thanks for this post...

    I need to use "NotesFactory.createSession(host, user, pass);" in C#, but I can't find it.

    I search in notes.dll, but I don't find it.

    (using C:>dumpbin.exe /exports nnotes.dll >nnotes.txt)

    Where is this class?

    Thanks in advance,

    Domingo

    5Bob Balaban  8/7/2011 5:58:28 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    The NotesFactory class is Java only, you can find it in notes.jar and/or ncso.jar

    6Domingo Trujillo  8/7/2011 8:59:05 PM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Thank very much... I will mix java and C# - :(

    7saikalyan  7/11/2012 12:11:09 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Hi Bob,

    Thanks for the post.

    It was very useful to get guided in the right direction for me.

    I just have a clarification of the what object type is filelist in the code.

    Thanks in advance

    8T. Fabre  8/25/2012 8:08:28 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Hello,

    First time here, but I was really compelled to leave a comment.

    First off, thanks a lot for your example which saved me a lot of trouble and headaches !

    However, your code works "in theory", as long as the database is local, but connecting to a remote server is quite painful... I had to adjust the signature of NSFDbOpen to use an IntPtr instead of a string, so that I could pass in an IntPtr returned from OSPathNetConstruct (code is VB.NET, sorry, and some names are changed since I had to convert from C#, but you'll get the idea), as it seems impossible to get a string back from this one :

    <DllImport("nnotes.dll")>

    Public Shared Function OSPathNetConstruct(port As String, serverName As String, fileName As String, retPathName As IntPtr) As Status

    End Function

    <DllImport("nnotes.dll")>

    Public Shared Function NSFDbOpen(nsfPath As IntPtr, ByRef dbHandle As Handle) As Status

    End Function

    Private Const Null As Bool = 0

    Private Const ForceUpdate As Bool = 1

    Private Const InPlaceDecryptAttachements As Bool = 1

    Private Const KeepPrivate As String = "$KeepPrivate"

    Public Sub ConvertToMIME(session As NotesSession, database As NotesDatabase, documentId As String)

    Dim databaseHandle As Handle = Null

    Dim notesHandle As Handle = Null

    Dim ccHandle As Handle = Null

    Dim status As Status = Null

    Dim nsfPath As IntPtr = Marshal.AllocCoTaskMem(1024)

    Dim docId As UInteger = 0

    status = NotesEntries.OSPathNetConstruct(String.Empty, "myServerName", database.FilePath, nsfPath)

    status = NotesEntries.NSFDbOpen(nsfPath, databaseHandle)

    docId = System.Convert.ToUInt32(documentId, 16)

    status = NotesEntries.NSFNoteOpenExt(DatabaseHandle, docId, NotesEntries.OpenRawMime, notesHandle)

    status = NotesEntries.NSFItemDelete(notesHandle, KeepPrivate, System.Convert.ToUInt16(KeepPrivate.Length))

    If (NotesEntries.NSFNoteHasMIME(notesHandle) Or NotesEntries.NSFNoteHasMIMEPart(notesHandle)) Then

    NotesEntries.MMCreateConvControls(ccHandle)

    NotesEntries.MMSetMessageContentEncoding(ccHandle, 2)

    If (NotesEntries.MIMEConvertCDParts(notesHandle, 0, 0, ccHandle) = Null) Then

    NotesEntries.NSFNoteUpdate(notesHandle, ForceUpdate)

    End If

    NotesEntries.MMDestroyConvControls(ccHandle)

    End If

    status = NotesEntries.NSFNoteClose(notesHandle)

    status = NotesEntries.NSFDbClose(DatabaseHandle)

    Marshal.FreeCoTaskMem(nsfPath)

    End Sub

    Additionnally, the conversion process is "destructive" (some fonts and layout get slightly modified), so I found it quite useful to create an exact copy of the original document, and convert the copy, which can be deleted after processing it, so that the original document stays intact.

    Apart from that, great job, thanks a lot !

    9T. Fabre  8/25/2012 9:33:30 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Ooops, there's an error in my comment...

    This line :

    If (NotesEntries.NSFNoteHasMIME(notesHandle) Or NotesEntries.NSFNoteHasMIMEPart(notesHandle)) Then

    Should read :

    If (NotesEntries.NSFNoteHasMIME(notesHandle) = 0 And NotesEntries.NSFNoteHasMIMEPart(notesHandle) = 0) Then

    My apologies...

    10Bob Balaban  8/26/2012 6:19:39 PM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Your points are completely valid, thanks for posting some updates! There is a much more convenient workaround I'll mention, although it isn't strictly speaking "correct":

    Instead of using OSPathNetConstruct(), you can pre-format the server/db path as follows in simpler code -- e.g., + "!!" + . This is all that OSPathNetConstruct does anyway

    Thanks again for contributing to the community!

    11Jignesh  3/8/2013 12:40:20 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Hi,

    Thanks for such a nice post.

    I have implement your both cs file in my console c# application.

    I am going to read MIME using given ConvertToMIME function in your cs file.

    But i got exception in line

    stat = NotesEntries.NSFNoteOpenExt(this.m_hDb, nid, NotesEntries.OPEN_RAW_MIME, ref hNote);

    Saying:

    "A first chance exception of type 'System.AccessViolationException' occurred in UsingCAPI.exe

    Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

    Do you have any idea about this exception..

    Thanks & Regards,

    Jignesh

    12Jignesh  3/8/2013 2:47:02 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    Will you please explain in detail about code:

    // acquire your list of documents to convert here

    for (i = 0; i < filelist.Length; i++)

    {

    if (ConvertOneDoc(session, nsf, filelist))

    count++;

    } // end

    13Bob Balaban  3/16/2013 10:34:45 AM  Geek-O-Terica 16: Easy conversion of Notes documents to MIME format (Part 2)

    That just means that your application decides what documents you want to convert. Implement some logic to assemble a list