Windows——进程间通信

2023-11-01

进程间通信的概念

每一个进程都是拥有自己独立的虚拟地址空间和页表结构,促使了进程独立,这也导致了进程之间合作存在问题,为了解决该问题,产生了进程间的通信。

Windows操作系统提供了促进应用程序之间的通信和数据共享的机制。

Mailslots

mailslot 是一种机制,用于通过 IPC (单向) 。 应用程序可以将消息存储在 mailslot 中。 mailslot 的所有者可以检索存储在其中的消息。 这些消息通常通过网络发送到指定计算机或指定域中的所有计算机。

一个重要的注意事项是 mailslots 使用数据报广播消息。这也意味着,通信过程中存在信息丢失的可能。

关于Mailslots

  1. mailslot 是驻留在内存中的伪文件,可以使用标准文件函数来访问它。 与磁盘文件不同,mailslot 是临时的。 关闭 mailslot 的所有句柄后,将删除 mailslot 及其包含的所有数据。
  2. 计算机之间发送时不能大于 424 字节。
  3. Mailslots 可以广播域中的消息。 如果域中多个进程使用同一名称创建 mailslot,则参与进程将接收发送到该 mailslot 并发送到域的每条消息。

命名规则

当进程创建 mailslot 时,mailslot 名称必须采用以下格式。

\.\mailslot \ [ 路径 \ ] 名称

Mailslot 名称需要以下元素:两个反斜杠开始名称、句点、句点后的反斜杠、单词 “mailslot” 和尾随反斜杠。 名称不区分大小写。 Mailslot 名称前面可以是由一个或多个目录的名称组成的路径,用反斜杠分隔。 例如,如果用户期望来自 Bob、Pete 和 Sue 的税收的邮件,用户的 mailslot 应用程序可能允许用户为每个发件人创建单独的 mailslots,如下所示:

\.\mailslot \ 税金 \ bobs-sfpreviewcluster _ 注释
\.\mailslot \ 税金 \petes _ 注释
\.\mailslot \ 税金 \ 使用 disk write _ 注释

  • 若要将消息放入 mailslot,进程会按名称打开邮件槽。 若要在本地计算机上写入 mailslot,进程可以使用格式与创建 mailslot 相同的 mailslot 名称。 但这种情况相对较为罕见。 更常见的情况是,使用以下形式写入特定远程计算机上的 mailslot:

\ComputerName \mailslot \ [ 路径 \ ] 名称

  • 若要将消息放入具有给定名称的指定域中的每个 mailslot,请使用以下形式:

\DomainName \mailslot \ [ 路径 \ ] 名称

  • 若要将消息放入系统主域中具有给定名称的每个 mailslot,请使用以下形式:

\*\mailslot \ [ 路径 \ ] 名称

使用

创建 Mailslot

HANDLE CreateMailslotA(
[in] LPCSTR lpName,
[in] DWORD nMaxMessageSize,
[in] DWORD lReadTimeout,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

返回值:HANDLE(void*)句柄
参数:

  • [in]lpName
    Mailslot的名称。此名称必须具有以下格式:
    \.\Mailslot[path]名称
    名称字段必须是唯一的。该名称可能包括由反斜杠分隔的多级伪目录

  • [in]nMaxMessageSize
    可以写入邮件槽的单个邮件的最大大小,以字节为单位。要指定消息的大小,将此值设置为零。

  • [in]lReadTimeout
    在超时发生之前,读取操作可以等待消息写入邮件槽的时间,以毫秒为单位。以下值具有特殊含义。为0时:如果没有消息,则立即返回;为-1:
    永远等待消息。

  • [in,optional]lpSecurityAttributes
    指向安全属性结构的指针。结构的bInheritHandle成员确定返回的句柄是否可以被子进程继承。如果lpSecurityAttributes为NULL,则无法继承句柄。

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

bool Create(LPCTSTR name, HANDLE& slot) {
  slot = CreateMailslot(name,
    0,//默认为大消息
    -1,//始终等待消息
    NULL);//无法继承句柄
  if (slot == INVALID_HANDLE_VALUE) {
    return false;
  } else {
    return true;
  }
}

void main()
{
  LPCTSTR SlotName = TEXT("\\.\\mailslot\\test_mailslot");
  HANDLE slot;
  bool ret = Create(SlotName,slot);
  if (ret == false) {
    printf("create Mailslot faild,%d\n", GetLastError());
  } else {
    printf("create Mailslot success\n");
  }
}

在这里插入图片描述

写入 Mailslot

流程:用CreateFile在已经运行的mailslot服务器上创建一个文件,用WriteFile写入内容

写入 mailslot 类似于写入标准磁盘文件。 可使用 CreateFile、WriteFile和 CloseHandle函数在mailslot 中放入消息。 该消息将广播到本地计算机上的 mailslot 服务器。

HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile );

返回值:操作句柄
参数:

  • [in] lpFileName
    要创建或打开的文件或设备的名称。

[in] dwDesiredAccess
请求的对文件或设备的访问权限,可概括为读、写、二者均为0

[in] dwShareMode
文件或设备的请求共享模式,可以是读、写、两者都可以
[in] dwCreationDisposition
对存在或不存在的文件或设备执行的操作。

[in] dwFlagsAndAttributes
文件或设备属性和标志,文件属性是文件最常见的默认值。
此参数可以包括可用文件属性的任意组合(文件属性)。所有其他文件属性将覆盖文件属性

[in, optional] hTemplateFile
具有通用读取权限的模板文件的有效句柄。模板文件为正在创建的文件提供文件属性和扩展属性。
此参数可以为NULL。
打开现有文件时,CreateFile会忽略此参数

BOOL WriteFile(
[in] HANDLE hFile,
[in] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[out, optional] LPDWORD lpNumberOfBytesWritten,
[in, out, optional] LPOVERLAPPED lpOverlapped
);

返回值:true false
参数:

  • [in] hFile
    文件或I/O设备的句柄

  • [in] lpBuffer
    指向包含要写入文件或设备的数据的缓冲区的指针

  • [in] nNumberOfBytesToWrite
    要写入文件或设备的字节数。

  • [out, optional] lpNumberOfBytesWritten
    指向使用同步hFile参数时接收写入字节数的变量的指针。

  • [in, out, optional] lpOverlapped
    如果在打开hFile参数时文件_FLAG_OVERLAPPED,则需要指向重叠结构的指针,否则该参数可以为NULL。

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

bool Create(LPCTSTR name, HANDLE& slot) {
  slot = CreateMailslot(name,
    0,//默认为大消息
    -1,//始终等待消息
    NULL);//无法继承句柄
  if (slot == INVALID_HANDLE_VALUE) {
    return false;
  } else {
    return true;
  }
}

bool Write(HANDLE slot,LPCTSTR message) {
  DWORD cb;
  bool ret = WriteFile(slot, message, (DWORD)(lstrlen(message) + 1 * sizeof(TCHAR)), &cb, (LPOVERLAPPED)NULL);
  if (ret == false) {
    return false;
  } else {
    return true;
  }
}

void main()
{
  LPCTSTR SlotName = TEXT("\\\\.\\mailslot\\test_mailslot");
  HANDLE slot;
  bool ret = Create(SlotName,slot);
  if (ret == false) {
    printf("create Mailslot faild,%d\n", GetLastError());
  } else {
    printf("create Mailslot success\n");
  }


  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());
  }
  ret = Write(hFile, TEXT("write for test1"));
  if (ret == false) {
    printf("write Mailslot faild,%d\n", GetLastError());
  } else {
    printf("write Mailslot success\n");
  }
}

读取Mailslot

BOOL ReadFile(
[in] HANDLE hFile,
[out] LPVOID lpBuffer,
[in] DWORD nNumberOfBytesToRead,
[out, optional] LPDWORD lpNumberOfBytesRead,
[in, out, optional] LPOVERLAPPED lpOverlapped
);

BOOL GetMailslotInfo(
[in] HANDLE hMailslot,
[out, optional] LPDWORD lpMaxMessageSize,
[out, optional] LPDWORD lpNextSize,
[out, optional] LPDWORD lpMessageCount,
[out, optional] LPDWORD lpReadTimeout
);

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

  cbMessage = cMessage = cbRead = 0;

  fResult = GetMailslotInfo(slot, // 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(slot,
      lpszBuffer,
      cbMessage,
      &cbRead,
      NULL);

    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(slot,  // 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;
    }
  }
  return TRUE;
}

