« ND6 Bible example: Save and Open Attachment code - PART 1 | Main| Is the "ubiquitous" Web going away? Or will standards save the day? »

ND6 Bible example: Save and Open Attachment code - PART 2

QuickImage   
Category
Bookmark : del.icio.us  Technorati  Digg This  Add To Furl  Add To YahooMyWeb  Add To Reddit  Add To NewsVine 


NOTE: This story is a continuation of ND6 Bible example: Save and Open Attachment code - PART 1, which can be viewed here.

In our last installment I introduced you to my Detach and Open feature, and how it works. This installment dives into the code itself.


Spelunking the code

The code is actually pretty simple for this. First, there is a field on the form called "Attachments" with the simple formula @AttachmentNames. The values in this field are used to check for multiple attachments, and as the choices for the dialog above if there is more than one attachment. This field is a Computed For Display (CFD) field.


The Detach and Open action calles an agent called DetachOpen. This agent loads a script library called LS.Utilities.FE (more on it later). The rest of the agent code looks like this....


The code:

Sub
Initialize
    On
Error Goto errHandler
    Dim
ws As New NotesUIWorkspace
    Dim
doc As NotesDocument
    Dim
attachname As Variant, attachnamelist As Variant
    Dim
retflg As Boolean
             

    Set
doc = ws.CurrentDocument.Document
   
attachnamelist = doc.Attachments
    If
Ubound(attachnamelist) = 0 And attachnamelist(0) = "" Then
            Error
1000, "No attachments in this document."
    End
If
    If
Ubound(attachnamelist) > 0 Then
           
attachname = ws.Prompt(PROMPT_OKCANCELLIST, "Attachment",_
           
"Choose the attachment to open...", attachnamelist(0), attachnamelist)
            If
Isempty(attachname) Then Exit Sub
    Else
           
attachname = attachnamelist(0)
    End
If
             

   
REM detachFileOpen(doc As NotesDocument, rtname As String, fname As String) As Boolean
   
retflg = detachFileOpen(doc, "Files", Cstr(attachname))
             
getOut
:
Exit
Sub

errHandler
:
    Select
Case Err
    Case
1000
            Msgbox
Error$,,"Error"
    Case
Else
            Msgbox
Error$ & " (" & Err & ") [in DetachOpen agent]",,"Unhandled Error"
    End
Select
    Resume
getOut
End
Sub

So, all this code really does is figure out if I need to prompt the user to choose an attachment (if there is more than one), get the file name, then hand it off to the detachFileOpen function, which comes from the LS.Utilities.FE (FE for "front-end") script library.


The LS.Utilities.FE library loads the LS.Utilities.BE (BE for "back-end") script library, which contains a collection of useful back-end (i.e. no UI) functions (incidentally, a version of this library is downloadable from
here). The detachFileOpen function in the LS.Utilities.FE library looks like this:

Function
detachFileOpen(doc As NotesDocument, rtname As String, fname As String) As Boolean
    On
Error Goto errHandler
   
detachFileOpen = True
    Dim
ws As New NotesUIWorkspace
    Dim
rtitem As NotesRichTextItem
    Dim
fpath As Variant
    Dim
retflg As Boolean, res As Long, res2 As Integer
    Dim
exepath As String, shellpath As String
             

   
REM this holds the executable path
   
exepath = String(MAX_FILENAME_LEN, 32)
             

   
REM prompt the user for where to save the attachment
   
fpath = ws.SaveFileDialog(False, "Save Attachment and Open", "", "", fname)
    If
Isempty(fpath) Then
           
detachFileOpen = False
            Exit
Function
    End
If
             

   
REM detach the file (this function is from the LS.utilities.BE lib)
   
retflg = detachFile(doc, rtname, fname, Cstr(fpath(0)))
             

   
REM use the C API call to find the associated executable
   
res = FindExecutable(Cstr(fpath(0)), "", exepath)
             

   
REM shorten the executable, removing the extra spaces
   
exepath = Left$(exepath, Instr(exepath, Chr$(0)) - 1)
             

   
REM prep the string to pass to the shell command
    If
Fulltrim(exepath) = "" Then Error 1000, "Unable to locate associated program"
             

   
shellpath = exepath & | "| & fpath(0) & |"|
             

   
REM open the file with the associated executable
   
res2 = Shell(shellpath)
             
getOut
:
    Exit
Function
errHandler
:
    On
Error Goto 0
    Error
Err, "(" & Err & ") " & Error$ & " [in " & Lsi_info(2) & "]"
    Resume
getOut
End
Function

This function prompts the user to find out the location where she wants to save the file, and then saves the file there using the detachFile function (which is from the LS.Utilities.BE library - and I'm not covering that function today, it is pretty straightforward). It then hands the filepath to a Win32 C API function called FindExecutable, which is declared in the Declarations of the FE library, like so:


(
Declarations)
REM this C API call is used to find the executable associated with a file
REM this function is used by the detachFileOpen function

Const
MAX_FILENAME_LEN = 260
Declare
Function FindExecutable Lib "shell32.dll" Alias "FindExecutableA" _
(Byval
lpFile As String, Byval lpDirectory As String, Byval lpResult As String) As Long

I discovered how to declare this function using the wonderful (and free) C API Guide (read more about it
here). This function figures out the associated program executable path from the file extension of the provided file path.

Then I simply take that executable path, trim it, and build a string containing the filepath and the executable path that I can hand to the Shell function. Some of you are asking, "Rock, you idiot! You can just hand the filepath itself to the shell function!" Wrong-o, my simpleton friend. The Shell function only works with executables, not with files. Therefore we need to hand it a string in the form of


EXEpath "FilePath"


This format tells the executable to open and launch the provided file. BTW, it is important to include the double-quotes around the filepath, which is what this line does in my function:


shellpath
= exepath & | "| & fpath(0) & |"|

Conclusion

I encourage you to download the example and look through it. It provides simple yet useful functionality that can be easily integrated into any application. It also demonstrates proper error handling, chaining of script libraries, and C API calls.


If you have any questions, or want to share kewl stuff you've done, please post them here!


Rock

**Good thing the guy's name was Henry Ford and not Henry Anal.  Otherwise, a half million Americans would be driving Anal Probes.

Comments

1 - OOHHHH, kewl! I gotta try that. Thanks for sharing, Nik!

Rock

2 - As an alternative, I've used the following code on newer versions of Windows to launch documents:

Shell( Environ$( "ComSpec" ) & " /C """ & path$ & """" )

Meet Rocky

Rock - February 2010
Rocky Oliver
If you see me at a conference, please stop me and say hi!

Calendar

Search

Categories

Proudly Employed By

Wofkflow Studios

LOTUS GEEK gear

Social Networking


Add to Technorati Favorites

View Rocky Oliver's profile on LinkedIn

Rocky  Oliver

LotusGeek Blog Roll

Why display a blog roll when Planet Lotus does it so much better?

Dilbert

Buy my book!

Blog Buttons

Atheist - Unitarian - Humanist

Poker Players Alliance