【问题标题】:How to call a MS-DOS batch program from delphi 2010 application如何从 delphi 2010 应用程序调用 MS-DOS 批处理程序
【发布时间】:2010-12-17 18:49:52
【问题描述】:

我正在尝试编写一个从 Delphi 2010 应用程序中执行 DOS 批处理程序的例程。我在 Delphi 6 中工作的旧例程不断给我错误消息:-

“Project1.exe 引发异常类 EAccessViolation,并带有消息“模块 'kernel32.dll' 中地址 7C82F29C 的访问冲突。写入地址 004A3B82”。

这是我在 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;

请让我知道我做错了什么,或者有更好的方法来解决这个问题...... 非常感谢。

【问题讨论】:

  • 在 StackOverflow 上搜索“[delphi] ShellExecute”或“[delphi] ShellExecuteEx”会给你一些选择。

标签: delphi batch-file


【解决方案1】:

您的函数在 Delphi 2010 中失败但在 Delphi 6 中有效的原因是不能使用只读的 lpCommandLine 参数调用 CreateProcessW()。引用 MSDN 文档:

此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。因此,此参数不能是指向只读内存的指针(例如 const 变量或文字字符串)。如果此参数是一个常量字符串,该函数可能会导致访问冲突。

它与 Delphi 6 一起工作的原因是所有 Windows 函数在内部都是真正的宽字符串,而 Ansi 版本除了将字符串参数转换为它们的宽字符串对应物,然后调用宽版本之外什么都不做。您使用常量调用该函数,并使用 Delphi 6 Windows 在内部为您创建一个可写缓冲区。使用 Delphi 2010,您可以体验 AV。

请注意,您的程序还有另一个错误,正如文档中所述:

要运行批处理文件,您必须启动命令解释器;将 lpApplicationName 设置为 cmd.exe 并将 lpCommandLine 设置为以下参数:/c 加上批处理文件的名称。

【讨论】:

  • 谢谢你提到命令行,否则我又要碰壁了。
【解决方案2】:

您可以阅读有关 CreateProcess 和 unicode 问题的这些文章。

此函数的 Unicode 版本 CreateProcessW 可以修改此字符串的内容。因此,此参数不能是指向只读内存的指针(例如 const 变量或文字字符串)。如果此参数是一个常量字符串,该函数可能会导致访问冲突。

您可以使用UniqueString 函数作为解决方法来解决问题。

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;

【讨论】:

  • +1 要链接到 CreateProcess 声明文章,请留下您的反应/投票,以便我们对 Jedi Apilib 进行更改!
【解决方案3】:

我在 Delphi 6 中做了类似的事情,使用了你的大部分代码但略有不同,不知道它是否适合你?

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;

使用方法:

WinExecAndWait32(sExtractProgramName, SW_SHOWNORMAL);

【讨论】:

    猜你喜欢
    • 2012-02-25
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    • 2010-11-12
    • 2023-03-13
    相关资源
    最近更新 更多