在 Windows 上向 QProcess 发送 Ctrl+C

2024-03-01

抓紧你的马鞍,这是一个很长的马鞍!如果您不想阅读所有内容,请跳至“MCVE”部分。

我正在尝试制定一个流程QProcess优雅地退出。我不控制有问题的进程如何退出,它只接受 Ctrl+C 信号。令我困惑的是,这听起来非常简单明了QProcess的 API。然而,我在这里:D

这是我到目前为止得到的:

就像我说的,QProcess并不真正支持这一点。所以我必须深入Windows生态系统并尝试在本地实现它。我发现GenerateConsoleCtrlEvent in 微软文档 https://learn.microsoft.com/en-us/windows/console/generateconsolectrlevent?redirectedfrom=MSDN。看起来它完全符合我的需要,所以我尝试使用它。经过一番努力处理 Windows API 中的错误消息后,我得到了以下结果:

QProcess myprocess = new QProcess(this);
myprocess->setReadChannel(QProcess::StandardOutput);

// I'm sorry that I have to be vague here. I can't really share this part.
myprocess->start("myexec", {"arg1", "arg2"});

//...

auto success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, myprocess->pid()->dwProcessId);
if (!success) {
    LPVOID lpMsgBuf;
    auto err = GetLastError();

    FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            reinterpret_cast<LPTSTR>(&lpMsgBuf),
            0, nullptr );

    // probably could have used wcerr, but after making this work I was happy enough with it :D
    auto error_string = QString::fromWCharArray((reinterpret_cast<LPTSTR>(lpMsgBuf)));
    std::cerr << error_string.toStdString();
    LocalFree(lpMsgBuf);
}

这只是打印the handle is invalid.到标准错误。我有点期待它,因为文档GenerateConsoleCtrlEvent say:

dwProcessGroupId [输入]
接收信号的进程组的标识符。当在对 CreateProcess 函数的调用中指定 CREATE_NEW_PROCESS_GROUP 标志时,将创建进程组。新进程的进程标识也是新进程组的进程组标识。

...我支持 Qt 已经传递了这个标志。这让我卡住了一段时间,而且这似乎是 SO 上关于此问题最多的问题的地方(是的,我已经看到了所有问题 -我想)似乎也死了。然后我发现QProcess::setCreateProcessArgumentsModifier(有一个很好的用法示例here https://doc.qt.io/qt-5/qprocess.html#CreateProcessArgumentModifier-typedef)这允许我将参数注入到CreateProcess称呼。然后我更新了我的代码来执行此操作:

QProcess myprocess = new QProcess(this);
myprocess->setCreateProcessArgumentsModifier([this] (QProcess::CreateProcessArguments *args) {
        args->flags |= CREATE_NEW_PROCESS_GROUP;
});
myprocess->setReadChannel(QProcess::StandardOutput);

myprocess->start("myexec", {"arg1", "arg2"});

//...

auto success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, myprocess->pid()->dwProcessId);
if (!success) {
    LPVOID lpMsgBuf;
    auto err = GetLastError();

    FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER |
            FORMAT_MESSAGE_FROM_SYSTEM |
            FORMAT_MESSAGE_IGNORE_INSERTS,
            nullptr,
            err,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
            reinterpret_cast<LPTSTR>(&lpMsgBuf),
            0, nullptr );

    auto error_string = QString::fromWCharArray((reinterpret_cast<LPTSTR>(lpMsgBuf)));
    std::cerr << error_string.toStdString();
    LocalFree(lpMsgBuf);
}

然而,这给了我同样的错误(the handle is invalid)。从那时起,我尝试了其他事情,比如注射我自己的PROCESS_INFORMATIONstruct 以确保我有正确的进程标识符,甚至添加CREATE_NEW_PROCESS_GROUP to the lpStartupInfo相反 - 我现在知道这是错误的地方,因为这导致一些奇怪的行为 https://social.msdn.microsoft.com/Forums/en-US/15d4899b-1848-4db3-a1e4-64bd42d2170a/starting-a-process-with-createnewprocessgroup-flag-produces-no-output(此链接中的提问者不是我:D)

有任何想法吗?我可以采取不同的做法吗?

我正在使用 Qt 5.14.2,使用 MSVC 2017(64 位)进行编译。


MCVE

为此制作“最小”MCVE 并不容易:)

我创建了一个简单的 Windows 应用程序,它通过简单地打印一条消息来处理 Ctrl+C。目标是使 QProcess 触发此处理程序,并且没有副作用。这是子进程的源代码:

#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>

#include "windows.h"

std::atomic<bool> should_stop = false;

BOOL WINAPI consoleHandler(DWORD signal) {
    if (signal == CTRL_C_EVENT) {
        std::cout << "\nThank you for your Ctrl+C event!\n";
        should_stop.store(true);
    }

    return TRUE;
}

