delphi 在线程中运行控制台命令(console)

在编程开发的时候,我们时常会调用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 - 1 downto 0 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 := 0;
  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 = 256;
  cnReadByteLen = cnBuffLen;
  cnSecAttrLen = sizeof(TSecurityAttributes);
  cnStartUpInfoLen = sizeof(TStartupInfo);
var
  sWorkDir: string;
  LStartupInfo: TStartupInfo;
  LSecAttr: TSecurityAttributes;
  sCmd: string;
  v: integer;
begin

  if length(WorkDir) > 0 then
  begin
    sWorkDir := WorkDir;
  end
  else
  begin
    sWorkDir := ExtractFileDir(Application.ExeName);
    WorkDir := sWorkDir;
  end;

  if ShowConsoleWindow then
    v := 1
  else
    v := 0;

  ZeroMemory(@LSecAttr, cnSecAttrLen);

  LSecAttr.nLength := cnSecAttrLen;
  LSecAttr.bInheritHandle := true;
  LSecAttr.lpSecurityDescriptor := nil;

  CreatePipe(FInRead, FInWrite, @LSecAttr, 0);
  CreatePipe(FOutRead, FOutWrite, @LSecAttr, 0);

  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[1], @FFileName[1], 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 + #13#10;
  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 [0 .. 255] 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 := 0;

  nOut := 0;
  sOut := ‘‘;

  BPeek := PeekNamedPipe(FOutRead, @strBuff, 256, @nBytesRead, nil, nil);

  while BPeek and (nBytesRead > 0) do
  begin

    inc(nOut, nBytesRead);
    setlength(sOut, nOut);
    CopyMemory(@sOut[nOut - nBytesRead + 1], @strBuff[0], nBytesRead);
    ReadFile(FOutRead, strBuff[0], nBytesRead, nBytesRead, nil);

    BPeek := PeekNamedPipe(FOutRead, @strBuff, 256, @nBytesRead, nil, nil);

  end;

  if length(sOut) > 0 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, 0);
      end;

      dispose(p);

    end;

  finally

    FCmdStrList.Unlock;
  end;

  Peek;
  SleepExceptStopped(200);

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[1], (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源码下载

原文地址:https://www.cnblogs.com/lackey/p/10357331.html

时间: 2024-08-27 02:00:21

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

控制台命令Console详解

控制台命令Console详解 一 什么是 Console Console 是用于显示 JS和 DOM 对象信息的单独窗口.并且向 JS 中注入1个 Console 对象,使用该对象 可以输出信息到 Console 窗口中. 二 什么浏览器支持 Console 很多人可能都知道 Chrome 和 FireFox(FireBug)中都支持 Console.而其他浏览器都支 持不好.比如 IE8 自带的开发工具虽然支持 Console,但功能比较单调,显示对象的时候都是显示 [Object,Objec

PowerShell中运行Bcdedit命令出错

见下图,当我尝试在PowerShell执行bcdedit删除其中一个启动项时,出现错误:而在CMD中运行时则正常. 执行的命令是: bcdedit /delete {f33a2785-b94a-11e3-a6eb-00e0661386c5} /cleanup 出错的原因: (1)PowerShell中将{}中的内容当作计算式对待: (2)CMD中将{-}整体看作字符: 事实上{f33a2785-b94a-11e3-a6eb-00e0661386c5}就是字符: 由此衍生出: (1)PowerSh

MATLAB2014a搭配Notebook实现在Word2013中运行MATLAB命令

测试环境: MATLAB2014a_64bit; Windows8.1_64bit; office2013_64bit(word2013); 设置方法: 在MATLAB指令窗中运行以下指令,配置过程将自动进行 notebook  –setup 假如指令窗中出现如下信息,就表示配置成功. >> notebook -setup Welcome to the utility for setting up the MATLAB Notebook for interfacing MATLAB to Mi

让NSURLConnection在子线程中运行

可以有两个办法让NSURLConnection在子线程中运行,即将NSURLConnection加入到run loop或者NSOperationQueue中去运行. 前面提到可以将NSTimer手动加入NSRunLoop,Cocoa库也为其它一些类提供了可以手动加入NSRunLoop的方法,这些类有NSPort.NSStream.NSURLConnection.NSNetServices,方法都是[scheduleInRunLoop:forMode:]形式.我暂时只介绍下最常用的NSURLCon

jenkins在pipline中运行后台命令

需求 在jenkin中启动java程序或者python程序的时候,希望程序在后台执行,并在jenkins构建完成之后继续执行.在工作中有两个地方我碰到了这种需求 在shell script输入框中pipline 的 sh 命令中解决方式 情况一: 在shell script输入框中的最后一行加入 BUILD_ID=dontKillMe情况二: 在pipline 的 sh 命令中加入JENKINS_NODE_COOKIE=dontKillMe 例:sh 'JENKINS_NODE_COOKIE=d

浏览器控制台命令调试——console

控制台命令调试时通过浏览器开发工具中的控制台命令嵌入到JavaScript中,输出特定的信息或日志,从而达到调试的目的. 我们常用的Chrome和FireFox,都可以通过F12来打开开发工具. 下面简要介绍几个常用的控制台命令: (1)常规信息输出 console.log()是我们最常用的命令,只需要将我们希望输出的内容传进入即可: console.log("这是我要输出的信息"); 除了console.log()命令外,我们还有其它三种命令: console.info("

C# 在EF中直接运行SQL命令

相信不少使用EF的同志们已经知道如何在EF中运行SQL命令了.我在这里简单总结下,希望对大家学习EF有所帮助! 在 EF第一个版本(.NET 3.5 SP1)中,我们只能通过将ObjectContext.Connection转换为EntityConnection,再把 EntityConnection.StoreConnection转换为SqlConnection.有了这个SqlConnection,我们再创建 SqlCommand便能顺利运行SQL命令了.(个人觉得其实很烦,呵呵) 例如: E

运行常用命令

Windows系统中运行常用命令 1.explorer.exe    资源管理器 2.iexplore.exe    IE浏览器 3.services.msc    系统服务 4.regedit.exe     注册表 5.mspaint         画图程序 6.snippingtool    截图工具 7.taskmgr         任务管理器 8.mstsc           远程桌面连接 9.secpol.msc      本地安全策略 10.gpedit.msc     本地

《python标准库》--subprocess &lt;一、运行外部命令&gt;

作用:创建附加进程,并与之通信.如果一个程序需要生产或利用文本,这个模块尤其有帮助,因为这个API支持通过新进程的标准输入和输出通道来回传递数据. subprocess模块提供了一种一致的方法来创建和处理附加进程.与标准库中的其他模块相比,它提供了一个更高级的接口,用以替换os.system().os.spawnv().os和popen2模块中的popen()函数,以及commands(). subprocess模块定义了一个类Popen,还定义了使用这个类的一些包装器函数.Popen的构造函数