Отправить команду и получить ответ от Windows УМК молча подскажу - последующие


Этот пост является обновленной версией первоначального поста, с кодом приведенные ниже примеры, отражающие изменения в ответ на очень подробный входов от пользователя @pacmaninbw. Обновленный приведенные ниже примеры также включают добавление функции, которая предназначена для размещения команд, которые не возвращают ответ. Блок комментарий описывает, почему эта функция также необходима, и ее использование.

Я заинтересован в получении обратной связи с акцентом на те же вещи, как и раньше, так будет повторять предисловии к предыдущему посту ниже.

Необходимость:

Мне нужен способ, чтобы программно отправить команды в Windows 7 Командной строки, и вернуть ответ не видя в консоли всплывающее окно в несколько приложений.

Дизайн:

Окружающей среды в дополнение к операционной системе Windows 7-это ANSI с (С99) компилятор из национальных инструментов, и драйверов Windows Комплект для Windows 8.1. Среди целей проекта было представить очень небольшое API, включая документированный и простой использования инструкции. В результате двух экспортируемые функции. Описания для каждой приводятся в их соответствующих блоков комментариев. В своей форме, это предназначается, чтобы быть построен в виде библиотеки DLL. Только заголовочные файлы, используемые в этой библиотекеwindows.h и stdlib.h.

Для рассмотрения:

Написал код, и я проверил это, но я новичок в используя pipes для stdin и stdoutкак с помощью Windows методы CreateProcess(...). Кроме того, поскольку требования размера буфера ответ не может быть известен во время компиляции, код включает в себя возможность расти буфера по мере необходимости в течение время выполнения. Например, я использовал этот код для рекурсивного чтения каталоги, используя dir /s из всех мест, за исключением c:\ каталог с помощью следующей команды:

cd c:\dev && dir /s  // approximately 1.8Mbyte buffer is returned on my system  

Особенно я бы высоко ценим обратную связь сосредоточена на следующем:

  • Создание труб и использование
  • CreateProcess использование
  • Способ для динамично растущих буфер ответа (очень интересуют отзывы об этом)
  • Обработка встроенных null байт содержания ReadFile функция. (Кредит @chux, это недавно обнаружен дефицит)

Пример использования:

#include <stdio.h>         // printf()
#include <stdlib.h>        // NULL
#include "cmd_rsp.h"

#define BUF_SIZE    100

int main(void)
{
    char *buf = NULL;

    /// test cmd_rsp
    buf = calloc(BUF_SIZE, 1);
    if(!buf)return 0;
    if (!cmd_rsp("dir /s", &buf, BUF_SIZE))
    {
        printf("%s", buf);
    }
    else
    {
        printf("failed to send command.\n");
    }
    free(buf);

    /// test cmd_no_rsp
    buf = calloc(BUF_SIZE, 1);
    if(!buf)return 0;
    if (!cmd_no_rsp("dir /s", &buf, BUF_SIZE))
    {
        printf("success.\n"); // function provides no response
    }
    else
    {
        printf("failed to send command.\n");
    }
    free(buf);

    return 0;
}

cmd_rsp.ч

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Prototype:      int int __declspec(dllexport) cmd_rsp(char *command, char **chunk, size_t size)  
//
//  Description:    Executes any command that can be executed in a Windows cmd prompt and returns
//                  the response via auto-resizing buffer. 
///                 Note: this function will hang for executables or processes that run and exit 
///                 without ever writing to stdout.  
///                 The hang occurs during the call to the read() function.
//
//  Inputs:         const char *command - string containing complete command to be sent
//                  char **chunk - initialized pointer to char array to return results
//                  size_t size - Initial memory size in bytes char **chunk was initialized to.
//
//  Return:         0 for success
//                 -1 for failure
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int int __declspec(dllexport) cmd_rsp(const char *command, char **chunk, unsigned int chunk_size);

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Prototype:      int int __declspec(dllexport) cmd_no_rsp(char *command)  
//
//  Description:    Variation of cmd_rsp that does not wait for a response.  This is useful for
//                  executables or processes that run and exit without ever sending a response to stdout,
//                  causing cmd_rsp to hang during the call to the read() function.
//
//  Inputs:         const char *command - string containing complete command to be sent
//
//  Return:         0 for success
//                 -1 for failure
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int int __declspec(dllexport) cmd_no_rsp(const char *command);
#endif

cmd_rsp.с

#include <windows.h>
#include <stdlib.h> // calloc, realloc & free
#include "cmd_rsp.h"

#define BUFSIZE 1000 

typedef struct  {
    /* child process's STDIN is the user input or data entered into the child process - READ */
    void * in_pipe_read;
    /* child process's STDIN is the user input or data entered into the child process - WRITE */
    void * in_pipe_write;
    /* child process's STDOUT is the program output or data that child process returns - READ */
    void * out_pipe_read;
    /* child process's STDOUT is the program output or data that child process returns - WRITE */
    void * out_pipe_write;
}IO_PIPES;

// Private prototypes
static int CreateChildProcess(const char *cmd, IO_PIPES *io);
static int CreateChildProcessNoStdOut(const char *cmd, IO_PIPES *io);
static int ReadFromPipe(char **rsp, unsigned int size, IO_PIPES *io);
static char * ReSizeBuffer(char **str, unsigned int size);
static void SetupSecurityAttributes(SECURITY_ATTRIBUTES *saAttr);
static void SetupStartUpInfo(STARTUPINFO *siStartInfo, IO_PIPES *io);
static int SetupChildIoPipes(IO_PIPES *io, SECURITY_ATTRIBUTES *saAttr);

