Windows 编程(多进程)
进程组成:
- 操作系统用来管理进行的内核对象
内核对象也是系统用来存放关于进程的统计信息的地方.内核对象是
操作系统内部分配的一个内存块,该内存块是一种数据结构,其成员负责维护
该对象的各种信息. - 地址空间
它包含所有可执行模块或 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;
}
}
进程间通讯方式
- 剪切板
- 邮槽
- 匿名管道
- 命名管道
- 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.hStdInput
和strStartupInfo.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;
}