Example #1
0
TEST(CPath, GetPath_FullName)
{
	{
		ASSERT_EQUALS(CPath().GetPath(), CPath());
		ASSERT_EQUALS(CPath().GetFullName(), CPath());
	}

	{
		const CPath path = Norm(wxT("/home/mule/"));

		ASSERT_EQUALS(path.GetPath(), Norm(wxT("/home/mule")));
		ASSERT_EQUALS(path.GetFullName(), CPath());
	}

	{
		const CPath path = Norm(wxT("/home/mule"));

		ASSERT_EQUALS(path.GetPath(), Norm(wxT("/home")));
		ASSERT_EQUALS(path.GetFullName(), Norm(wxT("mule")));
	}

	{
		const CPath path = Norm(wxT("mule"));
		
		ASSERT_EQUALS(path.GetPath(), CPath());
		ASSERT_EQUALS(path.GetFullName(), Norm(wxT("mule")));
	}

	{
		const CPath path = Norm(wxT("mule.ext"));
		
		ASSERT_EQUALS(path.GetPath(), CPath());
		ASSERT_EQUALS(path.GetFullName(), Norm(wxT("mule.ext")));
	}
}
Example #2
0
TEST(CPath, DefaultConstructor)
{
	CPath tmp;

	ASSERT_FALSE(tmp.IsOk());
	ASSERT_EQUALS(tmp, CPath());

	ASSERT_FALSE(tmp.FileExists());
	ASSERT_FALSE(tmp.DirExists());

	ASSERT_EQUALS(wxEmptyString, tmp.GetRaw());
	ASSERT_EQUALS(wxEmptyString, tmp.GetPrintable());
	ASSERT_EQUALS(CPath(), tmp.GetPath());
	ASSERT_EQUALS(CPath(), tmp.GetFullName());
}
Example #3
0
ConvStatus CPartFileConvert::performConvertToeMule(const CPath& fileName)
{
    wxString filepartindex;

    CPath folder	= fileName.GetPath();
    CPath partfile	= fileName.GetFullName();
    CPath newfilename;

    CDirIterator finder(folder);

    Notify_ConvertUpdateProgressFull(0, _("Reading temp folder"), s_pfconverting->folder.GetPrintable());

    filepartindex = partfile.RemoveAllExt().GetRaw();

    Notify_ConvertUpdateProgress(4, _("Retrieving basic information from download info file"));

    CPartFile* file = new CPartFile();
    s_pfconverting->partmettype = file->LoadPartFile(folder, partfile, false, true);

    switch (s_pfconverting->partmettype) {
    case PMT_UNKNOWN:
    case PMT_BADFORMAT:
        delete file;
        return CONV_BADFORMAT;
    }

    CPath oldfile = folder.JoinPaths(partfile.RemoveExt());

    {
        wxMutexLocker lock(s_mutex);
        s_pfconverting->size = file->GetFileSize();
        s_pfconverting->filename = file->GetFileName();
        s_pfconverting->filehash = file->GetFileHash().Encode();
    }

    Notify_ConvertUpdateJobInfo(s_pfconverting);

    if (theApp->downloadqueue->GetFileByID(file->GetFileHash())) {
        delete file;
        return CONV_ALREADYEXISTS;
    }

    if (s_pfconverting->partmettype == PMT_SPLITTED) {
        unsigned fileindex;
        char *ba = new char [PARTSIZE];

        try {
            CFile inputfile;

            // just count
            unsigned maxindex = 0;
            unsigned partfilecount = 0;
            CPath filePath = finder.GetFirstFile(CDirIterator::File, filepartindex + wxT(".*.part"));
            while (filePath.IsOk()) {
                long l;
                ++partfilecount;
                filePath.GetFullName().RemoveExt().GetExt().ToLong(&l);
                fileindex = (unsigned)l;
                filePath = finder.GetNextFile();
                if (fileindex > maxindex) maxindex = fileindex;
            }
            float stepperpart;
            {
                wxMutexLocker lock(s_mutex);
                if (partfilecount > 0) {
                    stepperpart = (80.0f / partfilecount);
                    if (maxindex * PARTSIZE <= s_pfconverting->size) {
                        s_pfconverting->spaceneeded = maxindex * PARTSIZE;
                    } else {
                        s_pfconverting->spaceneeded = s_pfconverting->size;
                    }
                } else {
                    stepperpart = 80.0f;
                    s_pfconverting->spaceneeded = 0;
                }
            }

            Notify_ConvertUpdateJobInfo(s_pfconverting);

            sint64 freespace = CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
            if (freespace != wxInvalidOffset) {
                if (static_cast<uint64>(freespace) < maxindex * PARTSIZE) {
                    delete file;
                    delete [] ba;
                    return CONV_OUTOFDISKSPACE;
                }
            }

            // create new partmetfile, and remember the new name
            file->CreatePartFile();
            newfilename = file->GetFullName();

            Notify_ConvertUpdateProgress(8, _("Creating destination file"));

            file->m_hpartfile.SetLength( s_pfconverting->spaceneeded );

            unsigned curindex = 0;
            CPath filename = finder.GetFirstFile(CDirIterator::File, filepartindex + wxT(".*.part"));
            while (filename.IsOk()) {
                // stats
                ++curindex;

                Notify_ConvertUpdateProgress(10 + (curindex * stepperpart), CFormat(_("Loading data from old download file (%u of %u)")) % curindex % partfilecount);

                long l;
                filename.GetFullName().RemoveExt().GetExt().ToLong(&l);
                fileindex = (unsigned)l;
                if (fileindex == 0) {
                    filename = finder.GetNextFile();
                    continue;
                }

                uint32 chunkstart = (fileindex - 1) * PARTSIZE;

                // open, read data of the part-part-file into buffer, close file
                inputfile.Open(filename, CFile::read);
                uint64 toReadWrite = std::min<uint64>(PARTSIZE, inputfile.GetLength());
                inputfile.Read(ba, toReadWrite);
                inputfile.Close();

                Notify_ConvertUpdateProgress(10 + (curindex * stepperpart), CFormat(_("Saving data block into new single download file (%u of %u)")) % curindex % partfilecount);

                // write the buffered data
                file->m_hpartfile.WriteAt(ba, chunkstart, toReadWrite);

                filename = finder.GetNextFile();
            }
            delete[] ba;
        } catch (const CSafeIOException& e) {
            AddDebugLogLineC(logPfConvert, wxT("IO error while converting partfiles: ") + e.what());

            delete[] ba;
            file->Delete();
            return CONV_IOERROR;
        }

        file->m_hpartfile.Close();
    }
    // import an external common format partdownload
    else //if (pfconverting->partmettype==PMT_DEFAULTOLD || pfconverting->partmettype==PMT_NEWOLD || Shareaza  )
    {
        if (!s_pfconverting->removeSource) {
            wxMutexLocker lock(s_mutex);
            s_pfconverting->spaceneeded = oldfile.GetFileSize();
        }

        Notify_ConvertUpdateJobInfo(s_pfconverting);

        sint64 freespace = CPath::GetFreeSpaceAt(thePrefs::GetTempDir());
        if (freespace == wxInvalidOffset) {
            delete file;
            return CONV_IOERROR;
        } else if (freespace < s_pfconverting->spaceneeded) {
            delete file;
            return CONV_OUTOFDISKSPACE;
        }

        file->CreatePartFile();
        newfilename = file->GetFullName();

        file->m_hpartfile.Close();

        bool ret = false;

        Notify_ConvertUpdateProgress(92, _("Copy"));

        CPath::RemoveFile(newfilename.RemoveExt());
        if (!oldfile.FileExists()) {
            // data file does not exist. well, then create a 0 byte big one
            CFile datafile;
            ret = datafile.Create(newfilename.RemoveExt());
        } else if (s_pfconverting->removeSource) {
            ret = CPath::RenameFile(oldfile, newfilename.RemoveExt());
        } else {
            ret = CPath::CloneFile(oldfile, newfilename.RemoveExt(), false);
        }
        if (!ret) {
            file->Delete();
            //delete file;
            return CONV_FAILED;
        }

    }

    Notify_ConvertUpdateProgress(94, _("Retrieving source downloadfile information"));

    CPath::RemoveFile(newfilename);
    if (s_pfconverting->removeSource) {
        CPath::RenameFile(folder.JoinPaths(partfile), newfilename);
    } else {
        CPath::CloneFile(folder.JoinPaths(partfile), newfilename, false);
    }

    file->m_hashlist.clear();

    if (!file->LoadPartFile(thePrefs::GetTempDir(), file->GetPartMetFileName(), false)) {
        //delete file;
        file->Delete();
        return CONV_BADFORMAT;
    }

    if (s_pfconverting->partmettype == PMT_NEWOLD || s_pfconverting->partmettype == PMT_SPLITTED) {
        file->SetCompletedSize(file->transferred);
        file->m_iGainDueToCompression = 0;
        file->m_iLostDueToCorruption = 0;
    }

    Notify_ConvertUpdateProgress(100, _("Adding download and saving new partfile"));

    theApp->downloadqueue->AddDownload(file, thePrefs::AddNewFilesPaused(), 0);
    file->SavePartFile();

    if (file->GetStatus(true) == PS_READY) {
        theApp->sharedfiles->SafeAddKFile(file); // part files are always shared files
    }

    if (s_pfconverting->removeSource) {
        CPath oldFile = finder.GetFirstFile(CDirIterator::File, filepartindex + wxT(".*"));
        while (oldFile.IsOk()) {
            CPath::RemoveFile(folder.JoinPaths(oldFile));
            oldFile = finder.GetNextFile();
        }

        if (s_pfconverting->partmettype == PMT_SPLITTED) {
            CPath::RemoveDir(folder);
        }
    }

    return CONV_OK;
}
Example #4
0
// запустить через ShellExecuteEx в скрытом режиме
// (см. шаманство с консолью)
static BOOL ExecuteHide( CPath& path, DWORD* out_exitcode, CSimpleString* strOut )
{
	HANDLE hSaveStdin = NULL;
	HANDLE hSaveStdout = NULL;
	HANDLE hChildStdoutRdDup = NULL;
	HANDLE hChildStdoutWr = NULL;
	try
	{
		// подключаем консоль
		STARTUPINFOA si = { sizeof(STARTUPINFOA) };
		si.dwFlags = STARTF_USESHOWWINDOW;
		si.wShowWindow = SW_HIDE;
		PROCESS_INFORMATION pi = { 0 };
		char command_line[] = "cmd";
		::CreateProcessA( NULL, // не используем имя файла, все в строке запуска
						  command_line, // Command line
						  NULL, // Process handle not inheritable
						  NULL, // Thread handle not inheritable
						  TRUE, // Set handle inheritance to FALSE
						  0, // No creation flags
						  NULL, // Use parent's environment block
						  NULL, // Use parent's starting directory
						  &si, // STARTUPINFO
						  &pi ); // PROCESS_INFORMATION
		// задержка чтобы консоль успела создаться
		::WaitForSingleObject( pi.hProcess, 100 );
		BOOL hResult = FALSE;
		HMODULE hLib = LoadLibraryA("Kernel32.dll");
		if ( hLib != NULL )
		{
			typedef BOOL (STDAPICALLTYPE *ATTACHCONSOLE)( DWORD dwProcessId );
			ATTACHCONSOLE _AttachConsole = NULL;
			_AttachConsole = (ATTACHCONSOLE)GetProcAddress( hLib, "AttachConsole" );
			if ( _AttachConsole ) hResult = _AttachConsole( pi.dwProcessId );
			FreeLibrary( hLib );
		}
		if ( hResult == FALSE ) AllocConsole();

		TerminateProcess( pi.hProcess, 0 );
		CloseHandle( pi.hProcess );
		CloseHandle( pi.hThread );

		HANDLE hChildStdinRd;
		HANDLE hChildStdinWr;
		HANDLE hChildStdinWrDup;
		HANDLE hChildStdoutRd;

		// Set the bInheritHandle flag so pipe handles are inherited. 
		SECURITY_ATTRIBUTES saAttr = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
		BOOL fSuccess;

		// The steps for redirecting child process's STDOUT: 
		//     1. Save current STDOUT, to be restored later. 
		//     2. Create anonymous pipe to be STDOUT for child process. 
		//     3. Set STDOUT of the parent process to be write handle to 
		//        the pipe, so it is inherited by the child process. 
		//     4. Create a noninheritable duplicate of the read handle and
		//        close the inheritable read handle. 

		// Save the handle to the current STDOUT. 
		hSaveStdout = GetStdHandle( STD_OUTPUT_HANDLE );

		// Create a pipe for the child process's STDOUT.
		if ( !CreatePipe( &hChildStdoutRd, &hChildStdoutWr, &saAttr, 0 ) ) throw(1);

		// Set a write handle to the pipe to be STDOUT. 
		if ( !SetStdHandle( STD_OUTPUT_HANDLE, hChildStdoutWr ) ) throw(1);

		// Create noninheritable read handle and close the inheritable read 
		// handle.
		fSuccess = DuplicateHandle( GetCurrentProcess(),
									hChildStdoutRd,
									GetCurrentProcess(),
									&hChildStdoutRdDup,
									0,
									FALSE,
									DUPLICATE_SAME_ACCESS );
		if( fSuccess == FALSE ) throw(1);
		CloseHandle( hChildStdoutRd );

		// The steps for redirecting child process's STDIN: 
		//     1.  Save current STDIN, to be restored later. 
		//     2.  Create anonymous pipe to be STDIN for child process. 
		//     3.  Set STDIN of the parent to be the read handle to the 
		//         pipe, so it is inherited by the child process. 
		//     4.  Create a noninheritable duplicate of the write handle, 
		//         and close the inheritable write handle. 

		// Save the handle to the current STDIN. 
		hSaveStdin = GetStdHandle( STD_INPUT_HANDLE );

		// Create a pipe for the child process's STDIN. 
		if ( !CreatePipe( &hChildStdinRd, &hChildStdinWr, &saAttr, 0 ) ) throw(1);

		// Set a read handle to the pipe to be STDIN. 
		if ( !SetStdHandle( STD_INPUT_HANDLE, hChildStdinRd ) ) throw(1);

		// Duplicate the write handle to the pipe so it is not inherited. 
		fSuccess = DuplicateHandle( GetCurrentProcess(),
									hChildStdinWr,
									GetCurrentProcess(),
									&hChildStdinWrDup,
									0,
									FALSE,
									DUPLICATE_SAME_ACCESS );
		if ( fSuccess == FALSE ) throw(1);

		CloseHandle( hChildStdinWr );
	}
	catch (...)
	{
		return FALSE;
	}

	// Now create the child process.
	SHELLEXECUTEINFOA shinf = { sizeof(SHELLEXECUTEINFOA) };
	shinf.lpFile = path.GetPath();
	shinf.lpParameters = path.GetFileParams();
	//shinf.lpDirectory = path.GetDirectory();
	shinf.fMask = SEE_MASK_FLAG_NO_UI |
				  SEE_MASK_NO_CONSOLE |
				  SEE_MASK_FLAG_DDEWAIT |
				  SEE_MASK_NOCLOSEPROCESS;
	shinf.nShow = SW_HIDE;
	BOOL bSuccess = ::ShellExecuteExA( &shinf );
	if ( bSuccess && shinf.hInstApp <= (HINSTANCE)32 ) bSuccess = FALSE;
	HANDLE hProcess = shinf.hProcess;

	try
	{
		if ( bSuccess == FALSE || hProcess == NULL ) throw(1);

		if ( hChildStdoutWr != NULL )
		{
			CloseHandle( hChildStdoutWr );
			hChildStdoutWr = NULL;
		}

		// After process creation, restore the saved STDIN and STDOUT.
		if ( hSaveStdin != NULL )
		{
			if ( !SetStdHandle( STD_INPUT_HANDLE, hSaveStdin ) ) throw(1);
			CloseHandle( hSaveStdin );
			hSaveStdin = NULL;
		}

		if ( hSaveStdout != NULL )
		{
			if ( !SetStdHandle( STD_OUTPUT_HANDLE, hSaveStdout ) ) throw(1);
			CloseHandle( hSaveStdout );
			hSaveStdout = NULL;
		}

		if ( hChildStdoutRdDup != NULL )
		{
			// Read output from the child process, and write to parent's STDOUT.
			const int BUFSIZE = 1024;
			DWORD dwRead;
			CMemBuffer< char, BUFSIZE > bufStr; // строковой буфер
			CMemBuffer< char, BUFSIZE > bufCmdLine; // строковой буфер
			for (;;)
			{
				if( ReadFile( hChildStdoutRdDup,
							  bufCmdLine.GetBuffer(),
							  BUFSIZE,
							  &dwRead,
							  NULL ) == FALSE ||
					dwRead == 0 )
				{
					DWORD exit_code = 0;
					if ( ::GetExitCodeProcess( hProcess, &exit_code ) == FALSE ||
						 exit_code != STILL_ACTIVE )
					{
						break;
					}
					else
					{
						continue;
					}
				}
				bufCmdLine[ dwRead ] = '\0';
				::OemToAnsi( bufCmdLine.GetBuffer(), bufStr.GetBuffer() );
				strOut->Append( bufStr.GetBuffer() );
			}
			CloseHandle( hChildStdoutRdDup );
			hChildStdoutRdDup = NULL;
		}
		FreeConsole();
	}
	catch (...)
	{
		if ( hChildStdoutWr != NULL ) CloseHandle( hChildStdoutWr );
		if ( hSaveStdin != NULL ) CloseHandle( hSaveStdin );
		if ( hSaveStdout != NULL ) CloseHandle( hSaveStdout );
		if ( hChildStdoutRdDup != NULL ) CloseHandle( hChildStdoutRdDup );
		if ( bSuccess == FALSE || hProcess == NULL ) return FALSE;
	}

	::GetExitCodeProcess( hProcess, out_exitcode );
	CloseHandle( hProcess );
	return TRUE;
}
Example #5
0
// запустить через CreateProcess в скрытом режиме
static BOOL RunProcessHide( CPath& path, DWORD* out_exitcode, CSimpleString* strOut )
{
	static const int MAX_CMD = 1024;

	STARTUPINFOA si = { sizeof(STARTUPINFOA) };
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = SW_HIDE;

	// устанавливаем именованные каналы на потоки ввода/вывода
	BOOL bUsePipes = FALSE;
	HANDLE FWritePipe = NULL;
	HANDLE FReadPipe = NULL;
	SECURITY_ATTRIBUTES pa = { sizeof(pa), NULL, TRUE };
	bUsePipes = ::CreatePipe( &FReadPipe, &FWritePipe, &pa, 0 );
	if ( bUsePipes != FALSE )
	{
		si.hStdOutput = FWritePipe;
		si.hStdInput = FReadPipe;
		si.hStdError = FWritePipe;
		si.dwFlags = STARTF_USESTDHANDLES | si.dwFlags;
	}

	// запускаем процесс
	CMemBuffer< char, MAX_CMD > bufCmdLine; // строковой буфер длиной MAX_CMD
	bufCmdLine.GetBuffer()[0] = 0;
	strcat( bufCmdLine.GetBuffer(), "\"" );
	strcat( bufCmdLine.GetBuffer(), path.GetPath() );
	strcat( bufCmdLine.GetBuffer(), "\"" );
	if ( path.GetFileParams() != NULL )
	{
		strcat( bufCmdLine.GetBuffer(), " " );
		strcat( bufCmdLine.GetBuffer(), path.GetFileParams() );
	}
	char *lp = NULL;
	if (strlen(currentDir) > 0)	lp = currentDir;
	PROCESS_INFORMATION pi = { 0 };
	BOOL RetCode = ::CreateProcessA( NULL, // не используем имя файла, все в строке запуска
									 bufCmdLine.GetBuffer(), // строка запуска
									 NULL, // Process handle not inheritable
									 NULL, // Thread handle not inheritable
									 TRUE, // Set handle inheritance to FALSE
									 0, // No creation flags
									 NULL, // Use parent's environment block
									 lp, //path.GetDirectory(), // устанавливаем дирректорию запуска
									 &si, // STARTUPINFO
									 &pi ); // PROCESS_INFORMATION

	// если провалили запуск сообщаем об ошибке
	if ( RetCode == FALSE )
	{
		::CloseHandle( FReadPipe );
		::CloseHandle( FWritePipe );
		return FALSE;
	}

	// закрываем описатель потока, в нем нет необходимости 
	::CloseHandle( pi.hThread );

	// ожидаем завершение работы процесса
	try
	{
		DWORD BytesToRead = 0;
		DWORD BytesRead = 0;
		DWORD TotalBytesAvail = 0;
		DWORD PipeReaded = 0;
		DWORD exit_code = 0;
		CMemBuffer< char, MAX_CMD > bufStr; // строковой буфер длиной MAX_CMD
		while ( ::PeekNamedPipe( FReadPipe, NULL, 0, &BytesRead, &TotalBytesAvail, NULL ) )
		{
			if ( TotalBytesAvail == 0 )
			{
				if ( ::GetExitCodeProcess( pi.hProcess, &exit_code ) == FALSE ||
					 exit_code != STILL_ACTIVE )
				{
					break;
				}
				else
				{
					Sleep(10);
					continue;
				}
			}
			else
			{
				while ( TotalBytesAvail > BytesRead )
				{
					if ( TotalBytesAvail - BytesRead > MAX_CMD - 1 )
					{
						BytesToRead = MAX_CMD - 1;
					}
					else
					{
						BytesToRead = TotalBytesAvail - BytesRead;
					}
					if ( ::ReadFile( FReadPipe,
									 bufCmdLine.GetBuffer(),
									 BytesToRead,
									 &PipeReaded,
									 NULL ) == FALSE )
					{
						break;
					}
					if ( PipeReaded <= 0 ) continue;
					BytesRead += PipeReaded;
					bufCmdLine[ PipeReaded ] = '\0';
					::OemToAnsi( bufCmdLine.GetBuffer(), bufStr.GetBuffer() );
					strOut->Append( bufStr.GetBuffer() );
				}
			}
		}
	}
	catch (...)
	{
	}

	// Код завершения процесса
	::GetExitCodeProcess( pi.hProcess, out_exitcode );
	::CloseHandle( pi.hProcess );
	::CloseHandle( FReadPipe );
	::CloseHandle( FWritePipe );
	return TRUE;
}
Example #6
0
static int exec( lua_State* L )
{
	// считываем запускаемую команду
	CPath file = luaL_checkstring( L, 1 );
	const char* verb = lua_tostring( L, 2 );
	int noshow = lua_toboolean( L, 3 );
	int dowait = lua_toboolean( L, 4 );

	BOOL useConsoleOut = dowait && noshow && ( verb == NULL );

	DWORD exit_code = (DWORD)-1;
	BOOL bSuccess = FALSE;
	CSimpleString strOut;

	if ( useConsoleOut != FALSE )
	{
		bSuccess = RunProcessHide( file, &exit_code, &strOut ) ||
				   ExecuteHide( file, &exit_code, &strOut );
	}
	else
	{
		HANDLE hProcess = NULL;
		// запускаем процесс
		if ( verb != NULL && // если есть команда запуска
			 strcmp( verb, "explore" ) == 0 && // если команда запуска explore
			 CPath::IsFileExists( file.GetPath() ) ) // проверяем файл ли это
		{
			SHELLEXECUTEINFOA shinf = { sizeof(SHELLEXECUTEINFOA) };
			shinf.lpFile = "explorer.exe";
			CSimpleString sFileParams;
			sFileParams.Append( "/e, /select," );
			sFileParams.Append( file.GetPath() );
			shinf.lpParameters = sFileParams.GetString();
			shinf.fMask = SEE_MASK_FLAG_NO_UI |
						  SEE_MASK_NO_CONSOLE |
						  SEE_MASK_FLAG_DDEWAIT |
						  SEE_MASK_NOCLOSEPROCESS;
			shinf.nShow = noshow ? SW_HIDE : SW_SHOWNORMAL;
			bSuccess = ::ShellExecuteExA( &shinf );
			if ( bSuccess && shinf.hInstApp <= (HINSTANCE)32 ) bSuccess = FALSE;
			hProcess = shinf.hProcess;
		}
		else if ( verb != NULL && // если есть команда запуска
				  strcmp( verb, "select" ) == 0 && // если команда запуска select
				  CPath::IsPathExist( file.GetPath() ) ) // проверяем правильный путь
		{
			SHELLEXECUTEINFOA shinf = { sizeof(SHELLEXECUTEINFOA) };
			shinf.lpFile = "explorer.exe";
			CSimpleString sFileParams;
			sFileParams.Append( "/select," );
			sFileParams.Append( file.GetPath() );
			shinf.lpParameters = sFileParams.GetString();
			shinf.fMask = SEE_MASK_FLAG_NO_UI |
						  SEE_MASK_NO_CONSOLE |
						  SEE_MASK_FLAG_DDEWAIT |
						  SEE_MASK_NOCLOSEPROCESS;
			shinf.nShow = noshow ? SW_HIDE : SW_SHOWNORMAL;
			bSuccess = ::ShellExecuteExA( &shinf );
			if ( bSuccess && shinf.hInstApp <= (HINSTANCE)32 ) bSuccess = FALSE;
			hProcess = shinf.hProcess;
		}
		else
		{
			SHELLEXECUTEINFOA shinf = { sizeof(SHELLEXECUTEINFOA) };
			shinf.lpFile = file.GetPath();
			shinf.lpParameters = file.GetFileParams();
			shinf.lpVerb = verb;
			if (strlen(currentDir) > 3) {
				shinf.lpDirectory = currentDir;
			}
			shinf.fMask = SEE_MASK_FLAG_NO_UI |
						  SEE_MASK_NO_CONSOLE |
						  SEE_MASK_FLAG_DDEWAIT;
			if ( verb == NULL )
			{
				shinf.fMask |= SEE_MASK_NOCLOSEPROCESS;
			}
			else
			{
				shinf.fMask |= SEE_MASK_INVOKEIDLIST;
			}
			shinf.nShow = noshow ? SW_HIDE : SW_SHOWNORMAL;
			bSuccess = ::ShellExecuteExA( &shinf );
			if ( bSuccess && shinf.hInstApp <= (HINSTANCE)32 ) bSuccess = FALSE;
			hProcess = shinf.hProcess;
		}

		if ( dowait != FALSE && hProcess != NULL )
		{
			// ждем пока процесс не завершится
			::WaitForSingleObject( hProcess, INFINITE );
		}

		if ( hProcess != NULL )
		{
			if ( dowait != FALSE ) ::GetExitCodeProcess( hProcess, &exit_code );
			CloseHandle( hProcess );
		}

		if ( bSuccess != FALSE )
		{
			::SetLastError( 0 );
			DWORD dw;
			int len;
			char* lpMsgBuf = GetLastErrorString( &dw, &len );
			strOut.Append( lpMsgBuf );
			::LocalFree( lpMsgBuf );
		}
	}

	if ( bSuccess == FALSE )
	{
		lua_pushboolean( L, FALSE );
		lua_pushlasterr( L, NULL );
	}
	else
	{
		exit_code != (DWORD)-1 ? lua_pushnumber( L, exit_code ) : lua_pushboolean( L, TRUE );
		lua_pushstring( L, strOut.GetString() );
	}

	return 2;
}