int __stdcall DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            /* Respond to DLL loading by initializing the RTE */
            if (InitCVIRTE (hinstDLL, 0, 0) == 0) return 0;

            break;
        case DLL_PROCESS_DETACH:

            /* Respond to DLL unloading by closing the RTE for its use */
            if (!CVIRTEHasBeenDetached ()) CloseCVIRTE ();

            break;
    }
    /* Return 1 to indicate successful initialization */
    return 1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Prototype:      int int __declspec(dllexport) cmd_rsp(char *command, char **chunk, size_t chunk_size)  
//
//  Description:    Executes any command that can be executed in a Windows cmd prompt and returns
//                  the response via auto-resizing buffer.
//
//  Inputs:         const char *command - string containing complete command to be sent
//                  char **chunk - initialized pointer to char array to return results
//                  size_t chunk_size - Initial memory size in bytes of char **chunk.
//
//  Return:         0 for success
//                 -1 for failure
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int int __declspec(dllexport) cmd_rsp(const char *command, char **chunk, unsigned int chunk_size)
{
    SECURITY_ATTRIBUTES saAttr;

    /// All commands that enter here must contain (and start with) the substring: "cmd.exe /c 
    /// /////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// char cmd[] = ("cmd.exe /c \"dir /s\"");  /// KEEP this comment until format used for things like
                                                 /// directory command (i.e. two parts of syntax) is captured
    /// /////////////////////////////////////////////////////////////////////////////////////////////////////////

    const char rqdStr[] = {"cmd.exe /c "};
    int len = (int)strlen(command);
    char *Command = NULL;
    int status = 0;

    Command = calloc(len + sizeof(rqdStr), 1);
    if(!Command) return -1;
    strcat(Command, rqdStr);
    strcat(Command, command);

    SetupSecurityAttributes(&saAttr);

    IO_PIPES io;

    if(SetupChildIoPipes(&io, &saAttr) < 0) return -1;

    //eg: CreateChildProcess("adb");
    if(CreateChildProcess(Command, &io) == 0)
    {
        // Read from pipe that is the standard output for child process. 
        ReadFromPipe(chunk, chunk_size, &io);
        status = 0;
    }
    else
    {
        status = -1;
    }
    free(Command);

    return status;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//  Prototype:      int int __declspec(dllexport) cmd_no_rsp(char *command)  
//
//  Description:    Variation of cmd_rsp that does not wait for a response.  This is useful for
//                  executables or processes that run and exit without ever sending a response to stdout,
//                  causing cmd_rsp to hang during the call to the read() function.
//
//  Inputs:         const char *command - string containing complete command to be sent
//
//  Return:         0 for success
//                 -1 for failure
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int int __declspec(dllexport) cmd_no_rsp(const char *command)
{
    /// All commands that enter here must contain (and start with) the substring: "cmd.exe /c 
    /// /////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// char cmd[] = ("cmd.exe /c \"dir /s\"");  /// KEEP this comment until format used for things like
                                                 /// directory command (i.e. two parts of syntax) is captured
    /// /////////////////////////////////////////////////////////////////////////////////////////////////////////

    SECURITY_ATTRIBUTES saAttr;
    const char rqdStr[] = {"cmd.exe /c "};
    int len = (int)strlen(command);
    char *Command = NULL;
    int status = 0;

    Command = calloc(len + sizeof(rqdStr), 1);
    if(!Command) return -1;
    strcat(Command, rqdStr);
    strcat(Command, command);

    SetupSecurityAttributes(&saAttr);

    IO_PIPES io;

    if(SetupChildIoPipes(&io, &saAttr) < 0) return -1;

    status = CreateChildProcessNoStdOut(Command, &io);

    free(Command);

    return status;
}

static int SetupChildIoPipes(IO_PIPES *io, SECURITY_ATTRIBUTES *saAttr)
{
    //child process's STDOUT is the program output or data that child process returns
    // Create a pipe for the child process's STDOUT. 
    if (!CreatePipe(&io->out_pipe_read, &io->out_pipe_write, saAttr, 0))
    {
        return -1;
    }

    // Ensure the read handle to the pipe for STDOUT is not inherited.
    if (!SetHandleInformation(io->out_pipe_read, HANDLE_FLAG_INHERIT, 0))
    {
        return -1;
    }

    //child process's STDIN is the user input or data entered into the child process
    // Create a pipe for the child process's STDIN. 
    if (!CreatePipe(&io->in_pipe_read, &io->in_pipe_write, saAttr, 0))
    {
        return -1;
    } 

    // Ensure the write handle to the pipe for STDIN is not inherited. 
    if (!SetHandleInformation(io->in_pipe_write, HANDLE_FLAG_INHERIT, 0))
    {
        return -1;
    }
    return 0;
}


// Create a child process that uses the previously created pipes for STDIN and STDOUT.
static int CreateChildProcess(const char *cmd, IO_PIPES *io)
{
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    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));
    SetupStartUpInfo(&siStartInfo, io);

    // Create the child process. 
    bSuccess = CreateProcess(NULL,
        cmd,                // command line 
        NULL,               // process security attributes 
        NULL,               // primary thread security attributes 
        TRUE,               // handles are inherited 
        CREATE_NO_WINDOW,   // creation flags            
        //CREATE_NEW_CONSOLE,   // creation flags            
        NULL,               // use parent's environment 
        NULL,               // use parent's current directory 
        &siStartInfo,       // STARTUPINFO pointer 
        &piProcInfo);       // receives PROCESS_INFORMATION 

    // If an error occurs, exit the application. 
    if (!bSuccess)
    {
        return -1;
    }
    else
    {
        // Close handles to the child process and its primary thread.
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
        CloseHandle(io->out_pipe_write);
    }
    return 0;
}