管道

管道 是处理用于通信的共享内存部分。 创建管道的进程是 管道服务器。 连接到管道的进程是 管道客户端。
通信过程简单来说就是:一个进程向管道写入信息,而另一个进程从管道中读取信息。

关于管道

有两种类型的管道: 匿名管道 和 命名管道。 匿名管道比命名管道需要更少的开销,但提供有限的服务。

匿名管道

匿名 管道 是一种未命名的单向管道,在父进程和子进程之间传输数据。 匿名管道始终为本地管道;它们不能用于网络通信。

匿名管道创建

BOOL CreatePipe(
[out] HANDLE* hReadPipe,
[out] HANDLE* hWritePipe,
[in, optional] &SECURITY_ATTRIBUTES lpPipeAttributes,
[in] DWORD nSize
);

返回值:
失败返回0,否则返回非0

参数:

  • [out] hReadPipe
    出参,接收管道读取句柄的指针

  • [out] hWritePipe
    出参,接收管道写入句柄的指针

  • [in, optional] lpPipeAttributes
    指向安全属性结构的指针,该结构确定返回的句柄是否可以由子进程继承。如果lpPipeAttributes为NULL,则无法继承句柄。 如果管道服务器将此结构的 bInheritHandle 成员设置为 TRUE,则可以继承 CreatePipe 创建的句柄。使用SECURITY_ATTRIBUTES来操作较为方便。

  • [in] nSize
    管道缓冲区的大小,以字节为单位。大小只是一个建议;系统使用该值来计算适当的缓冲机制。如果此参数为零,系统将使用默认缓冲区大小。

从管道中读取和写入,使用ReadFile和WriteFile即可。如果管道缓冲区已满并且要写入的字节数更多, 则 WriteFile 不会返回,直到另一个进程从管道读取数据,从而提供更多的缓冲区空间。

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

bool Create(PHANDLE&& write, PHANDLE&& read) {
  WORD ret = 0;
  SECURITY_ATTRIBUTES sa;
  sa.nLength = sizeof(sa);
  sa.lpSecurityDescriptor = 0;
  sa.bInheritHandle = true;
 
  ret = CreatePipe(read, write, &sa, 0);
  if (ret == 0) {
    return false;
  } else {
    return true;
  }
}

int main()
{
  HANDLE write, read;
  bool ret = false;
  ret = Create(&write, &read);
  if (ret == false) {
    std::cout << "create pipe faild   " << GetLastError() << std::endl;
  } else {
    std::cout << "create pipe success" << std::endl;
  }
  return 0;
}

在这里插入图片描述

命名管道

命名 管道 是命名单向或双工管道,用于管道服务器与一个或多个管道客户端之间的通信。 命名管道的所有实例共享相同的管道名称,但每个实例都有自己的缓冲区和句柄,并且为客户端/服务器通信提供单独的管道。

任何进程都可以充当服务器和客户端,使对等通信成为可能。

用于实例化命名管道的服务器端函数是 CreateNamedPipe。 用于接受连接的服务器端函数为 ConnectNamedPipe。 客户端进程使用 CreateFile 或 CallNamedPipe 函数连接到命名管道。

命名管道可用于在同一台计算机上的进程之间或网络中不同计算机上的进程之间提供通信。

命名规则

在 CreateFile 、WaitNamedPipe或 CallNamedPipe 函数中指定管道的名称时,请使用以下形式:

\ServerName \pipe \ PipeName

其中 ServerName 是远程计算机的名称或时间段,用于指定本地计算机。 PipeName 指定的管道名称字符串可以包含反杠外的任何字符,包括数字和特殊字符。 整个管道名称字符串的长度最多为 256 个字符。 管道名称不区分大小写。

管道服务器无法在另一台计算机上创建管道,因此 CreateNamedPipe 必须为服务器名称使用一个时间段,如以下示例所示。

\.\pipe \ PipeName

访问模式

