zoukankan      html  css  js  c++  java
  • Delphi与管道操作

    什么是管道?参考《WIN32汇编编程》是这样描述的
        Windows 引入了多进程和多线程机制。同时也提供了多个进程之间的通信手段,包括剪贴板、DDE、OLE、管道等,和其他通信手段相比,管道有它自己的限制和特点,管道实际上是一段共享内存区,进程把共享消息放在那里。并通过一些 API 提供信息交换。
    管道是两个头的东西,每个头各连接一个进程或者同一个进程的不同代码,按照管道的类别分有两种管道,匿名的和命名的;按照管道的传输方向分也可以分成两种,单向的双向的。根据管道的特点,命名管道通常用在网络环境下不同计算机上运行的进程之间的通信(当然也可以用在同一台机的不同进程中)它可以是单向或双向的;而匿名管道只能用在同一台计算机中,它只能是单向的。匿名管道其实是通过用给了一个指定名字的有名管道来实现的。
    使用管道的好处在于:读写它使用的是对文件操作的 api,结果操作管道就和操作文件一样。即使你在不同的计算机之间用命名管道来通信,你也不必了解和自己去实现网络间通信的具体细节。
    使用匿名管道的步骤如下:
    使用 CreatePipe 建立两个管道,得到管道句柄,一个用来输入,一个用来输出
    准备执行控制台子进程,首先使用 GetStartupInfo 得到 StartupInfo
    使用第一个管道句柄代替 StartupInfo 中的 hStdInput,第二个代替 hStdOutput、hStdError,即标准输入、输出、错误句柄
    使用 CreateProcess 执行子进程,这样建立的子进程输入和输出就被定向到管道中
    父进程通过 ReadFile 读第二个管道来获得子进程的输出,通过 WriteFile 写第一个管道来将输入写到子进程
    父进程可以通过 PeekNamedPipe 来查询子进程有没有输出
    子进程结束后,要通过 CloseHandle 来关闭两个管道。
    下面是具体的说明和定义:
    
    1. 建立匿名管道使用 CreatePipe 原形如下:
    BOOL CreatePipe(
    PHANDLE hReadPipe, // address of variable for read handle
    PHANDLE hWritePipe, // address of variable for write handle
    LPSECURITY_ATTRIBUTES lpPipeAttributes, // pointer to security attributes
    DWORD nSize // number of bytes reserved for pipe
    );
    当管道建立后,结构中指向的 hReadPipe 和 hWritePipe 可用来读写管道,当然由于匿名管道是单向的,你只能使用其中的一个句柄,参数中的 SECURITY_ATTRIBUTES 的结构必须填写,定义如下:typedef struct_SECURITY_ATTRIBUTES{
    DWORD nLength: //定义以字节为单位的此结构的长度
    LPVOID lpSecurityDescriptor; //指向控制这个对象共享的安全描述符,如果为NULL这个对象将被分配一个缺省的安全描述
    BOOL bInheritHandle; //当一个新过程被创建时,定义其返回是否是继承的.供系统API函数使用.
    }SECURITY_ATTRIBUTES;
    
    2. 填写创建子进程用的 STARTUPINFO 结构,一般我们可以先用 GetStartupInfo 来填写一个缺省的结构,然后改动我们用得到的地方,它们是:
    hStdInput -- 用其中一个管道的 hWritePipe 代替
    hStdOutput、hStdError -- 用另一个管道的 hReadPipe 代替
    dwFlags -- 设置为 STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW 表示输入输出句柄及 wShowWindow 字段有效
    wShowWindow -- 设置为 SW_HIDE,这样子进程执行时不显示窗口。
    填写好以后,就可以用 CreateProcess 来执行子进程了。
    
    3. 在程序中可以用 PeekNamedPipe 查询子进程有没有输出,原形如下:
    OOL PeekNamedPipe(HANDLE hNamedPipe, // handle to pipe to copy from 
    LPVOID lpBuffer, // pointer to data buffer
    DWORD nBufferSize, // size, in bytes, of data buffer 
    LPDWORD lpBytesRead, // pointer to number of bytes read 
    LPDWORD lpTotalBytesAvail, // pointer to total number of bytes available 
    LPDWORD lpBytesLeftThisMessage // pointer to unread bytes in this message );
    我们可以将尝试读取 nBuffersize 大小的数据,然后可以通过返回的 BytesRead 得到管道中有多少数据,如果不等于零,则表示有数据可以读取。
    
    4. 用 ReadFile 和 WriteFile 来读写管道,它们的参数是完全一样的,原形如下:
    ReadFile or WriteFile(HANDLE hFile, // handle of file to read 在这里使用管道句柄
    LPVOID lpBuffer, // address of buffer that receives data 缓冲区地址
    DWORD nNumberOfBytesToRead, // number of bytes to read 准备读写的字节数
    LPDWORD lpNumberOfBytesRead, // address of number of bytes read,实际读到的或写入的字节数
    LPOVERLAPPED lpOverlapped // address of structure for data 在这里用 NULL);
    
    5. 用 CloseHandle 关闭管道一和管道二的 hReadPipe和 hWritePipe 这四个句柄。
    
    下面是一个演示DEMO,可以使用MEMO来制作一个控制台,所使用的技术就是管道
    
     procedure RunDosInMemo(Que:String;EnMemo:TMemo);
      const
         CUANTOBUFFER = 2000;
      var
        Seguridades         : TSecurityAttributes;
        PaLeer,PaEscribir   : THandle;
        start               : TStartUpInfo;
        ProcessInfo         : TProcessInformation;
        Buffer              : Pchar;
        BytesRead           : DWord;
        CuandoSale          : DWord;
      begin
        //安全描述 可以省略
        with Seguridades do
        begin
          nlength              := SizeOf(TSecurityAttributes);
          binherithandle       := true;
          lpsecuritydescriptor := nil;
        end;
     
        {Creamos el pipe...}
        if Createpipe (PaLeer, PaEscribir, @Seguridades, 0) then
        begin
          //申请缓冲
          Buffer  := AllocMem(CUANTOBUFFER + 1); 
       
          //创建STARTUPINFO
          FillChar(Start,Sizeof(Start),#0);
          start.cb          := SizeOf(start);
          start.hStdOutput  := PaEscribir;
          start.hStdInput   := PaLeer;
          start.dwFlags     := STARTF_USESTDHANDLES +
                               STARTF_USESHOWWINDOW;
          start.wShowWindow := SW_HIDE;
            
          //执行子进程 
          if CreateProcess(nil,
             PChar(Que),
             @Seguridades,
             @Seguridades,
             true,
             NORMAL_PRIORITY_CLASS,
             nil,
             nil,
             start,
             ProcessInfo)
          then
            begin
              {Espera a que termine la ejecucion}
              repeat
                //使用信号量技术来避免CPU时间片被抢占
                CuandoSale := WaitForSingleObject( ProcessInfo.hProcess,100);
                Application.ProcessMessages;
              until (CuandoSale <> WAIT_TIMEOUT);
              {Leemos la Pipe}
              repeat
                BytesRead := 0;
       
                {Llenamos un troncho de la pipe, igual a nuestro buffer}
                //执行标准输出
                ReadFile(PaLeer,Buffer[0],CUANTOBUFFER,BytesRead,nil);
                {La convertimos en una string terminada en cero}
                Buffer[BytesRead]:= #0;
                {Convertimos caracteres DOS a ANSI}
                OemToAnsi(Buffer,Buffer);
                EnMemo.Text := EnMemo.text + String(Buffer);
              until (BytesRead < CUANTOBUFFER);
            end;
          FreeMem(Buffer);
       
          //释放资源
          CloseHandle(ProcessInfo.hProcess);
          CloseHandle(ProcessInfo.hThread);
          CloseHandle(PaLeer);
          CloseHandle(PaEscribir);
        end;
      end;
    
  • 相关阅读:
    09-13练习
    IDEA设置项目文件自动Add到Svn/Git
    虚拟机启动项目时报错
    微服务调用时报错
    查询每个月每一天的访问量
    在IDEA中关于项目java版本问题
    复习宝典之设计模式
    复习宝典之Maven项目管理
    复习宝典之Mysql数据库
    复习宝典之Redis
  • 原文地址:https://www.cnblogs.com/MaxWoods/p/3025570.html
Copyright © 2011-2022 走看看