ShellExecute and OpenAs

improved by Tim Townsend

Documents can be associated with executables, Windows recognizes a document by its file extention.
Thanks to associations, the ShellExecute procedure knows how to "open" a document and the Explorer knows which icon to draw next to a document.
If a document does not have an association yet and you choose "open" in the Explorer, you will be presented a "Open As" dialog where you can choose an application. The following example shows how to do this in Progress.
The procedure first tries to open (or print) the document. If this fails the first time, it will try a second time using the OpenAs dialog.

/*--------------------------------------------------------------------------
       File        : open-doc.p
       Purpose     : Open a windows document using the associated application.
                     If no assocoated application, run the OpenAs dialog to
                     allow the user to pick an application.
 
       Syntax      :
 
       Description :
 
       Author(s)   : TWT
       Created     : 22 Dec 1999
       Notes       :
--------------------------------------------------------------------------*/
 
DEFINE INPUT        PARAM cFileName            AS CHARACTER        NO-UNDO.
DEFINE INPUT        PARAM cParams              AS CHARACTER        NO-UNDO.
DEFINE INPUT        PARAM cDirectory           AS CHARACTER        NO-UNDO.
DEFINE INPUT        PARAM lPrint               AS LOG         NO-UNDO.
 
&SCOPED-DEFINE SE_ERR_NOASSOC 31
&SCOPED-DEFINE SE_ERR_ASSOCINCOMPLETE 27
 
DEFINE VARIABLE iInstance            AS INTEGER                        NO-UNDO.
DEFINE VARIABLE cWorkDirectory       AS CHARACTER                       NO-UNDO.
 
/* in case parameter cDirectory contains a relative path 
   it has to be replaced by a fully-qualified path: */
 
ASSIGN FILE-INFO:FILE-NAME = cDirectory.
IF FILE-INFO:FULL-PATHNAME > "" THEN
  cWorkDirectory = FILE-INFO:FULL-PATHNAME.
 
/* try to execute the document: */
 
RUN ShellExecuteA(INPUT 0,
                  INPUT (IF lPrint THEN "print":u ELSE "open":u),
                  INPUT cFileName,
                  INPUT cParams,
                  INPUT cWorkDirectory,
                  INPUT 1,  /* normal mode */
                  OUTPUT iInstance).
 
/* if no associated application, run OpenAs dialog: */
 
IF (iInstance = {&SE_ERR_NOASSOC} OR 
    iInstance = {&SE_ERR_ASSOCINCOMPLETE}) 
   AND NOT lPrint THEN DO:
 
   /* Ignore cParams because cFileName is a document.
      cParams is only valid with executables */
   RUN ShellExecuteA (INPUT 0,
                      INPUT "open":u,
                      INPUT "rundll32.exe":u,
                      INPUT "shell32.dll,OpenAs_RunDLL ":u + cFileName,
                      INPUT cWorkDirectory,
                      INPUT 1,
                      OUTPUT iInstance).
END.  /* if */
 
/* test for error: */
 
RUN TestErrorCode(iInstance).
IF RETURN-VALUE > "" THEN
  MESSAGE RETURN-VALUE
    VIEW-AS ALERT-BOX ERROR BUTTON OK.
 
/****************************************************************************/
 
PROCEDURE ShellExecuteA EXTERNAL "shell32":U :
  DEFINE INPUT PARAMETER HWND         AS LONG.
  DEFINE INPUT PARAMETER lpOperation  AS CHARACTER.
  DEFINE INPUT PARAMETER lpFile       AS CHARACTER.
  DEFINE INPUT PARAMETER lpParameters AS CHARACTER.
  DEFINE INPUT PARAMETER lpDirectory  AS CHARACTER.
  DEFINE INPUT PARAMETER nShowCmd     AS LONG.
  DEFINE RETURN PARAMETER hInstance   AS LONG.
END PROCEDURE.
 
 
PROCEDURE TestErrorCode :
DEFINE INPUT PARAMETER iCode AS INTEGER.
DEFINE VARIABLE cTxt AS CHARACTER NO-UNDO.
 
IF iCode < 0 OR iCode > 32 THEN RETURN "". /* no error */
 
CASE iCode :
  WHEN  0 THEN cTxt = "The operating system is out of memory or resources.":T132.
  WHEN  2 THEN cTxt = "The specified file was not found":T132.
  WHEN  3 THEN cTxt = "The specified path was not found.":T132.
  WHEN  5 THEN cTxt = "The operating system denied access to the specified file.":T132.
  WHEN  8 THEN cTxt = "There was not enough memory to complete the operation.":T132.
  WHEN 10 THEN cTxt = "Wrong Windows version":T132.
  WHEN 11 THEN cTxt = "The .EXE file is invalid (non-Win32 .EXE or error in .EXE image).":T132.
  WHEN 12 THEN cTxt = "Application was designed for a different operating system.":T132.
  WHEN 13 THEN cTxt = "Application was designed for MS-DOS 4.0.":T132.
  WHEN 15 THEN cTxt = "Attempt to load a real-mode program.":T132.
  WHEN 16 THEN cTxt = "Attempt to load a second instance of an application with non-readonly data segments.":T132.
  WHEN 19 THEN cTxt = "Attempt to load a compressed application file.":T132.
  WHEN 20 THEN cTxt = "Dynamic-link library (DLL) file failure.":T132.
  WHEN 26 THEN cTxt = "A sharing violation occurred.":T132.
  WHEN 27 THEN cTxt = "The filename association is incomplete or invalid.":T132.
  WHEN 28 THEN cTxt = "The DDE transaction could not be completed because the request timed out.":T132.
  WHEN 29 THEN cTxt = "The DDE transaction failed.":T132.
  WHEN 30 THEN cTxt = "The DDE transaction could not be completed because other DDE transactions were being processed.":T132.
  WHEN 31 THEN cTxt = "There is no application associated with the given filename extension.":T132.
  WHEN 32 THEN cTxt = "The specified dynamic-link library was not found.":T132.
  OTHERWISE    cTxt = "Undocumented error code returned":T132.
END.
 
RETURN cTxt.
 
END PROCEDURE.

notes

This example by Tim Townsend replaces the old example, which was published here until 5 januari 2000. For those who still use a copy of the old source: this new one is better because you can now pass a URL, like "www.progress.com", to the cFilename parameter.