// Create a child process that uses the previously created pipes for STDIN and STDOUT.
static int CreateChildProcessNoStdOut(const char *cmd, IO_PIPES *io)
{
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    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));
    SetupStartUpInfo(&siStartInfo, io);

    // Create the child process. 
    bSuccess = CreateProcess(NULL,
        cmd,                // command line 
        NULL,               // process security attributes 
        NULL,               // primary thread security attributes 
        TRUE,               // handles are inherited 
        CREATE_NO_WINDOW,   // creation flags            
        //CREATE_NEW_CONSOLE,   // creation flags            
        NULL,               // use parent's environment 
        NULL,               // use parent's current directory 
        &siStartInfo,       // STARTUPINFO pointer 
        &piProcInfo);       // receives PROCESS_INFORMATION 

    // If an error occurs, exit the application. 
    if (!bSuccess)
    {
        return -1;
    }
    else
    {
        // Close handles to the child process and its primary thread.
        CloseHandle(piProcInfo.hProcess);
        CloseHandle(piProcInfo.hThread);
        CloseHandle(io->out_pipe_write);
    }
    return 0;
}

// Read output from the child process's pipe for STDOUT
// Grow the buffer as needed
// Stop when there is no more data. 
static int ReadFromPipe(char **rsp, unsigned int size, IO_PIPES *io)
{
    COMMTIMEOUTS ct;
    int size_recv = 0;
    unsigned int total_size = 0;
    unsigned long dwRead;
    BOOL bSuccess = TRUE;
    char *accum;
    char *tmp1 = NULL;
    char *tmp2 = NULL;


    //Set timeouts for stream
    ct.ReadIntervalTimeout = 0;
    ct.ReadTotalTimeoutMultiplier = 0;
    ct.ReadTotalTimeoutConstant = 10;
    ct.WriteTotalTimeoutConstant = 0;
    ct.WriteTotalTimeoutMultiplier = 0;
    SetCommTimeouts(io->out_pipe_read, &ct);


    //This accumulates each read into one buffer, 
    //and copies back into rsp before leaving
    accum = (char *)calloc(1, sizeof(char)); //grow buf as needed
    if(!accum) return -1;
    memset(*rsp, 0, size);

    do
    {
        //Reads stream from child stdout 
        bSuccess = ReadFile(io->out_pipe_read, *rsp, size-1, &dwRead, NULL);
        if (!bSuccess || dwRead == 0) 
        {
            free(accum);
            return 0;//successful - reading is done
        }

        (*rsp)[dwRead] = 0;
        size_recv = (int)strlen(*rsp);


        if(size_recv == 0)
        {
            //should not get here for streaming
            (*rsp)[total_size]=0;
            return total_size;
        }
        else
        {
            //New Chunk:
            (*rsp)[size_recv]=0;
            //capture increased byte count
            total_size += size_recv+1;
            //increase size of accumulator
            tmp1 = ReSizeBuffer(&accum, total_size);
            if(!tmp1)
            {
                free(accum);
                strcpy(*rsp, "");
                return -1;
            }
            accum = tmp1;
            strcat(accum, *rsp);
            if(total_size > (size - 1))
            {   //need to grow buffer
                tmp2 = ReSizeBuffer(&(*rsp), total_size+1);
                if(!tmp2)
                {
                    free(*rsp);
                    return -1;
                }
                *rsp = tmp2;
            }
            strcpy(*rsp, accum);//refresh rsp
        }


    }while(1);
}

// return '*str' after number of bytes realloc'ed to 'size'
static char * ReSizeBuffer(char **str, unsigned int size)
{
    char *tmp=NULL;

    if(!(*str)) return NULL;

    if(size == 0)
    {
        free(*str);
        return NULL;
    }

    tmp = (char *)realloc((char *)(*str), size);
    if(!tmp)
    {
        free(*str);
        return NULL;
    }
    *str = tmp;

    return *str;
}

static void SetupSecurityAttributes(SECURITY_ATTRIBUTES *saAttr)
{
    // Set the bInheritHandle flag so pipe handles are inherited.
    saAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr->bInheritHandle = TRUE;
    saAttr->lpSecurityDescriptor = NULL;
}

static void SetupStartUpInfo(STARTUPINFO *siStartInfo, IO_PIPES *io)
{
    siStartInfo->cb = sizeof(STARTUPINFO);
    siStartInfo->hStdError = io->out_pipe_write;
    siStartInfo->hStdOutput = io->out_pipe_write;
    siStartInfo->hStdInput = io->in_pipe_read;
    siStartInfo->dwFlags |= STARTF_USESTDHANDLES;
}


187
1
задан 1 марта 2018 в 06:03 Источник Поделиться
Комментарии
3 ответа

Управление вложенными нулевые байты

ReadFile(, lpBuffer,,,) может читать символы null в lpBuffer. Если это произойдет, значительная часть кода использование str...() будет страдать. Код должен следить за данными, как прочитал "байт" с длиной, а не как строку. Я бы рекомендовал формировать структуру с членами unsigned char data[BUF_SIZE] и DWORD sz или size_t sz. Это существенно влияет на код. Эффективно заменить str...() звонки с mem...() те.

Минор: с помощью "байт" буфера, а не строку, буфер могли бы начать с NULL.

// char *accum;
// accum = (char *)calloc(1, sizeof(char));
char *accum = NULL;

Создание труб и использование

Хотя есть много хорошего обработку ошибок, cmd_rsp() не удается проверить возвращаемое значение ReadFromPipe(chunk, chunk_size, &io);.

// ReadFromPipe(chunk, chunk_size, &io);
if (ReadFromPipe(chunk, chunk_size, &io) == -1) TBD_Code();

Минор: с помощью sizeof(char) а не sizeof *accum обязывает рецензента и сопровождающего для проверки типа accum. В C код можно упростить:

// accum = (char *)calloc(1, sizeof(char));
accum = calloc(1, sizeof *accum);

Незначительные: непонятно, почему код не использует unsigned для индексации массива, а не идиоматические size_t. Позже код спокойно возвращает это как int. Я ожидал более меняя ухода за знак-Несс. Еще раз использовать int.

// Hmmm
unsigned int total_size = 0;
int size_recv = 0;

