Windows进程间通信(中)


二、文件映射

文件映射(Memory-MappedFiles)能使进程把文件内容当作进程地址区间一块内存那样来对待。因此,进程不必使用文件I/O操作,只需简单的指针操作就可读取和修改文件的内容。
  Win32API允许多个进程访问同一文件映射对象,各个进程在它自己的地址空间里接收内存的指针。通过使用这些指针,不同进程就可以读或修改文件的内容,实现了对文件中数据的共享。
  应用程序有三种方法来使多个进程共享一个文件映射对象。
  (1)继承:第一个进程建立文件映射对象,它的子进程继承该对象的句柄。
  (2)命名文件映射:第一个进程在建立文件映射对象时可以给该对象指定一个名字(可与文件名不同)。第二个进程可通过这个名字打开此文件映射对象。另外,第一个进程也可以通过一些其它IPC机制(有名管道、邮件槽等)把名字传给第二个进程。
  (3)句柄复制:第一个进程建立文件映射对象,然后通过其它IPC机制(有名管道、邮件槽等)把对象句柄传递给第二个进程。第二个进程复制该句柄就取得对该文件映射对象的访问权限。
  文件映射是在多个进程间共享数据的非常有效方法,有较好的安全性。但文件映射只能用于本地机器的进程之间,不能用于网络中,而开发者还必须控制进程间的同步。

三、共享内存:

Win32 API中共享内存(Shared Memory)实际就是文件映射的一种特殊情况。进程在创建文件映射对象时用0xFFFFFFFF来代 替文件句柄(HANDLE),就表示了对应的文件映射对象是从操作系统页面文件访问内存,其它进程打开该文件映射对象就可以访问该内存块。由于共享内存是 用文件映射实现的,所以它也有较好的安全性,也只能运行于同一计算机上的进程之间。

CreateFileMapping:Creates or opens a named or unnamed file mapping object for a specified file.

HANDLE WINAPI CreateFileMapping(
  _In_     HANDLE                hFile,//指定欲在其中创建映射的一个文件句柄。通常取值:(HANDLE)0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示在页面文件中创建一个可共享的文件映射。
  _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes,
  _In_     DWORD                 flProtect,//文件映射所需的访问权限
  _In_     DWORD                 dwMaximumSizeHigh,
  _In_     DWORD                 dwMaximumSizeLow,
  _In_opt_ LPCTSTR               lpName
);

MapViewOfFile:Maps a view of a file mapping into the address space of a calling process.

LPVOID WINAPI MapViewOfFile(
  _In_ HANDLE hFileMappingObject,
  _In_ DWORD  dwDesiredAccess,
  _In_ DWORD  dwFileOffsetHigh,
  _In_ DWORD  dwFileOffsetLow,
  _In_ SIZE_T dwNumberOfBytesToMap//指定将文件映射内核对象的多少字节映射到进程地址中;如果取值为0,则整个文件映射对象被映射进来
);

OpenFileMapping:Opens a named file mapping object.

HANDLE WINAPI OpenFileMapping(
  _In_ DWORD   dwDesiredAccess,//指明打开文件映射内核对象的访问权限,与CreateFileMapping()中的参数取值相同
  _In_ BOOL    bInheritHandle,
  _In_ LPCTSTR lpName
);

The first process creates the file mapping object by calling the CreateFileMapping function with INVALID_HANDLE_VALUE and a name for the object. By using the PAGE_READWRITE flag, the process has read/write permission to the memory through any file views that are created.

Then the process uses the file mapping object handle that CreateFileMapping returns in a call to MapViewOfFile to create a view of the file in the process address space. The MapViewOfFile function returns a pointer to the file view, pBuf. The process then uses the CopyMemory function to write a string to the view that can be accessed by other processes.

Prefixing the file mapping object names with "Global\" allows processes to communicate with each other even if they are in different terminal server sessions. This requires that the first process must have the SeCreateGlobalPrivilege privilege.

When the process no longer needs access to the file mapping object, it should call the CloseHandle function. When all handles are closed, the system can free the section of the paging file that the object uses.

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>

#define BUF_SIZE 256
TCHAR szName[] = TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[] = TEXT("Message from first process.");

int _tmain()
{
    HANDLE hMapFile;
    LPCTSTR pBuf;

    hMapFile = CreateFileMapping(
        INVALID_HANDLE_VALUE,    // use paging file
        NULL,                    // default security
        PAGE_READWRITE,          // read/write access
        0,                       // maximum object size (high-order DWORD)
        BUF_SIZE,                // maximum object size (low-order DWORD)
        szName);                 // name of mapping object

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("Could not create file mapping object (%d).\n"),
            GetLastError());
        return 1;
    }
    pBuf = (LPTSTR)MapViewOfFile(hMapFile,   // handle to map object
        FILE_MAP_ALL_ACCESS, // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());

        CloseHandle(hMapFile);

        return 1;
    }


    CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);
    system("pause");
    return 0;
}

