Lotus Notes 6.5 Domino COM API and extracting Attachments

This is definitely an expert question. Please let me know if I need to post this to a different discussion group and which one that would be. Thanks.

Does anyone know of a rock-solid approach to extracting attachments from Notes using the COM API?  Maybe the answer is to NOT use the COM API? We have tried the following:


a)       The following approach intermittently has a null EmbeddedObject.  The biggest problem with this approach is that I have no way to match the externalFileNames (attachment name shown in viewer) with the internal names (pName required by NotesDocument.GetAttachment()).

·          Get the internal attachment names from the lotus script @AttachmentName

·          Iterate and call the NotesDocument.GetAttachment(internalAttachmentName) to get a Domino.NotesEmbeddedObject

·          Check the type of NotesEmbeddedObject to ensure it is a Domino.EMBED_TYPE.EMBED_ATTACHMENT

·          Then call EmbeddedObject.ExtractFile(externalFileName);

b)       This approach has not been stable.  The Navigator intermittently blows up; running the same test again may or may not work (can’t be attributed to a certain item)

·         Get the body: Domino.NotesItem bodyItem = _dominoNotesDocument.GetFirstItem("Body");

·         Check to ensure body is not null and is Domino.IT_TYPE.RICHTEXT

·         Create a NotesRichTextItem:  _dominoNotesDocument.CreateRichTextItem(TEMP_ITEM_NAME)

·         Append the body to the NotesRichTextItem: rti.AppendRTItem((Domino.NotesRichTextItem)bodyItem)

·         Save the Document: _dominoNotesDocument.Save(true, false, false);

·         Create a Navigator: Domino.NotesRichTextNavigator navigator = rti.CreateNavigator()

·         if(navigator.FindFirstElement(Domino.RT_ELEM.RTELEM_TYPE_FILEATTACHMENT))

·         do

·         {

·           Domino.NotesEmbeddedObject embeddedObj = navigator.GetElement()

·           embeddedObj.ExtractFile(externalFileName);

·         }

·         while(navigator.FindNextElement(Domino.RT_ELEM.RTELEM_TYPE_FILEATTACHMENT, 1));

c)       This approach blows up intermittently when attempting to iterate the NoteRichTextItem.EmbeddedObject array

·         Get the body: Domino.NotesItem bodyItem = _dominoNotesDocument.GetFirstItem("Body");

·         Check to ensure body is not null and is Domino.IT_TYPE.RICHTEXT

·         Create a NotesRichTextItem:  _dominoNotesDocument.CreateRichTextItem(TEMP_ITEM_NAME)

·         Append the body to the NotesRichTextItem: rti.AppendRTItem((Domino.NotesRichTextItem)bodyItem)

·         Save the Document: _dominoNotesDocument.Save(true, false, false);

·         object[] embeddedObjects = (object[])rti.EmbeddedObjects;

·         //check null for embeddedObjects

·         foreach(Domino.NotesEmbeddedObject embeddedObj in (System.Array)rti.EmbeddedObjects)

·         {

·         if (embeddedObj.type == Domino.EMBED_TYPE.EMBED_ATTACHMENT)

·         {

·           embeddedObj.ExtractFile(externalFileName);

·         }

2)       How can we best match up the Attachment internal names with the external names (that display in the viewer)?  I can get the internal names from the @AttachmentNames lotus script call.  Ex. _dominoNotesDocument.ParentDatabase.Parent.Evaluate("@AttachmentNames", _dominoNotesDocument);.  But I need to extract the attachment to disk with the external name.

a)       I have exported DXL for documents and looked for the “attachmentref” attribute. This is the only way I have found to get the mapping between internal and external attachment names.  Unfortuneately, this is extremely slow and doesn’t work for all documents (occaissionally throws exception on certain documents.

Thanks in advance for any feedback!

Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Use notesDocument.getAttachment (which returns a notesEmbeddedObject) instead of looping through the embeddedObjects properties.  GetAttachment requires that you know the name of teh file, but there are two ways around that:

1) Use notesDocument.getFirsttem("$File"), which is the object reference data.  The returned notesItem has property notesIte.text, which will be the file name.  There can be multiple $File items, so delete each one after you process its correspodning getAttachment, by using notesItem.remove.  As long as you don't notesDocument.save, it will have no lasting effect, but will allow you to try to get .getFirstItem("$File") again (returning the next $File instead of the same one again)

2) Use notesSession.Evaluate("@AttachmentNames",notesDocument), which will return an array of file names to use with notesDocument.getAttachment
So, some basic code

Dim s as new NotesSession


Dim doc as notesDocument
Set doc = ...

Dim fileNames, index as integer, obj as notesEmbeddedObject
fileName = s..Evaluate("@AttachmentNames",doc)
For index = lbound(filenames) to uBound(fileNames)
  set obj = doc.getAttachment(fileNames(index))
  obj.extractFile( ... )
jhiebAuthor Commented:
We have tried this approach, but it gives us the internal names for the attachments, we need to get the names as they are displayed in the Notes API.
If there a reliable way to get the display name once we have the embedded object?
Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

Whoa, whoa whoa!!!!

That ***IS*** all COM API!

The value retruned by @AttachmentNames is valid for GetAttachments.  I've never, ever seen it otherwise.
jhiebAuthor Commented:
Opps, I meant Notes UI. The value from @AttachmentNames  is valid for GetAttachments, but is not the same name displayed for the attachment when the document is opened with the Notes UI Application.
Ah, normally it is teh same name.  In some older versions of Notes, if you attached a duplicate file name, it assigned a random internal name to the additional attachment.  It no longer does that.  If you have an example where it displays one thing but stores another, I wuld like to see it*.

* One caveat.  Attachment icons don't really have to display a file name.  The filename of the icon is essentially static.  It is not built at display time; it is inserted at edit/insert attachment time.  The Notes client, by convention, creates a bitmap merging the associated program's primary icon together with a rendered bitmap of the file's name.  Theoretically, the API could be used to construct an entirely different bitmap, including a bitmap with an incorrect filename.  It also does not ave to be entirely a bitmap - it could contain text within the "attachment hotspot."  If you use WinZip to zip-and-mail, the API does precisely that.  Whle you can't modiy the text directly at that point, you could search-and-replace or spell-check-and-correct it.

So, since this is static text, if it ever is modified -- e.g., by that search and replace fr externally created attachments -- then the user could se random nonsense in teh UI.  That has nothing whatsoever to do with the actua file, and there is no reasonable way to programmatically determine what that text is.
But even with that caveta, it is rare to come across a dispay name next to the icon that does not match the internal name.  An even if it did, you would only be doing the same thing teh Notes client does.  If yuo click on such an attachment, Notes suggests teh internal name in the prperties box or in the file-save dialog. Not the disaplyed value.  Though both usually match.

Again, please send me an eample of a document that you have seen behave differently.
jhiebAuthor Commented:
Unfortunately the approaches you refer to are techniques to get the “internal name”.  We need to be able to retrieve the external name.  We tried #1 and it returned null.
***Bangs head on wall three times***


Where on earth are you picking up this concept of "internal name" and "external name?"  There is only one name.  Chalenge: show me a document we there is an attachment that has these "two names" (internal/external), and that they are different.

As I've said before, I think you are just confused.

Oh, another caveat.  SOme "attachments" are not really attachments.  OLE instances are always stored as four file attachments, but teh attachments are meaningless except to the Notes client.
jhiebAuthor Commented:

Yes we are confused … confused why the COM API isn’t stable.

Look at the code we listed above and see if you can elaborate more. I have nothing to send you so if you are unable to elaborate any further then unfortunately you aren't able to help me. By looking at our code you can see we are seasoned programmers. We are working with the Com API. If you can elaborate further then provide us more detail. If not, thanks anyway for your help. I appreciate the help you have given. We may have to figure this out on our own. I look forward to your response. If you prefer to give up on this one I understand. Happy Thanksgiving.


Rather than using @AttachementNames or Name property, use Source property of the embeddedobject.

This will give you exact name of the attachment.

For further information.. http://www-1.ibm.com/support/docview.wss?rs=899&uid=swg21094308


I'm pretty certain I can solve your problem.  And if I can't, I'm not sure anyone can.  I'm not being conceited here... Hemantha, Zvonko, and I are the most knowledgable people here on all things Notes... and among the most knowledgable people in the world on this subject.

I'm gong to give you some background on the APIs, and the file attachment system.  I'm also going to give some analysis of the comments you have made.

There are three Notes APIs:

1) Core API, which is a C API

2) C++ API, which is a layer wrapped around C API, with limited functionality

3) DOM (Domino Object Model)

The DOM is exposed:

1) As LotusScript classes
2) As OLE automation classes (which interface into the Notes client, and require the clinet to be open)
3) As COM classes, which require Notes or Domino installation, but do not load the client
4) As a JNI interface (Java calling locally installed native code)
5) As CORBA classes, for RPC over IIOP, with a set of Java CORBA classes provided

All 5 of these are layers on top of the same sets of DLL calls, though the 5 have slight variations.  The "instability" you describe -- by which I think you mena the NotesRichTextNavigator object -- is not in the COM API itself, but in the underlying DLLs.  The services provided by the RTF nav DLL code is brand new, complex, and has some bugs.  However, what you are doing does not require RTF nav!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  You are using the RTF navigator merely t find the attachments.  Since you have other ways of getting the attachments, there is no need to use RTF nav.

Further, you have this notion of "viewer" attachment name.  If you ask the entire Notes/Domino development team what "viewer" means, you will hear only two answers:

1) The attachment viewer that the client uses to allow users to see attachment contents without opening a separate application; these are Verity's KeyFile viewers

2) The "DXL viewer" utility in Designer (which exports a design element to a file and loads IE to view the file)

The attachment viewer does not care what the file name is. The DXL viewer uses a temporary file name.  You've invented a term.  I assumed you meant by viewer as follows: when the Notes client displays an attachment, it shows the file's name in the attachment icon.  So far as I know, that name is ALWAYS going to be the same as the file that was on disk, and the same name as returned by @AttachmentNames, and the same name in the $File item properties, and the same name in the NotesEmbeddedObject properties, and the same name acceptable to getAttachment.

Let me explain how the Notes client, and in fact any part of Notes, stores attachments.

Attachment files are NOT stored in a document. They are stored in special "notes."  A "note" is the way an API refers to any set of data collected in Notes -- a document,  a design element, the access control list, the database icon (which includes control flags for the database, its title, and many other things).  When you attach a file, it gets its own note, which stores unstructured data.  Most notes contain fields, but an attachment note is a BLOB.

How does the document reference its file?  It create an item named $File, with a special field type: embedded object reference.  It contains some bare information about the attachment note, e.g., name, size, and the note ID of the attachment data.  In early versions of Notes, that's al there was. You could not tell that a document even contained an atachment unless the form or view designer elected to display an indicator based on @Attachments.

More than one file could be included; Notes supports fields with duplicate names, with a "duplicate item sequence number" dfferentaiating then in the API.  In early versions of Notes, the designers did not program it well enough to handle attaching two file with the same file name.  So, they used a subterfuge: the API assigned a new file name to the duplicate.  Later versons sucessfully handle multiple files with the same name, with no conflict - it still used aninternally generated file name, but also stored the original file name.  THIS MAY BE WHAT YOU MEAN BY VIEWER FILE NAME -- the original file name that is stored alongside the generated file name.  GetAttachment will use the generated file name, while the client, when it dispays the properties box, or puts a file name in the ComDlg extract box, will use the original file name.

In Release 3, they changed this a bit.  The client always "embedded attachments in rich text fields."  It still did what I describd above... but with one difference.  As the file was being attached, the client created a bitmap to represent the file visually, and placed this bitmap in the rich text item.  The bitmap was a "hotspot" code to match one of teh $File items... it icludes the dup sequence number.  As teh client renders a document, if theer are any $File items taht DON'T have a hotspot, a display-time hotspot is added at the bottom of the form temporarily, so that the attachment is not hidden.