对于服务器:

  • 如果管道服务器使用 PIPE ACCESS INBOUND 创建管道,则管道服务器为只读管道,管道客户端为只写。
  • 如果管道服务器使用 PIPE ACCESS OUTBOUND 创建管道,则管道对于管道服务器为只写,对于管道客户端为只读。
  • 使用PIPE ACCESS DUPLEX 创建的管道对于管道服务器和管道客户端都是读/写。

对于客户端:

使用 CreateFile 连接到命名管道 的管道客户端必须在 dwDesiredAccess参数中指定访问权限,该访问权限与管道服务器指定的访问模式兼容。
例如,客户端必须指定 GENERIC READ访问权限,以打开管道服务器使用 PIPE ACCESS OUTBOUND 创建的管道的句柄。
对于管道的所有实例,访问模式必须相同。

相关操作

HANDLE CreateNamedPipeA(
[in] LPCSTR lpName,
[in] DWORD dwOpenMode,
[in] DWORD dwPipeMode,
[in] DWORD nMaxInstances,
[in] DWORD nOutBufferSize,
[in] DWORD nInBufferSize,
[in] DWORD nDefaultTimeOut,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes
);

返回值:
如果函数成功,则返回值是命名管道实例服务器端的句柄。

参数:

  • [in] lpName
    唯一的管道名称。此字符串必须具有以下形式:
    \.\pipe\pipename

  • [in] dwOpenMode
    开放模式
    PIPE_ACCESS_DUPLEX :管道是双向的;服务器和客户端进程都可以读取和写入管道
    PIPE_ACCESS_INBOUND:管道中的数据流仅从客户端流向服务器。
    PIPE_ACCESS_OUTBOUND:管道中的数据流仅从服务器流向客户端。

  • [in] dwPipeMode
    管道模式。
    PIPE_TYPE_BYTE,数据以字节流的形式写入/流出管道。
    PIPE_TYPE_MESSAGE:数据作为消息流写入/流出管道。

  • [in] nMaxInstances
    可以为此管道创建的最大实例数。如果此参数为PIPE_UNLIMITED_INSTANCES,则可以创建的管道实例数量仅受系统资源可用性的限制。

  • [in] nOutBufferSize
    为输出缓冲区保留的字节数。

  • [in] nInBufferSize
    为输入缓冲区保留的字节数。

  • [in] nDefaultTimeOut
    超时值
    如果WaitNamedPipe函数指定NMPWAIT_USE_default_WAIT,则默认超时值

  • [in, optional] lpSecurityAttributes
    指向安全属性结构的指针,该结构为新命名管道指定安全描述符,并确定子进程是否可以继承返回的句柄。如果lpSecurityAttributes为NULL,则命名管道将获得默认的安全描述符,并且无法继承句柄。

管道服务器创建管道实例后,管道客户端就可以通过调用 CreateFile来连接管道实例

BOOL WaitNamedPipeA(
[in] LPCSTR lpNamedPipeName,
[in] DWORD nTimeOut
);

返回值:
如果在超时间隔过去之前管道实例可用,则返回值为非零。

参数:

  • [in] lpNamedPipeName
    命名管道的名称。

  • [in] nTimeOut
    函数等待命名管道的实例可用的毫秒数。
    NMPWAIT_USE_DEFAULT_WAIT:超时间隔是服务器进程在CreateNamedPipe函数中指定的默认值
    **NMPWAIT_WAIT_FOREVER:**在指定管道的实例可用之前,函数不会返回。

BOOL ConnectNamedPipe(
[in] HANDLE hNamedPipe,
[in, out, optional] LPOVERLAPPED lpOverlapped
);

返回值:
如果操作是同步的,则ConnectNamedPipe在操作完成之前不会返回。如果函数成功,则返回值为非零。如果函数失败,返回值为零。

参数:

  • [in] hNamedPipe
    命名管道实例的服务器端句柄。此句柄由CreateNamedPipe函数返回

  • [in, out, optional] lpOverlapped
    指向重叠结构的指针。

管道进阶

多线程管道服务器

服务器创建管道,采用多线程,当有连接管道时创建新线程服务

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

#define BUFSIZE 512

