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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • activexobject java_ActiveXObject[通俗易懂]

    activexobject java_ActiveXObject[通俗易懂]只有IE浏览器才支持这个构造函数,可以用这个来判断,当前是否为IE浏览器varisIE=!!window.ActiveXObject;在IE的不同版本下,要创建XHR对象,也需要通过这个构造函数来创建(构造时的参数不一样)。因为ActiveXObject的使用容易存在安全问题,如IE11中报错Automation服务器不能创建对象解决办法:Internet选项-安全-自定义安全级别-启用“对未…

    2022年10月15日
    0
  • Http 和 Tcp 的区别

    Http 和 Tcp 的区别Http是包装数据的,Tcp是传输数据的。都是先Tcp建立连接,然后传输数据,如果没用http,对方无法识别你传输的数据是什么,所以需要http包装数据,其他应用层协议也可以。

    2022年9月2日
    3
  • 微信小程序必用接口「建议收藏」

    微信小程序必用接口获取openiduni-app示例获取openidopenid是微信用户的一个唯一的标识,只针对当前的微信号有效。微信开发时,用户使用小程序需要授权,这时就要用到openid进行绑定这个用户。可用于永久标记一个用户,同时也是微信JSAPI支付的必传参数。一般都是将code值传到后端去获取openid,因为在前端可能会被抓包或爬取到你的appid和secret,不安全,如果放在后端获取openid,除非你的服务器被攻击了,不然就是安全的。下面的实例是在前端直接获取的,这个明白后,可

    2022年4月11日
    39
  • 伽马校正-「建议收藏」

    伽马校正-「建议收藏」伽马校正问题描述:读取图像,然后对图像进行伽玛校正。伽马校正这里是一篇写伽马校正比较好的文章,我觉得可以作为背景知识补充。伽马校正用来对照相机等电子设备传感器的非线性光电转换特性进行校正。如果图像原样显示在显示器等上,画面就会显得很暗。伽马校正通过预先增大RGB的值来排除显示器的影响,达到对图像修正的目的。由于下式引起非线性变换,在该式中,xxx被归一化,限定在[0,1][0,1][0,1]范围内。ccc是常数,ggg为伽马变量(通常取2.22.22.2):x′=c Iingx

    2022年9月25日
    0
  • jsoup 1.5.2 发布,超棒的 HTML 解析器

    jsoup 1.5.2 发布,超棒的 HTML 解析器

    2021年8月10日
    59
  • jni断点调试「建议收藏」

    jni断点调试「建议收藏」jni断点调试

    2022年5月16日
    40

发表回复

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

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