开发者

How to call a MS-DOS batch program from delphi 2010 application

I am trying to write a routine which will execute a DOS batch program from within a Delphi 2010 application. My old routine which works in Delphi 6 keeps giving me the error message:-

"Project1.exe raised exception class EAccessViolation with message 'Access violation at address 7C82F29C in module 'kernel32.dll'. Write of address 004A3B82".

Here is my old routine that works in Delphi 6:-

Procedure TForm1.BatchProgramCall;  
var  
    StartInfo: TStartUpInfo;  
    ProcInfo: TProcessInformation;  
    createOK: Boolean;  
begin  
     FillChar(StartInfo, SizeOf(TStartUpInfo), #0);  
     FillChar(ProcInfo, SizeOf(TProcessInformation), #0);  
     StartInfo.cb := SizeOf(TStartUpInfo);  
     StartInfo.dwFlags := STARTF_USESHOWWINDOW;  
     StartInfo.wShowWindow := SW_SHOWMINIMIZED;  

     createOK := CreateProcess(Nil,PCHAR('SOMEBATCHPROGRAM.BAT'),Nil, Nil, false,
                               CREATE_NEW_PROCESS_GROUP+HIGH_PRIORITY_CLASS,
                     开发者_开发问答          NIL, NIL, STARTINFO, PROCINFO);
     if createOK then
        waitForSingleObject(PROCINFO.HPROCESS, Infinite);
end;

Please let me know what I am doing wrong or there is much better way to go about this... Thanks a lot.


you can read these articles about CreateProcess and the unicode issues.

  • Delphi in a Unicode World Part III: Unicodifying Your Code

  • CreateProcess Declaration

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

You can use the UniqueString function as workaround to fix the problem.

Procedure TForm1.BatchProgramCall;  
var
    StartInfo: TStartUpInfo;
    ProcInfo: TProcessInformation;
    createOK: Boolean;
    sMyBat: string;

begin
     FillChar(StartInfo, SizeOf(TStartUpInfo), #0);
     FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
     StartInfo.cb := SizeOf(TStartUpInfo);
     StartInfo.dwFlags      := STARTF_USESHOWWINDOW;
     StartInfo.wShowWindow := SW_SHOWMINIMIZED;

     sMyBat  :='SOMEBATCHPROGRAM.BAT';
     UniqueString(sMyBat); //this make the magic.
     createOK := CreateProcess(Nil,pchar(sMyBat),Nil, Nil, false,
                               CREATE_NEW_PROCESS_GROUP+HIGH_PRIORITY_CLASS,
                               NIL, NIL, STARTINFO, PROCINFO);
     if createOK then
        waitForSingleObject(PROCINFO.HPROCESS, Infinite);
end;


The reason that your function fails in Delphi 2010 but works in Delphi 6 is that CreateProcessW() must not be called with a read-only lpCommandLine parameter. To quote the MSDN documentation:

The Unicode version of this function, CreateProcessW, can modify the contents of this string. Therefore, this parameter cannot be a pointer to read-only memory (such as a const variable or a literal string). If this parameter is a constant string, the function may cause an access violation.

The reason that it works with Delphi 6 is that all Windows functions are really wide string internally, and the Ansi versions do nothing but convert string parameters to their wide string counterpart, and then call the wide version. You call the function with a constant, and with Delphi 6 Windows internally creates a writeable buffer for you. With Delphi 2010 you experience the AV.

Note that your program has another bug, as the documentation does also state:

To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.


I do a similar thing in Delphi 6, using much of your code but slightly different, I wonder if it will work for you?

function WinExecAndWait32(FileName: String; Visibility: integer): integer;
var
   zAppName: array[0..512] of char;
   zCurDir: array[0..255] of char;
   WorkDir: String;
   StartupInfo: TStartupInfo;
   ProcessInfo: TProcessInformation;
   Res: UINT;
begin
     StrPCopy(zAppName, FileName);
     GetDir(0, WorkDir);
     StrPCopy(zCurDir, WorkDir);
     FillChar(StartupInfo, Sizeof(StartupInfo), #0);
     StartupInfo.cb := Sizeof(StartupInfo);
     StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
     StartupInfo.wShowWindow := Visibility;

     if not (CreateProcess(nil,
       zAppName,             { pointer to command line string }
       nil,                  { pointer to process security attributes}
       nil,                  { pointer to thread security attributes }
       false,                { handle inheritance flag }
       CREATE_NEW_CONSOLE or { creation flags }
       NORMAL_PRIORITY_CLASS,
       nil,                  { pointer to new environment block }
       nil,                  { pointer to current directory name }
       StartupInfo,          { pointer to STARTUPINFO }
       ProcessInfo)) then     { pointer to PROCESS_INF }
       Result := -1
     else
     begin
          WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
          GetExitCodeProcess(ProcessInfo.hProcess, Res);
          {Added v2.4.4 (JS)}
          CloseHandle(ProcessInfo.hProcess);
          CloseHandle(ProcessInfo.hThread);
          Result := Res;
     end;
end;

To use:

WinExecAndWait32(sExtractProgramName, SW_SHOWNORMAL);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