searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Windows下命令执行函数封装

2024-07-01 03:26:43
31
0

总体概述

开源推动了IT技术的高速发展,日新月异持续创新。越来越多的企业使用开源技术构建自己的商业软件,用"“如果我能看得更远一点的话,是因为我站在巨人的肩膀上"来说,"我的产品如果能持续高效生产价值,是得到了开源的帮助"。一个应用也是类似,为完成需求,经常需要借助外部代码片段、库、程序。对于交互外部程序,有几种方式,通过命令执行调用、IPC通信等,本文对Windows下命令执行调用封装展开描述。
备注:C语言下编码,由于篇幅关系不对Linux做跨平台支持实现。

接口实现

C下命令执行函数有不少,Windows下有winExec, CreateProcess, System, pipopen, ShellExecute等,具体用法需要到Microsoft官网看开发手册。下面就CreateProcess,辅助其他API实现可返回结果、指定命令执行时间的命令执行封装。
实现思路为:
创建子进程负责命令的执行
创建线程负责输出的读取
创建管道联通子进程的输出和线程的输入
父进程根据时间和子进程状态做执行超时的处理
 
具体实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define w_create_thread(x, y, z, a, b, c) ({HANDLE hd; if (!(hd = CreateThread(x,y,z,a,b,c))) exit(0); hd;})

#define WM_ERROR_TIMEOUT 1
#define WM_BUFFER_MAX 1024
#define WM_STRING_MAX 6108864
#define WM_POOL_SIZE 8

int wm_task_nice = 0;

typedef char CHAR;
typedef void* HANDLE;
typedef struct ThreadInfo {
    CHAR *output;
    HANDLE pipe;
} ThreadInfo;


#include <namedpipeapi.h>
#include <winerror.h>
#include <handleapi.h>
#include <processthreadsapi.h>
#include <windows.h>
#include <synchapi.h>
#include <fileapi.h>

static DWORD WINAPI Reader(LPVOID args);

// Execute command with timeout of secs

int wm_exec(char *command, char **output, int *status, int secs) {
    printf("windows ...\n");
    HANDLE hThread = NULL;
    DWORD dwCreationFlags;
    STARTUPINFO sinfo = { 0 };
    PROCESS_INFORMATION pinfo = { 0 };
    ThreadInfo tinfo = { 0 };
    int retval = 0;
    int winerror = 0;

    sinfo.cb = sizeof(STARTUPINFO);

    if (output) {
        sinfo.dwFlags = STARTF_USESTDHANDLES;

        if (!CreatePipe(&tinfo.pipe, &sinfo.hStdOutput, NULL, 0)) {
            winerror = GetLastError();
            printf("at wm_exec(): CreatePipe(%d)", winerror);
            return -1;
        }

        sinfo.hStdError = sinfo.hStdOutput;

        if (!SetHandleInformation(sinfo.hStdOutput, HANDLE_FLAG_INHERIT, 1)) {
            winerror = GetLastError();
            printf("at wm_exec(): SetHandleInformation(%d)", winerror);
            return -1;
        }
    }

    // Create child process and close inherited pipes

    dwCreationFlags = wm_task_nice < -10 ? HIGH_PRIORITY_CLASS :
                      wm_task_nice < 0 ? ABOVE_NORMAL_PRIORITY_CLASS :
                      wm_task_nice == 0 ? NORMAL_PRIORITY_CLASS :
                      wm_task_nice < 10 ? BELOW_NORMAL_PRIORITY_CLASS :
                      IDLE_PRIORITY_CLASS;

    if (!CreateProcess(NULL, command, NULL, NULL, TRUE, dwCreationFlags, NULL, NULL, &sinfo, &pinfo)) {
        winerror = GetLastError();
        printf("at wm_exec(): CreateProcess(%d)", winerror);
        return -1;
    }

    if (output) {
        CloseHandle(sinfo.hStdOutput);
        hThread = w_create_thread(NULL, 0, Reader, &tinfo, 0, NULL);
    }

    switch (WaitForSingleObject(pinfo.hProcess, secs ? (unsigned)(secs * 1000) : INFINITE)) {
    case 0:
        if (status) {
            DWORD exitcode;
            GetExitCodeProcess(pinfo.hProcess, &exitcode);
            *status = exitcode;
        }

        break;

    case WAIT_TIMEOUT:
        TerminateProcess(pinfo.hProcess, 1);
        retval = WM_ERROR_TIMEOUT;
        break;

    default:
        winerror = GetLastError();
        printf("at wm_exec(): WaitForSingleObject(%d)", winerror);
        TerminateProcess(pinfo.hProcess, 1);
        retval = -1;
    }

    if (output) {
        if (WaitForSingleObject(hThread, 1000) == WAIT_TIMEOUT) {
            TerminateThread(hThread, 1);
            WaitForSingleObject(hThread, INFINITE);
        }

        if (retval >= 0) {
            *output = tinfo.output ? tinfo.output : strdup("");
        } else {
            free(tinfo.output);
        }

        CloseHandle(hThread);
        CloseHandle(tinfo.pipe);
    }

    CloseHandle(pinfo.hProcess);
    CloseHandle(pinfo.hThread);

    return retval;
}

