问题
I have made a Game Launcher and I use this command:
procedure TFMain.ImgBtn1Click(Sender: TObject);
begin
ShellExecute(TForm(Owner).Handle, nil, 'starter.exe', '-lang rus', nil, SW_SHOWNORMAL);
end;
with '-lang rus' as a parameter. Everything works fine. The Game Launches and the language is in russian(if i put '-lang eng' it still works fine and the game is in english).
The starter.exe application is inside a folder named ''bin''. When i want to relocate the launcher outside this folder i use this command:
procedure TFMain.ImgBtn1Click(Sender: TObject);
begin
ShellExecute(TForm(Owner).Handle, nil, 'bin\starter.exe', '-lang rus', nil, SW_SHOWNORMAL);
end;
But then the game isn't launching. Actually nothing happens. What should i change?
回答1:
You have to use full path to the application you are trying to start.
ExtractFilePath(Application.ExeName) will give you full path to your launcher exe.
Solution 1: using ShellExecute
procedure TFMain.ImgBtn1Click(Sender: TObject);
var
ExecuteResult: integer;
Path: string;
begin
Path := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName));
ExecuteResult := ShellExecute(0, nil, PChar(Path + 'bin\starter.exe'), '-lang rus', nil, SW_SHOWNORMAL);
if ExecuteResult <= 32 then ShowMessage('Error: ' + IntToStr(ExecuteResult));
end;
You can find list of error codes at: ShellExecute function documentation
Most common error codes:
ERROR_FILE_NOT_FOUND 0x2ERROR_PATH_NOT_FOUND 0x3
Solution 2: using ShellExecuteEx
var
FileName, Parameters, Folder: string;
sei: TShellExecuteInfo;
Error: DWORD;
OK: boolean;
begin
Folder := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName)) + 'bin\';
FileName := Folder + 'starter.exe';
Parameters := '-lang rus';
ZeroMemory(@sei, SizeOf(sei));
sei.cbSize := SizeOf(sei);
sei.lpFile := PChar(FileName);
sei.lpParameters := PChar(Parameters);
sei.lpDirectory := PChar(Folder);
sei.nShow := SW_SHOWNORMAL;
OK := ShellExecuteEx(@sei);
if not OK then
begin
Error := GetLastError;
ShowMessage('Error: ' + IntToStr(Error));
end;
end;
ShellExecuteEx documentation
Solution 3: using CreateProcess
function ExecuteProcess(const FileName, Params: string; Folder: string; WaitUntilTerminated, WaitUntilIdle, RunMinimized: boolean;
var ErrorCode: integer): boolean;
var
CmdLine: string;
WorkingDirP: pchar;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
begin
Result := true;
CmdLine := '"' + FileName + '" ' + Params;
if Folder = '' then Folder := ExcludeTrailingPathDelimiter(ExtractFilePath(FileName));
ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
StartupInfo.cb := SizeOf(StartupInfo);
if RunMinimized then
begin
StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow := SW_SHOWMINIMIZED;
end;
if Folder <> '' then WorkingDirP := pchar(Folder)
else WorkingDirP := nil;
if not CreateProcess(nil, pchar(CmdLine), nil, nil, false, 0, nil, WorkingDirP, StartupInfo, ProcessInfo) then
begin
Result := false;
ErrorCode := GetLastError;
exit;
end;
with ProcessInfo do
begin
CloseHandle(hThread);
if WaitUntilIdle then WaitForInputIdle(hProcess, INFINITE);
if WaitUntilTerminated then
repeat
Application.ProcessMessages;
until MsgWaitForMultipleObjects(1, hProcess, false, INFINITE, QS_ALLINPUT) <> WAIT_OBJECT_0 + 1;
CloseHandle(hProcess);
end;
end;
procedure TForm1.Button4Click(Sender: TObject);
var
FileName, Parameters, Folder: string;
Error: integer;
OK: boolean;
begin
Folder := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName)) + 'bin\';
FileName := Folder + 'starter.exe';
Parameters := '-lang rus';
OK := ExecuteProcess(FileName, Parameters, Folder, false, false, false, Error);
if not OK then
begin
Error := GetLastError;
ShowMessage('Error: ' + IntToStr(Error));
end;
end;
CreateProcess documentation
回答2:
You should use fully-qualified (absolute) paths. For instance, if you know that the path is
C:\Program Files (x86)\My Company\My Game\bin\starter.exe
you should pass that string. Of course, you should never hard-code such a string, since it may be different on different systems. If your application is a general application launcher, you get the path from the user. If your application launches your own company's games, you have to figure out a clever way to communicate paths.
It is not clear from your question, but if bin\starter.exe is relative to the path of your application, you can use
ExtractFilePath(Application.ExeName) + 'bin\starter.exe'
By the way, you could have figured all this out by yourself by looking at the return value of ShellExecute. Of course, you have read the ShellExecute documentation carefully, so you know what the return values are. So, you would easily have recognised ERROR_FILE_NOT_FOUND and realised you need a fully-qualified path.
来源:https://stackoverflow.com/questions/27249995/delphi-7-shellexecute-command-not-working-in-situations