text: //-----------------------------------------------------------------------------
  // Copyright (c) 2012 GarageGames, LLC
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to
  // deal in the Software without restriction, including without limitation the
  // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  // sell copies of the Software, and to permit persons to whom the Software is
  // furnished to do so, subject to the following conditions:
  //
  // The above copyright notice and this permission notice shall be included in
  // all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  // IN THE SOFTWARE.
  //-----------------------------------------------------------------------------
  
  #include "platformWin32/platformWin32.h"
  #include "console/console.h"
  #include "console/simBase.h"
  #include "core/strings/unicode.h"
  #include "platform/threads/thread.h"
  #include "platform/threads/mutex.h"
  #include "core/util/safeDelete.h"
  #include "util/tempAlloc.h"
  
  //-----------------------------------------------------------------------------
  // Thread for executing in
  //-----------------------------------------------------------------------------
  
  class ExecuteThread : public Thread
  {
     // [tom, 12/14/2006] mProcess is only used in the constructor before the thread
     // is started and in the thread itself so we should be OK without a mutex.
     HANDLE mProcess;
  
  public:
     ExecuteThread(const char *executable, const char *args = NULL, const char *directory = NULL);
  
     virtual void run(void *arg = 0);
  };
  
  //-----------------------------------------------------------------------------
  // Event for cleanup
  //-----------------------------------------------------------------------------
  
  class ExecuteCleanupEvent : public SimEvent
  {
     ExecuteThread *mThread;
     bool mOK;
  
  public:
     ExecuteCleanupEvent(ExecuteThread *thread, bool ok)
     {
        mThread = thread;
        mOK = ok;
     }
  
     virtual void process(SimObject *object)
     {
        if( Con::isFunction( "onExecuteDone" ) )
           Con::executef( "onExecuteDone", Con::getIntArg( mOK ) );
        SAFE_DELETE(mThread);
     }
  };
  
  //-----------------------------------------------------------------------------
  
  ExecuteThread::ExecuteThread(const char *executable, const char *args /* = NULL */, const char *directory /* = NULL */) : Thread(0, NULL, false)
  {
     SHELLEXECUTEINFO shl;
     dMemset(&shl, 0, sizeof(shl));
  
     shl.cbSize = sizeof(shl);
     shl.fMask = SEE_MASK_NOCLOSEPROCESS;
     
     char exeBuf[1024];
     Platform::makeFullPathName(executable, exeBuf, sizeof(exeBuf));
     
     TempAlloc< TCHAR > dirBuf( ( directory ? dStrlen( directory ) : 0 ) + 1 );
     dirBuf[ dirBuf.size - 1 ] = 0;
  
  #ifdef UNICODE
     WCHAR exe[ 1024 ];
     convertUTF8toUTF16( exeBuf, exe, sizeof( exe ) / sizeof( exe[ 0 ] ) );
  
     TempAlloc< WCHAR > argsBuf( ( args ? dStrlen( args ) : 0 ) + 1 );
     argsBuf[ argsBuf.size - 1 ] = 0;
  
     if( args )
        convertUTF8toUTF16( args, argsBuf, argsBuf.size );
     if( directory )
        convertUTF8toUTF16( directory, dirBuf, dirBuf.size );
  #else
     char* exe = exeBuf;
     char* argsBuf = args;
     if( directory )
        dStrpcy( dirBuf, directory );
  #endif
  
     backslash( exe );
     backslash( dirBuf );
     
     shl.lpVerb = TEXT( "open" );
     shl.lpFile = exe;
     shl.lpParameters = argsBuf;
     shl.lpDirectory = dirBuf;
  
     shl.nShow = SW_SHOWNORMAL;
  
     if(ShellExecuteEx(&shl) && shl.hProcess)
     {
        mProcess = shl.hProcess;
        start();
     }
  }
  
  void ExecuteThread::run(void *arg /* = 0 */)
  {
     if(mProcess == NULL)
        return;
  
     DWORD wait = WAIT_OBJECT_0 - 1; // i.e., not WAIT_OBJECT_0
     while(! checkForStop() && (wait = WaitForSingleObject(mProcess, 200)) != WAIT_OBJECT_0) ;
  
     Sim::postEvent(Sim::getRootGroup(), new ExecuteCleanupEvent(this, wait == WAIT_OBJECT_0), -1);
  }
  
  //-----------------------------------------------------------------------------
  // Console Functions
  //-----------------------------------------------------------------------------
  
  ConsoleFunction(shellExecute, bool, 2, 4, "(string executable, string args, string directory)"
  				"@brief Launches an outside executable or batch file\n\n"
  				"@param executable Name of the executable or batch file\n"
  				"@param args Optional list of arguments, in string format, to pass to the executable\n"
  				"@param directory Optional string containing path to output or shell\n"
  				"@ingroup Platform")
  {
     ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL);
     if(! et->isAlive())
     {
        delete et;
        return false;
     }
  
     return true;
  }
  
  void Platform::openFolder(const char* path )
  {
     char filePath[1024];
     Platform::makeFullPathName(path, filePath, sizeof(filePath));
  
  #ifdef UNICODE
     WCHAR p[ 1024 ];
     convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
  #else
     char* p = filePath;
  #endif
  
     backslash( p );
  
     ::ShellExecute( NULL,TEXT("explore"),p, NULL, NULL, SW_SHOWNORMAL);
  }
  
  void Platform::openFile(const char* path )
  {
     char filePath[1024];
     Platform::makeFullPathName(path, filePath, sizeof(filePath));
  
  #ifdef UNICODE
     WCHAR p[ 1024 ];
     convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
  #else
     char* p = filePath;
  #endif
  
     backslash( p );
  
     ::ShellExecute( NULL,TEXT("open"),p, NULL, NULL, SW_SHOWNORMAL);
  }
  
  