#include  "stdafx.h"
#include  "gcommlib.h"
#include  "FileUtil.h"
#include  "ShellCmd.h"
#include  "ThreadShell.h"


enum ExeType
{
     Unknown,
     DosExe,
     Batch,
     DosCom,
     Win32Gui,
     Win32Console,
     Win16Gui
};


static GBOOL IsGui( const CHAR *cmdLine, ExeType &type );
static ExeType GetExeType( const CString &fname );


GBOOL ShellCommand( const CHAR *cmdLine, int &retVal, BOOL minimize, const char *title )
{
     GBOOL     result = FALSE;
     ExeType   type;

     retVal = -1;

     if (IsGui( cmdLine, type ) || (type == Win32Console))
     {
          PROCESS_INFORMATION processInfo;
          STARTUPINFO startInfo;

          GetStartupInfo( &startInfo );

          if (title != NULL)
          {
               startInfo.lpTitle = (char *)title;
          }

          result = CreateProcess( NULL, (char *)(const char *)cmdLine,
                                  NULL, NULL,
                                  FALSE, 
                                  NORMAL_PRIORITY_CLASS | ((type == Win32Console) ? CREATE_NEW_CONSOLE : 0),
                                  NULL,
                                  NULL,
                                  &startInfo, &processInfo
                                );

          if (result)
          {
               WaitForSingleObject( processInfo.hProcess, INFINITE );

               DWORD     rv;

               if (! GetExitCodeProcess( processInfo.hProcess, &rv ))
               {
                    DWORD     err = GetLastError();
                    err = 0;
               }
               else
               {
                    retVal = rv;
               }

               CloseHandle( processInfo.hThread );
               CloseHandle( processInfo.hProcess );
          }
          else
          {
               LPVOID lpMsgBuf;
 
               FormatMessage( 
                   FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                   NULL,
                   GetLastError(),
                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                   (LPTSTR) &lpMsgBuf,
                   0,
                   NULL 
               );

               // Display the string.
               MessageBox( NULL, (const char *)lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );
          }
     }
     else
     {
          CString   runCmd( cmdLine );

          if (type == Batch)
          {
               runCmd = "call ";
               runCmd += cmdLine;
          }

          ThreadShell shell;

          result = shell.StartApp( runCmd, minimize, title );

          if (result)
          {
               shell.WaitForCompletion();
               retVal = 0;
          }
     }

     return( result );
}    


static GBOOL IsGui( const CHAR *cmdLine, ExeType &type )
{
     CString   path;
     CString   root;
     CString   ext;
     GBOOL     guiFound = FALSE;

     type = Unknown;

     if (which( cmdLine, path, root, ext ))
     {
          type = GetExeType( path + root + ext );

          guiFound = (type == Win32Gui) || (type == Win16Gui);
     }

     return( guiFound );
}


static ExeType GetExeType( const CString &fname )
{
    DWORD  SectionOffset;
    DWORD  CoffHeaderOffset;
    DWORD  MoreDosHeader[16];
 
    ULONG  ntSignature;
 
    IMAGE_DOS_HEADER      image_dos_header;
    IMAGE_FILE_HEADER     image_file_header;
    IMAGE_OPTIONAL_HEADER image_optional_header;
 
    ExeType    type = Unknown;

    int        winVerMajor = -1;
    int        winVerMinor = -1;
 
    if (samend( fname, ".bat" ))
    {
         type = Batch;
    }
    else if (samend( fname, ".com" ))
    {
         type = DosCom;
    }
    else if (samend( fname, ".exe" ))
    {
          type = DosExe;

         GBOOL      ok = TRUE;
         FILE *f = fopen( fname, "rb" );
         ok = (f != NULL);

         if (ok)
         {
              ok = (fread( &image_dos_header, 1, sizeof( IMAGE_DOS_HEADER ), f ) == sizeof( IMAGE_DOS_HEADER ));
         }

         if (ok)
         {
              ok = (IMAGE_DOS_SIGNATURE == image_dos_header.e_magic);
         }

         if (ok)
         {
              ok = (fread( &MoreDosHeader, 1, sizeof( MoreDosHeader ), f ) == sizeof( MoreDosHeader ));
         }

         if (ok)
         {
              ok = (fseek( f, image_dos_header.e_lfanew, SEEK_SET) == 0);

              if (ok)
              {
                   CoffHeaderOffset = image_dos_header.e_lfanew + sizeof( ULONG );
              }
         }
 
         if (ok)
         {
              ok = (fread( &ntSignature, 1, sizeof( ULONG ), f ) == sizeof( ULONG ));
         }

         if (ok)
         {
              ok = (IMAGE_NT_SIGNATURE == ntSignature);

              if (! ok)
              {
                   IMAGE_OS2_HEADER neHeader;

                   fseek( f, image_dos_header.e_lfanew, SEEK_SET );

                   if (fread( &neHeader, 1, sizeof( IMAGE_OS2_HEADER ), f ) == sizeof( IMAGE_OS2_HEADER ))
                   {
                        if ((neHeader.ne_exetyp == 2) && (neHeader.ne_expver < 0x0400))
                        {
                             type = Win16Gui;
                             winVerMajor = neHeader.ne_expver >> 8;
                             winVerMinor = neHeader.ne_expver & 0xFF;
                        }
                   }
              }
         }

         if (ok)
         {
              SectionOffset = CoffHeaderOffset + IMAGE_SIZEOF_FILE_HEADER +
                              IMAGE_SIZEOF_NT_OPTIONAL_HEADER;
 
              ok = (fread( &image_file_header, 1, IMAGE_SIZEOF_FILE_HEADER, f ) == IMAGE_SIZEOF_FILE_HEADER);
         }
 
         if (ok)
         {
              ok = (fread( &image_optional_header, 1, IMAGE_SIZEOF_NT_OPTIONAL_HEADER, f ) == IMAGE_SIZEOF_NT_OPTIONAL_HEADER);
         }
 
         if (ok)
         {
              switch (image_optional_header.Subsystem)
              {
                    case IMAGE_SUBSYSTEM_WINDOWS_GUI:
                         type = Win32Gui;
                         break;
 
                    case IMAGE_SUBSYSTEM_OS2_CUI:
                    case IMAGE_SUBSYSTEM_POSIX_CUI:
                    case IMAGE_SUBSYSTEM_WINDOWS_CUI:
                         type = Win32Console;
                         break;

                    case IMAGE_SUBSYSTEM_NATIVE:
                    case IMAGE_SUBSYSTEM_UNKNOWN:
                    default:
                         type = Unknown;
                         break;
              }
         }

         if (f != NULL)
         {
              fclose( f );
         }
    }

    return( type );
}
 


