Application Configuration File Not Being Read When Application Called via ShellExecute
I have a .NET application that is launched via a Delphi program using ShellExecute. Unfortunately when launched in this manner, the application does not seem to be reading its app.config file correctly, as if the file did not exist.
I have tried testing the application in other scenarios, e.g. calling from a shortcut with the the working directory set to a different folder and it runs fine.
[Edit]The Environment.CurrentDirect开发者_开发知识库ory property returns the directory of the Delphi program.
Any ideas would be really appreciated.
Cheers,
James
Apparently the process you spawn cannot handle the fact that the working directory is not it's own.
You could open the file using CreateProcess(). I have a small example with waiting for (but you can clip that out):
procedure ExecuteAndWaitFor(CommandLine, CurrentDirectory: string; Environment: TStrings);
var
List: TList;
ActiveWin: HWnd;
i: Integer;
Ret: Longword;
SI: TStartupInfo;
PI: TProcessInformation;
MadeForeground: Boolean;
AssociatedCommandLine: string;
begin
// find the association ot use
AssociatedCommandLine := GetAssociatedCommandLine(CommandLine);
// first we create a list of windows which we need to block...
List := TList.Create;
try
ActiveWin := Windows.GetForegroundWindow;
// get the list of all visible and active top windows...
if not Windows.EnumThreadWindows(GetCurrentThreadId,@InternallyThreadWindowCallback,Integer(List)) then RaiseLastOSError;
// disable all those windows...
for i := 0 to List.Count - 1 do Windows.EnableWindow(HWnd(List[i]),False);
try
// create the process
System.FillChar(SI,sizeof(SI),0);
SI.cb := sizeof(SI.cb);
// todo: environment
if not Windows.CreateProcess(nil,PChar(AssociatedCommandLine),nil,nil,False,NORMAL_PRIORITY_CLASS,nil,PChar(CurrentDirectory),SI,PI) then RaiseLastOSError;
// wait until the process is finished...
MadeForeGround := False;
repeat
// process any pending messages in the thread's message queue
Application.ProcessMessages;
if not MadeForeground then begin
Windows.EnumThreadWindows(PI.dwThreadId,@InternallyTopWindowToForeMost,Integer(@MadeForeGround));
end;
// wait for a message or the process to be finished
Ret := Windows.MsgWaitForMultipleObjects(1, PI.hProcess, False, INFINITE, QS_ALLINPUT);
if Ret = $FFFFFFFF then RaiseLastOSError;
until Ret = 0;
// free the process handle
Windows.CloseHandle(PI.hProcess);
WIndows.CloseHandle(PI.hThread);
finally
// enable all those windows
for i := 0 to List.Count - 1 do Windows.EnableWindow(HWnd(List[i]), True);
end;
Windows.SetForegroundWindow(ActiveWin);
finally
List.Free;
end;
end;
Added some missing utility functions:
uses
SysUtils, Registry;
function GetAssociatedFile(const Extension: string; const RemoveParameters: Boolean = False): string;
var
FileClass: string;
Reg: TRegistry;
Position: Integer;
begin
// initialize
Result := '';
// create registry entry
Reg := TRegistry.Create(KEY_EXECUTE);
try
// find the given extension
Reg.RootKey := HKEY_CLASSES_ROOT;
FileClass := '';
if Reg.OpenKeyReadOnly(ExtractFileExt(Extension)) then begin
FileClass := Reg.ReadString('');
Reg.CloseKey;
end;
if FileClass <> '' then begin
if Reg.OpenKeyReadOnly(FileClass + '\Shell\Open\Command') then begin
Result := Reg.ReadString('');
Reg.CloseKey;
end;
end;
finally
Reg.Free;
end;
// remove the additional parameters
Position := Pos('"%1"', Result);
if RemoveParameters and (Position > 0) then
Result := Trim(Copy(Result, 1, Position - 1))
else
Result := Trim(Result);
end;
function GetAssociatedCommandLine(const CommandLine: string): string;
begin
// build the command line with the associated file in front of it
Result := Trim(GetAssociatedFile(CommandLine, True) + ' ') + '"' + CommandLine + '"';
end;
function InternallyThreadWindowCallback(Window: HWnd; Data: Longint): Bool; stdcall;
var
List: TList;
begin
Result := True;
if (not IsWindowVisible(Window)) or (not IsWindowEnabled(Window)) then Exit;
List := TList(Data);
List.Add(Pointer(Window));
end;
function InternallyTopWindowToForeMost(Window: HWnd; Data: LongInt): Bool; stdcall;
begin
Result := True;
if (not IsWindowVisible(Window)) or (not IsWindowEnabled(Window)) then Exit;
SetForegroundWindow(Window);
PBoolean(Data)^ := True;
end;
Well I researched it a bit and there does not appear to be a solution that is both simple and elegant.
The easiest way round it seems to be to call an intermediate .NET program that then runs the target application via Process.Start and passes the parameters on. Not ideal but it's simpler than the other solutions I have found so far.
精彩评论