
/*
 *@@sourcefile xwppgm.c:
 *      This file contains SOM code for the following XWorkplace classes:
 *
 *      --  XWPProgram (WPProgram replacement)
 *
 *      XFldProgram is only responsible for overriding the
 *      "Associations" page at this point.
 *
 *      Installation of this class is optional.
 *
 *@@added V0.9.9 (2001-04-02) [umoeller]
 *@@somclass XWPProgram xpg_
 *@@somclass M_XWPProgram xpgM_
 */

/*
 *      Copyright (C) 2001-2003 Ulrich M”ller.
 *
 *      This file is part of the XWorkplace source package.
 *      XWorkplace is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published
 *      by the Free Software Foundation, in version 2 as it comes in the
 *      "COPYING" file of the XWorkplace main distribution.
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 */

/*
 *  This file was generated by the SOM Compiler and Emitter Framework.
 *  Generated using:
 *      SOM Emitter emitctm: 2.41
 */

#ifndef SOM_Module_xwppgm_Source
#define SOM_Module_xwppgm_Source
#endif
#define XWPProgram_Class_Source
#define M_XWPProgram_Class_Source

#pragma strings(readonly)

/*
 *  Suggested #include order:
 *  1)  os2.h
 *  2)  C library headers
 *  3)  setup.h (code generation and debugging options)
 *  4)  headers in helpers\
 *  5)  at least one SOM implementation header (*.ih)
 *  6)  dlgids.h, headers in shared\ (as needed)
 *  7)  headers in implementation dirs (e.g. filesys\, as needed)
 *  8)  #pragma hdrstop and then more SOM headers which crash with precompiled headers
 */

#define INCL_DOSPROCESS
#define INCL_DOSEXCEPTIONS
#define INCL_DOSSEMAPHORES
#define INCL_DOSSESMGR          // DosQueryAppType
#define INCL_DOSERRORS

#define INCL_WINPOINTERS
#define INCL_WINPROGRAMLIST     // needed for PROGDETAILS, wppgm.h
#define INCL_WINSHELLDATA
#include <os2.h>

// C library headers
#include <stdio.h>
#include <setjmp.h>             // needed for except.h
#include <assert.h>             // needed for except.h

// generic headers
#include "setup.h"                      // code generation and debugging options

// headers in /helpers
#include "helpers\apps.h"               // application helpers
#include "helpers\dosh.h"
#include "helpers\except.h"             // exception handling
#include "helpers\exeh.h"               // executable helpers
#include "helpers\prfh.h"               // INI file helper routines
#include "helpers\standards.h"          // some standard macros
#include "helpers\stringh.h"            // string helper routines
#include "helpers\winh.h"

// SOM headers which don't crash with prec. header files
// #include "xfobj.ih"
#include "xfdataf.ih"
#include "xwppgm.ih"

// XWorkplace implementation headers
#include "dlgids.h"                     // all the IDs that are shared with NLS
#include "shared\common.h"              // the majestic XWorkplace include file
#include "shared\kernel.h"              // XWorkplace Kernel
#include "shared\init.h"                // XWorkplace initialization
#include "shared\notebook.h"            // generic XWorkplace notebook handling
#include "shared\wpsh.h"                // some pseudo-SOM functions (WPS helper routines)

#include "filesys\filetype.h"           // extended file types implementation
#include "filesys\icons.h"              // icons handling
#include "filesys\object.h"             // XFldObject implementation
#include "filesys\program.h"            // program implementation; WARNING: this redefines macros

#pragma hdrstop                         // VAC++ keeps crashing otherwise

/* ******************************************************************
 *
 *   Global variables
 *
 ********************************************************************/

static PCSZ     G_pcszWPProgramOrig = "WPProgramRef";

/* ******************************************************************
 *
 *   Helpers
 *
 ********************************************************************/

#ifdef _PMPRINTF_

STATIC void DumpMemoryBlock(PBYTE pb,       // in: start address
                     ULONG ulSize,   // in: size of block
                     ULONG ulIndent) // in: how many spaces to put
                                     //     before each output line
{
    TRY_QUIET(excpt1)
    {
        PSZ psz;
        if (psz = strhCreateDump(pb, ulSize, ulIndent))
        {
            _Pmpf(("\n%s", psz));
            free(psz);
        }
    }
    CATCH(excpt1)
    {
        _Pmpf(("Crash in " __FUNCTION__ ));
    } END_CATCH();
}

#endif

/* ******************************************************************
 *
 *   XWPProgram instance methods
 *
 ********************************************************************/

/*
 *@@ xwpAddAssociationsPage:
 *      this new XWPProgram method adds our replacement
 *      "Associations" page to an executable's settings notebook.
 *
 *      Gets called from our
 *      XWPProgram::wpAddProgramAssociationPage override,
 *      if extended associations are enabled.
 */

SOM_Scope ULONG  SOMLINK xpg_xwpAddAssociationsPage(XWPProgram *somSelf,
                                                    HWND hwndNotebook)
{
    ULONG ulrc = 0;
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xwppgm_xwpAddAssociationsPage");

#ifndef __NOTURBOFOLDERS__
    ulrc = ftypInsertAssociationsPage(somSelf,
                                      hwndNotebook);
#endif
    return ulrc;
}

/*
 *@@ xwpQuerySetup2:
 *      this XFldObject method is overridden to support
 *      setup strings for program files.
 *
 *      This uses code in filesys\filesys.c which is
 *      shared for WPProgram and WPProgramFile instances.
 *
 *      See XFldObject::xwpQuerySetup2 for details.
 *
 *@@changed V0.9.16 (2001-10-11) [umoeller]: adjusted to new implementation
 *@@changed V1.0.1 (2002-12-08) [umoeller]: now calling parent methods directly
 */

SOM_Scope BOOL  SOMLINK xpg_xwpQuerySetup2(XWPProgram *somSelf,
                                           PVOID pstrSetup)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xwppgm_xwpQuerySetup2");

    // call implementation
    if (progQuerySetup(somSelf, pstrSetup))
        return parent_xwpQuerySetup2(somSelf,
                                     pstrSetup);

    return FALSE;
}

/*
 *@@ xwpQueryExecutable:
 *      this new XWPProgram instance method writes the
 *      executable that this program represents into
 *      the specified buffer, which must be CCHMAXPATH
 *      in size.
 *
 *      This method is handy if you only need the program's
 *      executable and do not want to go thru the overhead
 *      of calling wpQueryProgDetails.
 *
 *      If TRUE is returned, pszBuffer has received the
 *      program's executable name, which may or may not
 *      be fully qualified. Note that the buffer receives
 *      a "*" string only if the app is a command prompt.
 *
 *      Returns FALSE if the internal executable data is
 *      empty or invalid.
 *
 *@@added V0.9.16 (2002-01-04) [umoeller]
 *@@changed V0.9.18 (2002-03-16) [umoeller]: adjusted for new instance data handling
 *@@changed V0.9.18 (2002-03-16) [umoeller]: added exception handling
 */

