今天一个朋友提到以前在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/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。