Как вызвать пакетную программу MS-DOS из приложения delphi 2010 - PullRequest
1 голос
/ 18 ноября 2009

Я пытаюсь написать процедуру, которая будет выполнять пакетную программу DOS из приложения Delphi 2010. Моя старая рутина, которая работает в Delphi 6, постоянно выдает мне сообщение об ошибке: -

"Project1.exe поднял класс исключений EAccessViolation с сообщением« Нарушение доступа по адресу 7C82F29C в модуле «kernel32.dll». Запись адреса 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;

Пожалуйста, дайте мне знать, что я делаю неправильно, или есть лучший способ сделать это ... Большое спасибо.

Ответы [ 3 ]

10 голосов
/ 18 ноября 2009

Вы можете прочитать эти статьи о CreateProcess и проблемах Unicode.

Версия этой функции Unicode, CreateProcessW, может изменять содержимое этой строки. Следовательно, этот параметр не может быть указателем на постоянную память (например, константную переменную или литеральную строку). Если этот параметр является константной строкой, функция может вызвать нарушение прав доступа.

Вы можете использовать функцию 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;
6 голосов
/ 18 ноября 2009

Причина, по которой ваша функция не работает в Delphi 2010, но работает в Delphi 6, заключается в том, что CreateProcessW() нельзя вызывать с параметром lpCommandLine только для чтения. Цитировать документацию MSDN:

Версия этой функции Unicode, CreateProcessW, может изменять содержимое этой строки. Следовательно, этот параметр не может быть указателем на постоянную память (например, константную переменную или литеральную строку). Если этот параметр является константной строкой, функция может вызвать нарушение прав доступа.

Причина, по которой он работает с Delphi 6, заключается в том, что все функции Windows внутренне являются действительно широкими строками, а версии Ansi не делают ничего, кроме преобразования строковых параметров в их широкий аналог строки, а затем вызывают широкую версию. Вы вызываете функцию с константой, а в Delphi 6 Windows внутренне создает для вас доступный для записи буфер. С Delphi 2010 вы испытываете AV.

Обратите внимание, что в вашей программе есть еще одна ошибка, поскольку в документации также указано:

Чтобы запустить командный файл, вы должны запустить интерпретатор команд; установите для lpApplicationName значение cmd.exe и задайте для lpCommandLine следующие аргументы: / c плюс имя пакетного файла.

0 голосов
/ 18 ноября 2009

Я делаю аналогичные вещи в 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...