A second process can access the string written to the shared memory by the first process by calling the OpenFileMapping function specifying the same name for the mapping object as the first process. Then it can use the MapViewOfFile function to obtain a pointer to the file view, pBuf. The process can display this string as it would any other string. In this example, the message box displayed contains the message "Message from first process" that was written by the first process.

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#pragma comment(lib, "user32.lib")

#define BUF_SIZE 256
TCHAR szName[] = TEXT("Global\\MyFileMappingObject");

int _tmain()
{
    HANDLE hMapFile;
    LPCTSTR pBuf;

    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS,   // read/write access
        FALSE,                 // do not inherit the name
        szName);               // name of mapping object

    if (hMapFile == NULL)
    {
        _tprintf(TEXT("Could not open file mapping object (%d).\n"),
            GetLastError());
        return 1;
    }

    pBuf = (LPTSTR)MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_ALL_ACCESS,  // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        _tprintf(TEXT("Could not map view of file (%d).\n"),
            GetLastError());

        CloseHandle(hMapFile);

        return 1;
    }

    MessageBox(NULL, pBuf, TEXT("Process2"), MB_OK);

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);
    system("pause");
    return 0;
}

先运行程序一,创建一个内核映射对象并映射到内存:

再运行程序二,打开内核映射对象并映射到内存,读取数据:

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa366551

http://blog.csdn.net/xiaobai1593/article/details/7488351

四、邮件槽

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

看一看官方的说法:

A mailslot is a mechanism for one-way interprocess communications (IPC). Applications can store messages in a mailslot. The owner of the mailslot can retrieve messages that are stored there. These messages are typically sent over a network to either a specified computer or to all computers in a specified domain. A domain is a group of workstations and servers that share a group name.

You can choose to use named pipes or Windows Sockets instead of mailslots for interprocess communications. Named pipes are a simple way for two processes to exchange messages. Mailslots, on the other hand, are a simple way for a process to broadcast messages to multiple processes. One important consideration is that mailslots broadcast messages using datagrams. A datagram is a small packet of information that the network sends along the wire. Like a radio or television broadcast, a datagram offers no confirmation of receipt; there is no way to guarantee that a datagram has been received. Just as mountains, large buildings, or interfering signals might cause a radio or television signal to get lost, there are things that can prevent a datagram from reaching a particular destination. Named pipes are like telephone calls: you talk only to one party, but you know that the message is being received.

CreateMailslot:Creates a mailslot with the specified name and returns a handle that a mailslot server can use to perform operations on the mailslot. The mailslot is local to the computer that creates it. An error occurs if a mailslot with the specified name already exists.

