home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / apps.c < prev    next >
C/C++ Source or Header  |  2002-08-11  |  74KB  |  2,142 lines

  1.  
  2. /*
  3.  *@@sourcefile apps.c:
  4.  *      contains program helpers (environments, application start).
  5.  *
  6.  *      This file is new with V0.9.12 and contains functions
  7.  *      previously in winh.c and dosh2.c.
  8.  *
  9.  *      Note: Version numbering in this file relates to XWorkplace version
  10.  *            numbering.
  11.  *
  12.  *@@header "helpers\apps.h"
  13.  *@@added V0.9.12 (2001-05-26) [umoeller]
  14.  */
  15.  
  16. /*
  17.  *      Copyright (C) 1997-2002 Ulrich Möller.
  18.  *      This file is part of the "XWorkplace helpers" source package.
  19.  *      This is free software; you can redistribute it and/or modify
  20.  *      it under the terms of the GNU General Public License as published
  21.  *      by the Free Software Foundation, in version 2 as it comes in the
  22.  *      "COPYING" file of the XWorkplace main distribution.
  23.  *      This program is distributed in the hope that it will be useful,
  24.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26.  *      GNU General Public License for more details.
  27.  */
  28.  
  29. #define OS2EMX_PLAIN_CHAR
  30.     // this is needed for "os2emx.h"; if this is defined,
  31.     // emx will define PSZ as _signed_ char, otherwise
  32.     // as unsigned char
  33.  
  34. #define INCL_DOSPROCESS
  35. #define INCL_DOSEXCEPTIONS
  36. #define INCL_DOSMODULEMGR
  37. #define INCL_DOSSESMGR
  38. #define INCL_DOSERRORS
  39.  
  40. #define INCL_WINPROGRAMLIST     // needed for PROGDETAILS, wppgm.h
  41. #define INCL_WINSHELLDATA
  42. #define INCL_WINERRORS
  43. #define INCL_SHLERRORS
  44. #include <os2.h>
  45.  
  46. #include <stdio.h>
  47. #include <setjmp.h>             // needed for except.h
  48. #include <assert.h>             // needed for except.h
  49.  
  50. #include "setup.h"                      // code generation and debugging options
  51.  
  52. #include "helpers\dosh.h"
  53. #include "helpers\except.h"             // exception handling
  54. #include "helpers\prfh.h"
  55. #include "helpers\standards.h"          // some standard macros
  56. #include "helpers\stringh.h"
  57. #include "helpers\winh.h"
  58. #include "helpers\xstring.h"
  59.  
  60. #include "helpers\apps.h"
  61.  
  62. /*
  63.  *@@category: Helpers\PM helpers\Application helpers
  64.  */
  65.  
  66. /* ******************************************************************
  67.  *
  68.  *   Environment helpers
  69.  *
  70.  ********************************************************************/
  71.  
  72. /*
  73.  *@@ appQueryEnvironmentLen:
  74.  *      returns the total length of the passed in environment
  75.  *      string buffer, including the terminating two null bytes.
  76.  *
  77.  *@@added V0.9.16 (2002-01-09) [umoeller]
  78.  */
  79.  
  80. ULONG appQueryEnvironmentLen(PCSZ pcszEnvironment)
  81. {
  82.     ULONG   cbEnvironment = 0;
  83.     if (pcszEnvironment)
  84.     {
  85.         PCSZ    pVarThis = pcszEnvironment;
  86.         // go thru the environment strings; last one has two null bytes
  87.         while (*pVarThis)
  88.         {
  89.             ULONG ulLenThis = strlen(pVarThis) + 1;
  90.             cbEnvironment += ulLenThis;
  91.             pVarThis += ulLenThis;
  92.         }
  93.  
  94.         cbEnvironment++;        // last null byte
  95.     }
  96.  
  97.     return cbEnvironment;
  98. }
  99.  
  100. /*
  101.  *@@ appParseEnvironment:
  102.  *      this takes one of those ugly environment strings
  103.  *      as used by DosStartSession and WinStartApp (with
  104.  *      lots of zero-terminated strings one after another
  105.  *      and a duplicate zero byte as a terminator) as
  106.  *      input and splits it into an array of separate
  107.  *      strings in pEnv.
  108.  *
  109.  *      The newly allocated strings are stored in in
  110.  *      pEnv->papszVars. The array count is stored in
  111.  *      pEnv->cVars.
  112.  *
  113.  *      Each environment variable will be copied into
  114.  *      one newly allocated string in the array. Use
  115.  *      appFreeEnvironment to free the memory allocated
  116.  *      by this function.
  117.  *
  118.  *      Use the following code to browse thru the array:
  119.  +
  120.  +          DOSENVIRONMENT Env = {0};
  121.  +          if (appParseEnvironment(pszEnv,
  122.  +                                   &Env)
  123.  +                  == NO_ERROR)
  124.  +          {
  125.  +              if (Env.papszVars)
  126.  +              {
  127.  +                  PSZ *ppszThis = Env.papszVars;
  128.  +                  for (ul = 0;
  129.  +                       ul < Env.cVars;
  130.  +                       ul++)
  131.  +                  {
  132.  +                      PSZ pszThis = *ppszThis;
  133.  +                      // pszThis now has something like PATH=C:\TEMP
  134.  +                      // ...
  135.  +                      // next environment string
  136.  +                      ppszThis++;
  137.  +                  }
  138.  +              }
  139.  +              appFreeEnvironment(&Env);
  140.  +          }
  141.  *
  142.  *@@added V0.9.4 (2000-08-02) [umoeller]
  143.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  144.  */
  145.  
  146. APIRET appParseEnvironment(const char *pcszEnv,
  147.                            PDOSENVIRONMENT pEnv)        // out: new environment
  148. {
  149.     APIRET arc = NO_ERROR;
  150.     if (!pcszEnv)
  151.         arc = ERROR_INVALID_PARAMETER;
  152.     else
  153.     {
  154.         PSZ     pszVarThis = (PSZ)pcszEnv;
  155.         ULONG   cVars = 0;
  156.         // count strings
  157.         while (*pszVarThis)
  158.         {
  159.             cVars++;
  160.             pszVarThis += strlen(pszVarThis) + 1;
  161.         }
  162.  
  163.         pEnv->cVars = 0;
  164.         pEnv->papszVars = 0;
  165.  
  166.         if (cVars)
  167.         {
  168.             ULONG cbArray = sizeof(PSZ) * cVars;
  169.             PSZ *papsz;
  170.             if (!(papsz = (PSZ*)malloc(cbArray)))
  171.                 arc = ERROR_NOT_ENOUGH_MEMORY;
  172.             else
  173.             {
  174.                 PSZ *ppszTarget = papsz;
  175.                 memset(papsz, 0, cbArray);
  176.                 pszVarThis = (PSZ)pcszEnv;
  177.                 while (*pszVarThis)
  178.                 {
  179.                     ULONG ulThisLen;
  180.                     if (!(*ppszTarget = strhdup(pszVarThis, &ulThisLen)))
  181.                     {
  182.                         arc = ERROR_NOT_ENOUGH_MEMORY;
  183.                         break;
  184.                     }
  185.                     (pEnv->cVars)++;
  186.                     ppszTarget++;
  187.                     pszVarThis += ulThisLen + 1;
  188.                 }
  189.  
  190.                 pEnv->papszVars = papsz;
  191.             }
  192.         }
  193.     }
  194.  
  195.     return arc;
  196. }
  197.  
  198. /*
  199.  *@@ appGetEnvironment:
  200.  *      calls appParseEnvironment for the current
  201.  *      process environment, which is retrieved from
  202.  *      the info blocks.
  203.  *
  204.  *      Returns:
  205.  *
  206.  *      --  NO_ERROR:
  207.  *
  208.  *      --  ERROR_INVALID_PARAMETER
  209.  *
  210.  *      --  ERROR_BAD_ENVIRONMENT: no environment found in
  211.  *          info blocks.
  212.  *
  213.  *@@added V0.9.4 (2000-07-19) [umoeller]
  214.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  215.  */
  216.  
  217. APIRET appGetEnvironment(PDOSENVIRONMENT pEnv)
  218. {
  219.     APIRET  arc = NO_ERROR;
  220.     if (!pEnv)
  221.         arc = ERROR_INVALID_PARAMETER;
  222.     else
  223.     {
  224.         PTIB    ptib = 0;
  225.         PPIB    ppib = 0;
  226.         arc = DosGetInfoBlocks(&ptib, &ppib);
  227.         if (arc == NO_ERROR)
  228.         {
  229.             PSZ pszEnv;
  230.             if (pszEnv = ppib->pib_pchenv)
  231.                 arc = appParseEnvironment(pszEnv, pEnv);
  232.             else
  233.                 arc = ERROR_BAD_ENVIRONMENT;
  234.         }
  235.     }
  236.  
  237.     return arc;
  238. }
  239.  
  240. /*
  241.  *@@ appFindEnvironmentVar:
  242.  *      returns the PSZ* in the pEnv->papszVars array
  243.  *      which specifies the environment variable in pszVarName.
  244.  *
  245.  *      With pszVarName, you can either specify the variable
  246.  *      name only ("VARNAME") or a full environment string
  247.  *      ("VARNAME=BLAH"). In any case, only the variable name
  248.  *      is compared.
  249.  *
  250.  *      Returns NULL if no such variable name was found in
  251.  *      the array.
  252.  *
  253.  *@@added V0.9.4 (2000-07-19) [umoeller]
  254.  *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
  255.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  256.  *@@changed V0.9.16 (2002-01-01) [umoeller]: removed extra heap allocation
  257.  */
  258.  
  259. PSZ* appFindEnvironmentVar(PDOSENVIRONMENT pEnv,
  260.                            PSZ pszVarName)
  261. {
  262.     PSZ     *ppszRet = 0;
  263.  
  264.     if (    (pEnv)
  265.          && (pEnv->papszVars)
  266.          && (pszVarName)
  267.        )
  268.     {
  269.         ULONG   ul = 0;
  270.         ULONG   ulVarNameLen = 0;
  271.  
  272.         PSZ     pFirstEqual;
  273.         // rewrote all the following for speed V0.9.16 (2002-01-01) [umoeller]
  274.         if (pFirstEqual = strchr(pszVarName, '='))
  275.             // VAR=VALUE
  276.             //    ^ pFirstEqual
  277.             ulVarNameLen = pFirstEqual - pszVarName;
  278.         else
  279.             ulVarNameLen = strlen(pszVarName);
  280.  
  281.         for (ul = 0;
  282.              ul < pEnv->cVars;
  283.              ul++)
  284.         {
  285.             PSZ pszThis = pEnv->papszVars[ul];
  286.             if (pFirstEqual = strchr(pszThis, '='))
  287.             {
  288.                 ULONG ulLenThis = pFirstEqual - pszThis;
  289.                 if (    (ulLenThis == ulVarNameLen)
  290.                      && (!memicmp(pszThis,
  291.                                   pszVarName,
  292.                                   ulVarNameLen))
  293.                    )
  294.                 {
  295.                     ppszRet = &pEnv->papszVars[ul];
  296.                     break;
  297.                 }
  298.             }
  299.         }
  300.     }
  301.  
  302.     return ppszRet;
  303. }
  304.  
  305. /*
  306.  *@@ appSetEnvironmentVar:
  307.  *      sets an environment variable in the specified
  308.  *      environment, which must have been initialized
  309.  *      using appGetEnvironment first.
  310.  *
  311.  *      pszNewEnv must be a full environment string
  312.  *      in the form "VARNAME=VALUE".
  313.  *
  314.  *      If "VARNAME" has already been set to something
  315.  *      in the string array in pEnv, that array item
  316.  *      is replaced.
  317.  *
  318.  *      OTOH, if "VARNAME" has not been set yet, a new
  319.  *      item is added to the array, and pEnv->cVars is
  320.  *      raised by one. In that case, fAddFirst determines
  321.  *      whether the new array item is added to the front
  322.  *      or the tail of the environment list.
  323.  *
  324.  *@@added V0.9.4 (2000-07-19) [umoeller]
  325.  *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
  326.  *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
  327.  *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed crash if !fAddFirst
  328.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  329.  */
  330.  
  331. APIRET appSetEnvironmentVar(PDOSENVIRONMENT pEnv,
  332.                             PSZ pszNewEnv,
  333.                             BOOL fAddFirst)
  334. {
  335.     APIRET  arc = NO_ERROR;
  336.     if ((!pEnv) || (!pszNewEnv))
  337.         arc = ERROR_INVALID_PARAMETER;
  338.     else
  339.     {
  340.         if (!pEnv->papszVars)
  341.         {
  342.             // no variables set yet:
  343.             pEnv->papszVars = (PSZ*)malloc(sizeof(PSZ));
  344.             pEnv->cVars = 1;
  345.  
  346.             *(pEnv->papszVars) = strdup(pszNewEnv);
  347.         }
  348.         else
  349.         {
  350.             PSZ *ppszEnvLine;
  351.             if (ppszEnvLine = appFindEnvironmentVar(pEnv, pszNewEnv))
  352.                 // was set already: replace
  353.                 arc = strhStore(ppszEnvLine,
  354.                                 pszNewEnv,
  355.                                 NULL);
  356.             else
  357.             {
  358.                 // not set already:
  359.                 PSZ *ppszNew = NULL;
  360.  
  361.                 // allocate new array, with one new entry
  362.                 // fixed V0.9.12 (2001-05-26) [umoeller], this crashed
  363.                 PSZ *papszNew;
  364.  
  365.                 if (!(papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1))))
  366.                     arc = ERROR_NOT_ENOUGH_MEMORY;
  367.                 else
  368.                 {
  369.                     if (fAddFirst)
  370.                     {
  371.                         // add as first entry:
  372.                         // overwrite first entry
  373.                         ppszNew = papszNew;
  374.                         // copy old entries
  375.                         memcpy(papszNew + 1,                // second new entry
  376.                                pEnv->papszVars,             // first old entry
  377.                                sizeof(PSZ) * pEnv->cVars);
  378.                     }
  379.                     else
  380.                     {
  381.                         // append at the tail:
  382.                         // overwrite last entry
  383.                         ppszNew = papszNew + pEnv->cVars;
  384.                         // copy old entries
  385.                         memcpy(papszNew,                    // first new entry
  386.                                pEnv->papszVars,             // first old entry
  387.                                sizeof(PSZ) * pEnv->cVars);
  388.                     }
  389.  
  390.                     free(pEnv->papszVars);      // was missing V0.9.12 (2001-05-21) [umoeller]
  391.                     pEnv->papszVars = papszNew;
  392.                     pEnv->cVars++;
  393.                     *ppszNew = strdup(pszNewEnv);
  394.                 }
  395.             }
  396.         }
  397.     }
  398.  
  399.     return arc;
  400. }
  401.  
  402. /*
  403.  *@@ appConvertEnvironment:
  404.  *      converts an environment initialized by appGetEnvironment
  405.  *      to the string format required by WinStartApp and DosExecPgm,
  406.  *      that is, one memory block is allocated in *ppszEnv and all
  407.  *      strings in pEnv->papszVars are copied to that block. Each
  408.  *      string is terminated with a null character; the last string
  409.  *      is terminated with two null characters.
  410.  *
  411.  *      Use free() to free the memory block allocated by this
  412.  *      function in *ppszEnv.
  413.  *
  414.  *@@added V0.9.4 (2000-07-19) [umoeller]
  415.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  416.  */
  417.  
  418. APIRET appConvertEnvironment(PDOSENVIRONMENT pEnv,
  419.                              PSZ *ppszEnv,     // out: environment string
  420.                              PULONG pulSize)  // out: size of block allocated in *ppszEnv; ptr can be NULL
  421. {
  422.     APIRET  arc = NO_ERROR;
  423.     if (    (!pEnv)
  424.          || (!pEnv->papszVars)
  425.        )
  426.         arc = ERROR_INVALID_PARAMETER;
  427.     else
  428.     {
  429.         // count memory needed for all strings
  430.         ULONG   cbNeeded = 0,
  431.                 ul = 0;
  432.         PSZ     *ppszThis = pEnv->papszVars;
  433.  
  434.         for (ul = 0;
  435.              ul < pEnv->cVars;
  436.              ul++)
  437.         {
  438.             cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
  439.  
  440.             // next environment string
  441.             ppszThis++;
  442.         }
  443.  
  444.         cbNeeded++;     // for another null terminator
  445.  
  446.         if (!(*ppszEnv = (PSZ)malloc(cbNeeded)))
  447.             arc = ERROR_NOT_ENOUGH_MEMORY;
  448.         else
  449.         {
  450.             PSZ     pTarget = *ppszEnv;
  451.             if (pulSize)
  452.                 *pulSize = cbNeeded;
  453.             ppszThis = pEnv->papszVars;
  454.  
  455.             // now copy each string
  456.             for (ul = 0;
  457.                  ul < pEnv->cVars;
  458.                  ul++)
  459.             {
  460.                 PSZ pSource = *ppszThis;
  461.  
  462.                 while ((*pTarget++ = *pSource++))
  463.                     ;
  464.  
  465.                 // *pTarget++ = 0;     // append null terminator per string
  466.  
  467.                 // next environment string
  468.                 ppszThis++;
  469.             }
  470.  
  471.             *pTarget++ = 0;     // append second null terminator
  472.         }
  473.     }
  474.  
  475.     return arc;
  476. }
  477.  
  478. /*
  479.  *@@ appFreeEnvironment:
  480.  *      frees memory allocated by appGetEnvironment.
  481.  *
  482.  *@@added V0.9.4 (2000-07-19) [umoeller]
  483.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
  484.  */
  485.  
  486. APIRET appFreeEnvironment(PDOSENVIRONMENT pEnv)
  487. {
  488.     APIRET  arc = NO_ERROR;
  489.     if (    (!pEnv)
  490.          || (!pEnv->papszVars)
  491.        )
  492.         arc = ERROR_INVALID_PARAMETER;
  493.     else
  494.     {
  495.         PSZ     *ppszThis = pEnv->papszVars;
  496.         PSZ     pszThis;
  497.         ULONG   ul = 0;
  498.  
  499.         for (ul = 0;
  500.              ul < pEnv->cVars;
  501.              ul++)
  502.         {
  503.             pszThis = *ppszThis;
  504.             free(pszThis);
  505.             // *ppszThis = NULL;
  506.             // next environment string
  507.             ppszThis++;
  508.         }
  509.  
  510.         free(pEnv->papszVars);
  511.         pEnv->cVars = 0;
  512.     }
  513.  
  514.     return arc;
  515. }
  516.  
  517. /* ******************************************************************
  518.  *
  519.  *   Application information
  520.  *
  521.  ********************************************************************/
  522.  
  523. /*
  524.  *@@ appQueryAppType:
  525.  *      returns the Control Program (Dos) and
  526.  *      Win* PROG_* application types for the
  527.  *      specified executable. Essentially, this
  528.  *      is a wrapper around DosQueryAppType.
  529.  *
  530.  *      pcszExecutable must be fully qualified.
  531.  *      You can use doshFindExecutable to qualify
  532.  *      it.
  533.  *
  534.  *      This returns the APIRET of DosQueryAppType.
  535.  *      If this is NO_ERROR; *pulDosAppType receives
  536.  *      the app type of DosQueryAppType. In addition,
  537.  *      *pulWinAppType is set to one of the following:
  538.  *
  539.  *      --  PROG_FULLSCREEN
  540.  *
  541.  *      --  PROG_PDD
  542.  *
  543.  *      --  PROG_VDD
  544.  *
  545.  *      --  PROG_DLL
  546.  *
  547.  *      --  PROG_WINDOWEDVDM
  548.  *
  549.  *      --  PROG_PM
  550.  *
  551.  *      --  PROG_31_ENHSEAMLESSCOMMON
  552.  *
  553.  *      --  PROG_WINDOWABLEVIO
  554.  *
  555.  *      --  PROG_DEFAULT
  556.  *
  557.  *@@added V0.9.9 (2001-03-07) [umoeller]
  558.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
  559.  *@@changed V0.9.14 (2001-08-07) [pr]: use FAPPTYP_* constants
  560.  *@@changed V0.9.16 (2001-12-08) [umoeller]: added checks for batch files, other optimizations
  561.  */
  562.  
  563. APIRET appQueryAppType(const char *pcszExecutable,
  564.                        PULONG pulDosAppType,            // out: DOS app type
  565.                        PULONG pulWinAppType)            // out: PROG_* app type
  566. {
  567.     APIRET arc;
  568.  
  569. /*
  570.    #define FAPPTYP_NOTSPEC         0x0000
  571.    #define FAPPTYP_NOTWINDOWCOMPAT 0x0001
  572.    #define FAPPTYP_WINDOWCOMPAT    0x0002
  573.    #define FAPPTYP_WINDOWAPI       0x0003
  574.    #define FAPPTYP_BOUND           0x0008
  575.    #define FAPPTYP_DLL             0x0010
  576.    #define FAPPTYP_DOS             0x0020
  577.    #define FAPPTYP_PHYSDRV         0x0040  // physical device driver
  578.    #define FAPPTYP_VIRTDRV         0x0080  // virtual device driver
  579.    #define FAPPTYP_PROTDLL         0x0100  // 'protected memory' dll
  580.    #define FAPPTYP_WINDOWSREAL     0x0200  // Windows real mode app
  581.    #define FAPPTYP_WINDOWSPROT     0x0400  // Windows protect mode app
  582.    #define FAPPTYP_WINDOWSPROT31   0x1000  // Windows 3.1 protect mode app
  583.    #define FAPPTYP_32BIT           0x4000
  584. */
  585.  
  586.     ULONG   ulWinAppType = PROG_DEFAULT;
  587.  
  588.     if (!(arc = DosQueryAppType((PSZ)pcszExecutable, pulDosAppType)))
  589.     {
  590.         // clear the 32-bit flag
  591.         // V0.9.16 (2001-12-08) [umoeller]
  592.         ULONG ulDosAppType = (*pulDosAppType) & ~FAPPTYP_32BIT,
  593.               ulLoAppType = ulDosAppType & 0xFFFF;
  594.  
  595.         if (ulDosAppType & FAPPTYP_PHYSDRV)            // 0x40
  596.             ulWinAppType = PROG_PDD;
  597.         else if (ulDosAppType & FAPPTYP_VIRTDRV)       // 0x80
  598.             ulWinAppType = PROG_VDD;
  599.         else if ((ulDosAppType & 0xF0) == FAPPTYP_DLL) // 0x10
  600.             // DLL bit set
  601.             ulWinAppType = PROG_DLL;
  602.         else if (ulDosAppType & FAPPTYP_DOS)           // 0x20
  603.             // DOS bit set?
  604.             ulWinAppType = PROG_WINDOWEDVDM;
  605.         else if ((ulDosAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) // 0x0003)
  606.             // "Window-API" == PM
  607.             ulWinAppType = PROG_PM;
  608.         else if (ulLoAppType == FAPPTYP_WINDOWSREAL)
  609.             ulWinAppType = PROG_31_ENHSEAMLESSCOMMON;  // @@todo really?
  610.         else if (   (ulLoAppType == FAPPTYP_WINDOWSPROT31) // 0x1000) // windows program (?!?)
  611.                  || (ulLoAppType == FAPPTYP_WINDOWSPROT) // ) // windows program (?!?)
  612.                 )
  613.             ulWinAppType = PROG_31_ENHSEAMLESSCOMMON;  // PROG_31_ENH;
  614.         else if ((ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_WINDOWCOMPAT) // 0x02)
  615.             ulWinAppType = PROG_WINDOWABLEVIO;
  616.         else if ((ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_NOTWINDOWCOMPAT) // 0x01)
  617.             ulWinAppType = PROG_FULLSCREEN;
  618.     }
  619.  
  620.     if (ulWinAppType == PROG_DEFAULT)
  621.     {
  622.         // added checks for batch files V0.9.16 (2001-12-08) [umoeller]
  623.         PCSZ pcszExt;
  624.         if (pcszExt = doshGetExtension(pcszExecutable))
  625.         {
  626.             if (!stricmp(pcszExt, "BAT"))
  627.             {
  628.                 ulWinAppType = PROG_WINDOWEDVDM;
  629.                 arc = NO_ERROR;
  630.             }
  631.             else if (!stricmp(pcszExt, "CMD"))
  632.             {
  633.                 ulWinAppType = PROG_WINDOWABLEVIO;
  634.                 arc = NO_ERROR;
  635.             }
  636.         }
  637.     }
  638.  
  639.     *pulWinAppType = ulWinAppType;
  640.  
  641.     return arc;
  642. }
  643.  
  644. /*
  645.  *@@ PROGTYPESTRING:
  646.  *
  647.  *@@added V0.9.16 (2002-01-13) [umoeller]
  648.  */
  649.  
  650. typedef struct _PROGTYPESTRING
  651. {
  652.     PROGCATEGORY    progc;
  653.     PCSZ            pcsz;
  654. } PROGTYPESTRING, *PPROGTYPESTRING;
  655.  
  656. PROGTYPESTRING G_aProgTypes[] =
  657.     {
  658.         PROG_DEFAULT, "PROG_DEFAULT",
  659.         PROG_FULLSCREEN, "PROG_FULLSCREEN",
  660.         PROG_WINDOWABLEVIO, "PROG_WINDOWABLEVIO",
  661.         PROG_PM, "PROG_PM",
  662.         PROG_GROUP, "PROG_GROUP",
  663.         PROG_VDM, "PROG_VDM",
  664.             // same as PROG_REAL, "PROG_REAL",
  665.         PROG_WINDOWEDVDM, "PROG_WINDOWEDVDM",
  666.         PROG_DLL, "PROG_DLL",
  667.         PROG_PDD, "PROG_PDD",
  668.         PROG_VDD, "PROG_VDD",
  669.         PROG_WINDOW_REAL, "PROG_WINDOW_REAL",
  670.         PROG_30_STD, "PROG_30_STD",
  671.             // same as PROG_WINDOW_PROT, "PROG_WINDOW_PROT",
  672.         PROG_WINDOW_AUTO, "PROG_WINDOW_AUTO",
  673.         PROG_30_STDSEAMLESSVDM, "PROG_30_STDSEAMLESSVDM",
  674.             // same as PROG_SEAMLESSVDM, "PROG_SEAMLESSVDM",
  675.         PROG_30_STDSEAMLESSCOMMON, "PROG_30_STDSEAMLESSCOMMON",
  676.             // same as PROG_SEAMLESSCOMMON, "PROG_SEAMLESSCOMMON",
  677.         PROG_31_STDSEAMLESSVDM, "PROG_31_STDSEAMLESSVDM",
  678.         PROG_31_STDSEAMLESSCOMMON, "PROG_31_STDSEAMLESSCOMMON",
  679.         PROG_31_ENHSEAMLESSVDM, "PROG_31_ENHSEAMLESSVDM",
  680.         PROG_31_ENHSEAMLESSCOMMON, "PROG_31_ENHSEAMLESSCOMMON",
  681.         PROG_31_ENH, "PROG_31_ENH",
  682.         PROG_31_STD, "PROG_31_STD",
  683.  
  684. // Warp 4 toolkit defines, whatever these were designed for...
  685. #ifndef PROG_DOS_GAME
  686.     #define PROG_DOS_GAME            (PROGCATEGORY)21
  687. #endif
  688. #ifndef PROG_WIN_GAME
  689.     #define PROG_WIN_GAME            (PROGCATEGORY)22
  690. #endif
  691. #ifndef PROG_DOS_MODE
  692.     #define PROG_DOS_MODE            (PROGCATEGORY)23
  693. #endif
  694.  
  695.         PROG_DOS_GAME, "PROG_DOS_GAME",
  696.         PROG_WIN_GAME, "PROG_WIN_GAME",
  697.         PROG_DOS_MODE, "PROG_DOS_MODE",
  698.  
  699.         // added this V0.9.16 (2001-12-08) [umoeller]
  700.         PROG_WIN32, "PROG_WIN32"
  701.     };
  702.  
  703. /*
  704.  *@@ appDescribeAppType:
  705.  *      returns a "PROG_*" string for the given
  706.  *      program type. Useful for WPProgram setup
  707.  *      strings and such.
  708.  *
  709.  *@@added V0.9.16 (2001-10-06)
  710.  */
  711.  
  712. PCSZ appDescribeAppType(PROGCATEGORY progc)        // in: from PROGDETAILS.progc
  713. {
  714.     ULONG ul;
  715.     for (ul = 0;
  716.          ul < ARRAYITEMCOUNT(G_aProgTypes);
  717.          ul++)
  718.     {
  719.         if (G_aProgTypes[ul].progc == progc)
  720.             return G_aProgTypes[ul].pcsz;
  721.     }
  722.  
  723.     return NULL;
  724. }
  725.  
  726. /*
  727.  *@@ appIsWindowsApp:
  728.  *      checks the specified program category
  729.  *      (PROGDETAILS.progt.progc) for whether
  730.  *      it represents a Win-OS/2 application.
  731.  *
  732.  *      Returns:
  733.  *
  734.  *      -- 0: no windows app (it's VIO, OS/2
  735.  *            or DOS fullscreen, or PM).
  736.  *
  737.  *      -- 1: Win-OS/2 standard app.
  738.  *
  739.  *      -- 2: Win-OS/2 enhanced-mode app.
  740.  *
  741.  *@@added V0.9.12 (2001-05-26) [umoeller]
  742.  */
  743.  
  744. ULONG appIsWindowsApp(ULONG ulProgCategory)
  745. {
  746.     switch (ulProgCategory)
  747.     {
  748.         case PROG_31_ENHSEAMLESSVDM:        // 17
  749.         case PROG_31_ENHSEAMLESSCOMMON:     // 18
  750.         case PROG_31_ENH:                   // 19
  751.             return 2;
  752.  
  753. #ifndef PROG_30_STD
  754.     #define PROG_30_STD (PROGCATEGORY)11
  755. #endif
  756.  
  757. #ifndef PROG_30_STDSEAMLESSVDM
  758.     #define PROG_30_STDSEAMLESSVDM (PROGCATEGORY)13
  759. #endif
  760.  
  761.         case PROG_WINDOW_REAL:              // 10
  762.         case PROG_30_STD:                   // 11
  763.         case PROG_WINDOW_AUTO:              // 12
  764.         case PROG_30_STDSEAMLESSVDM:        // 13
  765.         case PROG_30_STDSEAMLESSCOMMON:     // 14
  766.         case PROG_31_STDSEAMLESSVDM:        // 15
  767.         case PROG_31_STDSEAMLESSCOMMON:     // 16
  768.         case PROG_31_STD:                   // 20
  769.             return 1;
  770.     }
  771.  
  772.     return 0;
  773. }
  774.  
  775. /* ******************************************************************
  776.  *
  777.  *   Application start
  778.  *
  779.  ********************************************************************/
  780.  
  781. /*
  782.  *@@ CheckAndQualifyExecutable:
  783.  *      checks the executable in the given PROGDETAILS
  784.  *      for whether it is fully qualified.
  785.  *
  786.  *      If so, the existence is verified.
  787.  *
  788.  *      If not, we search for it on the PATH. If we
  789.  *      find it, we use pstrExecutablePath to store
  790.  *      the fully qualified executable and set
  791.  *      pDetails->pszExecutable to it. The caller
  792.  *      must initialize the buffer and clear it
  793.  *      after the call.
  794.  *
  795.  *      Returns:
  796.  *
  797.  *      --  NO_ERROR: executable exists and might
  798.  *          have been fully qualified.
  799.  *
  800.  *      --  ERROR_FILE_NOT_FOUND
  801.  *
  802.  *@@added V0.9.20 (2002-07-03) [umoeller]
  803.  */
  804.  
  805. static APIRET CheckAndQualifyExecutable(PPROGDETAILS pDetails,          // in/out: program details
  806.                                         PXSTRING pstrExecutablePatched) // in/out: buffer for q'fied exec (must be init'ed)
  807. {
  808.     APIRET arc = NO_ERROR;
  809.  
  810.     ULONG ulAttr;
  811.     // check if the executable is fully qualified; if so,
  812.     // check if the executable file exists
  813.     if (    (pDetails->pszExecutable[1] == ':')
  814.          && (strchr(pDetails->pszExecutable, '\\'))
  815.        )
  816.     {
  817.         arc = doshQueryPathAttr(pDetails->pszExecutable,
  818.                                 &ulAttr);
  819.     }
  820.     else
  821.     {
  822.         // _not_ fully qualified: look it up on the PATH then
  823.         // V0.9.16 (2001-12-06) [umoeller]
  824.         CHAR    szFQExecutable[CCHMAXPATH];
  825.         if (!(arc = doshSearchPath("PATH",
  826.                                    pDetails->pszExecutable,
  827.                                    szFQExecutable,
  828.                                    sizeof(szFQExecutable))))
  829.         {
  830.             // alright, found it:
  831.             xstrcpy(pstrExecutablePatched, szFQExecutable, 0);
  832.             pDetails->pszExecutable = pstrExecutablePatched->psz;
  833.         }
  834.     }
  835.  
  836.     return arc;
  837. }
  838.  
  839. /*
  840.  *@@ CallBatchCorrectly:
  841.  *      fixes the specified PROGDETAILS for
  842.  *      command files in the executable part
  843.  *      by inserting /C XXX into the parameters
  844.  *      and setting the executable to the fully
  845.  *      qualified command interpreter specified
  846.  *      by the given environment variable.
  847.  *
  848.  *@@added V0.9.6 (2000-10-16) [umoeller]
  849.  *@@changed V0.9.7 (2001-01-15) [umoeller]: now using XSTRING
  850.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
  851.  *@@changed V0.9.20 (2002-07-03) [umoeller]: now always qualifying executable to fix broken BAT files
  852.  */
  853.  
  854. static APIRET CallBatchCorrectly(PPROGDETAILS pProgDetails,
  855.                                  PXSTRING pstrExecutablePatched, // in/out: buffer for q'fied exec (must be init'ed)
  856.                                  PXSTRING pstrParams,        // in/out: modified parameters (reallocated)
  857.                                  const char *pcszEnvVar,     // in: env var spec'g command proc
  858.                                                              // (e.g. "OS2_SHELL"); can be NULL
  859.                                  const char *pcszDefProc)    // in: def't command proc (e.g. "CMD.EXE")
  860. {
  861.     APIRET arc = NO_ERROR;
  862.  
  863.     // XXX.CMD file as executable:
  864.     // fix args to /C XXX.CMD
  865.  
  866.     PSZ     pszOldParams = NULL;
  867.     ULONG   ulOldParamsLength = pstrParams->ulLength;
  868.     if (ulOldParamsLength)
  869.         // we have parameters already:
  870.         // make a backup... we'll append that later
  871.         pszOldParams = strdup(pstrParams->psz);
  872.  
  873.     // set new params to "/C filename.cmd"
  874.     xstrcpy(pstrParams, "/C ", 0);
  875.     xstrcat(pstrParams,
  876.             pProgDetails->pszExecutable,
  877.             0);
  878.  
  879.     if (pszOldParams)
  880.     {
  881.         // .cmd had params:
  882.         // append space and old params
  883.         xstrcatc(pstrParams, ' ');
  884.         xstrcat(pstrParams,
  885.                 pszOldParams,
  886.                 ulOldParamsLength);
  887.         free(pszOldParams);
  888.     }
  889.  
  890.     // set executable to $(OS2_SHELL)
  891.     pProgDetails->pszExecutable = NULL;
  892.     if (pcszEnvVar)
  893.         pProgDetails->pszExecutable = getenv(pcszEnvVar);
  894.     if (!pProgDetails->pszExecutable)
  895.         pProgDetails->pszExecutable = (PSZ)pcszDefProc;
  896.                 // should be on PATH
  897.  
  898.     // and make sure this is always qualified
  899.     // V0.9.20 (2002-07-03) [umoeller]
  900.     return CheckAndQualifyExecutable(pProgDetails,
  901.                                      pstrExecutablePatched);
  902. }
  903.  
  904. /*
  905.  *@@ appQueryDefaultWin31Environment:
  906.  *      returns the default Win-OS/2 3.1 environment
  907.  *      from OS2.INI, which you can then merge with
  908.  *      your process environment to be able to
  909.  *      start Win-OS/2 sessions properly with
  910.  *      appStartApp.
  911.  *
  912.  *      Caller must free() the return value.
  913.  *
  914.  *@@added V0.9.12 (2001-05-26) [umoeller]
  915.  *@@changed V0.9.19 (2002-03-28) [umoeller]: now returning APIRET
  916.  */
  917.  
  918. APIRET appQueryDefaultWin31Environment(PSZ *ppsz)
  919. {
  920.     APIRET arc = NO_ERROR;
  921.     PSZ pszReturn = NULL;
  922.     ULONG ulSize = 0;
  923.  
  924.     // get default environment (from Win-OS/2 settings object) from OS2.INI
  925.     PSZ pszDefEnv;
  926.     if (pszDefEnv = prfhQueryProfileData(HINI_USER,
  927.                                          "WINOS2",
  928.                                          "PM_GlobalWindows31Settings",
  929.                                          &ulSize))
  930.     {
  931.         if (pszReturn = (PSZ)malloc(ulSize + 2))
  932.         {
  933.             PSZ p;
  934.             memset(pszReturn, 0, ulSize + 2);
  935.             memcpy(pszReturn, pszDefEnv, ulSize);
  936.  
  937.             for (p = pszReturn;
  938.                  p < pszReturn + ulSize;
  939.                  p++)
  940.                 if (*p == ';')
  941.                     *p = 0;
  942.  
  943.             // okay.... now we got an OS/2-style environment
  944.             // with 0, 0, 00 strings
  945.  
  946.             *ppsz = pszReturn;
  947.         }
  948.         else
  949.             arc = ERROR_NOT_ENOUGH_MEMORY;
  950.  
  951.         free(pszDefEnv);
  952.     }
  953.     else
  954.         arc = ERROR_BAD_ENVIRONMENT;
  955.  
  956.     return arc;
  957. }
  958.  
  959. #ifdef _PMPRINTF_
  960.  
  961. static void DumpMemoryBlock(PBYTE pb,       // in: start address
  962.                      ULONG ulSize,   // in: size of block
  963.                      ULONG ulIndent) // in: how many spaces to put
  964.                                      //     before each output line
  965. {
  966.     TRY_QUIET(excpt1)
  967.     {
  968.         PBYTE   pbCurrent = pb;                 // current byte
  969.         ULONG   ulCount = 0,
  970.                 ulCharsInLine = 0;              // if this grows > 7, a new line is started
  971.         CHAR    szTemp[1000];
  972.         CHAR    szLine[400] = "",
  973.                 szAscii[30] = "         ";      // ASCII representation; filled for every line
  974.         PSZ     pszLine = szLine,
  975.                 pszAscii = szAscii;
  976.  
  977.         for (pbCurrent = pb;
  978.              ulCount < ulSize;
  979.              pbCurrent++, ulCount++)
  980.         {
  981.             if (ulCharsInLine == 0)
  982.             {
  983.                 memset(szLine, ' ', ulIndent);
  984.                 pszLine += ulIndent;
  985.             }
  986.             pszLine += sprintf(pszLine, "%02lX ", (ULONG)*pbCurrent);
  987.  
  988.             if ( (*pbCurrent > 31) && (*pbCurrent < 127) )
  989.                 // printable character:
  990.                 *pszAscii = *pbCurrent;
  991.             else
  992.                 *pszAscii = '.';
  993.             pszAscii++;
  994.  
  995.             ulCharsInLine++;
  996.             if (    (ulCharsInLine > 7)         // 8 bytes added?
  997.                  || (ulCount == ulSize-1)       // end of buffer reached?
  998.                )
  999.             {
  1000.                 // if we haven't had eight bytes yet,
  1001.                 // fill buffer up to eight bytes with spaces
  1002.                 ULONG   ul2;
  1003.                 for (ul2 = ulCharsInLine;
  1004.                      ul2 < 8;
  1005.                      ul2++)
  1006.                     pszLine += sprintf(pszLine, "   ");
  1007.  
  1008.                 sprintf(szTemp, "%04lX:  %s  %ss",
  1009.                                 (ulCount & 0xFFFFFFF8),  // offset in hex
  1010.                                 szLine,         // bytes string
  1011.                                 szAscii);       // ASCII string
  1012.  
  1013.                 _Pmpf(("%s", szTemp));
  1014.  
  1015.                 // restart line buffer
  1016.                 pszLine = szLine;
  1017.  
  1018.                 // clear ASCII buffer
  1019.                 strcpy(szAscii, "         ");
  1020.                 pszAscii = szAscii;
  1021.  
  1022.                 // reset line counter
  1023.                 ulCharsInLine = 0;
  1024.             }
  1025.         }
  1026.  
  1027.     }
  1028.     CATCH(excpt1)
  1029.     {
  1030.         _Pmpf(("Crash in " __FUNCTION__ ));
  1031.     } END_CATCH();
  1032. }
  1033.  
  1034. #endif
  1035.  
  1036. /*
  1037.  *@@ appBuildProgDetails:
  1038.  *      extracted code from appStartApp to fix the
  1039.  *      given PROGDETAILS data to support the typical
  1040.  *      WPS stuff and allocate a single block of
  1041.  *      shared memory containing all the data.
  1042.  *
  1043.  *      This is now used by XWP's progOpenProgram
  1044.  *      directly as a temporary fix for all the
  1045.  *      session hangs.
  1046.  *
  1047.  *      As input, this takes a PROGDETAILS structure,
  1048.  *      which is converted in various ways. In detail,
  1049.  *      this supports:
  1050.  *
  1051.  *      -- starting "*" executables (command prompts
  1052.  *         for OS/2, DOS, Win-OS/2);
  1053.  *
  1054.  *      -- starting ".CMD" and ".BAT" files as
  1055.  *         PROGDETAILS.pszExecutable; for those, we
  1056.  *         convert the executable and parameters to
  1057.  *         start CMD.EXE or COMMAND.COM with the "/C"
  1058.  *         parameter instead;
  1059.  *
  1060.  *      -- starting apps which are not fully qualified
  1061.  *         and therefore assumed to be on the PATH
  1062.  *         (for which doshSearchPath("PATH") is called).
  1063.  *
  1064.  *      Unless it is "*", PROGDETAILS.pszExecutable must
  1065.  *      be a proper file name. The full path may be omitted
  1066.  *      if it is on the PATH, but the extension (.EXE etc.)
  1067.  *      must be given. You can use doshFindExecutable to
  1068.  *      find executables if you don't know the extension.
  1069.  *
  1070.  *      This also handles and merges special and default
  1071.  *      environments for the app to be started. The
  1072.  *      following should be respected:
  1073.  *
  1074.  *      --  As with WinStartApp, if PROGDETAILS.pszEnvironment
  1075.  *          is NULL, the new app inherits the default environment
  1076.  *          from the shell.
  1077.  *
  1078.  *      --  However, if you specify an environment, you _must_
  1079.  *          specify a complete environment. This function
  1080.  *          will not merge environments. Use
  1081.  *          appSetEnvironmentVar to change environment
  1082.  *          variables in a complete environment set.
  1083.  *
  1084.  *      --  If PROGDETAILS specifies a Win-OS/2 session
  1085.  *          and PROGDETAILS.pszEnvironment is empty,
  1086.  *          this uses the default Win-OS/2 environment
  1087.  *          from OS2.INI. See appQueryDefaultWin31Environment.
  1088.  *
  1089.  *      Even though this isn't clearly said in PMREF,
  1090.  *      PROGDETAILS.swpInitial is important:
  1091.  *
  1092.  *      -- To start a session minimized, set fl to SWP_MINIMIZE.
  1093.  *
  1094.  *      -- To start a VIO session with auto-close disabled,
  1095.  *         set the half-documented SWP_NOAUTOCLOSE flag (0x8000)
  1096.  *         This flag is now in the newer toolkit headers.
  1097.  *
  1098.  *      In addition, this supports the following session
  1099.  *      flags with ulFlags if PROG_DEFAULT is specified:
  1100.  *
  1101.  *      --  APP_RUN_FULLSCREEN: start a fullscreen session
  1102.  *          for VIO, DOS, and Win-OS/2 programs. Otherwise
  1103.  *          we start a windowed or (share) a seamless session.
  1104.  *          Ignored if the program is PM.
  1105.  *
  1106.  *      --  APP_RUN_ENHANCED: for Win-OS/2 sessions, use
  1107.  *          enhanced mode.
  1108.  *          Ignored if the program is not Win-OS/2.
  1109.  *
  1110.  *      --  APP_RUN_STANDARD: for Win-OS/2 sessions, use
  1111.  *          standard mode.
  1112.  *          Ignored if the program is not Win-OS/2.
  1113.  *
  1114.  *      --  APP_RUN_SEPARATE: for Win-OS/2 sessions, use
  1115.  *          a separate session.
  1116.  *          Ignored if the program is not Win-OS/2.
  1117.  *
  1118.  *      If NO_ERROR is returned, *ppDetails receives a
  1119.  *      new buffer of shared memory containing all the
  1120.  *      data packed together.
  1121.  *
  1122.  *      The shared memory is allocated unnamed and
  1123.  *      with OBJ_GETTABLE. It is the responsibility
  1124.  *      of the caller to call DosFreeMem on that buffer.
  1125.  *
  1126.  *      Returns:
  1127.  *
  1128.  *      --  NO_ERROR
  1129.  *
  1130.  *      --  ERROR_INVALID_PARAMETER: pcProgDetails or
  1131.  *          ppDetails is NULL; or PROGDETAILS.pszExecutable is NULL.
  1132.  *
  1133.  *      --  ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND:
  1134.  *          PROGDETAILS.pszExecutable and/or PROGDETAILS.pszStartupDir
  1135.  *          are invalid.
  1136.  *          A NULL PROGDETAILS.pszStartupDir is supported though.
  1137.  *
  1138.  *      --  ERROR_BAD_FORMAT
  1139.  *
  1140.  *      --  ERROR_BAD_ENVIRONMENT: environment is larger than 60.000 bytes.
  1141.  *
  1142.  *      --  ERROR_NOT_ENOUGH_MEMORY
  1143.  *
  1144.  *      plus the error codes from doshQueryPathAttr, doshSearchPath,
  1145.  *      appParseEnvironment, appSetEnvironmentVar, and appConvertEnvironment.
  1146.  *
  1147.  *@@added V0.9.18 (2002-03-27) [umoeller]
  1148.  *@@changed V0.9.19 (2002-03-28) [umoeller]: now allocating contiguous buffer
  1149.  *@@changed V0.9.20 (2002-07-03) [umoeller]: fixed Win-OS/2 full screen breakage
  1150.  *@@changed V0.9.20 (2002-07-03) [umoeller]: fixed broken bat and cmd files when PROG_DEFAULT was set
  1151.  */
  1152.  
  1153. APIRET appBuildProgDetails(PPROGDETAILS *ppDetails,           // out: shared mem with fixed program spec (req.)
  1154.                            const PROGDETAILS *pcProgDetails,  // in: program spec (req.)
  1155.                            ULONG ulFlags)                     // in: APP_RUN_* flags or 0
  1156. {
  1157.     APIRET          arc = NO_ERROR;
  1158.  
  1159.     XSTRING         strExecutablePatched,
  1160.                     strParamsPatched;
  1161.     PSZ             pszWinOS2Env = 0;
  1162.  
  1163.     PROGDETAILS     Details;
  1164.  
  1165.     *ppDetails = NULL;
  1166.  
  1167.     if (!pcProgDetails && !ppDetails)
  1168.         return ERROR_INVALID_PARAMETER;
  1169.  
  1170.     /*
  1171.      * part 1:
  1172.      *      fix up the PROGDETAILS fields
  1173.      */
  1174.  
  1175.     xstrInit(&strExecutablePatched, 0);
  1176.     xstrInit(&strParamsPatched, 0);
  1177.  
  1178.     memcpy(&Details, pcProgDetails, sizeof(PROGDETAILS));
  1179.             // pointers still point into old prog details buffer
  1180.     Details.Length = sizeof(PROGDETAILS);
  1181.     Details.progt.fbVisible = SHE_VISIBLE;
  1182.  
  1183.     // all this only makes sense if this contains something...
  1184.     // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
  1185.     if (    (!Details.pszExecutable)
  1186.          || (!Details.pszExecutable[0])
  1187.        )
  1188.         arc = ERROR_INVALID_PARAMETER;
  1189.     else
  1190.     {
  1191.         ULONG           ulIsWinApp;
  1192.  
  1193.         // memset(&Details.swpInitial, 0, sizeof(SWP));
  1194.         // this wasn't a good idea... WPProgram stores stuff
  1195.         // in here, such as the "minimize on startup" -> SWP_MINIMIZE
  1196.  
  1197.         // duplicate parameters...
  1198.         // we need this for string manipulations below...
  1199.         if (    (Details.pszParameters)
  1200.              && (Details.pszParameters[0])    // V0.9.18
  1201.            )
  1202.             xstrcpy(&strParamsPatched,
  1203.                     Details.pszParameters,
  1204.                     0);
  1205.  
  1206.         #ifdef DEBUG_PROGRAMSTART
  1207.             _PmpfF((" old progc: 0x%lX", pcProgDetails->progt.progc));
  1208.             _Pmpf(("  pszTitle: %s", STRINGORNULL(Details.pszTitle)));
  1209.             _Pmpf(("  pszExecutable: %s", STRINGORNULL(Details.pszExecutable)));
  1210.             _Pmpf(("  pszParameters: %s", STRINGORNULL(Details.pszParameters)));
  1211.             _Pmpf(("  pszIcon: %s", STRINGORNULL(Details.pszIcon)));
  1212.         #endif
  1213.  
  1214.         // program type fixups
  1215.         switch (Details.progt.progc)        // that's a ULONG
  1216.         {
  1217.             case ((ULONG)-1):       // we get that sometimes...
  1218.             case PROG_DEFAULT:
  1219.             {
  1220.                 // V0.9.12 (2001-05-26) [umoeller]
  1221.                 ULONG ulDosAppType;
  1222.                 appQueryAppType(Details.pszExecutable,
  1223.                                 &ulDosAppType,
  1224.                                 &Details.progt.progc);
  1225.             }
  1226.             break;
  1227.         }
  1228.  
  1229.         // set session type from option flags
  1230.         if (ulFlags & APP_RUN_FULLSCREEN)
  1231.         {
  1232.             if (Details.progt.progc == PROG_WINDOWABLEVIO)
  1233.                 Details.progt.progc = PROG_FULLSCREEN;
  1234.             else if (Details.progt.progc == PROG_WINDOWEDVDM)
  1235.                 Details.progt.progc = PROG_VDM;
  1236.         }
  1237.  
  1238.         if (ulIsWinApp = appIsWindowsApp(Details.progt.progc))
  1239.         {
  1240.             if (ulFlags & APP_RUN_FULLSCREEN)
  1241.                 Details.progt.progc = (ulFlags & APP_RUN_ENHANCED)
  1242.                                                 ? PROG_31_ENH
  1243.                                                 : PROG_31_STD;
  1244.             else
  1245.             {
  1246.                 if (ulFlags & APP_RUN_STANDARD)
  1247.                     Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
  1248.                                                 ? PROG_31_STDSEAMLESSVDM
  1249.                                                 : PROG_31_STDSEAMLESSCOMMON;
  1250.                 else if (ulFlags & APP_RUN_ENHANCED)
  1251.                     Details.progt.progc = (ulFlags & APP_RUN_SEPARATE)
  1252.                                                 ? PROG_31_ENHSEAMLESSVDM
  1253.                                                 : PROG_31_ENHSEAMLESSCOMMON;
  1254.             }
  1255.  
  1256.             // re-run V0.9.16 (2001-10-19) [umoeller]
  1257.             ulIsWinApp = appIsWindowsApp(Details.progt.progc);
  1258.         }
  1259.  
  1260.         /*
  1261.          * command lines fixups:
  1262.          *
  1263.          */
  1264.  
  1265.         if (!strcmp(Details.pszExecutable, "*"))
  1266.         {
  1267.             /*
  1268.              * "*" for command sessions:
  1269.              *
  1270.              */
  1271.  
  1272.             if (ulIsWinApp)
  1273.             {
  1274.                 // cheat: WinStartApp doesn't support NULL
  1275.                 // for Win-OS2 sessions, so manually start winos2.com
  1276.                 Details.pszExecutable = "WINOS2.COM";
  1277.                 // this is a DOS app, so fix this to DOS fullscreen
  1278.                 Details.progt.progc = PROG_VDM;
  1279.  
  1280.                 if (ulIsWinApp == 2)
  1281.                 {
  1282.                     // enhanced Win-OS/2 session:
  1283.                     PSZ psz = NULL;
  1284.                     if (strParamsPatched.ulLength)
  1285.                         // "/3 " + existing params
  1286.                         psz = strdup(strParamsPatched.psz);
  1287.  
  1288.                     xstrcpy(&strParamsPatched, "/3 ", 0);
  1289.  
  1290.                     if (psz)
  1291.                     {
  1292.                         xstrcat(&strParamsPatched, psz, 0);
  1293.                         free(psz);
  1294.                     }
  1295.                 }
  1296.             }
  1297.             else
  1298.                 // for all other executable types
  1299.                 // (including OS/2 and DOS sessions),
  1300.                 // set pszExecutable to NULL; this will
  1301.                 // have WinStartApp start a cmd shell
  1302.                 Details.pszExecutable = NULL;
  1303.  
  1304.         } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
  1305.  
  1306.         // else
  1307.  
  1308.         // no, this else breaks the WINOS2.COM hack above... we
  1309.         // need to look for that on the PATH as well
  1310.         // V0.9.20 (2002-07-03) [umoeller]
  1311.         if (Details.pszExecutable)
  1312.         {
  1313.             // check the executable and look for it on the
  1314.             // PATH if necessary
  1315.             if (!(arc = CheckAndQualifyExecutable(&Details,
  1316.                                                   &strExecutablePatched)))
  1317.             {
  1318.                 PSZ pszExtension;
  1319.  
  1320.                 // make sure startup dir is really a directory
  1321.                 // V0.9.20 (2002-07-03) [umoeller]: moved this down
  1322.                 if (Details.pszStartupDir)
  1323.                 {
  1324.                     ULONG ulAttr;
  1325.                     // it is valid to specify a startup dir of "C:"
  1326.                     if (    (strlen(Details.pszStartupDir) > 2)
  1327.                          && (!(arc = doshQueryPathAttr(Details.pszStartupDir,
  1328.                                                        &ulAttr)))
  1329.                          && (!(ulAttr & FILE_DIRECTORY))
  1330.                        )
  1331.                         arc = ERROR_PATH_NOT_FOUND;
  1332.                 }
  1333.  
  1334.                 // we frequently get here for BAT and CMD files
  1335.                 // with progtype == PROG_DEFAULT, so include
  1336.                 // that in the check, or all BAT files will fail
  1337.                 // V0.9.20 (2002-07-03) [umoeller]
  1338.  
  1339.                 switch (Details.progt.progc)
  1340.                 {
  1341.                     /*
  1342.                      *  .CMD files fixups
  1343.                      *
  1344.                      */
  1345.  
  1346.                     case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
  1347.                     case PROG_FULLSCREEN:       // OS/2 fullscreen
  1348.                     case PROG_WINDOWABLEVIO:    // OS/2 window
  1349.                     {
  1350.                         if (    (pszExtension = doshGetExtension(Details.pszExecutable))
  1351.                              && (!stricmp(pszExtension, "CMD"))
  1352.                            )
  1353.                         {
  1354.                             arc = CallBatchCorrectly(&Details,
  1355.                                                      &strExecutablePatched,
  1356.                                                      &strParamsPatched,
  1357.                                                      "OS2_SHELL",
  1358.                                                      "CMD.EXE");
  1359.                         }
  1360.                     }
  1361.                     break;
  1362.                 }
  1363.  
  1364.                 switch (Details.progt.progc)
  1365.                 {
  1366.                     case PROG_DEFAULT:          // V0.9.20 (2002-07-03) [umoeller]
  1367.                     case PROG_VDM:              // DOS fullscreen
  1368.                     case PROG_WINDOWEDVDM:      // DOS window
  1369.                     {
  1370.                         if (    (pszExtension = doshGetExtension(Details.pszExecutable))
  1371.                              && (!stricmp(pszExtension, "BAT"))
  1372.                            )
  1373.                         {
  1374.                             arc = CallBatchCorrectly(&Details,
  1375.                                                      &strExecutablePatched,
  1376.                                                      &strParamsPatched,
  1377.                                                      // there is no environment variable
  1378.                                                      // for the DOS shell
  1379.                                                      NULL,
  1380.                                                      "COMMAND.COM");
  1381.                         }
  1382.                     }
  1383.                     break;
  1384.                 } // end switch (Details.progt.progc)
  1385.             }
  1386.         }
  1387.  
  1388.         if (!arc)
  1389.         {
  1390.             if (    (ulIsWinApp)
  1391.                  && (    (!(Details.pszEnvironment))
  1392.                       || (!(*Details.pszEnvironment))
  1393.                     )
  1394.                )
  1395.             {
  1396.                 // this is a windoze app, and caller didn't bother
  1397.                 // to give us an environment:
  1398.                 // we MUST set one then, or we'll get the strangest
  1399.                 // errors, up to system hangs. V0.9.12 (2001-05-26) [umoeller]
  1400.  
  1401.                 DOSENVIRONMENT Env = {0};
  1402.  
  1403.                 // get standard WIN-OS/2 environment
  1404.                 PSZ pszTemp;
  1405.                 if (!(arc = appQueryDefaultWin31Environment(&pszTemp)))
  1406.                 {
  1407.                     if (!(arc = appParseEnvironment(pszTemp,
  1408.                                                     &Env)))
  1409.                     {
  1410.                         // now override KBD_CTRL_BYPASS=CTRL_ESC
  1411.                         if (    (!(arc = appSetEnvironmentVar(&Env,
  1412.                                                               "KBD_CTRL_BYPASS=CTRL_ESC",
  1413.                                                               FALSE)))        // add last
  1414.                              && (!(arc = appConvertEnvironment(&Env,
  1415.                                                                &pszWinOS2Env,   // freed at bottom
  1416.                                                                NULL)))
  1417.                            )
  1418.                             Details.pszEnvironment = pszWinOS2Env;
  1419.  
  1420.                         appFreeEnvironment(&Env);
  1421.                     }
  1422.  
  1423.                     free(pszTemp);
  1424.                 }
  1425.             }
  1426.  
  1427.             if (!arc)
  1428.             {
  1429.                 // if no title is given, use the executable
  1430.                 if (!Details.pszTitle)
  1431.                     Details.pszTitle = Details.pszExecutable;
  1432.  
  1433.                 // make sure params have a leading space
  1434.                 // V0.9.18 (2002-03-27) [umoeller]
  1435.                 if (strParamsPatched.ulLength)
  1436.                 {
  1437.                     if (strParamsPatched.psz[0] != ' ')
  1438.                     {
  1439.                         XSTRING str2;
  1440.                         xstrInit(&str2, 0);
  1441.                         xstrcpy(&str2, " ", 1);
  1442.                         xstrcats(&str2, &strParamsPatched);
  1443.                         xstrcpys(&strParamsPatched, &str2);
  1444.                         xstrClear(&str2);
  1445.                                 // we really need xstrInsert or something
  1446.                     }
  1447.                     Details.pszParameters = strParamsPatched.psz;
  1448.                 }
  1449.                 else
  1450.                     // never pass null pointers
  1451.                     Details.pszParameters = "";
  1452.  
  1453.                 // never pass null pointers
  1454.                 if (!Details.pszIcon)
  1455.                     Details.pszIcon = "";
  1456.  
  1457.                 // never pass null pointers
  1458.                 if (!Details.pszStartupDir)
  1459.                     Details.pszStartupDir = "";
  1460.  
  1461.             }
  1462.         }
  1463.     }
  1464.  
  1465.     /*
  1466.      * part 2:
  1467.      *      pack the fixed PROGDETAILS fields
  1468.      */
  1469.  
  1470.     if (!arc)
  1471.     {
  1472.         ULONG   cb,
  1473.                 cbTitle,
  1474.                 cbExecutable,
  1475.                 cbParameters,
  1476.                 cbStartupDir,
  1477.                 cbIcon,
  1478.                 cbEnvironment;
  1479.  
  1480.         #ifdef DEBUG_PROGRAMSTART
  1481.             _PmpfF((" new progc: 0x%lX", pcProgDetails->progt.progc));
  1482.             _Pmpf(("  pszTitle: %s", STRINGORNULL(Details.pszTitle)));
  1483.             _Pmpf(("  pszExecutable: %s", STRINGORNULL(Details.pszExecutable)));
  1484.             _Pmpf(("  pszParameters: %s", STRINGORNULL(Details.pszParameters)));
  1485.             _Pmpf(("  pszIcon: %s", STRINGORNULL(Details.pszIcon)));
  1486.         #endif
  1487.  
  1488.         // allocate a chunk of tiled memory from OS/2 to make sure
  1489.         // this is aligned on a 64K memory (backed up by a 16-bit
  1490.         // LDT selector); if it is not, and the environment
  1491.         // crosses segments, it gets truncated!!
  1492.         cb = sizeof(PROGDETAILS);
  1493.         if (cbTitle = strhSize(Details.pszTitle))
  1494.             cb += cbTitle;
  1495.  
  1496.         if (cbExecutable = strhSize(Details.pszExecutable))
  1497.             cb += cbExecutable;
  1498.  
  1499.         if (cbParameters = strhSize(Details.pszParameters))
  1500.             cb += cbParameters;
  1501.  
  1502.         if (cbStartupDir = strhSize(Details.pszStartupDir))
  1503.             cb += cbStartupDir;
  1504.  
  1505.         if (cbIcon = strhSize(Details.pszIcon))
  1506.             cb += cbIcon;
  1507.  
  1508.         if (cbEnvironment = appQueryEnvironmentLen(Details.pszEnvironment))
  1509.             cb += cbEnvironment;
  1510.  
  1511.         if (cb > 60000)     // to be on the safe side
  1512.             arc = ERROR_BAD_ENVIRONMENT; // 10;
  1513.         else
  1514.         {
  1515.             PPROGDETAILS pNewProgDetails;
  1516.             // alright, allocate the shared memory now
  1517.             if (!(arc = DosAllocSharedMem((PVOID*)&pNewProgDetails,
  1518.                                           NULL,
  1519.                                           cb,
  1520.                                           PAG_COMMIT | OBJ_GETTABLE | OBJ_TILE | PAG_EXECUTE | PAG_READ | PAG_WRITE)))
  1521.             {
  1522.                 // and copy stuff
  1523.                 PBYTE pThis;
  1524.  
  1525.                 memset(pNewProgDetails, 0, cb);
  1526.  
  1527.                 pNewProgDetails->Length = sizeof(PROGDETAILS);
  1528.  
  1529.                 pNewProgDetails->progt.progc = Details.progt.progc;
  1530.  
  1531.                 pNewProgDetails->progt.fbVisible = Details.progt.fbVisible;
  1532.                 memcpy(&pNewProgDetails->swpInitial, &Details.swpInitial, sizeof(SWP));
  1533.  
  1534.                 // start copying into buffer right after PROGDETAILS
  1535.                 pThis = (PBYTE)(pNewProgDetails + 1);
  1536.  
  1537.                 // handy macro to avoid typos
  1538.                 #define COPY(id) if (cb ## id) { \
  1539.                     memcpy(pThis, Details.psz ## id, cb ## id); \
  1540.                     pNewProgDetails->psz ## id = pThis; \
  1541.                     pThis += cb ## id; }
  1542.  
  1543.                 COPY(Title);
  1544.                 COPY(Executable);
  1545.                 COPY(Parameters);
  1546.                 COPY(StartupDir);
  1547.                 COPY(Icon);
  1548.                 COPY(Environment);
  1549.  
  1550.                 *ppDetails = pNewProgDetails;
  1551.             }
  1552.         }
  1553.     }
  1554.  
  1555.     xstrClear(&strParamsPatched);
  1556.     xstrClear(&strExecutablePatched);
  1557.  
  1558.     if (pszWinOS2Env)
  1559.         free(pszWinOS2Env);
  1560.  
  1561.     return arc;
  1562. }
  1563.  
  1564. /*
  1565.  *@@ CallDosStartSession:
  1566.  *
  1567.  *@@added V0.9.18 (2002-03-27) [umoeller]
  1568.  */
  1569.  
  1570. static APIRET CallDosStartSession(HAPP *phapp,
  1571.                                   const PROGDETAILS *pNewProgDetails, // in: program spec (req.)
  1572.                                   ULONG cbFailingName,
  1573.                                   PSZ pszFailingName)
  1574. {
  1575.     APIRET      arc = NO_ERROR;
  1576.  
  1577.     BOOL        fCrit = FALSE,
  1578.                 fResetDir = FALSE;
  1579.     CHAR        szCurrentDir[CCHMAXPATH];
  1580.  
  1581.     ULONG       sid,
  1582.                 pid;
  1583.     STARTDATA   SData;
  1584.     SData.Length  = sizeof(STARTDATA);
  1585.     SData.Related = SSF_RELATED_INDEPENDENT; // SSF_RELATED_CHILD;
  1586.     // per default, try to start this in the foreground
  1587.     SData.FgBg    = SSF_FGBG_FORE;
  1588.     SData.TraceOpt = SSF_TRACEOPT_NONE;
  1589.  
  1590.     SData.PgmTitle = pNewProgDetails->pszTitle;
  1591.     SData.PgmName = pNewProgDetails->pszExecutable;
  1592.     SData.PgmInputs = pNewProgDetails->pszParameters;
  1593.  
  1594.     SData.TermQ = NULL;
  1595.     SData.Environment = pNewProgDetails->pszEnvironment;
  1596.     SData.InheritOpt = SSF_INHERTOPT_PARENT;    // ignored
  1597.  
  1598.     switch (pNewProgDetails->progt.progc)
  1599.     {
  1600.         case PROG_FULLSCREEN:
  1601.             SData.SessionType = SSF_TYPE_FULLSCREEN;
  1602.         break;
  1603.  
  1604.         case PROG_WINDOWABLEVIO:
  1605.             SData.SessionType = SSF_TYPE_WINDOWABLEVIO;
  1606.         break;
  1607.  
  1608.         case PROG_PM:
  1609.             SData.SessionType = SSF_TYPE_PM;
  1610.             SData.FgBg = SSF_FGBG_BACK;     // otherwise we get ERROR_SMG_START_IN_BACKGROUND
  1611.         break;
  1612.  
  1613.         case PROG_VDM:
  1614.             SData.SessionType = SSF_TYPE_VDM;
  1615.         break;
  1616.  
  1617.         case PROG_WINDOWEDVDM:
  1618.             SData.SessionType = SSF_TYPE_WINDOWEDVDM;
  1619.         break;
  1620.  
  1621.         default:
  1622.             SData.SessionType = SSF_TYPE_DEFAULT;
  1623.     }
  1624.  
  1625.     SData.IconFile = 0;
  1626.     SData.PgmHandle = 0;
  1627.  
  1628.     SData.PgmControl = 0;
  1629.  
  1630.     if (pNewProgDetails->progt.fbVisible == SHE_VISIBLE)
  1631.         SData.PgmControl |= SSF_CONTROL_VISIBLE;
  1632.  
  1633.     if (pNewProgDetails->swpInitial.fl & SWP_HIDE)
  1634.         SData.PgmControl |= SSF_CONTROL_INVISIBLE;
  1635.  
  1636.     if (pNewProgDetails->swpInitial.fl & SWP_MAXIMIZE)
  1637.         SData.PgmControl |= SSF_CONTROL_MAXIMIZE;
  1638.     if (pNewProgDetails->swpInitial.fl & SWP_MINIMIZE)
  1639.     {
  1640.         SData.PgmControl |= SSF_CONTROL_MINIMIZE;
  1641.         // use background then
  1642.         SData.FgBg = SSF_FGBG_BACK;
  1643.     }
  1644.     if (pNewProgDetails->swpInitial.fl & SWP_MOVE)
  1645.         SData.PgmControl |= SSF_CONTROL_SETPOS;
  1646.     if (pNewProgDetails->swpInitial.fl & SWP_NOAUTOCLOSE)
  1647.         SData.PgmControl |= SSF_CONTROL_NOAUTOCLOSE;
  1648.  
  1649.     SData.InitXPos  = pNewProgDetails->swpInitial.x;
  1650.     SData.InitYPos  = pNewProgDetails->swpInitial.y;
  1651.     SData.InitXSize = pNewProgDetails->swpInitial.cx;
  1652.     SData.InitYSize = pNewProgDetails->swpInitial.cy;
  1653.  
  1654.     SData.Reserved = 0;
  1655.     SData.ObjectBuffer  = pszFailingName;
  1656.     SData.ObjectBuffLen = cbFailingName;
  1657.  
  1658.     // now, if a required module cannot be found,
  1659.     // DosStartSession still returns ERROR_FILE_NOT_FOUND
  1660.     // (2), but pszFailingName will be set to something
  1661.     // meaningful... so set it to a null string first
  1662.     // and we can then check if it has changed
  1663.     if (pszFailingName)
  1664.         *pszFailingName = '\0';
  1665.  
  1666.     TRY_QUIET(excpt1)
  1667.     {
  1668.         if (    (pNewProgDetails->pszStartupDir)
  1669.              && (pNewProgDetails->pszStartupDir[0])
  1670.            )
  1671.         {
  1672.             fCrit = !DosEnterCritSec();
  1673.             if (    (!(arc = doshQueryCurrentDir(szCurrentDir)))
  1674.                  && (!(arc = doshSetCurrentDir(pNewProgDetails->pszStartupDir)))
  1675.                )
  1676.                 fResetDir = TRUE;
  1677.         }
  1678.  
  1679.         if (    (!arc)
  1680.              && (!(arc = DosStartSession(&SData, &sid, &pid)))
  1681.            )
  1682.         {
  1683.             // app started:
  1684.             // compose HAPP from that
  1685.             *phapp = sid;
  1686.         }
  1687.         else if (pszFailingName && *pszFailingName)
  1688.             // DosStartSession has set this to something
  1689.             // other than NULL: then use error code 1804,
  1690.             // as cmd.exe does
  1691.             arc = 1804;
  1692.     }
  1693.     CATCH(excpt1)
  1694.     {
  1695.         arc = ERROR_PROTECTION_VIOLATION;
  1696.     } END_CATCH();
  1697.  
  1698.     if (fResetDir)
  1699.         doshSetCurrentDir(szCurrentDir);
  1700.  
  1701.     if (fCrit)
  1702.         DosExitCritSec();
  1703.  
  1704.     #ifdef DEBUG_PROGRAMSTART
  1705.         _Pmpf(("   DosStartSession returned %d, pszFailingName: \"%s\"",
  1706.                   arc, pszFailingName));
  1707.     #endif
  1708.  
  1709.     return arc;
  1710. }
  1711.  
  1712. /*
  1713.  *@@ CallWinStartApp:
  1714.  *      wrapper around WinStartApp which copies all the
  1715.  *      parameters into a contiguous block of tiled memory.
  1716.  *
  1717.  *      This might fix some of the problems with truncated
  1718.  *      environments we were having because apparently the
  1719.  *      WinStartApp thunking to 16-bit doesn't always work.
  1720.  *
  1721.  *@@added V0.9.18 (2002-02-13) [umoeller]
  1722.  *@@changed V0.9.18 (2002-03-27) [umoeller]: made failing modules work
  1723.  */
  1724.  
  1725. static APIRET CallWinStartApp(HAPP *phapp,            // out: application handle if NO_ERROR is returned
  1726.                               HWND hwndNotify,        // in: notify window or NULLHANDLE
  1727.                               const PROGDETAILS *pcProgDetails, // in: program spec (req.)
  1728.                               ULONG cbFailingName,
  1729.                               PSZ pszFailingName)
  1730. {
  1731.     APIRET arc = NO_ERROR;
  1732.  
  1733.     if (!pcProgDetails)
  1734.         return ERROR_INVALID_PARAMETER;
  1735.  
  1736.     if (pszFailingName)
  1737.         *pszFailingName = '\0';
  1738.  
  1739.     if (!(*phapp = WinStartApp(hwndNotify,
  1740.                                         // receives WM_APPTERMINATENOTIFY
  1741.                                (PPROGDETAILS)pcProgDetails,
  1742.                                pcProgDetails->pszParameters,
  1743.                                NULL,            // "reserved", PMREF says...
  1744.                                SAF_INSTALLEDCMDLINE)))
  1745.                                     // we MUST use SAF_INSTALLEDCMDLINE
  1746.                                     // or no Win-OS/2 session will start...
  1747.                                     // whatever is going on here... Warp 4 FP11
  1748.  
  1749.                                     // do not use SAF_STARTCHILDAPP, or the
  1750.                                     // app will be terminated automatically
  1751.                                     // when the calling process terminates!
  1752.     {
  1753.         // cannot start app:
  1754.         PERRINFO pei;
  1755.  
  1756.         #ifdef DEBUG_PROGRAMSTART
  1757.             _Pmpf((__FUNCTION__ ": WinStartApp failed"));
  1758.         #endif
  1759.  
  1760.         // unfortunately WinStartApp doesn't
  1761.         // return meaningful codes like DosStartSession, so
  1762.         // try to see what happened
  1763.  
  1764.         if (pei = WinGetErrorInfo(0))
  1765.         {
  1766.             #ifdef DEBUG_PROGRAMSTART
  1767.                 _Pmpf(("  WinGetErrorInfo returned 0x%lX, errorid 0x%lX, %d",
  1768.                             pei,
  1769.                             pei->idError,
  1770.                             ERRORIDERROR(pei->idError)));
  1771.             #endif
  1772.  
  1773.             switch (ERRORIDERROR(pei->idError))
  1774.             {
  1775.                 case PMERR_DOS_ERROR: //  (0x1200)
  1776.                 {
  1777.                     /*
  1778.                     PUSHORT pausMsgOfs = (PUSHORT)(((PBYTE)pei) + pei->offaoffszMsg);
  1779.                     PULONG  pulData    = (PULONG)(((PBYTE)pei) + pei->offBinaryData);
  1780.                     PSZ     pszMsg     = (PSZ)(((PBYTE)pei) + *pausMsgOfs);
  1781.  
  1782.                     CHAR szMsg[1000];
  1783.                     sprintf(szMsg, "cDetail: %d\nmsg: %s\n*pul: %d",
  1784.                             pei->cDetailLevel,
  1785.                             pszMsg,
  1786.                             *(pulData - 1));
  1787.  
  1788.                     WinMessageBox(HWND_DESKTOP,
  1789.                                   NULLHANDLE,
  1790.                                   szMsg,
  1791.                                   "Error",
  1792.                                   0,
  1793.                                   MB_OK | MB_MOVEABLE);
  1794.  
  1795.                     // Very helpful. The message is "UNK 1200 E",
  1796.                     // where I assume "UNK" means "unknown", which is
  1797.                     // exactly what I was trying to find out. Oh my.
  1798.                     // And cDetailLevel is always 1, which isn't terribly
  1799.                     // helpful either. V0.9.18 (2002-03-27) [umoeller]
  1800.                     // WHO THE &%⌡$ CREATED THESE APIS?
  1801.  
  1802.                     */
  1803.  
  1804.                     // this is probably the case where the module
  1805.                     // couldn't be loaded, so try DosStartSession
  1806.                     // to get a meaningful return code... note that
  1807.                     // this cannot handle hwndNotify then
  1808.                     /* arc = CallDosStartSession(phapp,
  1809.                                               pcProgDetails,
  1810.                                               cbFailingName,
  1811.                                               pszFailingName); */
  1812.                     arc = ERROR_FILE_NOT_FOUND;
  1813.                 }
  1814.                 break;
  1815.  
  1816.                 case PMERR_INVALID_APPL: //  (0x1530)
  1817.                         // Attempted to start an application whose type is not
  1818.                         // recognized by OS/2.
  1819.                         // This we get also if the executable doesn't exist...
  1820.                         // V0.9.18 (2002-03-27) [umoeller]
  1821.                     // arc = ERROR_INVALID_EXE_SIGNATURE;
  1822.                     arc = ERROR_FILE_NOT_FOUND;
  1823.                 break;
  1824.  
  1825.                 case PMERR_INVALID_PARAMETERS: //  (0x1208)
  1826.                         // An application parameter value is invalid for
  1827.                         // its converted PM type. For  example: a 4-byte
  1828.                         // value outside the range -32 768 to +32 767 cannot be
  1829.                         // converted to a SHORT, and a negative number cannot
  1830.                         // be converted to a ULONG or USHORT.
  1831.                     arc = ERROR_INVALID_DATA;
  1832.                 break;
  1833.  
  1834.                 case PMERR_STARTED_IN_BACKGROUND: //  (0x1532)
  1835.                         // The application started a new session in the
  1836.                         // background.
  1837.                     arc = ERROR_SMG_START_IN_BACKGROUND;
  1838.                 break;
  1839.  
  1840.                 case PMERR_INVALID_WINDOW: // (0x1206)
  1841.                         // The window specified with a Window List call
  1842.                         // is not a valid frame window.
  1843.  
  1844.                 default:
  1845.                     arc = ERROR_BAD_FORMAT;
  1846.                 break;
  1847.             }
  1848.  
  1849.             WinFreeErrorInfo(pei);
  1850.         }
  1851.     }
  1852.  
  1853.     return arc;
  1854. }
  1855.  
  1856. /*
  1857.  *@@ appStartApp:
  1858.  *      wrapper around WinStartApp which fixes the
  1859.  *      specified PROGDETAILS to (hopefully) work
  1860.  *      work with all executable types.
  1861.  *
  1862.  *      This first calls appBuildProgDetails (see
  1863.  *      remarks there) and then calls WinStartApp.
  1864.  *
  1865.  *      Since this calls WinStartApp in turn, this
  1866.  *      requires a message queue on the calling thread.
  1867.  *
  1868.  *      Note that this also does minimal checking on
  1869.  *      the specified parameters so it can return something
  1870.  *      more meaningful than FALSE like WinStartApp.
  1871.  *      As a result, you get a DOS error code now (V0.9.16).
  1872.  *
  1873.  *      Most importantly:
  1874.  *
  1875.  *      --  ERROR_INVALID_THREADID: not running on thread 1.
  1876.  *          See remarks below.
  1877.  *
  1878.  *      --  ERROR_NOT_ENOUGH_MEMORY
  1879.  *
  1880.  *      plus the many error codes from appBuildProgDetails,
  1881.  *      which gets called in turn.
  1882.  *
  1883.  *      <B>About enforcing thread 1</B>
  1884.  *
  1885.  *      OK, after long, long debugging hours, I have found
  1886.  *      that WinStartApp hangs the system in the following
  1887.  *      cases hard:
  1888.  *
  1889.  *      --  If a Win-OS/2 session is started and WinStartApp
  1890.  *          is _not_ on thread 1. For this reason, we check
  1891.  *          if the caller is trying to start a Win-OS/2
  1892.  *          session and return ERROR_INVALID_THREADID if
  1893.  *          this is not running on thread 1.
  1894.  *
  1895.  *      --  By contrast, there are many situations where
  1896.  *          calling WinStartApp from within the Workplace
  1897.  *          process will hang the system, most notably
  1898.  *          with VIO sessions. I have been unable to figure
  1899.  *          out why this happens, so XWorkplace now uses
  1900.  *          its daemon to call WinStartApp instead.
  1901.  *
  1902.  *          As a word of wisdom, do not call this from
  1903.  *          within the Workplace process. For some strange
  1904.  *          reason though, the XWorkplace "Run" dialog
  1905.  *          (which uses this) _does_ work. Whatever.
  1906.  *
  1907.  *@@added V0.9.6 (2000-10-16) [umoeller]
  1908.  *@@changed V0.9.7 (2000-12-10) [umoeller]: PROGDETAILS.swpInitial no longer zeroed... this broke VIOs
  1909.  *@@changed V0.9.7 (2000-12-17) [umoeller]: PROGDETAILS.pszEnvironment no longer zeroed
  1910.  *@@changed V0.9.9 (2001-01-27) [umoeller]: crashed if PROGDETAILS.pszExecutable was NULL
  1911.  *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed PROG_DEFAULT
  1912.  *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
  1913.  *@@changed V0.9.14 (2001-08-07) [pr]: removed some env. strings for Win. apps.
  1914.  *@@changed V0.9.14 (2001-08-23) [pr]: added session type options
  1915.  *@@changed V0.9.16 (2001-10-19) [umoeller]: added prototype to return APIRET
  1916.  *@@changed V0.9.16 (2001-10-19) [umoeller]: added thread-1 check
  1917.  *@@changed V0.9.16 (2001-12-06) [umoeller]: now using doshSearchPath for finding pszExecutable if not qualified
  1918.  *@@changed V0.9.16 (2002-01-04) [umoeller]: removed error report if startup directory was drive letter only
  1919.  *@@changed V0.9.16 (2002-01-04) [umoeller]: added more detailed error reports and *FailingName params
  1920.  *@@changed V0.9.18 (2002-02-13) [umoeller]: added CallWinStartApp to fix possible memory problems
  1921.  *@@changed V0.9.18 (2002-03-27) [umoeller]: no longer returning ERROR_INVALID_THREADID, except for Win-OS/2 sessions
  1922.  *@@changed V0.9.18 (2002-03-27) [umoeller]: extracted appBuildProgDetails
  1923.  *@@changed V0.9.19 (2002-03-28) [umoeller]: adjusted for new appBuildProgDetails
  1924.  */
  1925.  
  1926. APIRET appStartApp(HWND hwndNotify,        // in: notify window or NULLHANDLE
  1927.                    const PROGDETAILS *pcProgDetails, // in: program spec (req.)
  1928.                    ULONG ulFlags,          // in: APP_RUN_* flags  or 0
  1929.                    HAPP *phapp,            // out: application handle if NO_ERROR is returned
  1930.                    ULONG cbFailingName,
  1931.                    PSZ pszFailingName)
  1932. {
  1933.     APIRET          arc;
  1934.  
  1935.     PPROGDETAILS    pDetails;
  1936.  
  1937.     if (!phapp)
  1938.         return ERROR_INVALID_PARAMETER;
  1939.  
  1940.     if (!(arc = appBuildProgDetails(&pDetails,
  1941.                                     pcProgDetails,
  1942.                                     ulFlags)))
  1943.     {
  1944.         if (pszFailingName)
  1945.             strhncpy0(pszFailingName, pDetails->pszExecutable, cbFailingName);
  1946.  
  1947.         if (    (appIsWindowsApp(pDetails->progt.progc))
  1948.              && (doshMyTID() != 1)          // V0.9.16 (2001-10-19) [umoeller]
  1949.            )
  1950.             arc = ERROR_INVALID_THREADID;
  1951.         else
  1952.             arc = CallWinStartApp(phapp,
  1953.                                   hwndNotify,
  1954.                                   pDetails,
  1955.                                   cbFailingName,
  1956.                                   pszFailingName);
  1957.  
  1958.         DosFreeMem(pDetails);
  1959.  
  1960.     } // end if (ProgDetails.pszExecutable)
  1961.  
  1962.     #ifdef DEBUG_PROGRAMSTART
  1963.         _Pmpf((__FUNCTION__ ": returning %d", arc));
  1964.     #endif
  1965.  
  1966.     return arc;
  1967. }
  1968.  
  1969. /*
  1970.  *@@ appWaitForApp:
  1971.  *      waits for the specified application to terminate
  1972.  *      and returns its exit code.
  1973.  *
  1974.  *@@added V0.9.9 (2001-03-07) [umoeller]
  1975.  */
  1976.  
  1977. BOOL appWaitForApp(HWND hwndNotify,     // in: notify window
  1978.                    HAPP happ,           // in: app to wait for
  1979.                    PULONG pulExitCode)  // out: exit code (ptr can be NULL)
  1980. {
  1981.     BOOL brc = FALSE;
  1982.  
  1983.     if (happ)
  1984.     {
  1985.         // app started:
  1986.         // enter a modal message loop until we get the
  1987.         // WM_APPTERMINATENOTIFY for happ. Then we
  1988.         // know the app is done.
  1989.         HAB     hab = WinQueryAnchorBlock(hwndNotify);
  1990.         QMSG    qmsg;
  1991.         // ULONG   ulXFixReturnCode = 0;
  1992.         while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
  1993.         {
  1994.             if (    (qmsg.msg == WM_APPTERMINATENOTIFY)
  1995.                  && (qmsg.hwnd == hwndNotify)
  1996.                  && (qmsg.mp1 == (MPARAM)happ)
  1997.                )
  1998.             {
  1999.                 // xfix has terminated:
  2000.                 // get xfix return code from mp2... this is:
  2001.                 // -- 0: everything's OK, continue.
  2002.                 // -- 1: handle section was rewritten, restart Desktop
  2003.                 //       now.
  2004.                 if (pulExitCode)
  2005.                     *pulExitCode = (ULONG)qmsg.mp2;
  2006.                 brc = TRUE;
  2007.                 // do not dispatch this
  2008.                 break;
  2009.             }
  2010.  
  2011.             WinDispatchMsg(hab, &qmsg);
  2012.         }
  2013.     }
  2014.  
  2015.     return brc;
  2016. }
  2017.  
  2018. /*
  2019.  *@@ appQuickStartApp:
  2020.  *      shortcut for simply starting an app.
  2021.  *
  2022.  *      On errors, NULLHANDLE is returned.
  2023.  *
  2024.  *      Only if pulExitCode != NULL, we wait for
  2025.  *      the app to complete and return the
  2026.  *      exit code.
  2027.  *
  2028.  *@@added V0.9.16 (2001-10-19) [umoeller]
  2029.  *@@changed V0.9.20 (2002-08-10) [umoeller]: fixed missing destroy window, made wait optional
  2030.  *@@changed V0.9.20 (2002-08-10) [umoeller]: added pcszWorkingDir
  2031.  */
  2032.  
  2033. HAPP appQuickStartApp(const char *pcszFile,
  2034.                       ULONG ulProgType,             // e.g. PROG_PM
  2035.                       const char *pcszArgs,         // in: arguments (can be NULL)
  2036.                       const char *pcszWorkingDir,   // in: working dir (can be NULL)
  2037.                       PULONG pulExitCode)           // out: exit code; if ptr is NULL, we don't wait
  2038. {
  2039.     PROGDETAILS    pd = {0};
  2040.     HAPP           happ,
  2041.                    happReturn = NULLHANDLE;
  2042.     CHAR           szDir[CCHMAXPATH] = "";
  2043.     PCSZ           p;
  2044.     HWND           hwndObject;
  2045.  
  2046.     pd.Length = sizeof(pd);
  2047.     pd.progt.progc = ulProgType;
  2048.     pd.progt.fbVisible = SHE_VISIBLE;
  2049.     pd.pszExecutable = (PSZ)pcszFile;
  2050.     pd.pszParameters = (PSZ)pcszArgs;
  2051.  
  2052.     if (    (!(pd.pszStartupDir = (PSZ)pcszWorkingDir))
  2053.          && (p = strrchr(pcszFile, '\\'))
  2054.        )
  2055.     {
  2056.         strhncpy0(szDir,
  2057.                   pcszFile,
  2058.                   p - pcszFile);
  2059.         pd.pszStartupDir = szDir;
  2060.     }
  2061.  
  2062.     if (    (hwndObject = winhCreateObjectWindow(WC_STATIC, NULL))
  2063.          && (!appStartApp(hwndObject,
  2064.                           &pd,
  2065.                           0,
  2066.                           &happ,
  2067.                           0,
  2068.                           NULL))
  2069.        )
  2070.     {
  2071.         if (pulExitCode)
  2072.             appWaitForApp(hwndObject,
  2073.                           happ,
  2074.                           pulExitCode);
  2075.  
  2076.         happReturn = happ;
  2077.  
  2078.         WinDestroyWindow(hwndObject);       // was missing V0.9.20 (2002-08-10) [umoeller]
  2079.     }
  2080.  
  2081.     return happReturn;
  2082. }
  2083.  
  2084. /*
  2085.  *@@ appOpenURL:
  2086.  *      opens the system default browser with the given
  2087.  *      URL.
  2088.  *
  2089.  *@@added V0.9.20 (2002-08-10) [umoeller]
  2090.  */
  2091.  
  2092. BOOL appOpenURL(PCSZ pcszURL)
  2093. {
  2094.     BOOL        brc = FALSE;
  2095.  
  2096.     CHAR        szBrowser[CCHMAXPATH],
  2097.                 szStartupDir[CCHMAXPATH];
  2098.     XSTRING     strParameters;
  2099.  
  2100.     xstrInit(&strParameters, 0);
  2101.  
  2102.     if (PrfQueryProfileString(HINI_USER,
  2103.                               "WPURLDEFAULTSETTINGS",
  2104.                               "DefaultBrowserExe",
  2105.                               "NETSCAPE.EXE",
  2106.                               szBrowser,
  2107.                               sizeof(szBrowser)))
  2108.     {
  2109.         PSZ pszDefParams;
  2110.  
  2111.         if (pszDefParams = prfhQueryProfileData(HINI_USER,
  2112.                                                 "WPURLDEFAULTSETTINGS",
  2113.                                                 "DefaultParameters",
  2114.                                                 NULL))
  2115.         {
  2116.             xstrcpy(&strParameters, pszDefParams, 0);
  2117.             xstrcatc(&strParameters, ' ');
  2118.             free(pszDefParams);
  2119.         }
  2120.  
  2121.         xstrcat(&strParameters, pcszURL, 0);
  2122.  
  2123.         PrfQueryProfileString(HINI_USER,
  2124.                               "WPURLDEFAULTSETTINGS",
  2125.                               "DefaultWorkingDir",
  2126.                               "",
  2127.                               szStartupDir,
  2128.                               sizeof(szStartupDir));
  2129.  
  2130.  
  2131.         brc = !!appQuickStartApp(szBrowser,
  2132.                                  PROG_DEFAULT,
  2133.                                  strParameters.psz,
  2134.                                  szStartupDir,
  2135.                                  NULL);            // don't wait
  2136.     }
  2137.  
  2138.     xstrClear(&strParameters);
  2139.  
  2140.     return brc;
  2141. }
  2142.