int main() {

    if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
        std::cout << "Failed to set console handler\n";
        return 1;
    }

    while (!should_stop) {
        std::cout << "I'll keep printing this message until you stop me." << std::endl; // Yes, I want to flush every time.
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }

    return 0;
}

我的父应用程序的“MVCE”有一个微不足道的main.cpp,连同一个ProcessHolder带有头文件和源文件的类。这是必需的,以便我可以有一个事件循环,并且 Qt 能够moc正确的类(用于所述事件循环)。

main.cpp

#include <QCoreApplication>
#include <QTimer>

#include <memory>

#include "processholder.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    std::unique_ptr<ProcessHolder> ph(new ProcessHolder());

    // Just so I can get the event loop running
    QTimer::singleShot(0, ph.get(), &ProcessHolder::waitForInput);

    return a.exec();
}

进程持有者.h

#ifndef PROCESSHOLDER_H
#define PROCESSHOLDER_H

#include <QObject>
#include <QProcess>

class ProcessHolder : public QObject
{
    Q_OBJECT
public:
    explicit ProcessHolder(QObject *parent = nullptr);

signals:

public slots:
    void waitForInput();
private:
    QProcess* p;
};

#endif // PROCESSHOLDER_H

进程持有者.cpp

#include "processholder.h"

#include <iostream>

#include <QTimer>

#include "Windows.h"

void tryFinishProcess(QProcess* p) {
    auto success = GenerateConsoleCtrlEvent(CTRL_C_EVENT, p->pid()->dwProcessId);
    if (!success) {
        LPVOID lpMsgBuf;
        auto err = GetLastError();

        FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                nullptr,
                err,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                reinterpret_cast<LPTSTR>(&lpMsgBuf),
                0, nullptr );

        // probably could have used wcerr, but after making this work I was happy enough with it :D
        auto error_string = QString::fromWCharArray((reinterpret_cast<LPTSTR>(lpMsgBuf)));
        std::cerr << error_string.toStdString();
        LocalFree(lpMsgBuf);
    }
}

ProcessHolder::ProcessHolder(QObject *parent) : QObject(parent), p(new QProcess(this))
{
    connect(p, &QProcess::readyReadStandardOutput, [this]() {
        auto lines = p->readAllStandardOutput();
        std::cout << lines.toStdString();
    });

    // Doing this for this example makes things fail miserably when trying to close the parent program.
    // An when not doing it, the CtrlC event that is generated on tryFinishProcess actually ends the
    // parent program, rather than the child one.
    /*p->setCreateProcessArgumentsModifier([this] (QProcess::CreateProcessArguments *args) {
            args->flags |= CREATE_NEW_PROCESS_GROUP;
    });*/

    std::cout << "starting process...\n";

    p->start(R"(path\to\TrivialConsoleApp.exe)");
}

void ProcessHolder::waitForInput(){
   char c;
   bool quit = false;
   // Print a small prompt just so we can differentiate input from output
   std::cout << "> ";
   if (std::cin >> c) {
       switch(c) {
       case 'k':
           p->kill();
           break;
       case 't':
           p->terminate();
           break;
       case 'c':
           p->close();
           break;
       case 'g':
           tryFinishProcess(p);
       }
       // any other character will just reliquinsh the hold on standard io for a small time, enough for the
       // messages that were sent via cout to show up.

       if (!quit) {
           QTimer::singleShot(0, this, &ProcessHolder::waitForInput);
       }
   }
}

几个运行示例:

Using QProcess::kill()。子进程已终止,但没有 CtrlC 消息。

Using tryFinishProcess(参见上面的实现)实际上使父进程退出:

再次,使用tryFinishProcess,但这次与CREATE_NEW_PROCESS_GROUP添加(参见评论ProcessHolder的构造函数)。这里需要注意的是,按终端最后要求的 RETURN 不再起作用(它什么也不做),所以那里出现了问题:

我对上面三个样本(或至少最后两个)的期望是看到"Thank you for your Ctrl+C event!"消息(查看consoleHandler在子进程上)在请求进程完成后的某个地方。如果我在控制台上运行它然后按 Ctrl+C,就会发生这种情况:


when CTRL+C是控制台进程的输入,系统在该进程中创建具有入口点的线程

EXTERN_C
WINBASEAPI
ULONG 
WINAPI 
CtrlRoutine(_In_ DWORD dwCtrlEvent);

该函数由导出内核32.dll(可以转发导出到另一个 dll,例如内核库.dll)

this CtrlRoutine接下来做: 如果正在调试进程 - raiseDBG_CONTROL_C https://learn.microsoft.com/en-us/windows/win32/debug/debugging-events例外,那么 调用 注册者SetConsoleCtrlHandler回调。如果没有注册回调或全部返回 false -DefaultHandler被调用,它只是调用ExitProcess(STATUS_CONTROL_C_EXIT) (通过 CTRL+C 退出应用程序)

但可以自己直接打电话CreateRemoteThread在目标进程中,入口点位于CtrlRoutine and CTRL_C_EVENT作为参数。如果目标进程具有与我们相同的数字容量 - 32 位或 64 位 - 没有任何问题 - 我们可以导入CtrlRoutine已经在链接时(它定义在内核32.lib)或通过获取它的地址GetProcAddress。但是如果我们的进程是 64 位本机并且目标进程是 32 位(WoW64) - 这里的问题 - 我们需要获取地址CtrlRoutine inside 32-bit 内核32.dll- 但我们不能直接在自己的64位进程中加载​​它并调用GetProcAddress。需要自己映射这个dll并自己获取它的基础并解析导出。这个任务已经不简单了。如果我们在 wow64 进程(64 位 Windows 上的 32 位进程)中运行并且目标进程是 64 位(本机) - 任务已经变得非常困难(尽管也存在解决方案,但无需创建额外的进程)。但我假设控制进程是本机的(64 位 Windows 上的 64 位)

#pragma warning( disable : 4201)

#include <Windows.h>
#include <malloc.h>
#define LDR_DATA_TABLE_ENTRY _LDR_DATA_TABLE_ENTRY_
#define PLDR_DATA_TABLE_ENTRY _PLDR_DATA_TABLE_ENTRY_
#include <winternl.h>
#undef PLDR_DATA_TABLE_ENTRY
#undef LDR_DATA_TABLE_ENTRY

#define MAXUSHORT 0xffff 
#define MAXULONG 0xffffffff 

#define RtlOffsetToPointer(B,O) ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O)) ))
#define RtlPointerToOffset(B,P)  ((ULONG)( ((PCHAR)(P)) - ((PCHAR)(B))  ))

#define RTL_CONSTANT_STRING(s) { sizeof( s ) - sizeof( (s)[0] ), sizeof( s ), const_cast<PWSTR>(s) }

#define NtCurrentProcess() ( (HANDLE)(LONG_PTR) -1 )

typedef enum _SECTION_INHERIT {
    ViewShare = 1,
    ViewUnmap = 2
} SECTION_INHERIT;

typedef enum _SECTION_INFORMATION_CLASS
{
    SectionBasicInformation, // q; SECTION_BASIC_INFORMATION
    SectionImageInformation, // q; SECTION_IMAGE_INFORMATION
    SectionRelocationInformation, // name:wow64:whNtQuerySection_SectionRelocationInformation
    SectionOriginalBaseInformation, // PVOID BaseAddress
    SectionInternalImageInformation, // SECTION_INTERNAL_IMAGE_INFORMATION // since REDSTONE2
    MaxSectionInfoClass
} SECTION_INFORMATION_CLASS;

typedef struct SECTION_IMAGE_INFORMATION
{
    PVOID TransferAddress;
    ULONG ZeroBits;
    SIZE_T MaximumStackSize;
    SIZE_T CommittedStackSize;
    ULONG SubSystemType;
    union
    {
        struct
        {
            USHORT SubSystemMinorVersion;
            USHORT SubSystemMajorVersion;
        };
        ULONG SubSystemVersion;
    };
    union
    {
        struct
        {
            USHORT MajorOperatingSystemVersion;
            USHORT MinorOperatingSystemVersion;
        };
        ULONG OperatingSystemVersion;
    };
    USHORT ImageCharacteristics;
    USHORT DllCharacteristics;
    USHORT Machine;
    BOOLEAN ImageContainsCode;
    union
    {
        UCHAR ImageFlags;
        struct
        {
            UCHAR ComPlusNativeReady : 1;
            UCHAR ComPlusILOnly : 1;
            UCHAR ImageDynamicallyRelocated : 1;
            UCHAR ImageMappedFlat : 1;
            UCHAR BaseBelow4gb : 1;
            UCHAR ComPlusPrefer32bit : 1;
            UCHAR Reserved : 2;
        };
    };
    ULONG LoaderFlags;
    ULONG ImageFileSize;
    ULONG CheckSum;
} *PSECTION_IMAGE_INFORMATION;

typedef struct LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    void *DllBase;
    void *EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    // .. more members. trucated
} *PLDR_DATA_TABLE_ENTRY;

EXTERN_C_START

NTSYSAPI
NTSTATUS
NTAPI
LdrFindEntryForAddress(
                       _In_ PVOID DllHandle,
                       _Out_ PLDR_DATA_TABLE_ENTRY *Entry
                       );


NTSYSAPI
PIMAGE_NT_HEADERS
NTAPI
RtlImageNtHeader(
                 _In_ PVOID Base
                 );

