(如果你愿意,可以按Z键,它会减轻情绪)
在我决定深入研究的这个新项目中,我离开了我的舒适区域,至少部分是这样。
整个项目将是一个可以加载到TeamSpeak 3中的DLL,并允许人们(通过一小组命令)控制Pianobar(Pandora命令行播放器)。
这里的答案足以引导我让Pianobar(一个控制台应用程序)https://stackoverflow.com/a/17502224/1733365启动并运行,我可以得到它的STDOUT并显示直到它显示歌曲当前时间的位置,以及它接受用户输入。整个过程锁定在那一点,我猜是因为ReadFromPipe()命令认为还有更多内容要读取,因为该行不断刷新。
我还试图将初始WriteToPipe(void)覆盖到WriteToPipe(char * cmd),以便允许我从外部线程调用它。 (收听TeamSpeak 3服务器聊天以获取特定命令的那个。)
现在我的代码是一个巨大的混乱,但我清理了一下,所以希望有人可以帮助我理解。
真的,这只是我决定在我失学时尝试的夏季项目,也是我创建DLL的第一次经历。
以下大部分代码均来自Creating a Child Process with Redirected Input and Output
#include "pianobar.h"
//#include <windows.h>
//#include <tchar.h>
//#include <stdio.h>
#include <strsafe.h>
//#include <stdlib.h>
//#include <sys/types.h>
//#include <string.h>
#define BUFSIZE 4096
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
SECURITY_ATTRIBUTES saAttr;
void CreateChildProcess(void);
void WriteToPipe(char *command);
void ReadFromPipe(void);
void ErrorExit(PTSTR);
int pianobar (struct TS3Functions ts3Functions) {
int iFound = 0;
printf("\n->Start of parent execution.\n");
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child process's STDOUT.
if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT("StdoutRd CreatePipe"));
// Ensure the read handle to the pipe for STDOUT is not inherited.
if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdout SetHandleInformation"));
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT("Stdin CreatePipe"));
// Ensure the write handle to the pipe for STDIN is not inherited.
if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT("Stdin SetHandleInformation"));
// Create the child process.
CreateChildProcess();
// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe's buffers, so it is not necessary to wait
// until the child process is running before writing data.
// This should cause a help menu to be displayed on the next ReadFromPipe()
// However, ReadFromPipe() doesn't show help commands
//WriteToPipe("?\r\n");
// Read from pipe that is the standard output for child process.
// Reading causes a lock.
//ReadFromPipe();
printf("\n->End of parent execution.\n");
printf("\n->Pianobar started.\n");
iFound = 1;
return iFound;
}
void CloseChildProcess() {
//CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
TerminateProcess(piProcInfo.hProcess,0);
}
void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT("c:\\pianobar\\pianobar.exe");
BOOL bSuccess = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
TEXT("c:\\pianobar\\"), // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit(TEXT("CreateProcess"));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.
// I think I need these while I'm running...
//CloseHandle(piProcInfo.hProcess);
//CloseHandle(piProcInfo.hThread);
}
}
void WriteToPipe(char *command)
// Read from a file and write its contents to the pipe for the child's STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
DWORD dw;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
LPTSTR lpTStr;
printf("\n-> In WriteToPipe()\n");
bSuccess = WriteFile(g_hChildStd_IN_Wr, command, sizeof(command), &dwWritten, NULL);
if(bSuccess) {
printf("bSuccess was TRUE\n->Sent: ");
printf(command);
} else {
printf("bSuccess was FALSE\n");
}
// Close the pipe handle so the child process stops reading.
// my 2nd call to WriteToPipe results in a "The handle is invalid" error
if ( ! CloseHandle(g_hChildStd_IN_Wr) ) {
dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpTStr,
0, NULL );
printf(lpTStr);
}
if(command == "q\r\n") {
printf("Quit received.\n");
// this should have killed the process if it was received correctly...
CloseChildProcess();
}
}
void ReadFromPipe(void)
// Read output from the child process's pipe for STDOUT
// and write to the parent process's pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
printf("\n-> In ReadFromPipe()\n");
for (;;)
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;
printf("In ReadFromPipe loop\n");
bSuccess = WriteFile(hParentStdOut, chBuf,
dwRead, &dwWritten, NULL);
if (! bSuccess ) {
// we never get to this, it just waits...
printf("Leaving loop\n");
break;
}
}
}
void ErrorExit(PTSTR lpszFunction)
// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0, NULL );
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);
}
答案 0 :(得分:1)
我真的不了解你的设置,但对此:
...以及它接受用户输入的地方。整个过程锁定了这一点 我猜是因为ReadFromPipe()命令认为存在 更多要阅读,因为该行不断刷新。
这很有可能。如果没有什么可以从管道读取,那么你的进程阻塞,即卡在ReadFile()调用中。如果您只想阅读有需要的内容,则需要异步I / O或通知机制。我真的不知道Windows,但似乎可以使用IO完成端口(IOCP)和异步回调函数。也许这些链接有帮助: