CreateProcessW的参数陷阱

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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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'。

 

作者:空 
出处:http://www.cnblogs.com/vikong2012/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

你可能感兴趣的:(CreateProcessW的参数陷阱)