我正在尝试编写一个例程,它将在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;
请让我知道我做错了什么,或者有更好的方法来解决这个问题...... 非常感谢。
答案 0 :(得分:10)
您可以阅读有关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 :(得分:6)
您的函数在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 :(得分:0)
我在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);