Использование является CreateProcess

Утечка Памяти:

if(SetupChildIoPipes(&io, &saAttr) < 0) {
free(Command); // add
return -1;
}

Незначительные: не требуется int литой значение в size_t диапазон.

// int len = (int)strlen(command);
// Command = calloc(len + sizeof(rqdStr), 1);
Command = calloc(strlen(command) + sizeof(rqdStr), 1);

Способ для динамично растущих буфера ответа

Хорошая и правильная функция для ReSizeBuffer( ,size == 0);

Ошибка: когда realloc() терпит неудачу, ReSizeBuffer() и вызывающий код как освободить память же. Ре-дизайн идея: ReSizeBuffer() бесплатно данные и возвращает простой провал/успех флаг для вызывающего кода для тестирования. Для вызывающего кода для проверки NULL-Несс является проблемой, ReSizeBuffer( ,size == 0) возвращаясь NULL Это О. К.

Непонятно теста: if(!(*str)) return NULL;. Я бы не ждал запрет изменения размера буфера, который изначально указал на NULL.

if(!(*str)) return NULL; // why?
if(!str) return NULL; // Was this wanted?`

Гипс не нужны для компиляции. Код также предназначается для C++?

// tmp = (char *)realloc((char *)(*str), size);
tmp = realloc(*str, size);

Для меня, я хотел бы использовать форму ниже, и позвольте ей выполнить все крайние случаи нулей, переполнение, успех распределение, бесплатные-ки, обновления. Будьте готовы к большой буфер должен.

// return 0 on success
int ReSizeBuffer(void **buf, size_t *current_size, int increment);
// or
int ReSizeBuffer(void **buf, size_t *current_size, size_t new_size);

Лакомые кусочки

Следует избегать ! когда вещи работают. Это небольшой стиль вопроса - я найду ! или != более соответствует провал, чем успех.

