我正在尝试从以SYSTEM身份运行的Windows服务中启动子可执行文件以以登录用户身份(用golang编写)运行。我正在使用CreateProcessAsUser
方法来启动该过程。我观察到子进程实际上已经启动,但是我无法读取它的标准输出。我已经按照https://docs.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output中所述的步骤重定向了孩子的标准输入和标准输出。
我也已经将管道的权限设置为Everyone SID,但是仍然遇到相同的问题。
父流程的代码如下(在根据注释中的建议进行进一步编辑之后)
#include "test.h"
#include <windows.h>
#include <winbase.h>
#include <userenv.h>
#include <tchar.h>
#include <wtsapi32.h>
#include <strsafe.h>
#include <processthreadsapi.h>
#define BUFSIZE 4096
#define PROC_THREAD_ATTRIBUTE_HANDLE_LIST ( 2 | 0x00020000)
typedef struct _STARTUPINFOEXA {
STARTUPINFOA StartupInfo;
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;
void ReadAndHandleOutput(HANDLE hPipeRead, FILE* fp)
{
CHAR lpBuffer[BUFSIZE];
DWORD nBytesRead;
DWORD bytesAvailable;
DWORD bytesLeftInMsg;
do
{
if (!PeekNamedPipe(hPipeRead, lpBuffer, sizeof(lpBuffer), &nBytesRead, &bytesAvailable, &bytesLeftInMsg))
{
if (GetLastError() == ERROR_BROKEN_PIPE) {
fprintf(fp, "Broken Pipe \n");
fflush(fp);
break; // pipe done - normal exit path.
}
else {
fprintf(fp, "ReadFileError %d\n", GetLastError()); // Something bad happened.
fflush(fp);
}
}
} while(bytesLeftInMsg > 0);
fprintf(fp, "GOT OUTPUT, %s -- %d -- %d -- %d\n", lpBuffer, nBytesRead, bytesAvailable, bytesLeftInMsg);
fflush(fp);
}
bool LaunchProcess(char *process_path)
{
FILE *fp = fopen("C:\\test.log", "a");
DWORD SessionId = WTSGetActiveConsoleSessionId();
if (SessionId == -1) { // no-one logged in
fprintf(fp, "Session ID is -1\n");
fclose(fp);
return false;
}
HANDLE hToken;
BOOL ok = WTSQueryUserToken(SessionId, &hToken);
if (!ok) {
fprintf(fp, "Unable to get the token for session id %d\n", SessionId);
fclose(fp);
return false;
}
void *environment = NULL;
ok = CreateEnvironmentBlock(&environment, hToken, TRUE);
if (!ok)
{
fprintf(fp, "Unable to create environment with session ID %d\n", SessionId);
CloseHandle(hToken);
fclose(fp);
return false;
}
HANDLE hServerPipe = CreateNamedPipe("\\\\.\\Pipe\\test-pipe.em", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
HANDLE hClientPipe;
if (hServerPipe != INVALID_HANDLE_VALUE)
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
hClientPipe = CreateFile("\\\\.\\Pipe\\test-pipe.em", FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
if (hClientPipe == INVALID_HANDLE_VALUE)
{
fprintf(fp, "Error creating client handle %d", GetLastError());
CloseHandle(hServerPipe);
return false;
}
} else {
fprintf(fp, "Error creating server handle %d", GetLastError());
return false;
}
STARTUPINFOEXA si = { { sizeof(si) } };
PROCESS_INFORMATION pi;
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
si.StartupInfo.hStdInput = si.StartupInfo.hStdOutput = si.StartupInfo.hStdError = hClientPipe;
DWORD dwCreationFlags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
BOOL fInit = FALSE;
SIZE_T Size;
if (!InitializeProcThreadAttributeList(0, 1, 0, &Size) && GetLastError() == ERROR_INSUFFICIENT_BUFFER &&
InitializeProcThreadAttributeList(si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(Size), 1, 0, &Size))
{
fInit = TRUE;
if (UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &si.StartupInfo.hStdError, sizeof(HANDLE), 0, 0)) {
dwCreationFlags |= EXTENDED_STARTUPINFO_PRESENT;
}
}
fprintf(fp, "calling the process %s\n", process_path);
fflush(fp);
ok = CreateProcessAsUser(hToken, NULL, process_path, NULL, NULL, TRUE, dwCreationFlags, environment, NULL, &si.StartupInfo, &pi);
fprintf(fp, "Running process %s and %d with PID %d\n", process_path, ok, pi.dwProcessId);
fflush(fp);
if (!ok) {
wchar_t buf[BUFSIZE];
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
buf, (sizeof(buf) / sizeof(wchar_t)), NULL);
fprintf(fp, "Failed to create processes as user - %d, %d, %S, %s\n", SessionId, GetLastError(), buf, process_path);
fclose(fp);
return false;
} else {
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
// Close pipe handles (do not continue to modify the parent).
// You need to make sure that no handles to the write end of the
// output pipe are maintained in this process or else the pipe will
// not close when the child process exits and the ReadFile will hang.
if (!CloseHandle(si.StartupInfo.hStdError)) {
fprintf(fp, "CloseHandle - outputwrite Error %d\n", GetLastError());
fclose(fp);
return false;
}
ReadAndHandleOutput(hServerPipe, fp);
CloseHandle(hServerPipe);
fclose(fp);
return true;
Cleanup:
fprintf(fp, "Something went wrong");
fclose(fp);
return false;
}
子进程的代码如下
package main
import (
"fmt"
"os"
"time"
)
func main() {
os.Stdout.Write([]byte("Hello from the GO side..."))
f, err := os.OpenFile("C:\\log.text", os.O_APPEND|os.O_WRONLY, 0600)
if err != nil {
panic(err)
}
defer f.Close()
var output = fmt.Sprintf("I'm running as %d launched by %d", os.Getpid(), os.Getppid())
if _, err = f.WriteString(output); err != nil {
panic(err)
}
os.Stdout.Close()
}
我能够在log.text文件中看到当前的PID和父级的PID(服务),但是我在父级始终遇到管道中断错误。
任何指针都值得赞赏