在 windows 中是否有明确的方式来传达两个程序?

Is there a clear way of communicate two programs in windows?

我的目标是:

将 JSON 字符串化(从我的程序)发送到另一个程序(外部程序)并等待字符串响应,也是一个 JSON 字符串化。这与 JSON 编程无关。我使用的代码(这两个程序实际上都是 C++ 程序)是:

void initializePipeCommunication(){

//HANDLE que representa la salida estandar de este programa (DLL). Se conectara con la entrada estandar del otro programa
HANDLE this_write;

//HANDLE que representa la entrada estandar de este programa (DLL).
HANDLE this_read;

//informacion del proceso asociado al programa externo, es necesaria esta variable para cerrar la comunicacion
PROCESS_INFORMATION externalProcessInformation;

//HANDLE que representa la entrada estandar del otro programa.
HANDLE child_input_read;

//HANDLE que representa la salida estandar del otro programa.
HANDLE child_output_write;

STARTUPINFO startup_info;
SECURITY_ATTRIBUTES security_attributes;

// Set the security attributes for the pipe handles created
security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
security_attributes.bInheritHandle = TRUE;
security_attributes.lpSecurityDescriptor = NULL;

CreatePipe(&this_read, &child_output_write, &security_attributes, 0);
CreatePipe(&child_input_read, &this_write, &security_attributes, 0);

ZeroMemory(&externalProcessInformation, sizeof(PROCESS_INFORMATION));
ZeroMemory(&startup_info, sizeof(STARTUPINFO));

startup_info.cb = sizeof(STARTUPINFO);
startup_info.hStdInput = child_input_read;
startup_info.hStdOutput = child_output_write;
//startup_info.hStdError = child_output_write;
startup_info.dwFlags |= STARTF_USESTDHANDLES;
startup_info.dwFlags |= STARTF_USESHOWWINDOW;


TCHAR* szCommandLine = loadExecutablePathFromRegistryWide();
lstrcatW(szCommandLine, L" UsePipeMode");

//Creando el programa externo
if (!CreateProcess(NULL, szCommandLine, NULL, NULL,
    TRUE, 0/*CREATE_NEW_CONSOLE*/, NULL, NULL, &startup_info, &externalProcessInformation)){
    DWORD dwStatus = GetLastError();

    if (dwStatus == ERROR_CANCELLED || dwStatus == ERROR_ELEVATION_REQUIRED) {          
        Errors::report(Errors::BAD_EXTERNAL_PROGRAM_PRIVILEGES);
    }
    else if (dwStatus == ERROR_FILE_NOT_FOUND) {
        // The file defined by lpFile was not found and an error message popped up. 
        Errors::report(Errors::BAD_EXTERNAL_PROGRAM_PATH);
    }
    PluginHelper::log("error # ");
    PluginHelper::logn(std::to_string(dwStatus).c_str());
}}

void write(std::string msg){
    unsigned long dwWritten, toWrite = msg.length();
    WriteFile(this_write, msg.c_str(), toWrite, &dwWritten, NULL);
}



std::string read(){
    unsigned long dwRead, dwWritten;
    static char* chBuf = (char*)malloc(266240);
    ReadFile(this_read, chBuf, 266240, &dwRead, NULL);
    chBuf[dwRead] = 0;
    std::string g(chBuf);           
    return (g.substr(0, g.length()));
}

也就是使用管道,其实就是两边的匿名管道。我不关心异步通信。

问题是我发送的字符串大约是 266KB,有时来自外部程序的响应带有错误的 JSON 格式,同样,在外部程序中,字符串化的 JSON 很好构建并发送到我的程序(日志证实了这一点)。总而言之,两侧的匿名管道是相同的,在外部程序中,向我的程序发出的字符串很好,除此之外,在我的程序中,响应是另一个我期望的(加上不一致的错误,比如异常文本连接到JSON 毫无意义)。

有人问:

• 上面的代码很好地或至少是防错的?

• 有没有 Input/output 方式,即 cin/cout 不需要硬欺骗的沟通方式? (就像 Google Chrome 本机消息传递接口一样)

我从事 Win32 编码工作已有 20 多年,写过服务器、应用程序,应有尽有。我认为我使用管道的唯一原因是为了方便(但实用性有限)端点的 ACL,或者能够 impersonate the client。否则,它只是专有的,性能不是很好。

至于您报告的损坏,我的猜测是它被截断了,因为您假设所有数据都是一次读取的,OS 既不能保证这种情况,也不可能给出大小你的数据。我建议修改您的协议以始终先发送一个 DWORD dwBytesSent,读取它,然后循环直到您从管道中读取了那么多字节。

如果我是你,因为这个数据听起来很大,我可能会把它写入一个文件。您可以使用 GetTempFileName() 来确定将它写在哪里。您可以使用您的应用程序独有的简单前缀来清理孤立文件,例如一天多了。然后是 read/write 数据的简单(通常至少同样快)API。

如果您想要更高的性能(和复杂性),您可以使用共享内存或套接字。套接字非常适合可移植性,但有一些额外的开销(环回适配器不是非常快)并且您需要关闭 Nagling:setsockopt(TCP_NODELAY)。共享内存并不比您用于管道的 API 更难,但性能更高,但您需要在其中发明一个协议(例如,第一个 DWORD 是状态,第二个 DWORD 是长度,其余是数据)。微软有一个不错的 tutorial article.