// if (!cmd_no_rsp("dir /s", &buf, BUF_SIZE)) {
// printf("success.\n");
if (cmd_no_rsp("dir /s", &buf, BUF_SIZE) == 0) {
printf("success.\n");

С изменением для обработки передаются данные как строку, изменить имена переменных от str...

1
ответ дан 2 марта 2018 в 06:03 Источник Поделиться


Обработка незначащие байты содержания ReadFile
функция

вам не нужно обрабатывать все это. ReadFile возвращает количество прочитанных байт. и вам нужно использовать его. нет никакой разницы, какой байт Вы читаете.

(*rsp)[dwRead] = 0;
size_recv = (int)strlen(*rsp);

этот код неправильный и бессмысленный.

правильный (по смыслу)

size_recv = dwRead

в dwRead это точно, сколько байт вы прочли (если ReadFile не плохо)


SetCommTimeouts

опять же - бессмысленный код. этот API просто отправить IOCTL_SERIAL_SET_TIMEOUTS контрольный код для устройства, на котором открыт файл. обычно только драйвер последовательного порта контроллера справиться с этой ioctl. в npfs.sys, которые реализуют трубы - не понимают и не поддерживают этой функции ioctl. водитель вернется к вам STATUS_INVALID_DEVICE_REQUEST ( сопоставляется ERROR_INVALID_FUNCTION ошибки Win32), но вы не проверить на ошибки здесь.



Способ для динамично растущих буфер ответа (очень интересует
обратная связь на этом)

код ужасает. совершенно неправы во всем. начнем с декларации интерфейс:

_Inout_ char **chunk, _In_ unsigned int chunk_size

здесь chunk - в/из параметра, но chunk_size только - если вы перераспределить пользователей буфера - вы должны вернуть новый размер буфера для пользователей. может быть следующая подпись:

_Inout_ char **chunk, _Inout_ unsigned int* chunk_size

затем перераспределить абонента буфер - это очень плохая идея. для перераспределения вам нужно точно знать, как абонент выделить буфер. вы должны добавить в контракте интерфейс - как вызывающая сторона должна выделить исходного буфера и скачать Final. какие конкретные повседневного использования. скажем, например, вызывающая сторона должна выделить первоначальный буфер с LocalAlloc(LMEM_FIXED, *) и бесплатно LocalFree. но никто не делал этого. обычно используют 2 способами:


  1. API используйте буфер пользователя, как это

    _In_ char *chunk, _In_ SIZE_T chunk_size, _Out_ SIZE_T* ReturnSize

    если буфер не достаточно большой ошибки -
    ERROR_INSUFFICIENT_BUFFER (в случае отсутствия достоверных данных в буфере на всех)
    или ERROR_BUFFER_OVERFLOW - в случае, если существуют веские данные в
    буфер, но все равно нужен большой буфер. в любом случае нужно здесь
    дополнительные параметры ReturnSize


  2. выделить буфер себя

    _Out_ char** chunk, _Out_ SIZE_T* chunk_size

    в этом случае chunk и chunk_size только параметры. а вы
    нужно сказать абоненту - какие API нужно использовать для бесплатной возвращенного буфера.


потом искать ReadFromPipe (полный кошмар)

bSuccess = ReadFile(io->out_pipe_read, *rsp, size-1, &dwRead, NULL);

вы все время читали size-1 в буфера начать (*rsp) и никогда не меняются size. так что смысл попробовать перераспределить буфер, если ты хоть как старайся читать только size-1 байт. тогда вы все время читаю в буфер начать - так новый читать перезаписи предыдущих данных. вы используете неправильный и бессмысленный strcpy, strcat, strlen вместо memcpy. для чего вы используете free перед realloc ? при попытке освободить буфер после каждого чтения ? даже если еще существует свободное пространство в текущем буфере ? на какой размер вам попробовать realloc буфера ? на 1 байт ?? и каждый раз все равно попробуйте прочитать постоянной size-1 С чего начать ??

для динамического буфера буфера обычно используются 2 стратегии:

выделить кусок памяти (обычно 0x1000..0x10000 размер). читал этот кусок, пока существует свободное пространство в нем (конечно не все время на начало фрагмента, но на начало свободное пространство внутри блока). когда нет больше свободного пространства в текущий чанк - выделить новый кусок (не перераспределить существующие !!) и так далее. но не копировать ничего. когда чтение будет завершено - как выделить новый буфер и один раз скопировать содержимое чанков в этот буфер. например.

struct DATA_BUFFER 
{
struct DATA_CHUNK
{
DATA_CHUNK* _next;
ULONG _cbData;
BYTE _buf[0x10000];

DATA_CHUNK() : _next(0), _cbData(0) {}
};

DATA_CHUNK* _first, *_last;

DATA_BUFFER() : _first(0), _last(0) {}

~DATA_BUFFER()
{
if (DATA_CHUNK * chunk = _first)
{
do
{
PVOID pv = chunk;
chunk = chunk->_next;
delete pv;
} while (chunk);
}
}

PVOID AllocBuffer(PULONG pcb)
{
DATA_CHUNK* chunk = _last;

if (!chunk || chunk->_cbData == sizeof(chunk->_buf))
{
if (chunk = new DATA_CHUNK)
{
if (_first)
{
_last->_next = chunk;
_last = chunk;
}
else
{
_first = chunk;
_last = chunk;
}
}
else
{
return 0;
}
}

*pcb = sizeof(chunk->_buf) - chunk->_cbData;
return chunk->_buf + chunk->_cbData;
}

void AddData(ULONG cb)
{
_last->_cbData += cb;
}

PVOID GatherBuffer(PULONG pcb)
{
*pcb = 0;

if (DATA_CHUNK* chunk = _first)
{
ULONG cb = 0;
do
{
cb += chunk->_cbData;
} while (chunk = chunk->_next);

if (cb)
{

*pcb = cb;

if (PBYTE pb = new BYTE[cb])
{
PVOID pv = pb;

chunk = _first;

do
{
memcpy(pb, chunk->_buf, chunk->_cbData);
pb += chunk->_cbData;
PVOID p = chunk;
chunk = chunk->_next;
delete p;
} while (chunk);

_first = 0, _last = 0;

return pv;
}
}
}

return 0;
}
};

и возможности использования

        DATA_BUFFER db;
ULONG cb;
PVOID pv;

while (pv = db.AllocBuffer(&cb))
{
if (ReadFile(hFile, pv, cb, &cb, 0) && cb)
{
db.AddData(cb);
}
else break;
}

if (pv = db.GatherBuffer(&cb))
{
// use pv..
delete [] pv;
}

другой способ - использовать VirtualAlloc для буфера. Windows позволит резервировать пространство памяти. мы можем забронировать десятков мегабайт как минимум. эта операция не выделять память, а просто пометить область памяти резервируется. тогда мы уже можем совершать память для региона. когда, после чтения, будет недостаточно выделенной памяти - мы совершаем все больше и так далее. преимущество - у нас с начала будет непрерывной памяти - мы никогда не будем нуждаться копировать/перемещать/перераспределения памяти с этого пути. это решение лучше, когда мы предполагаем достаточно большой окончательные данные. пример:

class DynamicBuffer
{
PBYTE _BaseAddress;
SIZE_T _dwReserve, _dwSize, _dwCommit;

static SIZE_T RoundSize(SIZE_T size)
{
static SIZE_T s_dwAllocationGranularity;

if (!s_dwAllocationGranularity)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
s_dwAllocationGranularity = si.dwAllocationGranularity - 1;
}

return (size + s_dwAllocationGranularity) & ~s_dwAllocationGranularity;
}

public:

DynamicBuffer()
{
_BaseAddress = 0, _dwReserve = 0, _dwSize = 0, _dwCommit = 0;
}

~DynamicBuffer()
{
Reset();
}

ULONG Create(SIZE_T dwSize)
{
if (_BaseAddress = (PBYTE)VirtualAlloc(0, dwSize = RoundSize(dwSize), MEM_RESERVE, PAGE_READWRITE))
{
_dwReserve = dwSize;
return NOERROR;
}

return GetLastError();
}

ULONG AllocBuffer(PVOID* ppv, SIZE_T cb)
{
if (_dwReserve - _dwSize < cb)
{
return ERROR_OUTOFMEMORY;
}

SIZE_T dwSize = _dwSize + cb;

if (dwSize > _dwCommit)
{
SIZE_T dwCommit = RoundSize(dwSize);

if (!VirtualAlloc(_BaseAddress + _dwCommit, dwCommit - _dwCommit, MEM_COMMIT, PAGE_READWRITE))
{
return GetLastError();
}

_dwCommit = dwCommit;
}

*ppv = _BaseAddress + _dwSize;

return NOERROR;
}

void AddData(SIZE_T cb)
{
_dwSize += cb;

if (_dwSize > _dwCommit)
{
__debugbreak();
}
}

PVOID getData()
{
return _BaseAddress;
}

SIZE_T getDataSize()
{
return _dwSize;
}

SIZE_T getFreeSpace()
{
return _dwReserve - _dwSize;
}

void Reset()
{
if (_BaseAddress)
{
VirtualFree(_BaseAddress, 0, MEM_RELEASE);
_BaseAddress = 0;
}
_dwReserve = 0, _dwSize = 0, _dwCommit = 0;
}
};

и использование

    DynamicBuffer db;
ULONG cb;
PVOID pv;

if (!db.Create(0x1000000))
{
while (!db.AllocBuffer(&pv, cb = 0x10000))
{
if (ReadFile(hFile, pv, cb, &cb, 0) && cb)
{
db.AddData(cb);
}
else break;
}
}

CloseHandle(hFile);

// use data
db.getData();
db.getDataSize();

// free in destructor