EXTERN_C
NTSYSAPI
PVOID
NTAPI
RtlImageDirectoryEntryToData(
                             _In_ PVOID Base,
                             _In_ BOOLEAN MappedAsImage,
                             _In_ USHORT DirectoryEntry,
                             _Out_ PULONG Size
                             );

NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeSize(
                          _Out_ PULONG BytesInUnicodeString,
                          _In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
                          _In_ ULONG BytesInMultiByteString
                          );

NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeSize(
                          _Out_ PULONG BytesInUnicodeString,
                          _In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
                          _In_ ULONG BytesInMultiByteString
                          );

NTSYSAPI
NTSTATUS
NTAPI
RtlMultiByteToUnicodeN(
                       _Out_writes_bytes_to_(MaxBytesInUnicodeString, *BytesInUnicodeString) PWCH UnicodeString,
                       _In_ ULONG MaxBytesInUnicodeString,
                       _Out_opt_ PULONG BytesInUnicodeString,
                       _In_reads_bytes_(BytesInMultiByteString) const CHAR *MultiByteString,
                       _In_ ULONG BytesInMultiByteString
                       );

NTSYSAPI
NTSTATUS
NTAPI
RtlAppendUnicodeToString (
                          _Inout_ PUNICODE_STRING Destination,
                          _In_opt_z_ PCWSTR Source
                          );

NTSYSAPI
NTSTATUS
NTAPI
RtlAppendUnicodeStringToString (
                                _Inout_ PUNICODE_STRING Destination,
                                _In_ PCUNICODE_STRING Source
                                );

NTSYSAPI
BOOLEAN
NTAPI
RtlPrefixUnicodeString(
                       _In_ PCUNICODE_STRING String1,
                       _In_ PCUNICODE_STRING String2,
                       _In_ BOOLEAN CaseInSensitive
                       );

NTSYSAPI
NTSTATUS
NTAPI
ZwUnmapViewOfSection(
                     _In_ HANDLE ProcessHandle,
                     _In_opt_ PVOID BaseAddress
                     );

NTSYSAPI
NTSTATUS
NTAPI
ZwMapViewOfSection(
                   _In_ HANDLE SectionHandle,
                   _In_ HANDLE ProcessHandle,
                   _Outptr_result_bytebuffer_(*ViewSize) PVOID *BaseAddress,
                   _In_ ULONG_PTR ZeroBits,
                   _In_ SIZE_T CommitSize,
                   _Inout_opt_ PLARGE_INTEGER SectionOffset,
                   _Inout_ PSIZE_T ViewSize,
                   _In_ SECTION_INHERIT InheritDisposition,
                   _In_ ULONG AllocationType,
                   _In_ ULONG Win32Protect
                   );

NTSYSAPI
NTSTATUS
NTAPI
ZwOpenSection(
              _Out_ PHANDLE SectionHandle,
              _In_ ACCESS_MASK DesiredAccess,
              _In_ POBJECT_ATTRIBUTES ObjectAttributes
              );

NTSYSAPI
NTSTATUS
NTAPI
ZwQuerySection(
               _In_ HANDLE SectionHandle,
               _In_ ULONG SectionInformationClass,
               _Out_ PVOID SectionInformation,
               _In_ ULONG SectionInformationLength,
               _Out_ PSIZE_T ResultLength OPTIONAL
               );

NTSYSAPI
NTSTATUS
NTAPI
LdrLoadDll(
           _In_opt_ PWSTR DllPath,
           _In_opt_ PULONG DllCharacteristics,
           _In_ PUNICODE_STRING DllName,
           _Out_ HMODULE *DllHandle
           );

NTSYSAPI
NTSTATUS
NTAPI
LdrUnloadDll(
             _In_ PVOID DllHandle
             );

NTSYSAPI
NTSTATUS
NTAPI
LdrGetProcedureAddress(
                       _In_ PVOID DllHandle,
                       _In_opt_ PANSI_STRING ProcedureName,
                       _In_opt_ ULONG ProcedureNumber,
                       _Out_ PVOID *ProcedureAddress
                       );

EXTERN_C_END

ULONG GetNameOrdinal(PVOID Base, PDWORD AddressOfNames, DWORD NumberOfNames, PCSTR Name)
{
    if (NumberOfNames)
    {
        DWORD a = 0, o;

        do 
        {
            o = (a + NumberOfNames) >> 1;

            int i = strcmp(RtlOffsetToPointer(Base, AddressOfNames[o]), Name);

            if (!i)
            {
                return o;
            }

            0 > i ? a = o + 1 : NumberOfNames = o;

        } while (a < NumberOfNames);
    }

    return MAXULONG;
}

PVOID getWowProcs(PCUNICODE_STRING DllName, PCSTR Name);