DWORD WINAPI InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int main()
{
  BOOL   fConnected = FALSE;
  DWORD  dwThreadId = 0;
  HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
  LPCTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

  for (;;)
  {
    _tprintf(TEXT("\nPipe Server: Main thread awaiting client connection on %s\n"), lpszPipename);

    hPipe = CreateNamedPipe(
      lpszPipename,           
      PIPE_ACCESS_DUPLEX,     
      PIPE_TYPE_MESSAGE |     
      PIPE_READMODE_MESSAGE |   
      PIPE_WAIT,               
      PIPE_UNLIMITED_INSTANCES, 
      BUFSIZE,                  
      BUFSIZE,                 
      0,                       
      NULL);                    

    if (hPipe == INVALID_HANDLE_VALUE)
    {
      _tprintf(TEXT("CreateNamedPipe failed, GLE=%d.\n"), GetLastError());
      return -1;
    }

    fConnected = ConnectNamedPipe(hPipe, NULL) ?
      TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

    if (fConnected)
    {
      printf("Client connected, creating a processing thread.\n");

      hThread = CreateThread(
        NULL,              
        0,                
        InstanceThread,   
        (LPVOID)hPipe,   
        0,                
        &dwThreadId);      

      if (hThread == NULL)
      {
        _tprintf(TEXT("CreateThread failed, GLE=%d.\n"), GetLastError());
        return -1;
      } else CloseHandle(hThread);
    } else

      CloseHandle(hPipe);
  }

  return 0;
}

DWORD WINAPI InstanceThread(LPVOID lpvParam)
{
  HANDLE hHeap = GetProcessHeap();
  TCHAR* pchRequest = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));
  TCHAR* pchReply = (TCHAR*)HeapAlloc(hHeap, 0, BUFSIZE * sizeof(TCHAR));

  DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
  BOOL fSuccess = FALSE;
  HANDLE hPipe = NULL;


  if (lpvParam == NULL)
  {
    printf("\nERROR - Pipe Server Failure:\n");
    printf("   InstanceThread got an unexpected NULL value in lpvParam.\n");
    printf("   InstanceThread exitting.\n");
    if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
    if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
    return (DWORD)-1;
  }

  if (pchRequest == NULL)
  {
    printf("\nERROR - Pipe Server Failure:\n");
    printf("   InstanceThread got an unexpected NULL heap allocation.\n");
    printf("   InstanceThread exitting.\n");
    if (pchReply != NULL) HeapFree(hHeap, 0, pchReply);
    return (DWORD)-1;
  }

  if (pchReply == NULL)
  {
    printf("\nERROR - Pipe Server Failure:\n");
    printf("   InstanceThread got an unexpected NULL heap allocation.\n");
    printf("   InstanceThread exitting.\n");
    if (pchRequest != NULL) HeapFree(hHeap, 0, pchRequest);
    return (DWORD)-1;
  }

  printf("InstanceThread created, receiving and processing messages.\n");

  hPipe = (HANDLE)lpvParam;

  while (1)
  {
    fSuccess = ReadFile(
      hPipe,       
      pchRequest,   
      BUFSIZE * sizeof(TCHAR), 
      &cbBytesRead, 
      NULL);       

    if (!fSuccess || cbBytesRead == 0)
    {
      if (GetLastError() == ERROR_BROKEN_PIPE)
      {
        _tprintf(TEXT("InstanceThread: client disconnected.\n"));
      } else
      {
        _tprintf(TEXT("InstanceThread ReadFile failed, GLE=%d.\n"), GetLastError());
      }
      break;
    }

    GetAnswerToRequest(pchRequest, pchReply, &cbReplyBytes);
 
    fSuccess = WriteFile(
      hPipe,        
      pchReply,     
      cbReplyBytes, 
      &cbWritten,   
      NULL);       

    if (!fSuccess || cbReplyBytes != cbWritten)
    {
      _tprintf(TEXT("InstanceThread WriteFile failed, GLE=%d.\n"), GetLastError());
      break;
    }
  }

  FlushFileBuffers(hPipe);
  DisconnectNamedPipe(hPipe);
  CloseHandle(hPipe);

  HeapFree(hHeap, 0, pchRequest);
  HeapFree(hHeap, 0, pchReply);

  printf("InstanceThread exiting.\n");
  return 1;
}

