win32邮槽和进程的概念(三)

接《win32管道技术和进程通信实例(二)》,win32还有一种方法实现进程的通信,就是邮槽。

邮槽

邮槽是基于广播通信体系设计出来的,拥有一个服务端程序和一个客户端程序,服务端用来接收数据,客户端用来发送数据。

邮槽服务端编写步骤:

①使用CreateMailslot创建一个邮槽并且指定邮槽的名字和返回邮槽服务端的句柄。如果邮槽名称已经存在,那么会发生错误。

HANDLE CreateMailslot(
  LPCTSTR lpName,         // pointer to string for mailslot name
  DWORD nMaxMessageSize,  // maximum message size
  DWORD lReadTimeout,     // milliseconds before read time-out
  LPSECURITY_ATTRIBUTES lpSecurityAttributes 
                          // pointer to security structure
);

第一个参数邮槽的名字,它的格式是\\.\mailslot\[path]name;第二个参数,写入邮槽的最大字节数,如果设置为0表示,大小不限制(据说我们应该把传输的字节控制在424字节以下);第三个参数,以毫秒为单位设置超时时间,也可以使用0或者MAILSLOT_WAIT_FOREVER,前者表示当前没有消息就立即返回,后者表示一直等待消息。第四个参数,指向安全属性结构,主要设置创建的对象能都被子进程继承,可以设置为NULL,表示不可继承。(这样看来,邮槽也是个内核对象?)

②使用ReadFile读取数据。

邮槽客户端编写程序

①使用CreateFile打开邮槽

②使用WriteFile写入数据

看上去挺简单的,下面是一个实例:

 服务端:

#include<windows.h>
#include<iostream.h>

int main(){
    //创建邮槽
    HANDLE hMailslot=CreateMailslot("\\\\.\\mailslot\\MailslotForTest",0,MAILSLOT_WAIT_FOREVER,NULL);
    if(INVALID_HANDLE_VALUE==hMailslot){
        cout<<"Fail to create mailslot..."<<endl;
    }

    char readBuff[100];
    ZeroMemory(readBuff,sizeof(readBuff));
    if(!ReadFile(hMailslot,readBuff,100,0,NULL)){
        cout<<"Fail to read from mailslot..."<<endl;
        CloseHandle(hMailslot);
    }
    cout<<readBuff<<endl;
    CloseHandle(hMailslot);
    return 0;
}

客户端:

#include<windows.h>
#include<iostream.h>

