Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Передача HANDLE дочернему процессу


Автор: Igorious 8.12.2012, 16:42
Добрый день.
Помогите со следующей задачей.
В главном процессе создаются файлы и дочерний процесс. Дочернему процессу через командную строку передаётся хэндлы файлов. Дочерний процесс должен вывести некоторую информацию в эти файлы.

К сожалению, к меня не получается вывести в файл — выводит только в консоль. Помогите найти ошибку в коде.

Родительский процесс:
Код

// Sender.cpp

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

HANDLE OpenFile(const char* filePath)
{
    if (filePath == NULL || filePath[0] == 0)
        return INVALID_HANDLE_VALUE;

    SECURITY_ATTRIBUTES saAttr;
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = true;
    saAttr.lpSecurityDescriptor = NULL;

    HANDLE hFile = CreateFile(filePath, GENERIC_READ | GENERIC_WRITE, 0, &saAttr, OPEN_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL);    

    return hFile;
}

bool CreateProc(LPTSTR szCommandLine, BOOL bInherit, PROCESS_INFORMATION* pi)
{ 
    STARTUPINFO si;
    ZeroMemory(pi, sizeof(PROCESS_INFORMATION));
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO); 

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

    return CreateProcess(NULL, szCommandLine, &sa, NULL, bInherit, 0, NULL, NULL, &si,    pi);
}

void main(int argc, char* argv[])
{
    printf("Sender: Console\n");

    HANDLE hFile = OpenFile("D:\\[Programs]\\[Projects]\\[C++]\\HandleTest\\Debug\\out.txt");
    char buf[512];
    sprintf(buf, TEXT("D:\\[Programs]\\[Projects]\\[C++]\\HandleTest\\Debug\\Receiver.exe %i %i"), hFile, GetCurrentProcessId());

    PROCESS_INFORMATION pi;
    if(!CreateProc(buf, true, &pi))
        printf("FAILED!\n");

    if (!SetStdHandle(STD_OUTPUT_HANDLE, hFile))
        printf("Error SetFileHandle\n");
    printf("Sender: File\n");

    WaitForSingleObject(pi.hProcess, INFINITE);
    if (!CloseHandle(hFile))
        printf("Error CloseFileHandle\n");
}


Дочерний процесс:
Код

// Receiver.cpp
#include <windows.h>
#include <stdlib.h>
#include <iostream>

void main(int argc, char* argv[])
{
    if (argc == 3)
    {
        printf("Receiver: Console\n");
        HANDLE hFileSender = (HANDLE)atoi(argv[1]);
        DWORD idSender = (DWORD)atoi(argv[2]);
        HANDLE hSender = OpenProcess(PROCESS_ALL_ACCESS, TRUE, idSender);
        HANDLE hFile;
        if (!DuplicateHandle(hSender, hFileSender, GetCurrentProcess(), &hFile, NULL, FALSE, DUPLICATE_SAME_ACCESS))
            printf("Error Duplicate\n");

        HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
        if (!SetStdHandle(STD_OUTPUT_HANDLE, hFile))
            printf("Error SetFileHandle\n");

        printf("Receiver: File\n");

        if (!SetStdHandle(STD_OUTPUT_HANDLE, hStdOut))
            printf("Error SetStdOut\n");
        if (!CloseHandle(hFile))
            printf("Error CloseFileHandle\n");

        printf("Press any key...");
        std::cin.get();
    }
    else
        printf("Need args!");
}


upd. Добавил проверку результатов каждой функции.

Автор: artsb 8.12.2012, 19:26
У вас в дочернем процессе никак не обрабатываются результаты выполнения функций. Исправьте это и сообщите, где отваливается выполнение и с какой ошибкой.

Автор: tzirechnoy 8.12.2012, 19:35
Времена глобальных хэндлов прошли почти 20 лет назад, с приходом win32.

Для получения копии хэндла из другого процэсса используйте DuplicateHandle.

Добавлено через 1 минуту и 11 секунд
Да, я всё-таки дебил. Не заметил его при первом прочтении.

