Windows 编程(多进程)

Windows编程(多进程)进程组成:操作系统用来管理进行的内核对象内核对象也是系统用来存放关于进程的统计信息的地方.内核对象是操作系统内部分配的一个内存块,该内存块是一种数据结构,其成员负

大家好,又见面了,我是全栈君,祝每个程序员都可以多学几门语言。

Windows 编程(多进程)

进程组成:

  1. 操作系统用来管理进行的内核对象
    内核对象也是系统用来存放关于进程的统计信息的地方.内核对象是
    操作系统内部分配的一个内存块,该内存块是一种数据结构,其成员负责维护
    该对象的各种信息.
  2. 地址空间
    它包含所有可执行模块或 DLL 模块的代码和数据.另外,它也包含动态
    内存分配的空间,例如线程的栈和堆分配空间

进程从来不执行任何东西,它只是纯种的容器,若要使进行完成某项操作,它必
须拥有一个在它的环境中运行的纯种,此线程负责执行包含在进程的地址空
间的中的代码.也就是,真正完成代码执行的是线程,而进程只是纯种的容器,
或者说是线程的执行环境.

创建进程函数

CreateProcess

CreateProcessW(
    _In_opt_ LPCWSTR lpApplicationName,// 该字符串可以指定要执行的模块的完整路径和文件名
_Inout_opt_ LPWSTR lpCommandLine,  //命令行

_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
//该 结构确定子进程是否可以继承返回到新进程对象的句柄。如果//lpProcessAttributes为NULL,则不能继承该句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
//该 结构确定子进程是否可以继承返回到新线程对象的句柄。如果//lpThreadAttributes为NULL,则不能继承该句柄
_In_ BOOL bInheritHandles,
//如果此参数为TRUE,则新进程将继承调用进程中的每个可继承句柄。如果参数为FALSE,则不会继承句柄。请注意,继承的句柄与原始句柄具有相同的值和//访问权限
    _In_ DWORD dwCreationFlags,// 控制优先级类别和流程创建的标志 CREATE_NEW_CONSOLE
_In_opt_ LPVOID lpEnvironment,// 指向新进程的环境块的指针。如果此参数为//NULL,则新进程将使用调用进程的环境
//
    _In_opt_ LPCWSTR lpCurrentDirectory,// 进程当前目录的完整路径
    _In_ LPSTARTUPINFOW lpStartupInfo, //设置扩展属性
    _Out_ LPPROCESS_INFORMATION lpProcessInformation // 该 结构接收有关新进程的标识//信息
    );

lpStartupInfo结构体

typedef struct _STARTUPINFOW {
    DWORD   cb;
    LPWSTR  lpReserved;
    LPWSTR  lpDesktop;
    LPWSTR  lpTitle;
    DWORD   dwX;
    DWORD   dwY;
    DWORD   dwXSize;
    DWORD   dwYSize;
    DWORD   dwXCountChars;
    DWORD   dwYCountChars;
    DWORD   dwFillAttribute;
    DWORD   dwFlags;
    WORD    wShowWindow;
    WORD    cbReserved2;
    LPBYTE  lpReserved2;
    HANDLE  hStdInput;
    HANDLE  hStdOutput;
    HANDLE  hStdError;
}

PROCESS_INFORMATION结构体

typedef struct _PROCESS_INFORMATION {
    HANDLE hProcess;
    HANDLE hThread;
    DWORD dwProcessId;
    DWORD dwThreadId;
}
#include <Windows.h>
#include <stdio.h>


int main() {
	
	/*_In_ LPSTARTUPINFOW lpStartupInfo,
		_Out_ LPPROCESS_INFORMATION lpProcessInformation*/
	STARTUPINFO lpStartupInfo;
	memset(&lpStartupInfo, 0, sizeof(lpStartupInfo));
	lpStartupInfo.cb = sizeof(lpStartupInfo);
	PROCESS_INFORMATION lpProcessInformation;
	memset(&lpProcessInformation, 0, sizeof(lpProcessInformation));
	TCHAR commandline[] = L"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
	int a = CreateProcess(NULL, commandline, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &lpStartupInfo, &lpProcessInformation);
	if (a) {
		printf_s("Create Success iRet = %d\n", a);
		WaitForSingleObject(lpProcessInformation.hProcess, 3000);
		CloseHandle(lpProcessInformation.hProcess);
		CloseHandle(lpProcessInformation.hThread);
		lpProcessInformation.dwProcessId = 0;
		lpProcessInformation.dwThreadId = 0;
		lpProcessInformation.hProcess = NULL;
		lpProcessInformation.hThread = NULL;
	}
}