int main(){
    HANDLE hMailslot=CreateFile("\\\\.\\mailslot\\MailslotForTest",GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
    if(INVALID_HANDLE_VALUE==hMailslot){
        cout<<"Fail to open mailslot..."<<endl;
        return 0;
    }
    
    char *writeBuff="Hello!I‘am Client,I‘ll tell u something...";
    if(!WriteFile(hMailslot,writeBuff,strlen(writeBuff),0,NULL)){
        cout<<"Fail to write to mailslot..."<<endl;
        CloseHandle(hMailslot);
        return 0;
    }

    CloseHandle(hMailslot);
    return 0;
}

运行的时候先打开服务端,让它先创建一个邮槽并一直等待客户端打开邮槽,并往邮槽写入数据;再打开客户端打开邮槽并且写入数据。

进程的概念

前面了解了进程是一个内核对象,进程的创建,子进程如何继承父进程的句柄表,以及进程的通信,进程的诞生到死亡的过程等。那么进程的概念是什么?系统如何创建一个进程内核对象来管理每个进程?如何利用进程关联的内核对象来操作进程?下面的内容可以看作《windows核心编程》第四章的学习笔记。

①进程的定义
一般将进程定义成一个正在运行的一个实例。它由两部分构成:
一个内核对象,操作系统用它来管理进程。内核对象也是系统保存进程统计信息的地方。这是不是有点绕?
一个地址空间,其包含所有可执行文件或DLL模块的代码和数据。此外,它还包含动态内存分配,比如线程堆栈和堆的分配。
进程是有“惰性”的。进程要做任何事情,都必须让一个线程在它的上下文中运行。该线程负责执行进程地址空间包含的代码。一个进程可以有多个线程,它们在地址空间“同时”执行代码。为此,每个线程都有它自己的一组CPU寄存器和它自己的堆栈。进程至少要有一个主线程。使用CreateProcess创建进程的时候,其实也创建了一个主线程。没有了线程,进程中的代码得不到执行,进程也失去了意义,系统就把进程销毁了。

②对于标准的win32应用程序还有什么可说的

想一下,我们编写一段win32程序的代码,这段代码被编译链接为一个可执行文件,双击这个可执行文件,系统为它分配资源,一个进程就诞生了。这不就是一个进程的诞生吗?不管程序里的静态资源文件有没有参与编译,exe就是我们的程序生成的最终的程序。系统为这个程序配备资源,这个程序才最终得以运行,成为一个进程。我想的是我们通过编写程序,可以控制进程的哪些东西?哪些是我们没法控制的?

来看看一个标准的win32程序值得注意的?来看看WinMain入口函数:

int WINAPI WinMain(
  HINSTANCE hInstance,  // handle to current instance
  HINSTANCE hPrevInstance,  // handle to previous instance
  LPSTR lpCmdLine,      // pointer to command line
  int nCmdShow          // show state of window
);

 第一个参数hInstance,可执行文件的实例句柄,加载到进程地址空间的每一个可执行文件或者DLL文件都被赋予了一个独一无二的实例句柄。在需要加载资源的函数调用中,一般要用到此句柄,比如LoadIcon(hInstance,pszIcon)就是表示,从可执行文件的映像中加载图标资源。hInstance参数的实际值是一个内存基地址;系统将可执行文件的映像加载到进程地址空间中的这个位置。可以使用GetModileHandle函数返回一个句柄/基地址。

第二个参数hPrevInstance,在win32程序中总是设为NULL,目的兼容16位Windows系统。

第三个参数lpCmdLine指向命令行参数,系统创建新进程的时候,会传一个命令给它。试了一下,可以这样获得命令行参数:

case WM_CREATE:
        {
            LPSTR lpCmdLine=GetCommandLine();
            MessageBox(hwnd,lpCmdLine,"msg",MB_OK);
            return 0;
        }

直接运行,输出

win32邮槽和进程的概念(三)

 

 也可以在dos下输入命令“ProcessTest.exe param1 param2”,运行,得到的结果是:

win32邮槽和进程的概念(三)

 

 这样看来跟console程序的argc和argv没有差别嘛。事实上,我们也可以使用ShellAPI.h文件中申明并由Shell32.dll导出的函数CommandLineToArgvW(将任何Unicode字符串分解为单独的标记)来lpCmdLine分解为argc和argv。例如

LPSTR lpCmdLine=GetCommandLine();
            //MessageBox(hwnd,lpCmdLine,"msg",MB_OK);
            int argc;
            PWSTR *ppArgv=CommandLineToArgvW((LPCWSTR)lpCmdLine,&argc);
            MessageBox(hwnd,(char *)ppArgv[0],"",MB_OK);

③进程的环境变量

每个进程都有一个与它关联的环境块(Environment block),这是进程地址空间内分配的一块内存,其中包含字符串与下面相似。
=::=::\ ...
VarName1=VarValue1\0
VarName2=VarValue2\0
VarName3=VarValue3\0
VarNameX=VarValueX\0
\0
意思是“环境变量的名称=变量的值”

这里又需要理解了,进程的环境变量有什么用?

之前需要在dos下不带文件路径直接输命令的时候,经常配置这样的环境变量。而且通常是手工配置的,比如win10环境变量设置界面是一个这样的东东

win32邮槽和进程的概念(三)

 环境变量相当于给系统或用户应用程序设置一些参数,具体其什么作用视具体的环境变量而定。比如path,是告诉系统,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统出了在当前目录下寻找此程序外,还应到哪些目录下去寻找。在tc或者vc++中,set include=path1;path2;是告诉编译程序到哪里去找.h类型的文件。(就是比如在vc6依次打开工具-选项-目录,里面可以看到目录和路径的关系。在里面设置include=path;set include=是使用dos命令设置。具体可以参考《配置VC++环境变量》)当然不仅仅是指定什么路径,还有其他的作用,如set dircmd=/4设置一个环境变量的作用是在使用dir命令时会把/4作为缺省的参数添加到dir命令之后,它实际上是给命令解释程序command设置的一个环境变量,并且是给dir这个内部命令设置的。(百度百科)

我们可以通过GetEnvironmentStrings函数来获得完整的环境块。得到的环境块格式与前面描述的完全一致,因此需要从中提取环境变量和内容。(未完待续...)

win32邮槽和进程的概念(三)

上一篇:C# 9.0 新特性预览 - 空参数校验


下一篇:网络IO-IO操作的常用API