Автор: Igorious 8.12.2012, 20:29
Цитата(artsb @  8.12.2012,  19:26 Найти цитируемый пост)
У вас в дочернем процессе никак не обрабатываются результаты выполнения функций.


Исправил. 
Все функции возвращают TRUE. Но все четыре строчки по-прежнему выводятся лишь на консоль. Файл создаётся, но он пуст.

Автор: artsb 8.12.2012, 20:59
Похоже, через SetStdHandle ничего не получится. http://www.rsdn.ru/forum/winapi/52921.flat#52921 в самом конце темы.

Автор: Igorious 8.12.2012, 22:33
artsb, большое спасибо.

Это довольно неожиданно, что во время исполнения проги она не учитывает SetStdHandle.
С учётом приобретённых знаний переписал код следующим образом:

Код

...
#include <io.h>
#include <fcntl.h>
...
    printf("Receiver: Console\n");
    HANDLE hFileSender = (HANDLE)atoi(argv[1]);
    DWORD idSender = (DWORD)atoi(argv[2]);
    HANDLE hSender = OpenProcess(PROCESS_ALL_ACCESS, TRUE, idSender);
    HANDLE hFile;
    if (!DuplicateHandle(hSender, hFileSender, GetCurrentProcess(), &hFile, NULL, FALSE, DUPLICATE_SAME_ACCESS))
        printf("Error Duplicate\n");

    int h2StdOut = _dup(1); // STD_OUT;

    int h2File = _open_osfhandle((LONG)hFile, _O_TEXT);
    _dup2(h2File, 1); // stdout := file;

    printf("Receiver: File\n");
    printf("Hello, World!\n");
    fflush(stdout);

    _dup2(h2StdOut, 1); // stdout := console;

    _close(h2File); // CloseHandle не нужен;
        
    printf("Press any key...");
    std::cin.get();

Автор: feodorv 9.12.2012, 23:02
Цитата(Igorious @  8.12.2012,  23:33 Найти цитируемый пост)
Это довольно неожиданно, что во время исполнения проги она не учитывает SetStdHandle.

Почему не учитывает, учитывает. Вот здесь:
Код
WriteFile( GetStdHandle(STD_OUTPUT_HANDLE), buf, lstrlen(buf), &dummy, NULL);

Но Вы же хотите, чтобы работал printf в соответствии с новым STD_OUTPUT_HANDLE. printf является частью стандартной библиотеки, которая для Windows есть надстройка над WinAPI. Она (если программа вообще ею пользуется) инициализируется при старте приложения, при этом stdout связывается с тем STD_OUTPUT_HANDLE, который был актуальным на момент инициализации. Вызовы SetStdHandle (это чистый WinAPI) никак не влияют на stdin, stdout и stderr (а это уже из стандартной библиотеки). Чтобы синхронизировать STD_OUTPUT_HANDLE из WinAPI и stdout из стд.C.либ. нужно произвести дополнительные действия: http://www.tdoc.ru/c/cpp-sources/ui-windows/kak-v-gui-prilozhenii-otkryt-konsol-i-naznatchit-stdinstdoutstderr.html smile

Добавлено через 5 минут и 56 секунд
Или вот без переназначения консоли: http://www.halcyon.com/~ast/dload/guicon.htm.

Автор: HANDLE 6.1.2015, 05:51
Всё гораздо проще. При создании процесса нужно проинициализировать структуру STARTUPINFO;

STARTUPINFO si;
si.hStdInput = сюда положить хендлы созданных файлов;
si.hStdOutput = сюда положить хендлы созданных файлов;
si.hStdError = сюда положить хендлы созданных файлов;
И установить флажок
si.dwFlags = STARTF_USESTDHANDLES;

И будет вам счастье

Автор: feodorv 12.1.2015, 10:18
Цитата(HANDLE @  6.1.2015,  05:51 Найти цитируемый пост)
Всё гораздо проще.

Вопрос стоял о том, чтобы менять stdout 
Цитата(Igorious @  8.12.2012,  22:33 Найти цитируемый пост)
во время исполнения проги
.
И, может быть, неоднократно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)