// Reading thread's start point

DWORD WINAPI Reader(LPVOID args) {
    ThreadInfo *tinfo = (ThreadInfo *)args;
    CHAR buffer[WM_BUFFER_MAX + 1];
    DWORD length = 0;
    DWORD nbytes;

    while (ReadFile(tinfo->pipe, buffer, 1024, &nbytes, NULL), nbytes > 0) {
        int nextsize = length + nbytes;

        if (nextsize <= WM_STRING_MAX) {
            tinfo->output = (char*)realloc(tinfo->output, nextsize + 1);
            memcpy(tinfo->output + length, buffer, nbytes);
            length = nextsize;
            tinfo->output[length] = '\0';
        } else {
            printf("String limit reached.");
            break;
        }
    }

    return 0;
}



int main()
{
    char *out = NULL;
    int ret = 0;
    wm_exec("systeminfo.exe", &out, &ret, 10);
    printf("result: %d£º%s\n", ret, out?out:"None");
    if (out)
    {
        free(out);
        out = NULL;
    }
    return 0;
}
备注:代码在Windows 10 X86 下编译通过

参考文献

[1] microsoft的开发者文档
[2] wazuh源码
0条评论
0 / 1000
刘****成
15文章数
0粉丝数
刘****成
15 文章 | 0 粉丝
原创

Windows下命令执行函数封装

2024-07-01 03:26:43
31
0

总体概述

开源推动了IT技术的高速发展,日新月异持续创新。越来越多的企业使用开源技术构建自己的商业软件,用"“如果我能看得更远一点的话,是因为我站在巨人的肩膀上"来说,"我的产品如果能持续高效生产价值,是得到了开源的帮助"。一个应用也是类似,为完成需求,经常需要借助外部代码片段、库、程序。对于交互外部程序,有几种方式,通过命令执行调用、IPC通信等,本文对Windows下命令执行调用封装展开描述。
备注:C语言下编码,由于篇幅关系不对Linux做跨平台支持实现。

接口实现

C下命令执行函数有不少,Windows下有winExec, CreateProcess, System, pipopen, ShellExecute等,具体用法需要到Microsoft官网看开发手册。下面就CreateProcess,辅助其他API实现可返回结果、指定命令执行时间的命令执行封装。
实现思路为:
创建子进程负责命令的执行
创建线程负责输出的读取
创建管道联通子进程的输出和线程的输入
父进程根据时间和子进程状态做执行超时的处理
 
具体实现代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

#define w_create_thread(x, y, z, a, b, c) ({HANDLE hd; if (!(hd = CreateThread(x,y,z,a,b,c))) exit(0); hd;})

#define WM_ERROR_TIMEOUT 1
#define WM_BUFFER_MAX 1024
#define WM_STRING_MAX 6108864
#define WM_POOL_SIZE 8

int wm_task_nice = 0;

typedef char CHAR;
typedef void* HANDLE;
typedef struct ThreadInfo {
    CHAR *output;
    HANDLE pipe;
} ThreadInfo;


#include <namedpipeapi.h>
#include <winerror.h>
#include <handleapi.h>
#include <processthreadsapi.h>
#include <windows.h>
#include <synchapi.h>
#include <fileapi.h>

static DWORD WINAPI Reader(LPVOID args);

// Execute command with timeout of secs

