WinApi 中的 ReadFile 函数无法使用 CreatePipe 读取 CreateProcess

问题描述 投票:0回答:2

我正在编写一个应用程序来获取 C 上的地理位置。

为了获取坐标,我决定将 Popen 与 PowerShell 结合使用,这是我编写的代码:

BOOL getLatitude(wchar_t* latitudeBuffer)
{
    enableLocation();
    FILE* latitudePipe;
    if (latitudePipe = _wpopen(L"powershell -c \"Add-Type -AssemblyName System.Device; $GeoCoordinateWatcher = New-Object System.Device.Location.GeoCoordinateWatcher; $GeoCoordinateWatcher.Start(); Start-Sleep -Milliseconds 2500; $GeoCoordinateWatcher.Position.Location.Latitude 2>&1\"", L"r"))
    {
        wchar_t* latitudeResult = fgetws(latitudeBuffer, COORDINATES_BUFFER_SIZE, latitudePipe);
        _pclose(latitudePipe);
        if (latitudeResult)
        {
            if ('0' <= latitudeBuffer[0] && latitudeBuffer[0] <= '9')
            {
                latitudeBuffer[COORDINATES_SIZE] = '\0';
                wchar_t* latitudePoint = wcschr(latitudeBuffer, L'.');
                if (latitudePoint != NULL) *latitudePoint = '-';
                return TRUE;
            }
        }
    }
    wcsncpy_s(latitudeBuffer, COORDINATES_BUFFER_SIZE, L"NULL", COORDINATES_BUFFER_SIZE);
    return FALSE;
}

它可以工作,但是我遇到了一个问题,当使用 Popen 时,终端打开了几秒钟。我需要一切都在后台发生。

然后我决定使用

CreatePipe
CreateProcess
,然后使用
ReadFile
读取输出,但它并没有将结果放入缓冲区中,最终陷入了无限循环。这是代码:

#define COORDINATES_BUFFER_SIZE 4096

void getLat(wchar_t* latitudeBuffer)
{
    enableLocation();
    
    HANDLE latitudeWriteOutHandle = NULL;
    HANDLE latitudeReadOutHandle = NULL;

    SECURITY_ATTRIBUTES latitudeSecurityAttributes;
    latitudeSecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
    latitudeSecurityAttributes.bInheritHandle = TRUE;
    latitudeSecurityAttributes.lpSecurityDescriptor = NULL;

    CreatePipe(&latitudeReadOutHandle, &latitudeWriteOutHandle, &latitudeSecurityAttributes, 0);
    SetHandleInformation(&latitudeReadOutHandle, HANDLE_FLAG_INHERIT, 0);

    wchar_t cmd[] = L"powershell.exe -c \"Add-Type -AssemblyName System.Device; $GeoCoordinateWatcher = New-Object System.Device.Location.GeoCoordinateWatcher; $GeoCoordinateWatcher.Start(); Start-Sleep -Milliseconds 2500; $GeoCoordinateWatcher.Position.Location.Latitude 2>&1\"";
    STARTUPINFO latitudeStartUpInfo;
    PROCESS_INFORMATION latitudeProcessInformation;
    ZeroMemory(&latitudeStartUpInfo, sizeof(STARTUPINFO));
    ZeroMemory(&latitudeProcessInformation, sizeof(PROCESS_INFORMATION));
    latitudeStartUpInfo.cb = sizeof(STARTUPINFO);
    latitudeStartUpInfo.hStdOutput = latitudeWriteOutHandle;     // edited
    latitudeStartUpInfo.hStdError = latitudeWriteReadOutHandle;  // edited
    latitudeStartUpInfo.dwFlags |= STARTF_USESTDHANDLES;

    if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &latitudeStartUpInfo, &latitudeProcessInformation))
    {
        CloseHandle(latitudeProcessInformation.hThread);
        CloseHandle(latitudeProcessInformation.hProcess);
        CloseHandle(latitudeWriteOutHandle);
        
        DWORD dwRead;
        while (TRUE) if (ReadFile(latitudeReadOutHandle, latitudeBuffer, COORDINATES_BUFFER_SIZE, &dwRead, NULL)) break;
        CloseHandle(latitudeReadOutHandle);
        wprintf(L"%s\n", latitudeBuffer);
    }
}

我做错了什么?

附注要读取坐标,

ReadFile
的一次迭代对我来说就足够了。

c winapi readfile
2个回答
2
投票

您将管道的错误一端传给了孩子。

latitudeStartUpInfo.hStdOutput = latitudeReadOutHandle;
latitudeStartUpInfo.hStdError = latitudeReadOutHandle;

应该是

latitudeStartUpInfo.hStdOutput = latitudeWriteOutHandle;
latitudeStartUpInfo.hStdError = latitudeWriteOutHandle;

0
投票

我终于意识到我的代码出了什么问题。 首先,我要感谢 Ben Voigt 的正确答案。 所以这是开始为我工作的代码。

HANDLE latitudeWriteOutHandle = NULL;
HANDLE latitudeReadOutHandle = NULL;

SECURITY_ATTRIBUTES latitudeSecurityAttributes;
latitudeSecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
latitudeSecurityAttributes.bInheritHandle = TRUE;
latitudeSecurityAttributes.lpSecurityDescriptor = NULL;

if (CreatePipe(&latitudeReadOutHandle, &latitudeWriteOutHandle, &latitudeSecurityAttributes, 0))
{
    SetHandleInformation(latitudeReadOutHandle, HANDLE_FLAG_INHERIT, 0);
    //                   ^ - no link needed here
    wchar_t cmd[] = L"powershell.exe -c \"Add-Type -AssemblyName System.Device; $GeoCoordinateWatcher = New-Object System.Device.Location.GeoCoordinateWatcher; $GeoCoordinateWatcher.Start(); Start-Sleep -Milliseconds 2500; $GeoCoordinateWatcher.Position.Location.Latitude 2>&1\"";
    STARTUPINFO latitudeStartUpInfo;
    PROCESS_INFORMATION latitudeProcessInformation;
    ZeroMemory(&latitudeStartUpInfo, sizeof(STARTUPINFO));
    ZeroMemory(&latitudeProcessInformation, sizeof(PROCESS_INFORMATION));
    latitudeStartUpInfo.cb = sizeof(STARTUPINFO);
    latitudeStartUpInfo.hStdOutput = latitudeWriteOutHandle;
    latitudeStartUpInfo.hStdError = latitudeWriteOutHandle;
    latitudeStartUpInfo.dwFlags |= STARTF_USESTDHANDLES;

    if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &latitudeStartUpInfo, &latitudeProcessInformation))
    {
        CloseHandle(latitudeProcessInformation.hThread);
        CloseHandle(latitudeProcessInformation.hProcess);
        CloseHandle(latitudeWriteOutHandle);
        char latitudeBuffer[COORDINATES_BUFFER_SIZE];
        DWORD dwRead;
        if (ReadFile(latitudeReadOutHandle, latitudeBuffer, COORDINATES_BUFFER_SIZE, &dwRead, NULL)) {
            //       ^ - no link needed here too
            printf("%s\n", latitudeBuffer);
        }
    }
}

对于缓冲区,您需要使用

char
而不是
wchar_t

© www.soinside.com 2019 - 2024. All rights reserved.