// 进程之间共享数据,一般用在dll中;
#pragma data_seg("shared")  // 名称任意,长度不能超过8个字符,section亦未加点'.'
int g_iTotal = 0;  // 共享的数据必须初始化,否则不会放到共享数据段中

#pragma data_seg ()
#pragma comment(linker,"/section:shared,RWS")  // RWS:读,写,共享

Pipe

底层是依照共享内存来实现的!

Redirecting-an-arbitrary-Console-s-Input-Output

  • 匿名管道:父子进程间通信.单向,不支持异步读写!
  • 每一个管道都有Read,Write两端,只需要有一端连接到某个线程的标准输出.
  • 如果还需要控制某个线程的标准输入,则需再建立一个管道把StdInRead端连接到该线程input端.
  • 具体例子可参照xProcess.h
BOOL WINAPI CreatePipe(
    PHANDLE hReadPipe,
    PHANDLE hWritePipe,
    LPSECURITY_ATTRIBUTES lpPipeAttributes,
    DWORD nSize  // can be zero
);
HANDLE WINAPI CreateNamedPipe( // 不同机器的进程间通信
    LPCTSTR lpName,   // form: \\.\pipe\pipename,not case sensitive
    DWORD dwOpenMode, // PIPE_ACCESS_DUPLEX(双向的),余下的都是单向的
    DWORD dwPipeMode, // PIPE_TYPE_BYTE|PIPE_READMODE_BYTE :表示以字节流的形式
    DWORD nMaxInstances,  // 1~255,always 1
    DWORD nOutBufferSize, // 0
    DWORD nInBufferSize,  // 0
    DWORD nDefaultTimeOut,// 毫秒
    LPSECURITY_ATTRIBUTES lpSecurityAttributes // NULL
);
BOOL WINAPI WaitNamedPipe( // 客户端判断服务端的命名管道是否可以Connect
    LPCTSTR lpNamedPipeName, // form: \\.\pipe\pipename
    DWORD nTimeOut  // NMPWAIT_WAIT_FOREVER,NMPWAIT_USE_DEFAULT_WAIT(见上),单位毫秒
);
// 这个函数返回FALSE且GetLastError()!=ERROR_IO_PENDING时才表示失败!
BOOL WINAPI ConnectNamedPipe( // 名字取得不太好,实际上类似与listen
    HANDLE hNamedPipe,
    LPOVERLAPPED lpOverlapped // 如果不为0应使用一个人工重置的事件对象关联到Overlapped结构,并传进来
);
BOOL WINAPI DisconnectNamedPipe(
    HANDLE hNamedPipe
);
string GetConsoleResultFromCMD(const char* szCmd) {
    if (!szCmd || *szCmd==0) return "";
    BOOL ret = FALSE;
    HANDLE hChildWrite, hParentRead;

    // 使用系统默认的安全描述符,子进程可继承父进程所创建的管道句柄
    SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
    ret = CreatePipe(&hParentRead, &hChildWrite, &sa, 0); if (!ret) return "";

    STARTUPINFO si = {sizeof(STARTUPINFO)};
    GetStartupInfo(&si);  // 获取当前进程的startupinfo
    si.wShowWindow = SW_HIDE;  // CREATE_NO_WINDOW,时不必指明
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
    si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    si.hStdOutput= hChildWrite;

    // 创建子进程
    PROCESS_INFORMATION pi;
    char szTemp[1024] = {0};
    sprintf(szTemp,"C:\\Windows\\System32\\cmd.exe /c %s", szCmd);
    ret = CreateProcess(NULL,szTemp,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi);
    if (!ret) {
        printf("CreateProcess error: %d\n", GetLastError());
        CloseHandle(hChildWrite);
        CloseHandle(hParentRead);
        return "";
    }
    CloseHandle(hChildWrite);  // 必须关闭pipe的write端,否则下面的ReadFile会一直阻塞!

    DWORD dwExitCode;
    DWORD dwRet = WaitForSingleObject(pi.hProcess, INFINITE);
    if (dwRet != WAIT_OBJECT_0 && !GetExitCodeProcess(pi.hProcess, &dwExitCode)) {
        CloseHandle(hChildWrite);
        CloseHandle(hParentRead);
        return "";
    }
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    string sRes("");
    char buffer[1024] = {0};
    DWORD bytesRead;
    while (ReadFile(hParentRead,buffer,1023,&bytesRead,NULL) && bytesRead>0) {
        sRes += buffer;
        Sleep(80);
    }

    CloseHandle(hParentRead);
    return sRes;
}

Mailslot

油槽: Mailslot基于UDP的广播

CreateMailslot("\\\\.\\mailslot\\MyMS", 0, MAILSLOT_WAIT_FOREVER, NULL); // Server端,创建油槽
ReadFile(hMail, buf, len, &dwReaded, NULL);  // Server端,读取数据
// Client端,打开油槽
CreateFile("\\\\.\\mailslot\\MyMS", GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// Client端,广播数据
WriteFile(hMail, buf, len, &dwWrited, NULL);

// WM_COPYDATA
typedef struct COPYDATASTRUCT {
    ULONG_PTR dwData; // 0
    DWORD cb_data; // lpData内存区域的字节数
    PVOID lpData;  // 待发送的数据
} COPYDATASTRUCT, *PCOPYDATASTRUCT;
// send :该消息只能由SendMessage发送,因为系统必须管理用以传递数据的缓冲区的生命期
CString sendData;
GetDlgItemText(IDC_EDIT_SEND,sendData);
COPYDATASTRUCT cds = { 0, sendData.GetLength(), sendData.GetBuffer() };
SendMessage(FindWindow(NULL,"Receiver")/*目标窗口*/,WM_COPYDATA,(WPARAM)GetSafeHwnd()/*原窗口句柄*/,(LPARAM)&cds);
sendData.ReleaseBuffer();
// recv :如果接受进程处理该消息应当返回TRUE,否则返回FALSE
BOOL CReceiverDlg::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) {
    if (pCopyDataStruct->cbData > 0) {
        char recvData[256] = { 0 };
        strncpy(recvData, (char*)pCopyDataStruct->lpData, pCopyDataStruct->cbData);
        SetDlgItemText(IDC_EDIT_RECEIVE, (char*)recvData);
        Feedback(pWnd);
    }
    return CDialog::OnCopyData(pWnd, pCopyDataStruct);
}

RegisterWindowMessage

注册一个系统内唯一的消息,返回值: 0xC000~0xFFFF

UINT WM_MYWMSG = ::RegisterWindowMessage("MyFirstWindowMessage");
virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult); //重载该函数,或者-->
afx_msg void OnRegisterMsg1(WPARAM wParam, LPARAM lParam);
ON_REGISTERED_MESSAGE(WM_MYWMSG, OnRegisterMsg1)

memory map

  • Windows的内存映射和共享内存是统一在同一组API中.Linux有专门的内存映射与共享内存API;
  • file mapping:将硬盘上的文件映射到进程的虚拟地址空间(View).读写文件便无需通过IO,直接读写内存,效率高;
  • 还可以在进程间共享数据,把要映射的文件句柄写为0xFFFFFFFF.
  • 文件映射依赖于系统的分页机制!windows内存颗粒度一般为64KB.
  • http://www.cnblogs.com/alonezooo/articles/1530493.html
// 创建文件映射对象,可以比文件小,也可以比文件大
HANDLE CreateFileMapping( //返回映射对象的句柄
    HANDLE hFile,  // CreateFile创建的待映射的文件;进程间通讯时HANDLE=0xFFFFFFFF
    LPSECURITY_ATTRIBUTES lpAttributes,  // NULL:返回的句柄不能够被继承
    DWORD flProtect,   // PAGE_READWRITE,将要被映射到View的空间应该具有什么保护属性
    DWORD dwMaximumSizeHigh,  // 映射大小最大值的高32位,一般为0
    DWORD dwMaximumSizeLow,   // 低32位,0:文件当前大小,如果要给当前文件附加数据,可以更大些
    LPCTSTR lpName  // 映射对象的名称,可以为NULL;进程间通讯时如指定"hello"
);
// 打开其他进程创建的虚拟文件映射HANDLE(0xFFFFFFFF)
HANDLE OpenFileMapping(
    DWORD dwDesiredAccess, // FILE_MAP_READ
    BOOL bInheritHandle, // FALSE
    LPCTSTR lpName   // "hello"
);
// 将内存映射对象全部或部分映射到View,一个映射对象可以映射几个View
LPVOID MapViewOfFile( // 映射对象 --> View,返回在view中的位置;失败时返回NULL
    HANDLE hFileMappingObject, // 上面返回的映射对象
    DWORD dwDesiredAccess,  // FILE_MAP_WRITE FILE_MAP_ALL_ACCESS
    DWORD dwFileOffsetHigh, // 映射文件偏移指针的字节数,必须是内存颗粒度的整数倍64K
    DWORD dwFileOffsetLow,  // 低32位.大文件可以指定从多少字节处开始映射!
    SIZE_T dwNumberOfBytesToMap //待映射到View的字节数,0:上面的位移处到文件尾
);
BOOL FlushViewOfFile( // View --> Disk,数据会被立即写回到磁盘
    LPCVOID lpBaseAddress,  // 需要写会磁盘的起始位置
    SIZE_T dwNumberOfBytesToFlush  // 0:整个view都写会去
);
BOOL UnmapViewOfFile( //隐含调用FlushViewOfFile()
    LPCVOID lpBaseAddress //MapViewOfFile()的返回值
);
// 内存颗粒度
SYSTEM_INFO::dwAllocationGranularity;

// 获得uuid
#include <objbase.h>
#pragma comment(lib, "ole32.lib")
char uuid[37] = {0};
GUID guid;
CoCreateGuid(&guid);
snprintf(uuid, sizeof(uuid),
    "%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X",
    (unsigned int)guid.Data1, guid.Data2, guid.Data3,
    guid.Data4[0], guid.Data4[1], guid.Data4[2],
    guid.Data4[3], guid.Data4[4], guid.Data4[5],
    guid.Data4[6], guid.Data4[7]);

#include <rpc.h>
#pragma comment(lib, "rpcrt4.lib")
UuidCreateSequential(&uuid);
RPC_CSTR rpc_str;
UuidToStringA(&uuid, &rpc_str);
std::string result = (char*)(rpc_str);
RpcStringFreeA(&rpc_str);

#include <time.h>
#include <stdlib.h>
void guid4(char *guidStr/*36chars*/)  {
  char *pGuidStr = guidStr;
  int i = 0;
  srand((unsigned int)(time(NULL)));

  /*Data1 - 8 characters.*/
  //*pGuidStr++ = '{';
  for (i=0; i<8; i++,pGuidStr++)
    ((*pGuidStr = (rand() % 16)) < 10) ? *pGuidStr += 48 : *pGuidStr += 55;
  /*Data2 - 4 characters.*/
  *pGuidStr++ = '-';
  for (i=0; i<4; i++,pGuidStr++)
    ((*pGuidStr = (rand() % 16)) < 10) ? *pGuidStr += 48 : *pGuidStr += 55;
  /*Data3 - 4 characters.*/
  *pGuidStr++ = '-';
  for (i=0; i<4; i++,pGuidStr++)
    ((*pGuidStr = (rand() % 16)) < 10) ? *pGuidStr += 48 : *pGuidStr += 55;
  /*Data4 - 4 characters.*/
  *pGuidStr++ = '-';
  for (i=0; i<4; i++,pGuidStr++)
    ((*pGuidStr = (rand() % 16)) < 10) ? *pGuidStr += 48 : *pGuidStr += 55;
  /*Data5 - 12 characters.*/
  *pGuidStr++ = '-';
  for (i=0; i<12; i++,pGuidStr++)
    ((*pGuidStr = (rand() % 16)) < 10) ? *pGuidStr += 48 : *pGuidStr += 55;
  //*pGuidStr++ = '}';

  *pGuidStr = '\0';
}