int wm_exec(char *command, char **output, int *status, int secs) {
    printf("windows ...\n");
    HANDLE hThread = NULL;
    DWORD dwCreationFlags;
    STARTUPINFO sinfo = { 0 };
    PROCESS_INFORMATION pinfo = { 0 };
    ThreadInfo tinfo = { 0 };
    int retval = 0;
    int winerror = 0;

    sinfo.cb = sizeof(STARTUPINFO);

    if (output) {
        sinfo.dwFlags = STARTF_USESTDHANDLES;

        if (!CreatePipe(&tinfo.pipe, &sinfo.hStdOutput, NULL, 0)) {
            winerror = GetLastError();
            printf("at wm_exec(): CreatePipe(%d)", winerror);
            return -1;
        }

        sinfo.hStdError = sinfo.hStdOutput;

        if (!SetHandleInformation(sinfo.hStdOutput, HANDLE_FLAG_INHERIT, 1)) {
            winerror = GetLastError();
            printf("at wm_exec(): SetHandleInformation(%d)", winerror);
            return -1;
        }
    }

    // Create child process and close inherited pipes

    dwCreationFlags = wm_task_nice < -10 ? HIGH_PRIORITY_CLASS :
                      wm_task_nice < 0 ? ABOVE_NORMAL_PRIORITY_CLASS :
                      wm_task_nice == 0 ? NORMAL_PRIORITY_CLASS :
                      wm_task_nice < 10 ? BELOW_NORMAL_PRIORITY_CLASS :
                      IDLE_PRIORITY_CLASS;

    if (!CreateProcess(NULL, command, NULL, NULL, TRUE, dwCreationFlags, NULL, NULL, &sinfo, &pinfo)) {
        winerror = GetLastError();
        printf("at wm_exec(): CreateProcess(%d)", winerror);
        return -1;
    }

    if (output) {
        CloseHandle(sinfo.hStdOutput);
        hThread = w_create_thread(NULL, 0, Reader, &tinfo, 0, NULL);
    }

    switch (WaitForSingleObject(pinfo.hProcess, secs ? (unsigned)(secs * 1000) : INFINITE)) {
    case 0:
        if (status) {
            DWORD exitcode;
            GetExitCodeProcess(pinfo.hProcess, &exitcode);
            *status = exitcode;
        }

        break;

    case WAIT_TIMEOUT:
        TerminateProcess(pinfo.hProcess, 1);
        retval = WM_ERROR_TIMEOUT;
        break;

    default:
        winerror = GetLastError();
        printf("at wm_exec(): WaitForSingleObject(%d)", winerror);
        TerminateProcess(pinfo.hProcess, 1);
        retval = -1;
    }

    if (output) {
        if (WaitForSingleObject(hThread, 1000) == WAIT_TIMEOUT) {
            TerminateThread(hThread, 1);
            WaitForSingleObject(hThread, INFINITE);
        }

        if (retval >= 0) {
            *output = tinfo.output ? tinfo.output : strdup("");
        } else {
            free(tinfo.output);
        }

        CloseHandle(hThread);
        CloseHandle(tinfo.pipe);
    }

    CloseHandle(pinfo.hProcess);
    CloseHandle(pinfo.hThread);

    return retval;
}

// Reading thread's start point

DWORD WINAPI Reader(LPVOID args) {
    ThreadInfo *tinfo = (ThreadInfo *)args;
    CHAR buffer[WM_BUFFER_MAX + 1];
    DWORD length = 0;
    DWORD nbytes;

    while (ReadFile(tinfo->pipe, buffer, 1024, &nbytes, NULL), nbytes > 0) {
        int nextsize = length + nbytes;

        if (nextsize <= WM_STRING_MAX) {
            tinfo->output = (char*)realloc(tinfo->output, nextsize + 1);
            memcpy(tinfo->output + length, buffer, nbytes);
            length = nextsize;
            tinfo->output[length] = '\0';
        } else {
            printf("String limit reached.");
            break;
        }
    }

    return 0;
}



int main()
{
    char *out = NULL;
    int ret = 0;
    wm_exec("systeminfo.exe", &out, &ret, 10);
    printf("result: %d£º%s\n", ret, out?out:"None");
    if (out)
    {
        free(out);
        out = NULL;
    }
    return 0;
}
备注:代码在Windows 10 X86 下编译通过

参考文献

[1] microsoft的开发者文档
[2] wazuh源码
文章来自个人专栏
信息安全
15 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
1
0