VOID GetAnswerToRequest(LPTSTR pchRequest,
  LPTSTR pchReply,
  LPDWORD pchBytes)
{
  _tprintf(TEXT("Client Request String:\"%s\"\n"), pchRequest);

  if (FAILED(StringCchCopy(pchReply, BUFSIZE, TEXT("default answer from server"))))
  {
    *pchBytes = 0;
    pchReply[0] = 0;
    printf("StringCchCopy failed, no outgoing message.\n");
    return;
  }
  *pchBytes = (lstrlen(pchReply) + 1) * sizeof(TCHAR);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows——进程间通信 的相关文章

  • 从命令行运行 R 代码 (Windows)

    我在名为 analysis r 的文件中有一些 R 代码 我希望能够从命令行 CMD 运行该文件中的代码 而无需通过 R 终端 并且我还希望能够传递参数并在我的代码中使用这些参数 例如就像下面的伪代码 C gt execute r scri
  • 取消后调用 boost::asio 异步处理程序没有错误

    我的代码在单个线程中使用 boost asio 和 io service 来执行各种套接字操作 所有操作都是异步的 每个处理程序都依赖于boost system error code 特别boost asio error operation
  • C# - 方法必须有返回类型

    我在调用 C 中的方法时遇到问题 不断收到消息 方法 计算 必须有返回类型 using System Diagnostics namespace WindowsFormsApplication1 public partial class F
  • 在 Windows 上不使用 OpenSSL 从 pfx 文件或证书存储中提取私钥

    正如标题所示 我想在不使用 OpenSSL 或任何其他第三方工具的情况下导出我的私钥 如果我需要一个 cer文件或 pfx我可以通过 MMC 或 PowerShell 轻松导出这些文件pkiclient但我找不到获取私钥的方法 https
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • 如何从Windows阻止社交媒体[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我想根据时间阻止我的电脑上的社交媒体 晚上 9 点后屏蔽 上午 11 点后解锁 如家长控制 我尝试过关注但失败了 创建了
  • 对于多重继承,使用隐式转换而不是 QueryInterface() 是否合法?

    假设我有一个类实现两个或多个 COM 接口 正如here https stackoverflow com questions 1742848 why exactly do i need an explicit upcast when imp
  • NodeJS Express Windows 最大连接数设置

    在哪里设置nodejs的最大连接数 用于使用express get 在 Windows 10 中 与linux中的最大文件 描述符 设置有关吗 有该设置的 Windows 版本吗 最好是在nodejs中进行设置 以便在迁移到unix时兼容
  • 在哪里可以找到 Windows 7 UX 指南中推荐的图标/动画?

    Windows 7 UX 指南有很好的插图和图标示例 但我在 SDK 中确实找不到它们 他们藏在某个地方 还是找不到 如果您谈论的是常见的 UI 图标 那么您应该以编程方式获取它们 例如 您可以使用 var errIcon HICON be
  • Windows 操作系统中 ST_INO(os.stat() 输出)的含义

    谁能告诉我这个值的含义是什么st ino是跑步时os stat 在 Windows 上 Python 3 5 3 在早期的 Python 版本中 它包含虚拟值 但最近发生了变化 我找不到它是如何计算 生成的 我怀疑它因文件系统 NTFS F
  • 在 Win7 登录屏幕上运行应用程序[重复]

    这个问题在这里已经有答案了 我想通过服务在 Windows 7 的登录屏幕上运行应用程序 我对此进行了长期研究并尝试了不同的方法 但不幸的是到目前为止还没有完全成功 我设法在当前登录用户的锁定屏幕上运行该应用程序 起初我认为这就是我基本上试
  • Windows 中的蓝牙 AVRCP 命令会触发哪些事件

    可以这么说 只是在做一些高级侦察 对于我的潘多拉客户 Elpis http elpis adamhaile net 我支持全局媒体键 键盘上的 MediaPlayPause MediaNext 等 并且我希望能够支持AVRCP http e
  • 在 Windows 中使用 PHP 创建受密码保护的 Zip 文件

    我正在 PHP 中创建给定文件的 zip 文件 下面是函数 function create zip file file name zip new ZipArchive zip name file name zip Zip name zip
  • 生成尽可能最快的可执行文件

    我有一个非常大的程序 我一直在 Visual Studio 下编译 v6 然后迁移到 2008 我需要可执行文件尽可能快地运行 该程序大部分时间都花在处理各种大小的整数上 并且执行很少的 IO 显然 我会选择最大优化 但似乎可以做很多不属于
  • 将 kinit 与 keytab 文件一起使用时会发生什么

    希望对kinit和keytab文件的使用有更多的了解 例如 如果我已经为某个服务生成了一个密钥表文件 该服务通过以下方式注册到活动目录 ktpass mapuser to someuseraccount ktab k mykeytab a
  • 将 value 转换为 bool 的魔法

    今天我意识到将值转换为 bool 是一种魔法 int value 0x100 unsigned char uc static cast
  • C# 获取子窗口句柄

    我正在用 C 启动一个进程 然后使用 SendMessage 将 Windows 消息发送到该进程 通常我将消息发送到 Process MainWindowHandle 但在某些情况下 我可能需要找到子窗口句柄并向那里发送消息 我将如何在
  • Windows 上最快的屏幕捕获方法

    我想为Windows平台编写一个截屏程序 但不确定如何捕获屏幕 我知道的唯一方法是使用 GDI 但我很好奇是否还有其他方法可以实现此目的 如果有的话 哪种方法产生的开销最小 速度是首要任务 截屏程序将用于录制游戏镜头 不过 如果这确实缩小了
  • 如果我使用客户端计算机上未安装的字体,会发生什么情况?

    有人可以告诉我 如果我在 WinForms 应用程序中使用目标计算机上不可用的字体 会发生什么情况 它是否使用同一系列的字体 只是 Sans Serif 还是其他字体 您的应用程序将回退到 Segoe UI Tahoma 然后是 MS Sa
  • 无法访问127.0.0.1

    我不知道这种情况何时开始发生 但结果是 127 0 0 1 无法在任何地方的任何端口上工作 例如 浏览器显示无法连接 以下是我的研究结果 localhost COMPUTER NAME 和实际 IP 地址都工作正常 我的主机文件中没有什么特

随机推荐

  • 【2021-CVPR-3D人体姿态估计】Graph Stacked Hourglass Networks for 3D Human Pose Estimation

    Graph Stacked Hourglass Networks for 3D Human Pose Estimation 题目 用于3D人体姿态的图堆叠沙漏网络 作者 来源 CVPR 2021 研究内容 单人 单视图 有监督 创新点 提出
  • 目标检测数据集

    参考 You Only Look Once Unified Real Time Object Detection 你只看一次 统一的实时对象检测 CVPR 2016 1 PASCAL VOC PASCAL VOC 数据集是目标检测领域最常用
  • 推荐图灵出版社的图书

    最近半年买了不少图灵出版社的图书 有java oracle Linux和TCP IP方面的书 每本都读了差不多一半 读后有一种很亲切的感觉 总结下来一句话 很适合受中国传统教育的大部分学生 在此并无讽刺之意 很多学生习惯了 填鸭式 的教育模
  • 使用conda创建Python的虚拟环境

    目录 一 查看已有环境 二 创建虚拟环境 2 1 添加镜像地址 2 2 创建虚拟环境 2 3 激活虚拟环境 2 4 退出虚拟环境 三 pycharm使用环境 四 删除虚拟环境 五 快速创建虚拟环境 一 查看已有环境 键入以下命令 conda
  • nosql

    公共基础 一 NoSQL概述 1 NoSQL NoSQL定义 非关系型 分布式 开放源码和具有横向扩展能力的下一代数据库 NOSQL Not Only SQL 2 数据库的分类 1 TRDB数据库 传统关系型数据库 基于单机集中管理数据理念
  • 机器学习实战——第三章(分类):决策树算法与实例(一)

    前言 今天看了会 机器学习实战 第三章 决策树 很迷 似懂非懂 专业术语太多了 而且有点混乱 对于一个大一概率论没学好的学渣来说 如今大三的我看到那些概率公式和一些概率论专业术语就头疼 马上就打了退堂鼓 早起看了半个小时没看明白果断又躺回了
  • 设置swiper轮播图的css样式无效?

    项目场景 在做vue项目的时候 使用到了vue awesome swiper三方库 当时小编想重写 覆盖轮播图的分页器小圆点的css样式 其次 小编做项目时 使用的css预编译器是SCSS 问题描述 当小编在重写分页器小圆点css样式时 发
  • Vim配置及使用总结

    Vim配置及使用总结 我的Vim配置 Vim安装及配置文件 Vim安装 Vim配置文件 我的Vim配置 Vim配置详解 Vim使用技巧 基本使用介绍 命令模式 输入模式 底线命令模式 技巧总结 二进制文件查看 下面是我在使用vim时的一些配
  • Sass -- 条件语句、循环语句、自定义函数

    文章目录 条件语句 循环语句 for while each 自定义函数 条件语句 在Sass中可以使用 if if else if else 来进行条件判断 用法和JS相同 循环语句 在Sass中可以使用 for while each 来进
  • 4. Hadoop伪分布式运行模式

    文章目录 Hadoop伪分布式运行模式 1 启动HDFS并运行MapReduce程序 1 1 配置集群 配置hadoop env sh 配置core site xml 配置hdfs site xml 1 2 启动集群 格式化NameNode
  • window.open()的所有参数列表(转)

    前言 经常上网的朋友可能会到过这样一些网站 一进入首页立刻会弹出一个窗口 或者按一个连接或按钮弹出 通常在这个窗口里会显示一些注意事项 版权信息 警告 欢迎光顾之类的话或者作者想要特别提示的信息 其实制作这样的页面效果非常的容易 只要往该页
  • Python-玩转数据-Scrapy框架介绍及安装

    一 Scrapy框架说明 1 Scrapy介绍 Scrapy框架官方网址 http doc scrapy org en latest Scrapy是用纯Python实现一个为了爬取网站数据 提取结构性数据而编写的应用框架 用途非常广泛 用户
  • 如果访问云服务器上的文件,如果访问云服务器上的文件

    如果访问云服务器上的文件 内容精选 换一换 WinSCP工具可以实现在本地与远程计算机之间安全地复制文件 与使用FTP上传代码相比 通过 WinSCP 可以直接使用服务器账户密码访问服务器 无需在服务器端做任何配置 通常本地Windows计
  • [Qt]不带标题栏(FramelessWindowHint)的窗体移动及调整大小

    Qt窗体若设置了 setWindowFlags Qt FramelessWindowHint 运行后该窗体是无法进行移动和调整大小的 那要如何才能让它和普通窗体一样进行移动和调整其大小的呢 方案如下 void QFramelessWindo
  • 【八组输入输出你都了解多少】

    目录 前言 一 总览 一 只能从标准流 std 中输入和输出数据 二 可以从所以流中读取和写入数据 三 只能从字符串中读取和写入数据 二 详细描述 一 scanf fscanf sscanf 1 scanf 2 fscanf 3 sscan
  • 震惊,docker操作竟如此简单

    Docker概述 1 什么是Docker Docker 是一个开源的应用容器引擎 让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中 然后发布到任何流行的Linux和Windows操作系统的机器上 也可以实现虚拟化 容器是完全使用沙箱
  • c#—OpenFileDialog(打开文件对话框)

    OpenFileDialog是什么 OpenFileDialog是一个类 实例化此类可以设置弹出一个文件对话框 比如 我们发邮件时需要上传附件的时候 就会弹出一个让我们选择文件的对话框 我们可以根据自己的需求 自行设置一些对话框的属性 那么
  • ASP.NET WebAPI 连接数据库

    ASP NET Web API 是一种框架 用于轻松构建可以访问多种客户端 包括浏览器和移动设备 的 HTTP 服务 ASP NET Web API 是一种用于在 NET Framework 上构建 RESTful 应用程序的理想平台 本文
  • window11中Jdk1.8下载,安装和环境配置(超详细)

    一 下载安装包 这里为了方便大家 提供百度网盘下载 链接 https pan baidu com s 1 Qz7pO226To7yy6ytdPR Q https pan baidu com s 1 Qz7pO226To7yy6ytdPR Q
  • Windows——进程间通信

    进程间通信 进程间通信的概念 Mailslots 关于Mailslots 命名规则 使用 创建 Mailslot 写入 Mailslot 读取Mailslot 管道 关于管道 匿名管道 匿名管道创建 命名管道 命名规则 访问模式 相关操作