进程间通讯方式

  1. 剪切板
  2. 邮槽
  3. 匿名管道
  4. 命名管道
  5. Copy_Data

剪切板通讯

发送

void CMFCApplication2Dlg::OnBnClickedButton1()
{	 
	
	//打开剪切板 
	if (OpenClipboard()) {
		//清空剪切板 
		EmptyClipboard();
		char* szSendbuf;
		CStringW strSendW;
		//获取编辑框内容
		GetDlgItemText(IDC_EDIT1, strSendW);
		CStringA stringA = (CStringA)strSendW;
		//申请一块内存
		HANDLE Plic =  GlobalAlloc(GMEM_MOVEABLE, stringA.GetLength() + 1);
		//将剪切板句柄加锁
		szSendbuf = (char*)GlobalLock(Plic);
		//stringA 内容复制到szSendbuf中
		strcpy(szSendbuf, stringA);
		GlobalUnlock(Plic);
		//内容放到剪切板中
		SetClipboardData(CF_TEXT, Plic);
		//关闭剪切板
		CloseClipboard();
	}

接受

void CMFCApplication2Dlg::OnBnClickedButton2()
{
	HANDLE pilc;
	char* pBuf;
	if (OpenClipboard()) {
		if (IsClipboardFormatAvailable(CF_TEXT))
		{
		
			pilc = GetClipboardData(CF_TEXT);
			pBuf = (char*)GlobalLock(pilc);
			USES_CONVERSION;
			LPCWSTR strBuf = A2W(pBuf);
			GlobalUnlock(pilc);
			//SetDlgItemText(IDC_EDIT2, strBuf);
			SetDlgItemText(IDC_EDIT2, strBuf);
		}
		CloseClipboard();
	}
}

油槽通讯

使用邮槽通信的进程分为服务端和客户端。 邮槽由服务端创建, 在创建时需要指定邮
槽名, 创建后服务端得到邮槽的句柄。 在邮槽创建后, 客户端可以通过邮槽名打开邮槽,
在获得句柄后可以向邮槽写入消息。
邮槽通信是单向的, 只有服务端才能从邮槽中读取消息, 客户端只能写入消息。 消息
是先入先出的。 客户端先写入的消息在服务端先被读取。
通过邮槽通信的数据可以是任意格式的, 但是一条消息不能大于 424 字节。
邮槽除了在本机内进行进程间通信外, 在主机间也可以通信。 但是在主机间进行邮槽通
信, 数据通过网络传播时使用的是数据报协议(UDP), 所以是一种不可靠的通信。 通过网
络进行邮槽通信时, 客户端必须知道服务端的主机名或域名。

CreateMailslot

HANDLE CreateMailslotA(
  LPCSTR                lpName,
  DWORD                 nMaxMessageSize,
  DWORD                 lReadTimeout,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
void CMFCApplication2Dlg::OnBnClickedButton1()
{

	
	LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
	HANDLE hslot=  CreateMailslot(szSlotName, 0, 1000, NULL);

	if (hslot == INVALID_HANDLE_VALUE) {
	
	
	TRACE("CreateMailslot failed with %d\n", GetLastError());
	return ;
	}
	char szBuf[] = { 0 };
	DWORD dwRead;
	TRACE("Begin ReadFile");
	if (!ReadFile(hslot, szBuf, 100, &dwRead, NULL)) {
		MessageBox(NULL, L"读取失败",0 );
		CloseHandle(hslot);
		return;
	}
	MessageBox(NULL, (CString)szBuf, 0);

}
void CMFCApplication2Dlg::OnBnClickedButton2()
{
	LPCTSTR szSlotName = TEXT("\\\\.\\mailslot\\Mymailslot");
	HANDLE hMailSlot =CreateFile(szSlotName, FILE_GENERIC_WRITE,FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (hMailSlot == INVALID_HANDLE_VALUE) {
		TRACE("CreateFile failed with %d\n", GetLastError());

		return;
	
	}

	char szbuf[] = "Bingo is handsome";
	DWORD dwWrite;

	if (!WriteFile(hMailSlot, szbuf, strlen(szbuf) + 1, &dwWrite, NULL)) {
		MessageBox(NULL, L"CreateFile failed ", 0);
		/*TRACE("CreateFile failed with %d\n", GetLastError());*/
		CloseHandle(hMailSlot);
		return;
	}
	MessageBox(NULL, (CString)szbuf, 0);
	CloseHandle(hMailSlot);


}

匿名管道

匿名管道是一个没有命名的单向管道, 本质上就是一个共享的内存区域。 通常用来
在父进程和子进程之间通信。 只能实现本地两个进程之间的通信。 不能实现网络通
信。

CreatePipe(
_Out_ PHANDLE hReadPipe, //该变量接收管道的读取句柄
_Out_ PHANDLE hWritePipe,// 该变量接收管道的写句柄
_In_opt_ LPSECURITY_ATTRIBUTES lpPipeAttributes,//NULL
_In_ DWORD nSize //管道缓冲区的大小 0 :默认缓冲区大小
);
void CMFCApplication3Dlg::OnBnClickedButton2create()
{
	SECURITY_ATTRIBUTES sa;
	sa.bInheritHandle = TRUE;
	sa.lpSecurityDescriptor = NULL;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
	if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
	{
		MessageBox(_T("匿名管道创建失败"));
		return;
	} 
		STARTUPINFO strStartupInfo; //用来指定新进程窗口如何显示
	memset(&strStartupInfo, 0, sizeof(strStartupInfo));
	strStartupInfo.cb = sizeof(strStartupInfo);
	strStartupInfo.dwFlags = STARTF_USESTDHANDLES;
	strStartupInfo.hStdInput = hReadPipe;
	strStartupInfo.hStdOutput = hWritePipe;
	strStartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
	PROCESS_INFORMATION szProcessInformation;
	memset
	(&szProcessInformation, 0, sizeof(szProcessInformation));
	int iRet = CreateProcess(
		_T("MailSlotClient.exe"),//子进程名
		NULL,
		NULL,
		NULL,
		TRUE,
		0,
		NULL,
		NULL,
		&strStartupInfo,
		&szProcessInformation
	);
	if (iRet)
	{
		//创建成功
		CloseHandle
		(szProcessInformation.hProcess);
		CloseHandle
		(szProcessInformation.hThread);
		szProcessInformation.dwProcessId = 0;
		szProcessInformation.dwThreadId = 0;
		szProcessInformation.hThread = NULL;
		szProcessInformation.hProcess = NULL;
	} else
	{
	CloseHandle
	(hReadPipe);
	CloseHandle
	(hWritePipe);
	hReadPipe = NULL;
	hWritePipe = NULL;
	MessageBox(_T("创建子进程失败"));
	return;
	}
}

读取与写入数据

hReadCliPipe =GetStdHandle(STD_INPUT_HANDLE);
hWriteCliPipe = GetStdHandle(STD_OUTPUT_HANDLE);
//读取数据
{
char szBuf[100] = { 0 };
DWORD dwRead;

if (!ReadFile(hReadCliPipe, szBuf, 100, &dwRead, NULL))
{
MessageBox(_T("读取数据失败"));
return;
} T
RACE("End PipeReadFile");
MessageBox((CStringW)szBuf);
}

//写入数据
{
char szBuf[] = "Bingo Bingo";
DWORD dwWrite;
if (!WriteFile(hWriteCliPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
{
MessageBox(_T("写入数据失败"));
CloseHandle
(hWriteCliPipe);
return;
} CloseHandle(hWriteCliPipe);
}

大多步骤与前面一致,但在strStartupInfo属性中需要进行设置strStartupInfo.hStdInputstrStartupInfo.hStdOutput的值为输入和输出的匿名管道句柄。

命名管道

支持网络之间不同进程的通信

CreateNamedPipe

HANDLE CreateNamedPipeA(
LPCSTR lpName, // \.\pipe<i>pipename
DWORD dwOpenMode,
DWORD dwPipeMode,
DWORD nMaxInstances,
DWORD nOutBufferSize,
DWORD nInBufferSize,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

客户端连接代码

LPCTSTR szNamedPipeName = TEXT("\\\\.\\pipe\\mypipe");
if (0 == WaitNamedPipe(szNamedPipeName, NMPWAIT_WAIT_FOREVER))
{
MessageBox(_T("当前没有可以利用的管道"));
return;
} hNamedPipe =CreateFile(szNamedPipeName, GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
TRACE
("CreateFile failed with %d\n", GetLastError());
MessageBox(_T("打开命名管道失败! "));
hNamedPipe = NULL;
return;
}
}
//读取文件

{
char szBuf[] = "Bingo Bingo";
DWORD dwWrite;
if (!WriteFile(hWriteCliPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
{
MessageBox(_T("写入数据失败"));
CloseHandle
(hWriteCliPipe);
return;
} CloseHandle
(hWriteCliPipe);
}


//写入文件
{
char szBuf[] = "Bingo Bingo";
DWORD dwWrite;
if (!WriteFile(hWriteCliPipe, szBuf, strlen(szBuf) + 1, &dwWrite, NULL))
{
MessageBox(_T("写入数据失败"));
CloseHandle
(hWriteCliPipe);
return;
} C
loseHandle
(hWriteCliPipe);
}

服务端代码

//1 创建一个命名管道
LPCTSTR szPipeName = TEXT("\\\\.\\pipe\\mypipe");
hNamedPipe = CreateNamedPipe(szPipeName,PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
TRACE
("CreateNamedhPipe failed with %d\n", GetLastError());
MessageBox(_T("创建命名管道失败"));
return;
} // 2 等待客户端的连接
HANDLE hEvent = CreateEvent
(NULL, TRUE, FALSE, NULL);
if (NULL == hEvent)
{
MessageBox(_T("创建事件失败"));
CloseHandle
(hNamedPipe);
hNamedPipe = NULL;
return;
}
//1 创建一个命名管道
LPCTSTR szPipeName = TEXT("\\\\.\\pipe\\mypipe");
hNamedPipe = CreateNamedPipe(szPipeName,PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE, 1, 1024, 1024, 0, NULL);
if (hNamedPipe == INVALID_HANDLE_VALUE)
{
TRACE
("CreateNamedhPipe failed with %d\n", GetLastError());
MessageBox(_T("创建命名管道失败"));
return;
} // 2 等待客户端的连接
HANDLE hEvent = CreateEvent
(NULL, TRUE, FALSE, NULL);
if (NULL == hEvent)
{
MessageBox(_T("创建事件失败"));
CloseHandle(hNamedPipe);
hNamedPipe = NULL;
return;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119854.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • android错误之MediaPlayer用法的Media Player called in state *「建议收藏」

    用到Media Player,遇到几个问题,记一下用法就不说了,使用的时候最好参考一下mediaPlayer的这张图第一个错误是Media Player called in state 8这个是因为在调用start之前没有prepare,因为我是用了mediaPlayer = MediaPlayer.create(context, R.raw.notice);去初始化的播放器,这个接口说明只要成功

    2022年3月11日
    43
  • 代理ip免费网址_代理ip地址

    代理ip免费网址_代理ip地址西刺代理http://www.xicidaili.com快代理https://www.kuaidaili.com云代理http://www.ip3366.net无忧代理http://www.data5u.com/360代理http://www.swei360.com66ip代理http://www.66ip.cnip海代理http://www.iphai.com大…

    2025年7月2日
    0
  • Dreamweaver2021中文版 附安装教程

    Dreamweaver2021中文版 附安装教程当我们访问网站的时候看到的每一个页面都是前端程序员开发的,如果没有一款好的软件则会让开发效率大大降低,那么有没有一款高效的前端开发软件呢?推荐大家使用Dreamweaver2021,这是adobe旗下的一款非常受欢迎的网页设计软件,是该系列的全新版本,可以帮助广大学生、程序员制作出精美的网页,比如简洁的百度首页,复杂的淘宝页面,你都可以通过它让你游刃有余的制作出来。该软件可以帮助用户了解以及编辑HTML、CSS、Web、xml、json,各种前端语言都支持编码输入,还支持快捷键快速输入一大段代码,减少你重复

    2022年6月4日
    36
  • mysql经典面试题及答案_常见的SQL面试题

    mysql经典面试题及答案_常见的SQL面试题1、MySQL的复制原理以及流程基本原理流程,3个线程以及之间的关联;1.主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中;2.从:io线程——在使用startslave之后,负责从master上拉取binlog内容,放进自己的relaylog中;3.从:sql执行线程——执行relaylog中的语句;详解:mys…

    2022年8月27日
    2
  • 数据分析方法论和数据分析方法的区别(数据分析理论)

    如何理解数据分析的方法论问题?首先,数据分析方法论就如同国家的方针政策,指导和决策我们分析的方向。从宏观角度知道如何进行数据分析,就像是一个数据分析的前期规划,知道着后期数据分析工作的开展。数据分析法则就是指具体的分析方法,例如我们常见的对比分析、交叉分析、相关性分析、回归分析、聚类分析等数据分析法,数据分析法则是从微观角度指导我们如何进行数据分析。那么,数据分析方法论的作用有什么呢?…

    2022年4月15日
    37
  • ps插件套装imagenomic磨皮滤镜安装教程「建议收藏」

    ps插件套装imagenomic磨皮滤镜安装教程「建议收藏」为大家推荐一款知名的PS磨皮降噪滤镜套装,ImagenomicProfessionalSuitemac版中包含noiseware降噪插件、portraiture3磨皮滤镜和Realgrain胶片效果滤镜插件,各有各的功能,轻松进行磨皮、添加胶片效果、降噪等操作,小编这里准备了最新的imagenomic滤镜激活成功教程教程,赶紧试试imagenomic磨皮滤镜吧!imagenomic激活成功教程教程…

    2022年7月22日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号