SOM_Scope BOOL  SOMLINK xpg_xwpQueryExecutable(XWPProgram *somSelf,
                                               PSZ pszBuffer)
{
    BOOL                brc = FALSE;
    PIBMPROGRAMDATA   pData;
    XWPProgramData      *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_xwpQueryExecutable");

    // have we found the WPProgram instance data yet?
    if (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
    {
        TRY_LOUD(excpt1)
        {
            HOBJECT hobj;
            WPObject *pobj;

            PMPF_ICONREPLACEMENTS(("[%s] ulExecutableHandle 0x%lX",
                    _wpQueryTitle(somSelf),
                    pData->ulExecutableHandle));

            if (pData->ulExecutableHandle == 0xFFFF)
            {
                // command line
                memcpy(pszBuffer, "*", 2);
                brc = TRUE;
            }
            else if (pData->pszExecutable)
            {
                // executable string present: use that instead
                strhncpy0(pszBuffer, pData->pszExecutable, CCHMAXPATH);
                brc = TRUE;
            }
            else if (    (hobj = pData->ulExecutableHandle)
                      && (pobj = _wpclsQueryObject(_WPObject,
                                                   hobj | (G_usHiwordFileSystem << 16)))
                      && (objQueryFlags(pobj) & OBJFL_WPFILESYSTEM)
                      && (_wpQueryFilename(pobj, pszBuffer, TRUE))
                    )
                brc = TRUE;
        }
        CATCH(excpt1)
        {
        }
        END_CATCH();
    }

    return brc;
}

/*
 *@@ wpInitData:
 *      this WPObject instance method gets called when the
 *      object is being initialized (on wake-up or creation).
 *      We initialize our additional instance data here.
 *      Always call the parent method first.
 *
 *      Here we get a pointer to the WPProgram instance
 *      data, using a newly discovered hack.
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 *@@changed V0.9.18 (2002-03-16) [umoeller]: new WPProgram instance data handling!
 */

SOM_Scope void  SOMLINK xpg_wpInitData(XWPProgram *somSelf)
{
    static SOMClass *s_pWPProgram = NULL;

    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpInitData");

    XWPProgram_parent_WPProgram_wpInitData(somSelf);

    _fNeedsSetProgIcon = FALSE;

    _pWszUsingIconFile = NULL;

    // V0.9.18 (2002-03-16) [umoeller]

    // Alright, here's the new hack for getting a pointer to the
    // WPS instance data. We used to intercept the pointers in
    // XWPProgram::wpRestoreState, but that hasn't turned out
    // for the best because we'd never get the correct pointers
    // in the cases where a program was newly created (because
    // then it isn't restored in the first case). Even worse,
    // if people didn't have turbo folders enabled, the replacements
    // got partly confused.

    // Browsing through the SOM reference, I have found a few
    // half-documented methods for getting a pointer to the
    // instance data for any class. This is now used for hacking
    // ourselves into the WPProgram (IBM) instance data directly,
    // so we can hack that data instead of keeping a second copy.

    // So here we go:

    // (1)  Get a pointer to the actual WPProgram class object.
    //      We can't use _WPProgram because that returns the
    //      replacement class, XWPProgram. So climb up XWPProgram's
    //      parents until we find the actual class object. This
    //      should also work if there are several WPProgram
    //      replacements installed.
    //      Since the class object will always be the same, use a
    //      static variable.

    if (!s_pWPProgram)
    {
        // first call:
        SOMClass *pClass = _somGetClass(somSelf);
                        // XWPProgram class object now

        while (pClass = _somGetParent(pClass))
                    // either WPProgram class object or another
                    // WPProgram replacement now
        {
            if (!strcmp(_somGetName(pClass), "WPProgram"))
            {
                // got it:
                s_pWPProgram = pClass;
                break;
            }
        }
    }

    // (2)  Get the pointer to this object's instance data, using
    //      the class object determined above. somGetInstanceToken
    //      is a documented SOMClass method and should thus always
    //      work.
    //      Store the result in the object instance data so we can
    //      keep using it for this object. (Naturally, each object
    //      has its own instance data.)

    if (s_pWPProgram)
        _pvWPProgramData = somDataResolve(somSelf, _somGetInstanceToken(s_pWPProgram));
    else
        _pvWPProgramData = NULL; // shouldn't happen, but be safe

    //  We can now always use this pointer to hack ourselves into
    //  the WPProgram instance data at all times. For this, we
    //  need to typecast the above void* pointer to IBMPROGRAMDATA*,
    //  which is declared in include\filesys\program.h. Hopefully
    //  IBM hasn't changed the struct ordering with any versions of
    //  OS/2 (famous last words).

    // initialize other variables
    _fWeAllocatedExecutable = FALSE;
    _fWeAllocatedParameters = FALSE;
    _fWeAllocatedEnvironment = FALSE;
}

/*
 *@@ wpUnInitData:
 *      this WPObject instance method is called when the object
 *      is destroyed as a SOM object, either because it's being
 *      made dormant (via wpMakeDormant) or being deleted for
 *      good (via wpFree). All allocated in-memory resources
 *      should be freed here, but to destroy the physical
 *      representation of the object, override wpDestroyObject
 *      instead.
 *
 *      The parent method must always be called last.
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 *@@changed V0.9.18 (2002-03-16) [umoeller]: adjusted for new instance data handling
 */

SOM_Scope void  SOMLINK xpg_wpUnInitData(XWPProgram *somSelf)
{
    PIBMPROGRAMDATA pData;
    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpUnInitData");

    // have we found the WPProgram instance data yet?
    if (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
    {
        // alright, check if we allocated any strings...
        // if so, clean them out here, and set each ptr
        // to NULL so that the WPS won't try to free it
        // again in the parent method
        if (pData->pszExecutable && _fWeAllocatedExecutable)
        {
            _wpFreeMem(somSelf, pData->pszExecutable);
            pData->pszExecutable = NULL;
        }
        if (pData->pszParameters && _fWeAllocatedParameters)
        {
            _wpFreeMem(somSelf, pData->pszParameters);
            pData->pszParameters = NULL;
        }
        if (pData->pszEnvironment && _fWeAllocatedEnvironment)
        {
            _wpFreeMem(somSelf, pData->pszEnvironment);
            pData->pszEnvironment = NULL;
        }
    }

    wpshStore(somSelf,
              &_pWszUsingIconFile,
              NULL,
              NULL);

    XWPProgram_parent_WPProgram_wpUnInitData(somSelf);
}

/*
 *@@ wpDestroyObject:
 *      this undocumented WPObject method gets called during
 *      wpFree processing to destroy the physical storage of
 *      an object (for file-system objects, the file or folder,
 *      for abstracts, the INI data).
 *
 *      Starting with V0.9.20, we are now able to override this
 *      undocumented WPS method too, so the previous overhead
 *      with xwpDestroyStorage has been removed.
 *
 *      We override this in order to prevent the original
 *      WPProgram::wpDestroyObject to be called, which messes
 *      wrongly with our association data. Instead,
 *      we destroy any association data in the OS2.INI
 *      file ourselves and them jump directly to
 *      WPAbstract::wpDestroyObject.
 *
 *@@added V0.9.20 (2002-07-25) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xpg_wpDestroyObject(XWPProgram *somSelf)
{
    static somTD_WPObject_wpDestroyObject pWPAbstract_wpDestroyObject = NULL;

    BOOL brc = FALSE;

    // XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpDestroyObject");

    if (!pWPAbstract_wpDestroyObject)
    {
        // first call:
        // resolve WPAbstract::wpDestroyObject
        // (skip WPProgram parent call!)
        pWPAbstract_wpDestroyObject
            = (somTD_WPObject_wpDestroyObject)wpshResolveFor(somSelf,
                                                   _WPAbstract,
                                                   "wpDestroyObject");
    }

    if (pWPAbstract_wpDestroyObject)
    {
        // clean up program resources in INI file;
        // there's no way to avoid running through
        // the entire handles cache, unfortunately...
        // _wpQueryHandle is safe, since each WPProgram
        // must have one per definition
        ftypAssocObjectDeleted(somSelf);        // V0.9.20 (2002-07-31) [umoeller]

        // call WPAbstract::wpDestroyObject explicitly,
        // skipping WPProgram
        brc = pWPAbstract_wpDestroyObject(somSelf);
    }
    else
        cmnLog(__FILE__, __LINE__, __FUNCTION__,
               "Cannot resolve WPAbstract::wpDestroyObject.");

    return brc;

    // return XWPProgram_parent_WPProgram_wpDestroyObject(somSelf);
}

/*
 *@@ wpObjectReady:
 *      this WPObject notification method gets called by the
 *      WPS when object instantiation is complete, for any reason.
 *      ulCode and refObject signify why and where from the
 *      object was created.
 *      The parent method must be called first.
 *
 *      See XFldObject::wpObjectReady for remarks about using
 *      this method as a copy constructor.
 *
 *      In here, we check for whether a non-default icon
 *      was loaded for the object from the INI file. If not,
 *      we call XWPProgram::wpSetProgIcon. See the detailed
 *      remarks there for details.
 *
 *@@added V0.9.16 (2002-01-04) [umoeller]
 */

SOM_Scope void  SOMLINK xpg_wpObjectReady(XWPProgram *somSelf,
                                          ULONG ulCode,
                                          WPObject* refObject)
{
    XWPProgramMethodDebug("XWPProgram","xpg_wpObjectReady");

    XWPProgram_parent_WPProgram_wpObjectReady(somSelf, ulCode,
                                              refObject);
    if (icomRunReplacement())
    {
        XWPProgramData *somThis = XWPProgramGetData(somSelf);

        // now refresh the flag for wpQueryIcon so that it knows
        // if it needs to set a new icon for the program object
        // when it's first needed:
        if (    (ulCode == OR_NEW)
             || (    (_fNeedsSetProgIcon)
                     // did wpRestoreState restore an icon from os2.ini?
                  && (!(_wpQueryStyle(somSelf) & OBJSTYLE_NOTDEFAULTICON))
                )
           )
            _fNeedsSetProgIcon = TRUE;
        else
            _fNeedsSetProgIcon = FALSE;
    }
}

/*
 *@@ wpQueryIcon:
 *      this WPObject instance method returns the HPOINTER
 *      with the current icon of the object. For some WPS
 *      classes, icon loading is deferred until the first
 *      call to this method.
 *      See icons.c for an introduction.
 *
 *      Normally WPProgram icons are _always_ loaded when
 *      the object is instantiated, even if the icon is
 *      never displayed. We now introduce deferred icon
 *      loading as with WPDataFile.
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 */

SOM_Scope HPOINTER  SOMLINK xpg_wpQueryIcon(XWPProgram *somSelf)
{
    XWPProgramMethodDebug("XWPProgram","xpg_wpQueryIcon");

    if (icomRunReplacement())
    {
        XWPProgramData *somThis = XWPProgramGetData(somSelf);
        PMINIRECORDCORE pmrc = _wpQueryCoreRecord(somSelf);

        if (    (!pmrc->hptrIcon)
             || (_fNeedsSetProgIcon)
           )
        {
            _fNeedsSetProgIcon = FALSE;
            _wpSetProgIcon(somSelf, NULL);
        }

        return pmrc->hptrIcon;
    }

    return XWPProgram_parent_WPProgram_wpQueryIcon(somSelf);
}

/*
 *@@ ProgramIconHandler:
 *      common handler for XWPProgram::wpSetProgIcon
 *      and XWPProgram::wpQueryIconData. See remarks there.
 *
 *      Returns FALSE only on crashes.
 *
 *@@added V0.9.18 (2002-03-19) [umoeller]
 *@@changed V0.9.20 (2002-07-03) [umoeller]: fixed bad icons for "*" command prompts
 *@@changed V1.0.0 (2002-11-23) [umoeller]: this never checked for .ICON EAs (ProNews case) @@fixes 34
 */

STATIC BOOL ProgramIconHandler(XWPProgram *somSelf,
                               PIBMPROGRAMDATA pData,
                               XWPProgramData *somThis,
                               HPOINTER *phptr,      // out: if != NULL, icon handle
                               PULONG pcbIconInfo,   // out: if != NULL, size of ICONINFO buffer required
                               PICONINFO pIconInfo,  // out: if != NULL, icon info
                               BOOL *pfNotDefaultIcon) // out: set to TRUE if non-default icon (ptr can be NULL)
{
    BOOL        brc = FALSE;
    BOOL        fLocked = FALSE;

    PMPF_ICONREPLACEMENTS(("entering"));

    TRY_LOUD(excpt1)
    {
        CHAR            szExecutable[CCHMAXPATH];
        APIRET          arc;
        BOOL            fFound = FALSE;

        if (    (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
             && (_xwpQueryExecutable(somSelf, szExecutable))
           )
        {
            CHAR    szFQExecutable[CCHMAXPATH];
            PSZ     pszExec = NULL;

            PMPF_ICONREPLACEMENTS(("_xwpQueryExecutable returned %s", szExecutable));

            // handle icons for command lines V0.9.20 (2002-07-03) [umoeller]
            if (!strcmp(szExecutable, "*"))
            {
                if (    (pData->ProgType.progc)
                     && (!progFindIcon(NULL,     // no exec, get default icon for the type
                                       pData->ProgType.progc,
                                       phptr,
                                       pcbIconInfo,
                                       pIconInfo,
                                       pfNotDefaultIcon))
                   )
                    fFound = TRUE;
            }
            else if (    (szExecutable[1] != ':')
                      && (!strchr(szExecutable, '\\'))
                    )
            {
                // prog is not fully qualified: find it on path
                if (!doshSearchPath("PATH",
                                    szExecutable,
                                    szFQExecutable,
                                    sizeof(szFQExecutable)))
                    // alright, found it:
                    pszExec = szFQExecutable;
            }
            else
                pszExec = szExecutable;

            PMPF_ICONREPLACEMENTS(("pszExec is %s", STRINGORNULL(pszExec)));

            if (pszExec)
            {
                // this code was missing the check for .ICON EAs...
                // if the executable had an .ICON EA, this was completely
                // ignored because we always only searched for icons in
                // the executable resources (ProNews case): so look for
                // an .ICON first
                // V1.0.0 (2002-11-23) [umoeller]
                if (!(arc = icoBuildPtrFromEAs(pszExec,
                                               phptr,
                                               pcbIconInfo,
                                               pIconInfo)))
                {
                    if (phptr)
                        PMPF_ICONREPLACEMENTS(("got hptr 0x%lX from .ICON EA", *phptr));

                    if (pfNotDefaultIcon)
                        *pfNotDefaultIcon = TRUE;
                    fFound = TRUE;
                }
                else
                {
                    // next: check if an .ICO file of the same filestem
                    // exists in the folder -- if so, _always_ use that icon
                    PCSZ pExt;

                    PMPF_ICONREPLACEMENTS(("icoBuildPtrFromEAs returned %d", arc));

                    if (pExt = doshGetExtension(pszExec))
                    {
                        ULONG ulStemLen = pExt - pszExec;
                        CHAR szIconFile[CCHMAXPATH];
                        memcpy(szIconFile, pszExec, ulStemLen);
                        memcpy(szIconFile + ulStemLen, "ico", 4);   // include 0
                        // use the new icoLoadICOFile, this is way faster
                        // V0.9.16 (2001-12-08) [umoeller]
                                // NOTE: This only gets called for "create icon"
                                // mode, never with the "fill icon info" mode,
                                // because in that case wpQueryIconData checks
                                // _pWszUsingIconFile
                                // V0.9.18 (2002-03-19) [umoeller]
                        if (!(arc = icoLoadICOFile(szIconFile,
                                                   phptr,       // can be NULL
                                                   NULL,
                                                   NULL)))
                        {
                            // store this in the instance data so
                            // that wpQueryIconData can properly
                            // return this... we can't do this
                            // otherwise because we can't check
                            // if the icon file data is valid
                            // without building a pointer handle
                            // V0.9.18 (2002-03-19) [umoeller]
                            wpshStore(somSelf,
                                      &_pWszUsingIconFile,
                                      szIconFile,
                                      NULL);

                            if (pfNotDefaultIcon)
                                *pfNotDefaultIcon = TRUE;
                            fFound = TRUE;
                        }
                        // else: .ICO file doesn't exist, or bad format, so go on
                    }

                    if (!fFound)
                    {
                        PEXECUTABLE pExec = NULL;

                        wpshStore(somSelf,
                                  &_pWszUsingIconFile,
                                  NULL,
                                  NULL);

                        if (!(arc = exehOpen(pszExec, &pExec)))
                        {
                            ULONG ulProgType;
                            ULONG ulStdIcon = 0;

                            if (!(ulProgType = pData->ProgType.progc))
                                ulProgType = progQueryProgType(pszExec,
                                                               pExec);

                            PMPF_ICONREPLACEMENTS(("exehOpen'd %s, progtype is %d (%s)",
                                                    pszExec,
                                                    ulProgType,
                                                    exehDescribeProgType(ulProgType)));

                            if (!progFindIcon(pExec,
                                              ulProgType,
                                              phptr,
                                              pcbIconInfo,
                                              pIconInfo,
                                              pfNotDefaultIcon))
                                fFound = TRUE;

                            exehClose(&pExec);

                        } // end if (!(arc = exehOpen(pszExec, &pExec)))
                    }
                }
            }
        }

        if (!fFound)
        {
            /* cmnGetStandardIcon(STDICON_PROG_UNKNOWN,
                               phptr,
                               pcbIconInfo,
                               pIconInfo); */
            // no, bad decision above. The WPS "Ftp" and
            // "Telnet" classes override wpclsQueryIconData,
            // and we should respect that. So instead, run
            // thru the class methods; I have also overridden
            // wpclsQueryIconData to call the above.
            // V0.9.19 (2002-05-23) [umoeller]

            // output data, depending on what
            // the caller wants
            SOMClass *pClass = _somGetClass(somSelf);

            if (phptr)
                *phptr = _wpclsQueryIcon(pClass);

            if (pcbIconInfo)
                *pcbIconInfo = _wpclsQueryIconData(pClass,
                                                   NULL);        // return buffer size

            if (pIconInfo)
                _wpclsQueryIconData(pClass,
                                    pIconInfo);
        }

        brc = TRUE;
    }
    CATCH(excpt1)
    {
    } END_CATCH();

    if (fLocked)
        _wpReleaseObjectMutexSem(somSelf);

    return brc;
}

/*
 *@@ wpSetProgIcon:
 *      this instance method exists for both WPProgram and
 *      WPProgramFile and is supposed to set the visual icon
 *      for this executable file to the appropriate custom or
 *      default icon.
 *
 *      There are several reasons why we must replace this
 *      if icon replacements are on. Basically, icon management
 *      for abstracts apparently sucks even worse than that of
 *      data files.
 *
 *      The execution flow during object initialization is:
 *
 *      1)  WPProgram::wpRestoreState calls this even
 *          before calling the parent WPAbstract::wpRestoreState.
 *
 *      2)  Only WPAbstract::wpRestoreState looks in
 *          the INI file if the abstract has a non-default
 *          icon.
 *
 *      After long, long debugging sessions I have identified
 *      this method to be responsible for nuking my standard
 *      icons. In the worst case (which isn't all that rare),
 *      the following happens:
 *
 *      1)  WPProgram::wpRestoreState calls this method and
 *          we set a default executable icon, e.g. for a
 *          VIO window.
 *
 *      2)  WPProgram::wpRestoreState calls its parent,
 *          WPAbstract::wpRestoreState.
 *
 *      3)  WPAbstract::wpRestoreState calls WPObject::wpRestoreState
 *          and restores the object style. Even if we cleared
 *          OBJSTYLE_NOTDEFAULTICON a thousand times here,
 *          that style will be overwritten there.
 *
 *      4)  WPProgram::wpRestoreState then restores the icon
 *          data from OS2.INI if OBJSTYLE_NOTDEFAULTICON was
 *          set in the restored style. In the call to
 *          WPObject::wpSetIcon that follows, the WPS then
 *          nukes our default icon that was set during (1).
 *
 *      Sigh. Yes, the WPS apparently first goes thru the
 *      effort of finding an icon in the entire executable
 *      just to throw it away again right afterwards. In
 *      addition, it kills our shared icons.
 *
 *      Sooo... if icon replacements are enabled, what we
 *      MUST do is check for whether the object is
 *      initialized; if not, we simply return TRUE. In
 *      our XWPProgram::wpObjectReady override, we then
 *      call this method again if the OBJSTYLE_NOTDEFAULTICON
 *      flag wasn't set during wpRestoreState. This is not
 *      only _way_ faster but doesn't consume as many system
 *      resources.
 *
 *      If the object is initialized, we perform pretty much
 *      the same processing as with XWPProgramFile, i.e. we
 *      either load an icon from the executable or set a
 *      default icon. This method can be called several times
 *      (and will be called, e.g. if the user changes the
 *      executable).
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 *@@changed V0.9.18 (2002-03-19) [umoeller]: extracted ProgramIconHandler, reworked logic to no longer call the parent
 */

SOM_Scope BOOL  SOMLINK xpg_wpSetProgIcon(XWPProgram *somSelf,
                                          PFEA2LIST pfeal)
{
    PIBMPROGRAMDATA  pData;

    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpSetProgIcon");

    // have we found the WPProgram instance data yet?
    if (    (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
            // either turbo folders or ext assocs enabled?
         && (icomRunReplacement())
       )
    {
        BOOL        brc = FALSE;
        HPOINTER    hptr = NULLHANDLE;
        BOOL        fNotDefaultIcon = FALSE;

        // here's the important check that prevents our
        // standard icon from being nuked:
        if (!objIsObjectInitialized(somSelf))
        {
            // set flag for wpObjectReady so it can call
            // us again after the object has been fully
            // restored
            _fNeedsSetProgIcon = TRUE;
            // AND DO NOTHING YET, we will be called again,
            // but only if wpRestoreState hasn't found
            // an icon in os2.ini
            return TRUE;
        }

        if (brc = ProgramIconHandler(somSelf,
                                     pData,
                                     somThis,
                                     &hptr,
                                     NULL,
                                     NULL,
                                     &fNotDefaultIcon))
        {
            _wpSetIcon(somSelf, hptr);
            _wpModifyStyle(somSelf,
                           OBJSTYLE_NOTDEFAULTICON,
                           (fNotDefaultIcon) ? OBJSTYLE_NOTDEFAULTICON : 0);
        }

        return brc;
    }

    return XWPProgram_parent_WPProgram_wpSetProgIcon(somSelf, pfeal);
}

/*
 *@@ wpQueryIconData:
 *      this WPObject instance method can be called to find out the
 *      object's icon data.
 *
 *      See XWPProgramFile::wpQueryIconData for more information.
 *
 *      We need to override this too to fully support our replacement
 *      program icons in the WPS. Up to V0.9.18, the "Icon"
 *      notebook page was severly broken for programs.
 *
 *@@added V0.9.18 (2002-03-19) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xpg_wpQueryIconData(XWPProgram *somSelf,
                                             PICONINFO pIconInfo)
{
    PIBMPROGRAMDATA  pData;

    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpQueryIconData");

    // have we found the WPProgram instance data yet?
    if (    (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
            // either turbo folders or ext assocs enabled?
         && (icomRunReplacement())
       )
    {
        ULONG           cbRequired = sizeof(ICONINFO);

        TRY_LOUD(excpt1)
        {
            ULONG           cbData;
            // FIRST of all, check if we have a non-default icon
            // from OS2.INI... if so, this overrides anything else
            HOBJECT         hobj = _wpQueryHandle(somSelf);
            // abstract handles have the 16-bit hex handle as
            // the ini key
            CHAR            szHandle[10];
            sprintf(szHandle, "%lX", LOUSHORT(hobj));       // no leading zeros,
                                                            // hex letters in capitals
            PMPF_ICONREPLACEMENTS(("trying to load %s, %s",
                            WPINIAPP_ICONS, szHandle));

            if (    (PrfQueryProfileSize(HINI_USER,
                                         (PSZ)WPINIAPP_ICONS,  // "PM_Abstract:Icons"
                                         szHandle,
                                         &cbData))
                 && (cbData)
               )
            {
                // got custom icon data for this program object:
                cbRequired += cbData;

                wpshStore(somSelf,
                          &_pWszUsingIconFile,
                          NULL,
                          NULL);

                if (pIconInfo)
                {
                    // caller wants data too:
                    ZERO(pIconInfo);
                    pIconInfo->cb = cbRequired;
                    pIconInfo->fFormat = ICON_DATA;
                    pIconInfo->cbIconData = cbData;
                    pIconInfo->pIconData = (PBYTE)(pIconInfo + 1);

                    if (!PrfQueryProfileData(HINI_USER,
                                             (PSZ)WPINIAPP_ICONS,  // "PM_Abstract:Icons"
                                             szHandle,
                                             pIconInfo->pIconData,
                                             &cbData))
                        cbRequired = 0;
                        // if this fails, we're out of luck anyway... we
                        // can't fall back to loading from the exe because
                        // we probably won't have enough buffer
                }
            }
            else
            {
                // .ICON EA not found, or bad data:
                PMINIRECORDCORE pmrc = _wpQueryCoreRecord(somSelf);
                if (!pmrc->hptrIcon)
                    // we haven't set the icon yet (improbable but possible):
                    _wpSetProgIcon(somSelf, NULL);

                // now we can use the cached instance data fields
                if (_pWszUsingIconFile)
                {
                    // wpSetProgIcon has found an .ICO file:
                    ULONG ulNameLen = strlen(_pWszUsingIconFile);
                    cbRequired += ulNameLen;
                    if (pIconInfo)
                    {
                        PSZ psz = (PSZ)(pIconInfo + 1);
                        ZERO(pIconInfo);
                        pIconInfo->cb = cbRequired;
                        pIconInfo->fFormat = ICON_FILE;
                        memcpy(psz, _pWszUsingIconFile, ulNameLen + 1);
                        pIconInfo->pszFileName = psz;
                    }
                }
                else
                {
                    // not icon file: run the icon handler again
                    PMPF_ICONREPLACEMENTS(("calling ProgramIconHandler, cbRequired %d, pIconInfo 0x%lX",
                              cbRequired, pIconInfo));

                    if (!(ProgramIconHandler(somSelf,
                                             pData,
                                             somThis,
                                             NULL,          // HPOINTER *phptr,
                                             &cbRequired,   // PULONG pcbIconInfo,
                                             pIconInfo,     // can be NULL
                                             NULL)))
                        // crash:
                        cbRequired = 0;
                }
            }
        }
        CATCH(excpt1)
        {
            cbRequired = 0;
        } END_CATCH();

        return cbRequired;
    }

    return XWPProgram_parent_WPProgram_wpQueryIconData(somSelf,
                                                       pIconInfo);
}

/*
 *@@ wpSetIconData:
 *      this WPObject method sets a new persistent icon for
 *      the object and stores the new icon permanently.
 *
 *      We need to override this for our icon replacements
 *      because the WPS will do evil things to our standard
 *      icons again.
 *
 *      Note that we only handle the ICON_CLEAR case here
 *      to reload the exe default icon.
 *
 *@@added V0.9.18 (2002-03-19) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xpg_wpSetIconData(XWPProgram *somSelf,
                                          PICONINFO pIconInfo)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xpg_wpSetIconData");

    if (icomRunReplacement())
    {
        if (    (pIconInfo)
             && (pIconInfo->fFormat == ICON_CLEAR)
           )
        {
            // nuke icon data in OS2.INI
            HOBJECT         hobj = _wpQueryHandle(somSelf);
            CHAR            szHandle[10];
            sprintf(szHandle, "%lX", LOUSHORT(hobj));       // no leading zeros,
                                                            // hex letters in capitals
            PMPF_ICONREPLACEMENTS(("deleting %s, %s",
                            WPINIAPP_ICONS, szHandle));

            PrfWriteProfileData(HINI_USER,
                                (PSZ)WPINIAPP_ICONS,  // "PM_Abstract:Icons"
                                szHandle,
                                NULL,
                                0);

            // reset default executable icon
            _wpSetProgIcon(somSelf, NULL);

            // do not call parent
            return TRUE;
        }
    }

    // all other cases, or icon replacements disabled:
    // call parent (WPProgram)
    return XWPProgram_parent_WPProgram_wpSetIconData(somSelf,
                                                     pIconInfo);
}

/*
 *@@ wpQueryProgDetails:
 *      this instance method exists for both WPProgram
 *      and WPProgramFile and returns information about
 *      the executable program file in the given buffer.
 *      If pProgDetails is NULL, *pulSize receives the
 *      required size of the buffer; otherwise *pulSize
 *      is expected to specify the size of the buffer
 *      on input, which better be large enough.
 *
 *      This is a complete rewrite. We no longer call
 *      the parent because it's so damn slow and messes
 *      with too many things. The shared implementation
 *      for WPProgram and WPProgramFile is in
 *      progFillProgDetails; see remarks there.
 *
 *@@added V0.9.16 (2002-01-04) [umoeller]
 *@@changed V0.9.18 (2002-03-16) [umoeller]: added exception handling
 *@@changed V0.9.18 (2002-03-16) [umoeller]: adjusted for new instance data handling
 *@@changed V0.9.18 (2002-03-16) [umoeller]: fixed program creation failure
 */

SOM_Scope BOOL  SOMLINK xpg_wpQueryProgDetails(XWPProgram *somSelf,
                                               PPROGDETAILS pProgDetails,
                                               PULONG pulSize)
{
    PIBMPROGRAMDATA  pData;

    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpQueryProgDetails");

    // have we found the WPProgram instance data yet?
    if (    (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
            // either turbo folders or ext assocs enabled?
         && (icomRunReplacement())
       )
    {
        BOOL brc = FALSE;
        BOOL fLocked = FALSE;

        TRY_LOUD(excpt1)
        {
            if (    (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
                 // V0.9.18 (2002-03-16) [umoeller]
                 // no, no, we cannot return FALSE if there's no executable!!!
                 // this gets called from WPProgram::wpSetup which completely
                 // quits if this returns FALSE! This was the reason program
                 // creation failed all the time.
                 /* && (_xwpQueryExecutable(somSelf, szExecutable))
                 && (pszTitle = _wpQueryTitle(somSelf)) */
               )
            {
                CHAR        szExecutable[CCHMAXPATH];
                PCSZ        pcszExecutable = szExecutable;

                // V0.9.18 (2002-03-16) [umoeller]
                // set title and executable here instead
                PSZ         pszTitle = _wpQueryTitle(somSelf);
                if (!_xwpQueryExecutable(somSelf, szExecutable))
                    pcszExecutable = NULL;

                #ifdef __DEBUG__
                    PMPF_PROGRAMSTART(("[%s]: progc is %s",
                            pszTitle,
                            exehDescribeProgType(pData->ProgType.progc)));
                    PMPF_PROGRAMSTART(("pcszExecutable: \"%s\"", STRINGORNULL(pcszExecutable)));
                    PMPF_PROGRAMSTART(("startupdir 0x%lX", pData->ulStartupDirHandle));
                    PMPF_PROGRAMSTART(("params \"%s\"", STRINGORNULL(pData->pszParameters)));
                    PMPF_PROGRAMSTART(("pszEnvironment is 0x%lX",
                                        pData->pszEnvironment));
                    if (pData->pszEnvironment)
                    {
                        PSZ pszThis = pData->pszEnvironment;
                        while (*pszThis != 0)
                        {
                            PMPF_PROGRAMSTART(("  \"%s\"", pszThis));
                            pszThis += strlen(pszThis) + 1;
                        }
                    }
                #endif

                brc = progFillProgDetails(pProgDetails,     // can be NULL
                                          pData->ProgType.progc,
                                          pData->ProgType.fbVisible,
                                          &pData->SWPInitial,
                                          pszTitle,
                                          pcszExecutable,
                                          pData->ulStartupDirHandle,
                                          pData->pszParameters,
                                          pData->pszEnvironment,
                                          pulSize);

                PMPF_PROGRAMSTART(("progFillProgDetails returned %d", brc));
            }
        }
        CATCH(excpt1)
        {
        } END_CATCH();

        if (fLocked)
            _wpReleaseObjectMutexSem(somSelf);

        PMPF_PROGRAMSTART(("returning %d", brc));

        return brc;

    } // end if (RunReplacement)

    return XWPProgram_parent_WPProgram_wpQueryProgDetails(somSelf,
                                                          pProgDetails,
                                                          pulSize);
}

/*
 *@@ GetFSHandle:
 *      returns the 16-bit file-system handle for the
 *      given file, or null if the file is invalid.
 *
 *@@added V0.9.16 (2002-01-09) [umoeller]
 *@@changed V0.9.19 (2002-04-22) [pr]: Canonicalize path
 */

USHORT GetFSHandle(PCSZ pcszFile)
{
    ULONG           ulrc = 0;
    WPFileSystem    *pFile;
    CHAR            szFile[CCHMAXPATH];

    if (    (!doshCanonicalize(pcszFile,
                               szFile,
                               sizeof(szFile)))
         && (pFile = _wpclsQueryObjectFromPath(_WPFileSystem,
                                               szFile))
       )
    {
        ulrc = LOUSHORT(_wpQueryHandle(pFile));
        _wpUnlockObject(pFile);
    }

    return ulrc;
}

/*
 *@@ progStore:
 *      like wpshStore, but takes into account that
 *      the memory in *ppszTarget might have been
 *      allocated by the WPS.
 *
 *      Only if *pfWeAllocated is TRUE, we are allowed
 *      to use _wpFreeMem here. Reversely, if we allocate
 *      a new copy, we set *pfWeAllocated to TRUE.
 *
 *@@added V0.9.18 (2002-03-16) [umoeller]
 */

APIRET progStore(WPObject *somSelf,
                 PSZ *ppszTarget,
                 BOOL *pfWeAllocated,
                 PCSZ pcszSource,
                 PULONG pulLength)        // out: length of new string (ptr can be NULL)
{
    ULONG   ulLength = 0;

    if (ppszTarget && pfWeAllocated)
    {
        if (    (*ppszTarget)
             && (*pfWeAllocated)
           )
            _wpFreeMem(somSelf, *ppszTarget);
                            // possible memory leak here, but we can't free
                            // the WPS memory

        *pfWeAllocated = FALSE;

        if (    (pcszSource)
             && (ulLength = strlen(pcszSource))
           )
        {
            APIRET  arc = NO_ERROR;
            if (*ppszTarget = (PSZ)_wpAllocMem(somSelf,
                                               ulLength + 1,
                                               &arc))
            {
                memcpy(*ppszTarget, pcszSource, ulLength + 1);
                *pfWeAllocated = TRUE;
            }
            else
                return arc;
        }
        else
            *ppszTarget = NULL;
    }
    else
        return ERROR_INVALID_PARAMETER;

    if (pulLength)
        *pulLength = ulLength;

    return NO_ERROR;
}

/*
 *@@ wpSetProgDetails:
 *      this instance method exists for both WPProgram
 *      and WPProgramFile and must update the object's
 *      program data from the given buffer.
 *
 *      This is a complete rewrite.
 *
 *@@added V0.9.16 (2002-01-04) [umoeller]
 *@@changed V0.9.18 (2002-03-16) [umoeller]: adjusted for new instance data handling
 *@@changed V0.9.19 (2002-04-17) [umoeller]: added mutex
 */

SOM_Scope BOOL  SOMLINK xpg_wpSetProgDetails(XWPProgram *somSelf,
                                             PPROGDETAILS pProgDetails)
{
    PIBMPROGRAMDATA  pData;

    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpSetProgDetails");

    PMPF_PROGRAMSTART(("entering, _pvWPProgramData is 0x%lX", _pvWPProgramData));

    // have we found the WPProgram instance data yet?
    if (    (pData = (PIBMPROGRAMDATA)_pvWPProgramData)
            // either turbo folders or ext assocs enabled?
         && (icomRunReplacement())
       )
    {
        BOOL brc = FALSE;
        BOOL fLocked = FALSE;

        TRY_LOUD(excpt1)
        {
            // V0.9.19 (2002-04-17) [umoeller]
            if (fLocked = !_wpRequestObjectMutexSem(somSelf, SEM_INDEFINITE_WAIT))
            {
                PSZ     pszMyTitle = _wpQueryTitle(somSelf);
                BOOL    fSetProgIcon = FALSE;

                // progtype
                pData->ProgType.progc = pProgDetails->progt.progc;
                pData->ProgType.fbVisible = pProgDetails->progt.fbVisible;

                // title
                if (    (pProgDetails->pszTitle)
                     && (strhcmp(pszMyTitle, pProgDetails->pszTitle))
                   )
                {
                    PMPF_PROGRAMSTART(("    setting new title \"%s\"", pProgDetails->pszTitle));

                    _wpSetTitle(somSelf, pProgDetails->pszTitle);
                }

                // executable
                if (pProgDetails->pszExecutable)
                {
                    // executable specified:
                    ULONG hfs;

                    PMPF_PROGRAMSTART(("    input exec \"%s\"", pProgDetails->pszExecutable));

                    // "*" means command prompt
                    if (pProgDetails->pszExecutable[0] == '*')
                    {
                        if (    (pData->pszExecutable)
                             || (pData->ulExecutableHandle != 0xFFFF)
                           )
                        {
                            // handle changed:
                            progStore(somSelf,
                                      &pData->pszExecutable,
                                      &_fWeAllocatedExecutable,
                                      NULL,
                                      NULL);
                            pData->ulExecutableHandle = 0xFFFF;
                            fSetProgIcon = TRUE;
                        }
                    }
                    // try to find it
                    else if (    (pProgDetails->pszExecutable[1] == ':')
                              && (strchr(pProgDetails->pszExecutable, '\\'))
                              && (hfs = GetFSHandle(pProgDetails->pszExecutable))
                            )
                    {
                        // got executable file handle:
                        if (    (pData->pszExecutable)
                             || (pData->ulExecutableHandle != hfs)
                           )
                        {
                            // handle changed:
                            PMPF_PROGRAMSTART(("    setting new exe handle 0x%lX", hfs));

                            progStore(somSelf,
                                      &pData->pszExecutable,
                                      &_fWeAllocatedExecutable,
                                      NULL,
                                      NULL);
                            pData->ulExecutableHandle = hfs;
                            fSetProgIcon = TRUE;
                        }
                    }
                    else
                    {
                        // file doesn't exist (or is not fully qualified):
                        if (    (!pData->pszExecutable)
                             || (stricmp(pData->pszExecutable,
                                         pProgDetails->pszExecutable))
                           )
                        {
                            // file changed:
                            PMPF_PROGRAMSTART(("    setting new exe handle 0x%lX", hfs));

                            progStore(somSelf,
                                      &pData->pszExecutable,
                                      &_fWeAllocatedExecutable,
                                      pProgDetails->pszExecutable,
                                      NULL);
                            pData->ulExecutableHandle = 0;
                            fSetProgIcon = TRUE;
                        }
                    }
                }
                else
                {
                    // executable not specified: nuke it then
                    progStore(somSelf,
                              &pData->pszExecutable,
                              &_fWeAllocatedExecutable,
                              NULL,
                              NULL);
                    pData->ulExecutableHandle = NULLHANDLE;
                    fSetProgIcon = TRUE;
                }

                PMPF_PROGRAMSTART(("   new hfs 0x%lX", pData->ulExecutableHandle));
                PMPF_PROGRAMSTART(("   new _pszExecutable %s", pData->pszExecutable));

                // startup dir
                pData->ulStartupDirHandle = GetFSHandle(pProgDetails->pszStartupDir);

                // parameters
                progStore(somSelf,
                          &pData->pszParameters,
                          &_fWeAllocatedParameters,
                          pProgDetails->pszParameters,      // can be NULL
                          NULL);

                // environment
                if (pData->pszEnvironment && _fWeAllocatedEnvironment)
                {
                    _wpFreeMem(somSelf, pData->pszEnvironment);
                    _fWeAllocatedEnvironment = FALSE;
                }

                pData->pszEnvironment = NULL;

                if (pProgDetails->pszEnvironment)
                {
                    ULONG cb;
                    APIRET arc;
                    if (    (cb = appQueryEnvironmentLen(pProgDetails->pszEnvironment))
                         && (pData->pszEnvironment = _wpAllocMem(somSelf, cb, &arc))
                       )
                    {
                        memcpy(pData->pszEnvironment,
                               pProgDetails->pszEnvironment,
                               cb);
                    }
                }

                // pszIcon: ignored

                // SWPInitial
                memcpy(&pData->SWPInitial,
                       &pProgDetails->swpInitial,
                       sizeof(SWP));

                if (fSetProgIcon)
                    // refresh icon
                    _wpSetProgIcon(somSelf, NULL);

                // save ourselves
                _wpSaveDeferred(somSelf);

                brc = TRUE;
            }
        }
        CATCH(excpt1)
        {
        } END_CATCH();

        if (fLocked)
            _wpReleaseObjectMutexSem(somSelf);

        return brc;       // do not call parent, whatever happens
                            // V0.9.19 (2002-04-17) [umoeller]
    }

    return XWPProgram_parent_WPProgram_wpSetProgDetails(somSelf,
                                                        pProgDetails);
}

/*
 *@@ wpOpen:
 *      this WPObject instance method gets called when
 *      a new view needs to be opened. Normally, this
 *      gets called after wpViewObject has scanned the
 *      object's USEITEMs and has determined that a new
 *      view is needed.
 *
 *      This _normally_ runs on thread 1 of the WPS, but
 *      this is not always the case. If this gets called
 *      in response to a menu selection from the "Open"
 *      submenu or a double-click in the folder, this runs
 *      on the thread of the folder (which _normally_ is
 *      thread 1). However, if this results from WinOpenObject
 *      or an OPEN setup string, this will not be on thread 1.
 *
 *      We call our own implementation here if extended
 *      associations are enabled.
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 *@@changed V0.9.16 (2002-01-04) [umoeller]: if ext assocs are enabled, xwp now handles program open too
 */

SOM_Scope HWND  SOMLINK xpg_wpOpen(XWPProgram *somSelf,
                                   HWND hwndCnr,
                                   ULONG ulView,
                                   ULONG param)
{
    XWPProgramData *somThis = XWPProgramGetData(somSelf);
    XWPProgramMethodDebug("XWPProgram","xpg_wpOpen");

#ifndef __NOTURBOFOLDERS__
    if (cmnQuerySetting(sfTurboFolders))        // V1.0.1 (2002-12-15) [umoeller]
    {
        if (ulView == OPEN_RUNNING)
        {
            HWND hwnd = NULLHANDLE;
            APIRET arc;
            CHAR szFailing[CCHMAXPATH];

            if (arc = progOpenProgram(somSelf,
                                      NULL,      // no assoc data file
                                      ulView,
                                      &hwnd,
                                      sizeof(szFailing),
                                      szFailing))
            {
                // error opening program: ask user if he wants
                // to change the settings
                if (cmnProgramErrorMsgBox(NULLHANDLE,
                                          somSelf,
                                          szFailing,
                                          arc)
                            == MBID_YES)
                    krnPostThread1ObjectMsg(T1M_OPENOBJECTFROMPTR,
                                            (MPARAM)somSelf,
                                            (MPARAM)OPEN_SETTINGS);
            }

            return hwnd;
        }
    } // end if (cmnQuerySetting(sfTurboFolders))
#endif

    return XWPProgram_parent_WPProgram_wpOpen(somSelf,
                                              hwndCnr,
                                              ulView,
                                              param);
}

/*
 *@@ wpAddProgramPage:
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xpg_wpAddProgramPage(XWPProgram *somSelf,
                                              HWND hwndNotebook)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xpg_wpAddProgramPage");

    return XWPProgram_parent_WPProgram_wpAddProgramPage(somSelf,
                                                        hwndNotebook);
}

/*
 *@@ wpAddProgramSessionPage:
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xpg_wpAddProgramSessionPage(XWPProgram *somSelf,
                                                     HWND hwndNotebook)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xpg_wpAddProgramSessionPage");

    return XWPProgram_parent_WPProgram_wpAddProgramSessionPage(somSelf,
                                                               hwndNotebook);
}

/*
 *@@ wpAddProgramAssociationPage:
 *      this WPProgram method adds the "Associations" page
 *      to an executable's settings notebook.
 *
 *      If extended associations are enabled, we replace the
 *      standard "Associations" page with a new page which
 *      lists the extended file types.
 */

SOM_Scope ULONG  SOMLINK xpg_wpAddProgramAssociationPage(XWPProgram *somSelf,
                                                         HWND hwndNotebook)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xwppgm_wpAddProgramAssociationPage");

#ifndef __NOTURBOFOLDERS__
    if (cmnQuerySetting(sfTurboFolders))        // V1.0.1 (2002-12-15) [umoeller]
        return _xwpAddAssociationsPage(somSelf, hwndNotebook);
#endif

    return XWPProgram_parent_WPProgram_wpAddProgramAssociationPage(somSelf,
                                                                   hwndNotebook);
}

/*
 *@@ wpMoveObject:
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 */

SOM_Scope BOOL  SOMLINK xpg_wpMoveObject(XWPProgram *somSelf,
                                         WPFolder* Folder)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xpg_wpMoveObject");

    return XWPProgram_parent_WPProgram_wpMoveObject(somSelf,
                                                    Folder);
}

/*
 *@@ wpCopyObject:
 *
 *@@added V0.9.12 (2001-05-22) [umoeller]
 */

SOM_Scope WPObject*  SOMLINK xpg_wpCopyObject(XWPProgram *somSelf,
                                              WPFolder* Folder,
                                              BOOL fLock)
{
    /* XWPProgramData *somThis = XWPProgramGetData(somSelf); */
    XWPProgramMethodDebug("XWPProgram","xpg_wpCopyObject");

    return XWPProgram_parent_WPProgram_wpCopyObject(somSelf,
                                                    Folder,
                                                    fLock);
}

/* ******************************************************************
 *
 *   XWPProgram class methods
 *
 ********************************************************************/

/*
 *@@ wpclsInitData:
 *      this M_WPObject class method gets called when a class
 *      is loaded by the WPS (probably from within a
 *      somFindClass call) and allows the class to initialize
 *      itself.
 *
 *@@changed V0.9.17 (2002-02-05) [umoeller]: moved "repair desktop" to here to fix broken XFldDesktop class on repaired desktop
 */

SOM_Scope void  SOMLINK xpgM_wpclsInitData(M_XWPProgram *somSelf)
{
    /* M_XWPProgramData *somThis = M_XWPProgramGetData(somSelf); */
    M_XWPProgramMethodDebug("M_XWPProgram","xwppgmM_wpclsInitData");

    M_XWPProgram_parent_M_WPProgram_wpclsInitData(somSelf);

    if (krnClassInitialized(G_pcszXWPProgram))
        // first call:
        // since this gets initialized by the WPS _after_
        // the WPDesktop class, we can now repair the desktop
        // if it is broken
        // moved this here from XFldDesktop because WPProgram
        // is initialized later
        // V0.9.17 (2002-02-05) [umoeller]
        initRepairDesktopIfBroken();
        ;
}

/*
 *@@ wpclsQueryIconData:
 *      this M_WPObject class method must return information
 *      about how to build the default icon for objects
 *      of a class. This gets called from various other
 *      methods whenever a class default icon is needed;
 *      most importantly, M_WPObject::wpclsQueryIcon
 *      calls this to build a class default icon, which
 *      is then cached in the class's instance data.
 *      If a subclass wants to change a class default icon,
 *      it should always override _this_ method instead of
 *      wpclsQueryIcon.
 *      For details about icon management, refer to
 *      src\filesys\icons.c.
 *
 *      We override this method to give program objects a
 *      new default icon, if icon replacements are enabled.
 *      Note that this only gets called for program files
 *      when retrieving an icon for an executable failed
 *      somehow.
 *
 *@@added V0.9.19 (2002-05-23) [umoeller]
 */

SOM_Scope ULONG  SOMLINK xpgM_wpclsQueryIconData(M_XWPProgram *somSelf,
                                                 PICONINFO pIconInfo)
{
    ULONG       ulrc = 0;

    /* M_XWPProgramData *somThis = M_XWPProgramGetData(somSelf); */
    M_XWPProgramMethodDebug("M_XWPProgram","xpgM_wpclsQueryIconData");

#ifndef __NOICONREPLACEMENTS__
    if (cmnQuerySetting(sfIconReplacements))
    {
        ULONG cb = 0;
        if (!cmnGetStandardIcon(STDICON_PROG_UNKNOWN,
                                NULL,            // no hpointer
                                &cb,
                                pIconInfo))      // can be NULL
            return cb;

        return 0;
    }

    if (!ulrc)
#endif
        // icon replacements not allowed: call default
        ulrc = M_XWPProgram_parent_M_WPProgram_wpclsQueryIconData(somSelf,
                                                                  pIconInfo);

    return ulrc;
}