HANDLE WINAPI CreateMailslot(
  _In_     LPCTSTR               lpName,
  _In_     DWORD                 nMaxMessageSize,
  _In_     DWORD                 lReadTimeout,
  _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
Parameters:
lpName [in]

The name of the mailslot. This name must have the following form:

\\.\mailslot\[path]name

The name field must be unique. The name may include multiple levels of pseudo directories separated by backslashes. For example, both \\.\mailslot\example_mailslot_name and \\.\mailslot\abc\def\ghi are valid names.

nMaxMessageSize [in]

The maximum size of a single message that can be written to the mailslot, in bytes. To specify that the message can be of any size, set this value to zero.

lReadTimeout [in]

The time a read operation can wait for a message to be written to the mailslot before a time-out occurs, in milliseconds. The following values have special meanings.

SetMailslotInfo:Sets the time-out value used by the specified mailslot for a read operation.

BOOL WINAPI SetMailslotInfo(
  _In_ HANDLE hMailslot,
  _In_ DWORD  lReadTimeout
);

GetMailslotInfo:Retrieves information about the specified mailslot.

BOOL WINAPI GetMailslotInfo(
  _In_      HANDLE  hMailslot,
  _Out_opt_ LPDWORD lpMaxMessageSize,
  _Out_opt_ LPDWORD lpNextSize,
  _Out_opt_ LPDWORD lpMessageCount,
  _Out_opt_ LPDWORD lpReadTimeout
);

Parameters:

hMailslot [in]

A handle to a mailslot. The CreateMailslot function must create this handle.

lpMaxMessageSize [out, optional]

The maximum message size, in bytes, allowed for this mailslot. This value can be greater than or equal to the value specified in the cbMaxMsg parameter of the CreateMailslot function that created the mailslot. This parameter can be NULL.

lpNextSize [out, optional]

The size of the next message, in bytes. The following value has special meaning.

   lpMessageCount [out, optional]

  The total number of messages waiting to be read, when the function returns. This parameter can be NULL.

  lpReadTimeout [out, optional]

  The amount of time, in milliseconds, a read operation can wait for a message to be written to the mailslot before a time-out occurs. This parameter is filled in when the function returns. This parameter can be NULL.

举例:

The process that creates a mailslot can read messages from it by using the mailslot handle in a call to the ReadFile function. The following example calls the GetMailslotInfo function to determine whether there are messages in the mailslot. If messages are waiting, each is displayed along with the number of messages remaining to be read.

A mailslot exists until the CloseHandle function is called for all open server handles or until all server processes that own a mailslot handle exit. In both cases, any unread messages are deleted from the mailslot, all client handles to the mailslot are closed, and the mailslot itself is deleted from memory.

Server(Reader)端:

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

HANDLE hSlot;
LPTSTR SlotName = TEXT("\\\\.\\mailslot\\sample_mailslot");

BOOL ReadSlot()
{
    DWORD cbMessage, cMessage, cbRead;
    BOOL fResult;
    LPTSTR lpszBuffer;
    TCHAR achID[80];
    DWORD cAllMessages;
    HANDLE hEvent;
    OVERLAPPED ov;

    cbMessage = cMessage = cbRead = 0;

    hEvent = CreateEvent(NULL, FALSE, FALSE, TEXT("ExampleSlot"));
    if (NULL == hEvent)
        return FALSE;
    ov.Offset = 0;
    ov.OffsetHigh = 0;
    ov.hEvent = hEvent;

    fResult = GetMailslotInfo(hSlot, // mailslot handle 
        (LPDWORD)NULL,               // no maximum message size 
        &cbMessage,                   // size of next message 
        &cMessage,                    // number of messages 
        (LPDWORD)NULL);              // no read time-out 

    if (!fResult)
    {
        printf("GetMailslotInfo failed with %d.\n", GetLastError());
        return FALSE;
    }

    if (cbMessage == MAILSLOT_NO_MESSAGE)
    {
        printf("Waiting for a message...\n");
        return TRUE;
    }

    cAllMessages = cMessage;

    while (cMessage != 0)  // retrieve all messages
    {
        // Create a message-number string. 

        StringCchPrintf((LPTSTR)achID,
            80,
            TEXT("\nMessage #%d of %d\n"),
            cAllMessages - cMessage + 1,
            cAllMessages);

        // Allocate memory for the message. 

        lpszBuffer = (LPTSTR)GlobalAlloc(GPTR,
            lstrlen((LPTSTR)achID)*sizeof(TCHAR) + cbMessage);
        if (NULL == lpszBuffer)
            return FALSE;
        lpszBuffer[0] = '\0';

        fResult = ReadFile(hSlot,
            lpszBuffer,
            cbMessage,
            &cbRead,
            &ov);

        if (!fResult)
        {
            printf("ReadFile failed with %d.\n", GetLastError());
            GlobalFree((HGLOBAL)lpszBuffer);
            return FALSE;
        }

        // Concatenate the message and the message-number string. 

        StringCbCat(lpszBuffer,
            lstrlen((LPTSTR)achID)*sizeof(TCHAR) + cbMessage,
            (LPTSTR)achID);

        // Display the message. 

        _tprintf(TEXT("Contents of the mailslot: %s\n"), lpszBuffer);

        GlobalFree((HGLOBAL)lpszBuffer);

        fResult = GetMailslotInfo(hSlot,  // mailslot handle 
            (LPDWORD)NULL,               // no maximum message size 
            &cbMessage,                   // size of next message 
            &cMessage,                    // number of messages 
            (LPDWORD)NULL);              // no read time-out 

        if (!fResult)
        {
            printf("GetMailslotInfo failed (%d)\n", GetLastError());
            return FALSE;
        }
    }
    CloseHandle(hEvent);
    return TRUE;
}

BOOL WINAPI MakeSlot(LPTSTR lpszSlotName)
{
    hSlot = CreateMailslot(lpszSlotName,
        0,                             // no maximum message size 
        MAILSLOT_WAIT_FOREVER,         // no time-out for operations 
        (LPSECURITY_ATTRIBUTES)NULL); // default security

    if (hSlot == INVALID_HANDLE_VALUE)
    {
        printf("CreateMailslot failed with %d\n", GetLastError());
        return FALSE;
    }
    return TRUE;
}

void main()
{
    MakeSlot(SlotName);

    while (TRUE)
    {
        ReadSlot();
        Sleep(3000);
    }
}

运行Server端后一直处在等待状态:

Client(Writer)端:

#include <windows.h>
#include <stdio.h>

LPTSTR SlotName = TEXT("\\\\.\\mailslot\\sample_mailslot");

BOOL WriteSlot(HANDLE hSlot, LPTSTR lpszMessage)
{
    BOOL fResult;
    DWORD cbWritten;

    fResult = WriteFile(hSlot,
        lpszMessage,
        (DWORD)(lstrlen(lpszMessage) + 1)*sizeof(TCHAR),
        &cbWritten,
        (LPOVERLAPPED)NULL);

    if (!fResult)
    {
        printf("WriteFile failed with %d.\n", GetLastError());
        return FALSE;
    }

    printf("Slot written to successfully.\n");

    return TRUE;
}

int main()
{
    HANDLE hFile;

    hFile = CreateFile(SlotName,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        (LPSECURITY_ATTRIBUTES)NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        (HANDLE)NULL);

    if (hFile == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile failed with %d.\n", GetLastError());
        return FALSE;
    }

    WriteSlot(hFile, TEXT("Message one for mailslot."));
    WriteSlot(hFile, TEXT("Message two for mailslot."));

    Sleep(5000);

    WriteSlot(hFile, TEXT("Message three for mailslot."));

    CloseHandle(hFile);

    return TRUE;
}

一旦运行Client端往邮件槽中写入数据,Server端就会读取到:

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa365576%28v=vs.85%29.aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/aa365785%28v=vs.85%29.aspx

http://www.codeproject.com/Articles/13724/Windows-IPC

五,剪切板

剪贴板(Clipped Board)实质是Win32 API中一组用来传输数据的函数和消息,为Windows 应用程序之间进行数据共享提供了一个中介,Windows已建立的剪切(复制)-粘贴的机制为不同应用程序之间共享不同格式数据提供了一条捷径。当用户在 应用程序中执行剪切或复制操作时,应用程序把选取的数据用一种或多种格式放在剪贴板上。然后任何其它应用程序都可以从剪贴板上拾取数据,从给定格式中选择 适合自己的格式。
剪贴板是一个非常松散的交换媒介,可以支持任何数据格式,每一格式由一无符号整数标识,对标准(预定义)剪贴板格式,该值是 Win32 API定义的 常量;对非标准格式可以使用Register Clipboard Format函数注册为新的剪贴板格式。利用剪贴板进行交换的数据只需在数据格式上一 致或都可以转化为某种格式就行。但剪贴板只能在基于Windows的程序中使用,不能在网络上使用。

OpenClipboard:Opens the clipboard for examination and prevents other applications from modifying the clipboard content.

BOOL WINAPI OpenClipboard(
  _In_opt_ HWND hWndNewOwner
);
hWndNewOwner [in, optional]

Type: HWND

A handle to the window to be associated with the open clipboard. If this parameter is NULL, the open clipboard is associated with the current task.

SetClipboardData:Places data on the clipboard in a specified clipboard format. The window must be the current clipboard owner, and the application must have called the OpenClipboard function.

HANDLE WINAPI SetClipboardData(
  _In_     UINT   uFormat,
  _In_opt_ HANDLE hMem
);

GetClipboardData:Retrieves data from the clipboard in a specified format. The clipboard must have been opened previously.

HANDLE WINAPI GetClipboardData(
  _In_ UINT uFormat
);

运行程序一,内容拷贝到clipboard中:

#include <iostream>
#include<windows.h>
#include<string.h>
//#include <afx.h>
using namespace std;
void main()
{
    string source("Hello Clipboard!");

    if (OpenClipboard(NULL))
    {
        HGLOBAL clipbuffer;
        char * buffer;
        EmptyClipboard();
        clipbuffer = GlobalAlloc(GMEM_DDESHARE, source.size() + 1);
        buffer = (char*)GlobalLock(clipbuffer);
        strcpy(buffer, source.c_str());
        GlobalUnlock(clipbuffer);
        SetClipboardData(CF_TEXT, clipbuffer);
        CloseClipboard();

    }
    system("pause");
}

运行程序二,可以从clipboard中得到数据:

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

void main()
{
    char * buffer;
    if (OpenClipboard(NULL))
    {
        buffer = (char*)GetClipboardData(CF_TEXT);
        printf("%s\n", buffer);
    }
    CloseClipboard();
    system("pause");
}

得到:

即便不利用程序二,我们右键粘贴也可以得到数据:

参考:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms648709%28v=vs.85%29.aspx

优质内容筛选与推荐>>
1、Spring国际化
2、第九周作业补
3、关于双击事件.MouseEvent.DOUBLE_CLICK
4、JavaScript的this用法(转)
5、STM32 KEIL不能输入仿真引脚端口error 65: access violation at 0x40021000 : no 'read' permission


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号





    联系我们

    欢迎来到TinyMind。

    关于TinyMind的内容或商务合作、网站建议,举报不良信息等均可联系我们。

    TinyMind客服邮箱:support@tinymind.net.cn