PVOID getWowProcs(PVOID ImageBase, PVOID BaseAddress, PCSTR Name)
{
    ULONG exportSize, exportRVA;

    PIMAGE_EXPORT_DIRECTORY pied = (PIMAGE_EXPORT_DIRECTORY)
        RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &exportSize);

    if (!pied) return 0;

    exportRVA = RtlPointerToOffset(BaseAddress, pied);

    ULONG NumberOfFunctions = pied->NumberOfFunctions;

    if (!NumberOfFunctions) return 0;

    ULONG NumberOfNames = pied->NumberOfNames;
    ULONG OrdinalBase = pied->Base;

    PULONG AddressOfFunctions = (PULONG)RtlOffsetToPointer(BaseAddress, pied->AddressOfFunctions);
    PULONG AddressOfNames = (PULONG)RtlOffsetToPointer(BaseAddress, pied->AddressOfNames);
    PWORD AddressOfNameOrdinals = (PWORD)RtlOffsetToPointer(BaseAddress, pied->AddressOfNameOrdinals);

    ULONG o;

    if (*Name == '#')
    {
        if ((o = strtoul(Name + 1, const_cast<char**>(&Name), 10)) < OrdinalBase || *Name)
        {
            return 0;
        }
        o -= OrdinalBase;
    }
    else
    {
        o = GetNameOrdinal(BaseAddress, AddressOfNames, NumberOfNames, Name);
        if (o < NumberOfNames)
        {
            o = AddressOfNameOrdinals[o];
        }
    }

    if (o >= NumberOfFunctions)
    {
        return 0;
    }

    DWORD Rva = AddressOfFunctions[o];

    if ((ULONG_PTR)Rva - (ULONG_PTR)exportRVA >= exportSize)
    {
        return RtlOffsetToPointer(ImageBase, Rva);
    }

    // forward export
    PCSTR pfn = RtlOffsetToPointer(BaseAddress, Rva);

    if (!(Name = strrchr(pfn, '.')))
    {
        return 0;
    }

    static const WCHAR DLL[] = L"DLL";

    ULONG BytesInUnicodeString, BytesInMultiByteString = RtlPointerToOffset(pfn, ++Name);

    if (0 > RtlMultiByteToUnicodeSize(&BytesInUnicodeString, pfn, BytesInMultiByteString) || 
        (BytesInUnicodeString += sizeof(DLL)) >= MAXUSHORT )
    {
        return 0;
    }

    UNICODE_STRING DllName = {
        0, (USHORT)BytesInUnicodeString, (PWSTR)alloca(BytesInUnicodeString)
    };

    if (0 > RtlMultiByteToUnicodeN(DllName.Buffer, DllName.MaximumLength, 
        &BytesInUnicodeString, pfn, BytesInMultiByteString))
    {
        return 0;
    }

    DllName.Length = (USHORT)BytesInUnicodeString;

    if (0 > RtlAppendUnicodeToString(&DllName, DLL))
    {
        return 0;
    }

    static const UNICODE_STRING API_ = RTL_CONSTANT_STRING(L"API-");
    static const UNICODE_STRING EXT_ = RTL_CONSTANT_STRING(L"EXT-");

    if (!RtlPrefixUnicodeString(&API_, &DllName, TRUE) &&
        !RtlPrefixUnicodeString(&EXT_, &DllName, TRUE))
    {
        return getWowProcs(&DllName, Name);
    }

    HMODULE hmod;

    if (0 <= LdrLoadDll(0, 0, &DllName, &hmod))
    {
        ANSI_STRING as, *pas;

        if (*Name == '#')
        {
            pas = 0;
            o = strtoul(Name + 1, const_cast<char**>(&Name), 10);
            if (*Name)
            {
                o = 0;
            }
        }
        else
        {
            o = 0;
            RtlInitAnsiString(pas = &as, Name);
        }

        PVOID pv, pvfn = 0;
        if (0 <= LdrGetProcedureAddress(hmod, pas, o, &pv))
        {
            PLDR_DATA_TABLE_ENTRY ldte;
            if (0 <= LdrFindEntryForAddress(pv, &ldte))
            {
                pvfn = getWowProcs(&ldte->BaseDllName, Name);
            }
        }
        LdrUnloadDll(hmod);

        return pvfn;
    }

    return 0;
}

