在编程开发的时候,我们时常会调用windows本身的功能,如:检测网络通断,连接无线wifi等。

虽然,用 windows api 操作可以完美地完成这些操作,但是,函数参数太难了。令人望而生畏,不是普通开发者能办到的。

但是,我们可以用一种变通的方法,来解决这个问题,就是使用控制台命令行,如 ping , netsh 等。

我在网络上,搜索到了delphi调用命令行,并返回接收返回的结果(字符串信息)代码,但这些代码仅仅只是功能实现了,离实用性还差一步。

所以做了如下改进:

1.将 cmd 运行进程放入线程中,不放入线程,界面就卡死了,阻塞的,实用性大大降低,可能只能采用运行一次命令,就创建一次cmd进程的方式来实现。

本例的CMD只创建一次,可以复用。

2.提供了明确的执行结果事件,就是命令真正执行完毕的事件,因为返回的结果字符串不是一次性全部返回的,太长的结果是分批次返回的。这一点,但其它的控制台的设备中也是一样的。如路由器的 console 下。

3.实现了 ctrl + c 这类特殊事件的触发,如果没有这个功能,运行 ping 127.0.0.1 -t 就无法正常结束。

经过工作实践中运行,觉得还不错,不敢独享,故分享给大家。也算是 delphi 线程的一个教学实例。

unit uSimpleConsole;

interface