но главный вопрос - нужен непрерывный буфер памяти на всех ? для чего вам нужен УМК выход ? для отправить его на удаленной системе, она отображается в интерфейсе ? для этого не нужно. после того как вы прочитали некоторые сведения из cmd в фиксированный размер буфера - просто отображение этого блока в интерфейсе или послать на удаленную систему. и начать читать новую порцию данных. конечно, вы совершенно другой интерфейс для этого. для парсинга вывода ЦМД ? для этого да, нужен непрерывный буфер памяти.. но мы никогда не должны анализировать выходные данные УМК. за что ?! если говорим, что нам нужен список файлов/папок - нам нужно сделать это самостоятельно, но не запускать реж команду


Анси функция использования. например, является CreateProcessв

это очень плохо. Windows является Юникод (UTF-8) системы. почти все API, реализованный в юникоде. стандарт ANSI (С) по API - это оболочка над Юникод (Вт) API-интерфейс. в такой оболочке преобразования входной строки ANSI в Unicode, вызова Вт API и, наконец, преобразовать из юникода в ANSI строк. это очень не эффективный. еще плохо, что использовать ANSI строк просто неправильная конструкция. кодовую страницу не является инвариантной. она отличается на разных системах. если вы жестко кодировать некоторые символы ANSI строку в коде, которые используют символы > 0х80 - вы получили разные строки в юникоде после преобразования, на разных системах. наконец, не любую строку в юникоде может быть преобразован в текущей кодовой страницы ANSI.

использовать кодовую страницу ANSI для УМК - это неправильно. УМК использовать для трубы вход/выход не в ANSI , но ОЕМ код страницы. это разные кодовые страницы. когда ЦМД читать мульти-байтовую строку из потока stdin файл - он преобразовать его в Юникод через MultiByteToWideChar С CP_OEMCP. и когда он выведет что-то на stdout в файл - он переводит Юникод строку многобайтовых через WideCharToMultiByte С CP_OEMCP. так пока он вывод символов в ряд [0, 0x80) вы не просматривать различные. а если будут говорить "не английском" название файла в выходной, или передать какую-то команду с "не английском" название файла - будет ошибка - потому что вы и Cmd использовать различные кодовые страницы для перевода. вы передаете ему ANSI, но он ждал ОЕМ. он переходит к вам ОЕМ строк, но подожди Анси.

также обратите внимание, об унаследованных ручки - начать с Vista лучше использовать UpdateProcThreadAttribute С PROC_THREAD_ATTRIBUTE_HANDLE_LIST для ограничения списка дескрипторы наследуются потомком процесс - не все наследуемые дескрипторы, но только одна труба ручки. да один , а не два , как вы используете.



Создание труб и использование

опять плохо и неправильно. для чего вы создаете 2 трубы пар ?? когда 1 труба пара только нужна. одна труба ручки в обработке и один подключенный конец трубы в УМК. труба должна быть дуплексной. зачем вы пишете этот конец трубы в сам процесс - будет читать в УМК. что ЦМД писать самостоятельно справиться - Вы читаете через ручку в сам процесс. так что не нужны дополнительные трубы пара. далее - всегда нужно использовать асинхронные труб и Ио здесь. синхронно не эффективны и могут тупика.


интерфейс, конечно, также должны быть абсолютно другое. здесь быстрее нужно экспортировать класс с виртуальными функциями. класс реализации УМК Exec и писать команды. виртуальные вызовы с данными, считанными из УМК. вы наследуете свой класс от базового класса интерфейс, реализовать свой собственный обработчик на чтение данных и выхода из УМК (disconect). некоторые основные реализации класса:

class CPacket
{
ULONG _BufferSize, _DataSize;
LONG _dwRef;
LONG _pad;
char _buf[];

enum ST : ULONG {};

~CPacket() {}

void* operator new(size_t ByteSize, ST BufferSize)
{
return ::operator new(ByteSize + BufferSize);
}

CPacket(ULONG BufferSize) : _dwRef(1), _BufferSize(BufferSize), _DataSize(0) { }

public:

CPacket() { }

void* operator new(size_t , ULONG BufferSize)
{
return new(static_cast<ST>(BufferSize)) CPacket(BufferSize);
}

void AddRef()
{
InterlockedIncrement(&_dwRef);
}

void Release()
{
if (!InterlockedDecrement(&_dwRef)) delete this;
}

PSTR getFreeBuffer()
{
return _buf + _DataSize;
}

ULONG getFreeSize()
{
return _BufferSize - _DataSize;
}

PSTR getData()
{
return _buf;
}

ULONG getBufferSize()
{
return _BufferSize;
}

ULONG getDataSize()
{
return _DataSize;
}

ULONG setDataSize(ULONG DataSize)
{
return _DataSize = DataSize;
}
};

struct U_IRP;

class __declspec(novtable) CCmd
{
friend U_IRP;

enum { read, write };

HANDLE _hFile;
CPacket* _packet;
LONG _dwRef, _handleLock;

ULONG Read();

static ULONG CreatePipeAnonymousPair(PHANDLE phServerPipe, PHANDLE phClientPipe);
static ULONG CreatePipeAnonymousPair7(PHANDLE phServerPipe, PHANDLE phClientPipe);

ULONG CreatePipePair(HANDLE* phClient);

void OnIoComplete(ULONG OpCode, ULONG dwErrorCode, ULONG_PTR dwNumberOfBytesTransfered, PSTR buf);

BOOL LockHandle();

void UnlockHandle();

protected:

virtual ~CCmd()
{
Close();

if (_packet)
{
_packet->Release();
}
}

virtual BOOL OnRead(PSTR buf, ULONG cb) = 0;

virtual void OnWrite(ULONG /*dwErrorCode*/) { }

public:

void Close();

ULONG Write(PCWSTR lpWideCharStr, int cchWideChar = -1);

ULONG Write(PCSTR szOem)
{
return Write(szOem, (ULONG)strlen(szOem));
}

ULONG Write(const void* pv, ULONG cb);

ULONG Write(CPacket* packet);

ULONG Exec(ULONG cbBuffer);

void AddRef()
{
InterlockedIncrement(&_dwRef);
}

void Release()
{
if (!InterlockedDecrement(&_dwRef))
{
delete this;
}
}

CCmd() : _hFile(0), _packet(0), _dwRef(1), _handleLock(0)
{
}
};