PVOID getWowProcs(PCUNICODE_STRING DllName, PCSTR Name)
{
    static const WCHAR KnownDlls32[] = L"\\KnownDlls32\\";

    UNICODE_STRING ObjectName = { 
        0, 
        (USHORT)(sizeof(KnownDlls32) + DllName->Length), 
        (PWSTR)alloca(ObjectName.MaximumLength)
    };

    if (0 > RtlAppendUnicodeToString(&ObjectName, KnownDlls32) || 
        0 > RtlAppendUnicodeStringToString(&ObjectName, DllName))
    {
        return 0;
    }

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

    HANDLE hSection;

    PVOID pv = 0;

    if (0 <= ZwOpenSection(&hSection, SECTION_QUERY|SECTION_MAP_READ, &oa))
    {
        SECTION_IMAGE_INFORMATION sii;
        if (0 <= ZwQuerySection(hSection, SectionImageInformation, &sii, sizeof(sii), 0))
        {
            PVOID BaseAddress = 0;
            SIZE_T ViewSize = 0;

            if (0 <= ZwMapViewOfSection(hSection, NtCurrentProcess(), &BaseAddress, 0, 0, 0, &ViewSize, ViewUnmap, 0, PAGE_READONLY))
            {
                __try
                {
                    if (PIMAGE_NT_HEADERS32 pinth = (PIMAGE_NT_HEADERS32)RtlImageNtHeader(BaseAddress))
                    {
                        pv = getWowProcs((PBYTE)sii.TransferAddress - pinth->OptionalHeader.AddressOfEntryPoint, BaseAddress, Name);
                    }
                }
                __except(EXCEPTION_EXECUTE_HANDLER)
                {
                }

                ZwUnmapViewOfSection(NtCurrentProcess(), BaseAddress);
            }
        }
        NtClose(hSection);
    }

    return pv;
}

PVOID GetCtrlRoutine(HANDLE hProcess)
{
    BOOL bWow;
    if (IsWow64Process(hProcess, &bWow))
    {
        static const UNICODE_STRING kernel32 = RTL_CONSTANT_STRING(L"kernel32.dll");

        if (bWow)
        {
            static PVOID pvCtrlRoutine = 0;
            if (!pvCtrlRoutine)
            {
                // GetOverlappedResultEx
                // just for some extreme case, for better understand code in getWowProcs
                // it not need here
                // pvCtrlRoutine = getWowProcs(&kernel32, "GetOverlappedResultEx");
                pvCtrlRoutine = getWowProcs(&kernel32, "CtrlRoutine");
            }
            return pvCtrlRoutine;
        }

        static PVOID pvCtrlRoutine = 0;
        if (!pvCtrlRoutine)
        {
            if (HMODULE hmod = GetModuleHandle(kernel32.Buffer))
            {
                pvCtrlRoutine = GetProcAddress(hmod, "CtrlRoutine");
            }
        }
        return pvCtrlRoutine;
    }

    return 0;
}

void SendCtrlEvent(HANDLE hProcess)
{
    if (PVOID pv = GetCtrlRoutine(hProcess))
    {
        if (HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (PTHREAD_START_ROUTINE)pv, CTRL_C_EVENT, 0, 0))
        {
            CloseHandle(hThread);
            return ;
        }
    }

    TerminateProcess(hProcess, STATUS_CONTROL_C_EXIT);
}