uses
System.Classes, WinApi.Windows, uElegantThread, uSimpleThread, uSimpleList; type TSimpleConsole = class; TConsoleStatus = (ccUnknown, ccInit, ccCmdResult);
TOnConsoleStatus = procedure(Sender: TSimpleConsole; AStatus: TConsoleStatus) of object; TInnerConsoleStatus = (iccInit, iccExecCmd, iccSpecEvent, iccWait); PCmdStr = ^TCmdStr; TCmdStr = record
Status: TInnerConsoleStatus;
CmdStr: string;
Event: integer;
end; TCmdStrList = class(TSimpleList<PCmdStr>)
private
function AddCmdStr(ACmdStr: string): PCmdStr;
function AddSpecialEvent(AEvent: integer): PCmdStr;
protected
procedure FreeItem(Item: PCmdStr); override;
end; TSimpleConsole = class(TSimpleThread)
private FInRead: THandle; // in 用于控制台输入
FInWrite: THandle;
FOutRead: THandle; // out 用于控制台输出
FOutWrite: THandle;
FFileName: String;
FProcessInfo: TProcessInformation;
FProcessCreated: Boolean;
FCmdStrList: TCmdStrList;
FCmdResultStrs: TStringList; FConsoleStatus: TInnerConsoleStatus; procedure Peek;
procedure DoPeek;
procedure DoCreateProcess;
procedure DoExecCmd(ACmdStr: string);
function WriteCmd(ACmdStr: string): Boolean;
procedure DoOnConsoleStatus(AStatus: TConsoleStatus); procedure ClearCmdResultStrs;
procedure AddCmdResultText(AText: string);
function CheckCmdResultSign(AText: string): Boolean; public
constructor Create(AFileName: string); reintroduce;
destructor Destroy; override;
procedure StartThread; override;
procedure ExecCmd(ACmdStr: String);
procedure ExecSpecialEvent(AEvent: integer); // 执行特殊事件,如 ctrl + c
property CmdResultStrs: TStringList read FCmdResultStrs;
public
WorkDir: string;
ShowConsoleWindow: Boolean;
OnConsoleStatus: TOnConsoleStatus;
end; function AttachConsole(dwprocessid: DWORD): BOOL;
stdcall external kernel32; implementation uses
Vcl.Forms, System.SysUtils, System.StrUtils; { TSimpleConsole }
const
cnSecAttrLen = sizeof(TSecurityAttributes); procedure TSimpleConsole.AddCmdResultText(AText: string);
var
L: TStringList;
begin
L := TStringList.Create;
try
L.Text := Trim(AText);
FCmdResultStrs.AddStrings(L);
finally
L.Free;
end;
end; function TSimpleConsole.CheckCmdResultSign(AText: string): Boolean;
var
L: TStringList;
i, n: integer;
sTemp: string;
begin
Result := false;
L := TStringList.Create;
try
L.Text := Trim(AText);
for i := L.Count - downto do
begin
sTemp := Trim(L[i]);
n := length(sTemp);
if (PosEx(':\', sTemp) = 2) and (PosEx('>', sTemp, 3) >= n) then
begin
Result := true;
exit;
end;
end;
finally
L.Free;
end;
end; procedure TSimpleConsole.ClearCmdResultStrs;
begin
FCmdResultStrs.Clear;
end; constructor TSimpleConsole.Create(AFileName: string);
begin
inherited Create(true);
FFileName := AFileName;
FProcessCreated := false;
ShowConsoleWindow := false; FCmdResultStrs := TStringList.Create;
FCmdStrList := TCmdStrList.Create; end; destructor TSimpleConsole.Destroy;
var
Ret: integer;
begin
Ret := ;
if FProcessCreated then
begin TerminateProcess(FProcessInfo.hProcess, Ret); closehandle(FInRead);
closehandle(FInWrite);
closehandle(FOutRead);
closehandle(FOutWrite); end; FCmdResultStrs.Free;
FCmdStrList.Free; inherited;
end; procedure TSimpleConsole.DoCreateProcess;
const
cnBuffLen = ;
cnReadByteLen = cnBuffLen;
cnSecAttrLen = sizeof(TSecurityAttributes);
cnStartUpInfoLen = sizeof(TStartupInfo);
var
sWorkDir: string;
LStartupInfo: TStartupInfo;
LSecAttr: TSecurityAttributes;
sCmd: string;
v: integer;
begin if length(WorkDir) > then
begin
sWorkDir := WorkDir;
end
else
begin
sWorkDir := ExtractFileDir(Application.ExeName);
WorkDir := sWorkDir;
end; if ShowConsoleWindow then
v :=
else
v := ; ZeroMemory(@LSecAttr, cnSecAttrLen); LSecAttr.nLength := cnSecAttrLen;
LSecAttr.bInheritHandle := true;
LSecAttr.lpSecurityDescriptor := nil; CreatePipe(FInRead, FInWrite, @LSecAttr, );
CreatePipe(FOutRead, FOutWrite, @LSecAttr, ); ZeroMemory(@LStartupInfo, cnStartUpInfoLen); LStartupInfo.cb := cnStartUpInfoLen;
LStartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
LStartupInfo.wShowWindow := v; LStartupInfo.hStdInput := FInRead; // 如果为空,则可以由键盘输入
LStartupInfo.hStdOutput := FOutWrite; // 如果为空,则显示到屏幕上
LStartupInfo.hStdError := FOutWrite; setlength(sCmd, length(FFileName)); CopyMemory(@sCmd[], @FFileName[], length(FFileName) * sizeof(char)); if CreateProcess(nil, PChar(sCmd), { pointer to command line string }
@LSecAttr, { pointer to process security attributes }
@LSecAttr, { pointer to thread security attributes }
true, { handle inheritance flag }
NORMAL_PRIORITY_CLASS, nil, { pointer to new environment block }
PChar(sWorkDir), { pointer to current directory name, PChar }
LStartupInfo, { pointer to STARTUPINFO }
FProcessInfo) { pointer to PROCESS_INF }
then
begin
// ClearCmdResultStrs;
// FInnerConsoleList.AddInerStatus(iccInit);
end
else
begin
DoOnStatusMsg('进程[' + FFileName + ']创建失败');
end; end; procedure TSimpleConsole.DoExecCmd(ACmdStr: string);
var
sCmdStr: string;
begin
sCmdStr := ACmdStr + ##;
if WriteCmd(sCmdStr) then
begin
// FInnerConsoleList.AddCmdStr(iccExecCmd);
// Peek
end
else
begin
DoOnStatusMsg('执行:[' + ACmdStr + ']失败');
end;
end; procedure TSimpleConsole.DoOnConsoleStatus(AStatus: TConsoleStatus);
begin
if Assigned(OnConsoleStatus) then
OnConsoleStatus(self, AStatus);
end; procedure TSimpleConsole.DoPeek;
var
strBuff: array [ .. ] of AnsiChar;
nBytesRead: cardinal;
sOutStr: string;
sOut: AnsiString;
nOut: cardinal;
BPeek: Boolean;
p: PCmdStr; begin if not FProcessCreated then
begin
FConsoleStatus := iccInit;
DoCreateProcess;
FProcessCreated := true;
end; sOutStr := '';
nBytesRead := ; nOut := ;
sOut := ''; BPeek := PeekNamedPipe(FOutRead, @strBuff, , @nBytesRead, nil, nil); while BPeek and (nBytesRead > ) do
begin inc(nOut, nBytesRead);
setlength(sOut, nOut);
CopyMemory(@sOut[nOut - nBytesRead + ], @strBuff[], nBytesRead);
ReadFile(FOutRead, strBuff[], nBytesRead, nBytesRead, nil); BPeek := PeekNamedPipe(FOutRead, @strBuff, , @nBytesRead, nil, nil); end; if length(sOut) > then
begin
sOutStr := String(sOut); DoOnStatusMsg(sOutStr); if CheckCmdResultSign(sOutStr) then
begin if FConsoleStatus = iccInit then
begin
DoOnConsoleStatus(ccInit)
end
else if FConsoleStatus = iccExecCmd then
begin
AddCmdResultText(sOutStr);
DoOnConsoleStatus(ccCmdResult)
end
else
DoOnConsoleStatus(ccUnknown); ClearCmdResultStrs; end; end; FCmdStrList.Lock;
try p := FCmdStrList.PopFirst;
if Assigned(p) then
begin FConsoleStatus := iccExecCmd; if p.Status = iccExecCmd then
DoExecCmd(p.CmdStr)
else if p.Status = iccSpecEvent then
begin
AttachConsole(self.FProcessInfo.dwprocessid);
SetConsoleCtrlHandler(nil, true);
GenerateConsoleCtrlEvent(p.Event, );
end; dispose(p); end; finally FCmdStrList.Unlock;
end; Peek;
SleepExceptStopped(); end; procedure TSimpleConsole.ExecCmd(ACmdStr: String);
begin FCmdStrList.Lock;
try
FCmdStrList.AddCmdStr(ACmdStr);
finally
FCmdStrList.Unlock;
end; Peek; end; procedure TSimpleConsole.Peek;
begin
ExeProcInThread(DoPeek);
end; procedure TSimpleConsole.ExecSpecialEvent(AEvent: integer);
begin
FCmdStrList.Lock;
try
FCmdStrList.AddSpecialEvent(AEvent);
finally
FCmdStrList.Unlock;
end; Peek; end; procedure TSimpleConsole.StartThread;
begin
inherited;
Peek;
end; function TSimpleConsole.WriteCmd(ACmdStr: string): Boolean;
var
nCmdLen: cardinal;
nRetBytes: cardinal;
sCmdStr: AnsiString;
begin
nCmdLen := length(ACmdStr);
sCmdStr := AnsiString(ACmdStr);
Result := WriteFile(FInWrite, sCmdStr[], (nCmdLen), nRetBytes, nil);
end; { TInnerStatusList } function TCmdStrList.AddCmdStr(ACmdStr: string): PCmdStr;
begin
New(Result);
Add(Result);
Result.Status := iccExecCmd;
Result.CmdStr := ACmdStr;
end; function TCmdStrList.AddSpecialEvent(AEvent: integer): PCmdStr;
begin
New(Result);
Add(Result);
Result.Status := iccSpecEvent;
Result.Event := AEvent;
end; procedure TCmdStrList.FreeItem(Item: PCmdStr);
begin
inherited;
dispose(Item);
end; end.

uSimpleConsole

本例程XE8源码下载

delphi 在线程中运行控制台命令(console)的更多相关文章

  1. 让NSURLConnection在子线程中运行

    可以有两个办法让NSURLConnection在子线程中运行,即将NSURLConnection加入到run loop或者NSOperationQueue中去运行. 前面提到可以将NSTimer手动加 ...

  2. iOS多线程的初步研究(五)-- 如何让NSURLConnection在子线程中运行

    可以有两个办法让NSURLConnection在子线程中运行,即将NSURLConnection加入到run loop或者NSOperationQueue中去运行. 前面提到可以将NSTimer手动加 ...

  3. 让你提前认识软件开发(23):怎样在C语言中运行shell命令?

    第1部分 又一次认识C语言 怎样在C语言中运行shell命令? [文章摘要] Linux操作系统具备开源等诸多优秀特性,因此在很多通信类软件(主流开发语言为C语言)中,开发平台都迁移到了Linux上, ...

  4. 怎样在Java中运行Hive命令或HiveQL

    这里所说的在Java中运行Hive命令或HiveQL并非指Hive Client通过JDBC的方式连接HiveServer(or HiveServer2)运行查询,而是简单的在部署了HiveServe ...

  5. 如何让NSURLConnection在子线程中运行

    可以有两个办法让NSURLConnection在子线程中运行,即将NSURLConnection加入到run loop或者NSOperationQueue中去运行. 前面提到可以将NSTimer手动加 ...

  6. 在eclipse中运行maven命令没有反应,console也不打印信息

    eclipse的maven项目中,在run as  执行maven命令的时候发现毫无反应,console也不打印信息,原因是因为没有传参数,解决办法如下:①打开eclipse的window菜单: ②接 ...

  7. IDEA清空控制台以及Java中运行cmd命令实现清屏操作

    IDEA中清空控制台方法 在网上有看到各种的实现方法,比如: Runtime.getRuntime().exec("cls"); 或者: public static void cl ...

  8. 关于Qt中使用线程的时候函数具体在哪个线程中运行的问题

    在子线程中,run函数中以及其中调用的都在单独的子线程里面运行,但是其他的类似构造函数之流都是在主线程里面运行的,不要搞混了

  9. 在cmd窗口中运行php命令

    1.首先安装php.我使用的是wamp,里面包含php5.5.12 2.将C:\wamp\bin\php\php5.5.12添加到环境变量Path中 3.在cmd中运行php -v可以查看php版本相 ...

随机推荐

  1. adb 解说

    ADB是一个 客户端-服务器端 程序, 其中客户端是你用来操作的电脑, 服务器端是android设备. 先说安装方法, 电脑上需要安装客户端. 客户端包含在sdk里. 设备上不需要安装, 只需要在手机 ...

  2. 80X86寄存器详解<转载>

    引子 打算写几篇稍近底层或者说是基础的博文,浅要介绍或者说是回顾一些基础知识, 自然,还是得从最基础的开始,那就从汇编语言开刀吧, 从汇编语言开刀的话,我们必须还先要了解一些其他东西, 像  CPU ...

  3. 51nod 1009 数字1的数量 数位dp

    1009 数字1的数量 基准时间限制:1 秒 空间限制:131072 KB   给定一个十进制正整数N,写下从1开始,到N的所有正数,计算出其中出现所有1的个数.   例如:n = 12,包含了5个1 ...

  4. Pycharm更换pip源为国内

    Python里的pip是官方自带的源,国内使用pip安装的时候十分缓慢,所以最好是更换成中国国内的源地址. 目前国内靠谱的 pip 镜像源有: 清华: https://pypi.tuna.tsingh ...

  5. linux防火墙开关

    对于普通Linux机器开关防火墙命令: 1重启后生效 chkconfig iptables off chkconfig iptables on 2即时生效 serivce iptables statu ...

  6. SQL之merge into(转)

    简介 Merge关键字是一个神奇的DML关键字.它在SQL Server 2008被引入,它能将Insert,Update,Delete简单的并为一句.MSDN对于Merge的解释非常的短小精悍:”根 ...

  7. MSDN 单机 MVC 帮助文档

    因为微软的mvc框架也是从开源框架演变而来的,所以微软没把mvc帮助文档放到单击帮助文档中.sososos下载好msdn单机帮助后,却找不到 System.Web.MVC 等命名空间的东西. 解决办法 ...

  8. 如何理解python中的if __name__=='main'的作用

    一. 一个浅显易懂的比喻 我们在学习python编程时,不可避免的会遇到if __name__=='main'这样的语句,它到底有什么作用呢? <如何简单地理解Python中的if __name ...

  9. Mac的搜狗输入法和QQ输入法加入⌘⌥⌃⇧自定义短语

    搜狗输入法(Mac):http://pinyin.sogou.com/mac/ 创建名为『搜狗输入法自定义短语.ini』的文本文件(建议用Sublime Text),内容如下,然后偏好设置的自定义短语 ...

  10. IOC和DI,AOP的本质理解

    IOC: Inversion of Control,控制反转, 控制权从应用程序转移到框架(如IOC容器),是框架共有的特性. 对于IOC的理解,可以把IOC看作是一个生产和管理bean对象的容器.原 ...