struct U_IRP : OVERLAPPED
{
CCmd* _pIoObject;
CPacket* _packet;
PSTR _pv;
ULONG _code;

~U_IRP()
{
if (_packet)
{
_packet->Release();
}
_pIoObject->Release();
}

ULONG CheckIoResult(BOOL fOk)
{
if (fOk)
{
OnIoComplete(NOERROR, InternalHigh);
}
else
{
ULONG dwErrorCode = GetLastError();

if (dwErrorCode != ERROR_IO_PENDING)
{
OnIoComplete(dwErrorCode, 0);
}
}

return NOERROR;
}

VOID OnIoComplete(ULONG dwErrorCode, ULONG_PTR dwNumberOfBytesTransfered)
{
_pIoObject->OnIoComplete(_code, dwErrorCode, dwNumberOfBytesTransfered, _pv);
delete this;
}

static VOID WINAPI _onIoComplete(
ULONG dwErrorCode,
ULONG dwNumberOfBytesTransfered,
LPOVERLAPPED lpOverlapped
)
{
static_cast<U_IRP*>(lpOverlapped)->OnIoComplete(RtlNtStatusToDosError(dwErrorCode), dwNumberOfBytesTransfered);
}

public:

U_IRP(CCmd* pIoObject, ULONG code = 0, CPacket* packet = 0, PSTR pv = 0) : _pIoObject(pIoObject), _code(code), _packet(packet), _pv(pv)
{
if (packet)
{
packet->AddRef();
}
pIoObject->AddRef();
RtlZeroMemory(static_cast<OVERLAPPED*>(this), sizeof(OVERLAPPED));
}

static ULONG Bind(HANDLE hFile)
{
return BindIoCompletionCallback(hFile, U_IRP::_onIoComplete, 0)
&& SetFileCompletionNotificationModes(hFile, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)

? NOERROR : GetLastError();
}
};

ULONG CCmd::Read()
{
ULONG err = ERROR_INVALID_HANDLE;

if (LockHandle())
{
err = ERROR_NO_SYSTEM_RESOURCES;

PSTR buf = _packet->getFreeBuffer();

if (U_IRP* Irp = new U_IRP(this, read, _packet, buf))
{
err = Irp->CheckIoResult(ReadFile(_hFile, buf, _packet->getFreeSize(), 0, Irp));
}

UnlockHandle();
}

return err;
}

ULONG CCmd::Write(const void* pv, ULONG cb)
{
if (CPacket* packet = new(cb) CPacket)
{
memcpy(packet->getData(), pv, packet->setDataSize(cb));
ULONG err = Write(packet);
packet->Release();
return err;
}

return ERROR_NO_SYSTEM_RESOURCES;
}

ULONG CCmd::Write(PCWSTR lpWideCharStr, int cchWideChar)
{
if (cchWideChar < 0)
{
cchWideChar = (ULONG)wcslen(lpWideCharStr);
}

if (int cbMultiByte = WideCharToMultiByte(CP_OEMCP, 0, lpWideCharStr, cchWideChar, 0, 0, 0, 0))
{
if (CPacket* packet = new(cbMultiByte) CPacket)
{
ULONG err;
if (cbMultiByte = WideCharToMultiByte(CP_OEMCP, 0, lpWideCharStr, cchWideChar, packet->getData(), cbMultiByte, 0, 0))
{
packet->setDataSize(cbMultiByte);
err = Write(packet);
}
else
{
err = GetLastError();
}
packet->Release();

return err;
}

return ERROR_NO_SYSTEM_RESOURCES;
}

return GetLastError();
}

ULONG CCmd::Write(CPacket* packet)
{
ULONG err = ERROR_INVALID_HANDLE;

if (LockHandle())
{
err = ERROR_NO_SYSTEM_RESOURCES;

if (U_IRP* Irp = new U_IRP(this, write, packet))
{
err = Irp->CheckIoResult(WriteFile(_hFile, packet->getData(), packet->getDataSize(), 0, Irp));
}

UnlockHandle();
}

return err;
}

ULONG CCmd::CreatePipeAnonymousPair(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
static char flag_supported = -1;

if (flag_supported < 0)
{
ULONG dwMajorVersion, dwMinorVersion;
RtlGetNtVersionNumbers(&dwMajorVersion, &dwMinorVersion, 0);
flag_supported = _WIN32_WINNT_WIN7 <= ((dwMajorVersion << 8)| dwMinorVersion);
}

if (flag_supported)
{
return CreatePipeAnonymousPair7(phServerPipe, phClientPipe);
}

static LONG s;
if (!s)
{
ULONG seed = GetTickCount();
InterlockedCompareExchange(&s, RtlRandomEx(&seed), 0);
}

WCHAR name[64];

swprintf(name, L"\\\\?\\pipe\\Win32Pipes.%08x.%08x", GetCurrentProcessId(), InterlockedIncrement(&s));

HANDLE hClient, hServer = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, NMPWAIT_USE_DEFAULT_WAIT, 0);

if (hServer != INVALID_HANDLE_VALUE)
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };

hClient = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);

if (hClient != INVALID_HANDLE_VALUE)
{
*phServerPipe = hServer, *phClientPipe = hClient;
return NOERROR;
}

CloseHandle(hServer);
}

return GetLastError();
}