void DemoUseCtrlC()
{
    WCHAR AppName[MAX_PATH];

    if (ExpandEnvironmentStringsW(L"%SystemRoot%\\syswow64\\ping.exe", AppName, _countof(AppName)))
    {
        STARTUPINFO si = { sizeof(si)};
        PROCESS_INFORMATION pi;
        if (CreateProcessW(AppName, const_cast<PWSTR>(L"* -n 999 8.8.8.8"), 0, 0, 0, 0, 0, 0, &si, &pi))
        {
            CloseHandle(pi.hThread);
            MessageBoxW(HWND_DESKTOP, L"Break..", L"CTRL+C", MB_ICONINFORMATION);
            SendCtrlEvent(pi.hProcess);
            CloseHandle(pi.hProcess);
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Windows 上向 QProcess 发送 Ctrl+C 的相关文章

  • 使用具有现有访问令牌的 Google API .NET 客户端

    用例如下 移动应用程序正在通过 Google 对用户进行身份验证 并且在某些时候 我们需要将用户的视频发布到他的 YouTube 帐户 出于实际原因 实际发布应该由后端完成 已经存储在那里的大文件 由于用户已经通过应用程序的身份验证 因此应
  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 为什么大多数 C 开发人员使用 Define 而不是 const? [复制]

    这个问题在这里已经有答案了 在许多程序中 define与常量具有相同的用途 例如 define FIELD WIDTH 10 const int fieldWidth 10 我通常认为第一种形式优于另一种形式 它依赖于预处理器来处理基本上是
  • 如何将 Hudson/Jenkins 参数传递给 Windows 批处理命令

    好吧 我需要在我的 Hudson 作业中执行一个批处理文件 我有一个参数 Jenkis 参数 我需要将这个值 如参数 传递给批处理文件 我尝试了以下操作 Deploy cmd configuration DEPLOYCONFIGURATIO
  • 使用post方法将多个参数发送到asp.net core 3 mvc操作

    使用 http post 方法向 asp net mvc core 3 操作发送具有多个参数的 ajax 请求时存在问题 参数不绑定 在 dot net 框架 asp net web api 中存在类似的限制 但在 asp net mvc
  • Clang 编译器 (x86):80 位长双精度

    我正在尝试在 x86 Windows 平台上使用本机 80 位长双精度 海湾合作委员会选项 mlong double 80 https gcc gnu org onlinedocs gcc x86 Options html似乎不适用于 cl
  • 暂停下载线程

    我正在用 C 编写一个非常简单的批量下载程序 该程序读取要下载的 URL 的 txt 文件 我已经设置了一个全局线程和委托来更新 GUI 按下 开始 按钮即可创建并启动该线程 我想要做的是有一个 暂停 按钮 使我能够暂停下载 直到点击 恢复
  • 如何从 C# 控制器重定向到外部 url

    我使用 C 控制器作为网络服务 在其中我想将用户重定向到外部网址 我该怎么做 Tried System Web HttpContext Current Response Redirect 但没有成功 使用控制器的重定向 http msdn
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • Tkinter - 浮动窗口 - 调整大小

    灵感来自this https stackoverflow com a 22424245 13629335问题 我想为我的根窗口编写自己的调整大小函数 但我刚刚注意到我的代码显示了一些性能问题 如果你快速调整它的大小 你会发现窗口没有像我希望
  • 将构建日期放入“关于”框中

    我有一个带有 关于 框的 C WinForms 应用程序 我使用以下方法将版本号放入 关于 框中 FileVersionInfo GetVersionInfo Assembly GetExecutingAssembly Location F
  • 当“int”处于最大值并使用 postfix ++ 进行测试时,代码定义良好吗?

    示例 未定义行为的一个示例是整数溢出的行为 C11dr 3 4 3 3 int溢出是未定义的行为 但这是否适用于存在循环的以下内容 并且不使用现在超出范围的副作用i 特别是 这是否后缀增量规格帮助 结果的值计算在副作用之前排序 更新操作数的
  • 在 C 中使用 GNU automake 中的解析器

    我是 GNU autotools 的新手 在我的项目中使用了 lex 和 yacc 解析器 将它们作为 makefile am 中的源代码会产生以下错误 配置 in AC CHECK PROGS YACC bison yacc none i
  • 剪贴板在 .NET 3.5 和 4 中的行为有所不同,但为什么呢?

    我们最近将一个非常大的项目从 NET Framework 3 5 升级到 4 最初一切似乎都工作正常 但现在复制粘贴操作开始出现错误 我已经成功制作了一个小型的可复制应用程序 它显示了 NET 3 5 和 4 中的不同行为 我还找到了一种解
  • 转到定义:“无法导航到插入符号下的符号。”

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 我今天突然开始在我的项目中遇到一个问题 单击 转到定义 会出现一个奇怪的错误 无法导航到
  • 运算符“==”不能应用于“int”和“string”类型的操作数

    我正在编写一个程序 我想到了一个数字 然后计算机猜测了它 我一边尝试一边测试它 但我不断收到不应该出现的错误 错误是主题标题 我使用 Int Parse 来转换我的字符串 但我不知道为什么会收到错误 我知道它说 不能与整数一起使用 但我在网
  • 我在在线程序挑战编译器中遇到演示错误

    include
  • WinRT 定时注销

    我正在开发一个 WinRT 应用程序 要求之一是应用程序应具有 定时注销 功能 这意味着在任何屏幕上 如果应用程序空闲了 10 分钟 应用程序应该注销并导航回主屏幕 显然 执行此操作的强力方法是在每个页面的每个网格上连接指针按下事件 并在触
  • 匿名结构体作为返回类型

    下面的代码编译得很好VC 19 00 23506 http rextester com GMUP11493 标志 Wall WX Za 与VC 19 10 25109 0 标志 Wall WX Za permissive 这可以在以下位置检

随机推荐

  • 添加带动画的子视图

    任何人都可以帮我添加带有动画的子视图吗 我想添加带有像 CATransition 这样的动画的子视图 但是对于这个类 我们只有几种不同的动画类型 但我正在寻找实现它自己的动画的能力 视图的不同部分出现在不同的时间 也许存在一些例子或其他东西
  • watchOS2 通知模拟器

    我在 XCode 上的 watchOS2 模拟器中遇到了一个奇怪的错误 我使用 UILocalNotification 处理通知并修改静态通知的情节提要 但是当我检查值 Wants Sash Blur 并构建时 它会出现与 Carousel
  • 如何模拟反应路由器上下文

    我有相当简单的反应组件 链接包装器 如果路由处于活动状态 则会添加 活动 类 import React PropTypes from react import Link from react router const NavLink pro
  • C# 裁剪图像返回错误坐标

    几天来我一直在尝试使用 Selenium 和不同的裁剪方法来裁剪特定的图像 在我的代码之前有一个重要的说明 以下方法在两周前曾经有效 由于某种原因它现在返回一个坐标错误的图像 Go to site Driver Navigate GoToU
  • printf 忽略单个反斜杠 '\'

    我有这个代码 int main int argc char argv int i printf d s argc argv 1 return 0 如果我运行这段代码a out a b 我在用C shell 其输出为 a b 有什么方法可以将
  • 如何在大型机 COBOL 中“休眠”?

    我想我正在使用 Enterprise COBOL for z OS 模拟标准 C 库的 sleep 函数等功能的技术是什么 可能最简单的方法是使用语言环境可调用服务 https www ibm com docs en zos 2 4 0 t
  • 始终独立于目标执行 Ant 操作

    有没有办法在 Ant 构建文件中指定诸如 之类的操作 每次 ant 读取构建文件时都会执行该操作 无论调用的目标是什么 背景是 我希望在模板不存在时自动从模板创建 properties 文件 我知道 我可以指定一个执行此操作的目标 然后将其
  • 使用依赖项从 Swift 2.3 迁移到 Swift 3

    我正在努力将我的项目从 Swift 2 3 迁移到 Swift 3 但我通过 Cocoapods 使用的第三方框架遇到了问题 其中许多已经升级到 Swift 3 但也有一些尚未升级 当我构建项目时 出现错误 使用 Swift 2 3 编译的
  • 将图像放置在 CSS 中另一个现有的 `` 上

    我正在做一个页面 其中显示视频的缩略图 当您单击时 它会弹出一个 YouTube 视频 这个缩略图是 195x195 的简单图像 但最终客户端会按原样上传 我想通过 CSS 在视频图像上添加一个 播放图标 与 IE7 兼容 我不知道如何处理
  • 如何将本地 ORC 文件转换为 CSV?

    我的本地计算机上有一个 ORC 文件 我需要其中的任何合理格式 例如 CSV JSON YAML 如何将 ORC 转换为 CSV Download https orc apache org docs java tools html 解压文件
  • 访问类内的函数指针

    我在类中定义函数指针并尝试通过该类的实例访问它 但它显示错误 这是代码 1 include
  • 无法在 Eclipse 中将项目添加到 Tomcat 服务器

    我无法将我的项目添加到 Eclipse 中的服务器 这是为什么 我安装了所有必要的工具 Web Dev Java EE 服务器适配器和 Tomcat 本身 一切 我配置了运行时环境 将所有 Java 版本调整为 JDK 6 因为它应该在 T
  • 有效计算边界自适应邻域平均值

    我有一张图像 其值范围为 0 到 1 我喜欢做的是简单的平均 但是 更具体地说 对于图像边界处的单元格 我想计算位于图像范围内的邻域 内核部分的像素平均值 事实上 这可以归结为适应 平均公式 的分母 即总和除以的像素数 我设法做到这一点 如
  • 将风格应用于第一个孩子?

    有没有某种方法可以将样式应用于容器的第一个 或最后一个或第 n 个 子容器 任何包含子容器的容器 我正在尝试自定义选项卡项目的外观 以便第一个选项卡具有与其他选项卡不同的边框半径 这就是我现在所拥有的
  • 让 Java servlet 充当代理的代码?

    我有两个 Java Web 应用程序 它们有一个映射到特定 URL 的 servlet red war WEB INF classes com me myorg red RedServlet maps to http red example
  • 一个更好的程序来计算数字的平方根

    我是 C 编程语言的初学者 我的任务是编写一个程序来计算自然数的平方根 而不使用math h或任何其他库函数 除了stdio h 看看我们的合作伙伴int自始至终 对于非平方数 我们求 n 的平方根的下限 现在我已经编写了这个程序 但它只适
  • 带 goto 的扩展 asm(包括 gcc 文档中的示例)无法编译

    一些扩展的汇编语句使用goto限定符无法使用 GCC 10 1 0 进行编译 具体来说 int foo int count asm goto dec 0 jb l stop r count stop return count stop re
  • 仍登录 MVC 站点,但无法调用 Web API

    我有一个 ASP NET MVC 站点 IdentityServer4 主机和一个 Web API 当我使用外部提供商 Facebook 登录 MVC 站点时 我登录得很好 从 MVC 站点我还可以正确使用 Web API 然而 第二天 我
  • PHP 中的百分号是什么意思?

    这到底是什么意思 number 3 2 7 7 这是modulus http ca php net manual en language operators arithmetic php如上所述 运算符返回除法运算的余数 例子 3 5返回
  • 在 Windows 上向 QProcess 发送 Ctrl+C

    抓紧你的马鞍 这是一个很长的马鞍 如果您不想阅读所有内容 请跳至 MCVE 部分 我正在尝试制定一个流程QProcess优雅地退出 我不控制有问题的进程如何退出 它只接受 Ctrl C 信号 令我困惑的是 这听起来非常简单明了QProces