Now, what abou those original-versus-stored fle names?

If you run through all the items in a document, and for each $File item, check its item.value property, you will find it is a one-element array that contains the INTERNAL name.  @AttachmentNames returns the INTERNAL names.  If yo latch onto a NotesEmbeddedObject, its name property is the INTERNAL name, and its SOURCE property in teh internal name (though the documentation says SOURCE is the ORIGINAL name).

So, where is this original file name -- or what you are calling the external name -- stored?  I am not entirely certain.  It may be stored in the $File item (field).  It may be stored with the file's content note.  It may just be an attribute of the rich text hotspot.  Either way, the DOM does not provide any known way of latching onto it.

What about the other APIs?

I really don't know much about the C++ API.

The C API does containin some confusing information about it.  There are two functions used for attaching files: NSFNoteAttachFile, and NSFItemAppendObject.  AttachNote is simpler, and I believe it builds on AppendObject.  Both are pretty cryptic in description:

      NOTEHANDLE  note_handle,
      char far *item_name,
      WORD  item_name_length,
      char far *file_name,
      char far *orig_path_name,
      WORD  encoding_type);

file_name  -  The address of a null-terminated file name string containing the fully qualified file path specification for file being attached.

orig_path_name  -  The address of a null-terminated string containing a filename that will be stored internally with the attachment, displayed with the atttachment icon when the document is viewed in the Notes user interface, and subsequently used when selecting which attachment to Extract or Detach  and what path to create for an Extracted file.  Note that these operations may be carried out  both from the workstation application Attachments dialog box and programmatically, so try to choose meaningful filenames as opposed to attach.001, attach002, etc., whenever possible.  If attaching mulitiple files that have the same filename but different content to a single document, make sure this variable is unique in each call to NSFNoteAttachFile().

file_name seems to be a string referencing the existing file on disk to be attached.  orig_path_name says it has to be unique, so that must be the internal name.  Noweher do we see a place to specify what original name should be stored; is the API figuring this out for itself?  (Irony: doc suggests using a meaningful name, but the dup filename replacement generator uses a completely random string for filename.)

I suspect the doc is out of date, BTW.  After all, it describes orig_file_path as also being the source for the icon text, and the path string constructor in the Extract UI and Attachments dialog.  But, if it is the unique internal name, then this would not be true!

From the "hotspot" sample that comes with teh API, it also isn't clear where the "external" name is placed.  It does a two-stage sequence -- NSFNoteAttachFile, followed by an update to the rich text field so the "file hotspot" is present (so it becomes R3-style instead of older style attachment).  In the example,
file_name points to a string with a full path specification .....\readme.txt
orig_path_name points to a string containing "attach.lnk"
The hot spot is built with a text string containing both attach.lnk and readme.txt.  I suspect that attach.lnk is just a reference so that the RTF hotspot can be associated with a sepcific $File (contradicting my earlier assertion that it is by dup item number).  The readme.txt may be the external name.  But that is sepculative.

Now, if I still had a C compiler, it would be trivial to run this program, maybe changing some of the text strings, to see what we get.  Unfortunately, I don't.

However, Ben Langiriichs does have a C compiler, a thorough knowledge of how rich text fields work, and -- get this -- an API for manipulating rich text, which includes, I believe, manipulating files.  So, you may just want to get hold of his Midas toolkit, and just use that.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Richie_SimonettiIT OperationsCommented:
I successfuly automated Lotus with COM from VB, if it is useful i could post the link.
jhiebAuthor Commented:
We're still reviewing your post and I'll try to get back with you tomorrow. Thanks for taking the time to put in so much detail. We might need to take this offline and share contact information.
My e-Mail is in my profile.
Richie_SimonettiIT OperationsCommented:
Ok. Good luck then.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Lotus IBM

From novice to tech pro — start learning today.