ULONG CCmd::CreatePipeAnonymousPair7(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
HANDLE hNamedPipe;

IO_STATUS_BLOCK iosb;

static UNICODE_STRING NamedPipe = RTL_CONSTANT_STRING(L"\\Device\\NamedPipe\\");

OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, (PUNICODE_STRING)&NamedPipe, OBJ_CASE_INSENSITIVE };

NTSTATUS status;

if (0 <= (status = NtOpenFile(&hNamedPipe, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
oa.RootDirectory = hNamedPipe;

static LARGE_INTEGER timeout = { 0, MINLONG };
static UNICODE_STRING empty = {};

oa.ObjectName = &empty;

if (0 <= (status = ZwCreateNamedPipeFile(phServerPipe,
FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|
FILE_CREATE_PIPE_INSTANCE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_CREATE, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION, 1, 0, 0, &timeout)))
{
oa.RootDirectory = *phServerPipe;
oa.Attributes = OBJ_CASE_INSENSITIVE|OBJ_INHERIT;

if (0 > (status = NtOpenFile(phClientPipe, SYNCHRONIZE|FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT)))
{
NtClose(oa.RootDirectory);
}
}

NtClose(hNamedPipe);
}

return RtlNtStatusToDosError(status);
}

ULONG CCmd::CreatePipePair(HANDLE* phClient)
{
HANDLE hServer, hClient;

ULONG err = CreatePipeAnonymousPair(&hServer, &hClient);

if (!err)
{
_hFile = hServer;
_handleLock = 0x80000000;

if (!(err = U_IRP::Bind(hServer)) && !(err = Read()))
{
*phClient = hClient;
return NOERROR;
}

CloseHandle(hClient);
}
return err;
}

void CCmd::OnIoComplete(ULONG OpCode, ULONG dwErrorCode, ULONG_PTR dwNumberOfBytesTransfered, PSTR buf)
{
switch (OpCode)
{
case read:
if (dwErrorCode == NOERROR)
{
if (OnRead(buf, (ULONG)dwNumberOfBytesTransfered))
{
Read();
}
}
break;
case write:
OnWrite(dwErrorCode);
break;
default:
__debugbreak();
}

if (dwErrorCode)
{
DbgPrint("[%u]: error=%u\n", OpCode, dwErrorCode);
}
}

void CCmd::UnlockHandle()
{
if (!_InterlockedDecrement(&_handleLock))
{
CloseHandle(_hFile), _hFile = 0;
}
}

BOOL CCmd::LockHandle()
{
LONG Value, NewValue;

if (0 > (Value = _handleLock))
{
do
{
NewValue = _InterlockedCompareExchange(&_handleLock, Value + 1, Value);

if (NewValue == Value) return TRUE;

} while (0 > (Value = NewValue));
}

return FALSE;
}

void CCmd::Close()
{
if (LockHandle())
{
_bittestandreset(&_handleLock, 31);
UnlockHandle();
}
}

ULONG CCmd::Exec(ULONG cbBuffer)
{
WCHAR ApplicationName[MAX_PATH];
if (!GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
return GetLastError();
}

if (!(_packet = new(cbBuffer) CPacket))
{
return ERROR_NO_SYSTEM_RESOURCES;
}

STARTUPINFOEXW si = { { sizeof(si) } };
PROCESS_INFORMATION pi;
ULONG err = CreatePipePair(&si.StartupInfo.hStdError);

if (err)
{
return err;
}

si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
si.StartupInfo.hStdInput = si.StartupInfo.hStdOutput = si.StartupInfo.hStdError;

ULONG dwCreationFlags = CREATE_NO_WINDOW;
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;
}
}

if (CreateProcessW(ApplicationName, 0, 0, 0, TRUE, dwCreationFlags, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}

if (fInit)
{
DeleteProcThreadAttributeList(si.lpAttributeList);
}

CloseHandle(si.StartupInfo.hStdError);

return err;
}

и пример использования

class CMyCmd : public CCmd
{
ULONG _dwThreadId;

virtual ~CMyCmd()
{
PostThreadMessage(_dwThreadId, WM_QUIT, 0, 0);
}

static void PrintOem(PSTR buf, ULONG cb)
{
if (int cchWideChar = MultiByteToWideChar(CP_OEMCP, 0, buf, cb, 0, 0))
{
PWSTR wz = (PWSTR)alloca(cchWideChar * sizeof(WCHAR));

if (MultiByteToWideChar(CP_OEMCP, 0, buf, cb, wz, cchWideChar))
{
if (ULONG cbMultiByte = WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, 0, 0, 0, 0))
{
PSTR sz = (PSTR)alloca(cbMultiByte);

if (WideCharToMultiByte(CP_ACP, 0, wz, cchWideChar, sz, cbMultiByte, 0, 0))
{
DbgPrint("%.*s", cbMultiByte, sz);
}
}
}
}
}
virtual BOOL OnRead(PSTR buf, ULONG cbTotal)
{
if (cbTotal)
{
ULONG cb;

do
{
PrintOem(buf, cb= min(cbTotal, 256));

} while (buf += cb, cbTotal -= cb);
}
return TRUE;
}

public:
CMyCmd() : _dwThreadId(GetCurrentThreadId()) {}
};

void CmdTest()
{
if (CCmd* pCmd = new CMyCmd)
{
if (!pCmd->Exec(0x1000))
{
pCmd->Write("echo off\r\n");
pCmd->Write(L"echo ***\r\n");
pCmd->Write("cd /d d:\\\r\ndir\r\nexit\r\n");
}

pCmd->Release();
}
MessageBoxW(0,L"demo user interface",L"wait signal",0);
}

1
ответ дан 13 марта 2018 в 03:03 Источник Поделиться

Вы можете вызвать функцию, встроенную С в этот путь:

system("notepad");

-1
ответ дан 2 марта 2018 в 02:03 Источник Поделиться