CreateProcessW的参数陷阱

今天一个朋友提到以前在DELPHI 7中运行正常的CreateProcess代码在XE2总是报内存地址非法写入错误。当时调试了一下,果真如此,颇感奇怪,于是祭出MSDN宝典一查,才发现其中端倪。MSDN原文部分摘录如下。

BOOL WINAPI CreateProcess(
  _In_opt_     LPCTSTR lpApplicationName,
  _Inout_opt_  LPTSTR lpCommandLine,
  _In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_         BOOL bInheritHandles,
  _In_         DWORD dwCreationFlags,
  _In_opt_     LPVOID lpEnvironment,
  _In_opt_     LPCTSTR lpCurrentDirectory,
  _In_         LPSTARTUPINFO lpStartupInfo,
  _Out_        LPPROCESS_INFORMATION lpProcessInformation
);
其中关于lpCommandLine参数的说明中有一段话。
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.
大致意思如下:
在unicode版本的CreateProcessW中需要修改字符串的内容。所以,这个参数不能为指向只读内存的指针(比如该参数使用了const限定符或者literal string(不知如何翻译了))。如果这个参数是一个常量,函数将产生内存访问错误。

原来如此,这段话就解释了为何CreateProcess会报内存写错误。以下为可以在XE2下运行的代码,功能是将DOS程序运行的结果重定向到TMemo里,该函数可实时读取DOS程序运行结果,理论上还支持输入命令,命令尾部注意加\r\n;
procedure RunDosInMemo(const DosApp: string; AMemo: TRichEdit);
const
  { 设置ReadBuffer的大小 }
  ReadBuffer = 1024;
var
  Security: TSecurityAttributes;
  ReadPipe, WritePipe: THandle;
  start: TStartUpInfo;
  ProcessInfo: TProcessInformation;
  Buffer: PAnsiChar;
  BytesRead: DWORD;
  Buf: string;
  AppName: array [0 .. MAX_PATH - 1] of Char;
  Ret: Integer;
begin
  with Security do
  begin
    nlength := SizeOf(TSecurityAttributes);
    binherithandle := true;
    lpsecuritydescriptor := nil;
  end;
  { 创建一个命名管道用来捕获console程序的输出 }
  if Createpipe(ReadPipe, WritePipe, @Security, 0) then
  begin
    Buffer := AllocMem(ReadBuffer + 1);
    FillChar(start, SizeOf(start), #0);
    { 设置console程序的启动属性 }
    with start do
    begin
      cb := SizeOf(start);
      GetStartupInfo(start);
      hStdOutput := WritePipe; // 将输出定向到我们建立的WritePipe上
      hStdInput := ReadPipe; // 将输入定向到我们建立的ReadPipe上
      hStdError := WritePipe; // 将错误输出定向到我们建立的WritePipe上
      dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
      wShowWindow := SW_HIDE; // 设置窗口为hide
    end;

    try
      StrPCopy(@AppName, DosApp);
      { 创建一个子进程,运行console程序 }
      if CreateProcess(nil, @AppName, @Security, @Security, true,
        NORMAL_PRIORITY_CLASS, nil, nil, start, ProcessInfo) then
      begin
        Ret := WaitForSingleObject(ProcessInfo.hProcess, 0);
        { 当进程没有结束时,循环读取输出数据}
        while (Ret = WAIT_TIMEOUT) or (Ret = WAIT_OBJECT_0) do
        begin
          BytesRead := 0;
          { 预读管道数据,必须要加,否则部分系统ReadFile会进入挂起状态}
          if PeekNamedPipe(ReadPipe, nil, 0, nil, @BytesRead, nil) then
          begin
            if BytesRead > 0 then
            begin
              BytesRead := 0;
              { 读取管道数据}
              if not ReadFile(ReadPipe, Buffer[0], ReadBuffer, BytesRead, nil)
              then
                Break;
              if BytesRead = 0 then
                Break;
              Buffer[BytesRead] := #0;
              OemToAnsi(Buffer, Buffer);
              Buf := Buf + string(Buffer);
              { 处理分隔符}
              while pos(#10, Buf) > 0 do
              begin
                AMemo.Lines.add(Copy(Buf, 1, pos(#10, Buf) - 1));
                Delete(Buf, 1, pos(#10, Buf));
              end;
            end;
          end;
          { 进程结束时退出循环}
          if (Ret = WAIT_OBJECT_0) then
            Break;
          { 长时间操作时处理进程消息}
          Application.ProcessMessages;
          Ret := WaitForSingleObject(ProcessInfo.hProcess, 0);
        end;
      end
      else
        AMemo.Lines.add('CreateProcess Last error:' + IntToStr(GetLastError));
    finally
      FreeMem(Buffer);
      CloseHandle(WritePipe);
      CloseHandle(ReadPipe);
      CloseHandle(ProcessInfo.hProcess);
      CloseHandle(ProcessInfo.hThread);
    end;
  end;
end;
 
 

 有不少朋友反映使用CreateProcess执行cmd命令,然后WaitForSingleObject等待不到进程结束,其实很简单,将cmd加上参数/c 执行即可,例:'cmd /c dir'。

 

转载于:https://www.cnblogs.com/vikong2012/articles/2684352.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值