Skip to content

创建线程的方式打开记事本

通过创建进程的方式打开记事本


今天操作系统课老师讲到进程,提出了一个有趣的小实验:能否以系统调用的方式,利用 Windows 创建进程的系统调用函数来打开一个软件。闲得无聊的我立马来了兴趣,姑且写一个玩玩(


头文件


  • <windows.h>:包含了 Windows API 的核心功能。
  • <tchar.h>:提供了一种跨平台的方式来处理 UnicodeANSI 字符集,防止出现一些编码错误。
  • <cstdio>:包含了 C 标准输入输出函数的声明。

核心函数 CreateProcess


CreateProcessWindows API 中的一个核心函数,用于创建一个新进程及其主线程。这是我们程序的关键函数,其函数原型如下:

cpp
BOOL CreateProcess(
  LPCTSTR               lpApplicationName,
  LPTSTR                lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCTSTR               lpCurrentDirectory,
  LPSTARTUPINFO         lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

参数说明:

  • lpApplicationName:指向可执行文件名的指针。
  • lpCommandLine:命令行字符串。
  • lpProcessAttributes:指向进程安全属性的指针。
  • lpThreadAttributes:指向线程安全属性的指针。
  • bInheritHandles:是否继承句柄。
  • dwCreationFlags:创建标志。
  • lpEnvironment:指向环境块的指针。
  • lpCurrentDirectory:指向当前工作目录的指针。
  • lpStartupInfo:指向 STARTUPINFO 结构的指针。
  • lpProcessInformation:指向 PROCESS_INFORMATION 结构的指针,用于接收新进程标识信息。

即:CreateProcess 函数创建一个新进程,并通过 lpProcessInformation 返回其句柄等信息。如果函数成功,返回值为非零值;否则返回值为零。

句柄

  • 有趣的是,在 Windows 里并没有进程层次的概念,所有进程的地位都是相同的。
  • 在创建进程时,函数会通过 PROCESS_INFORMATION 结构返回进程和主线程的句柄,用于控制该进程和线程。
  • 这些句柄是可以传递的,但通常进程间并不因此形成层次关系。

注意

  • 新进程的执行体是通过 lpApplicationNamelpCommandLine 参数指定的。
    • 创建的进程可以并发地与其他进程执行,但执行顺序和调度由操作系统决定。
    • 在使用 CreateProcess 函数创建进程后,需要使用 CloseHandle 函数关闭获取的句柄,以释放资源。

实现代码


cpp
#include <windows.h>
#include <tchar.h>
#include <cstdio>

DWORD WINAPI OpenNotepadThread(LPVOID lpParam) {
    // 定义要打开的应用程序的路径
    LPCTSTR appName = _T("notepad.exe");

    // 创建进程信息结构体
    STARTUPINFO startupInfo;
    PROCESS_INFORMATION processInfo;

    // 初始化进程信息结构体
    ZeroMemory(&startupInfo, sizeof(startupInfo));
    startupInfo.cb = sizeof(startupInfo);
    ZeroMemory(&processInfo, sizeof(processInfo));

    // 创建新进程
    if (!CreateProcess(
        NULL,                   // 指向可执行文件名的指针
        (LPTSTR)appName,        // 命令行参数
        NULL,                   // 进程句柄不可继承
        NULL,                   // 线程句柄不可继承
        FALSE,                  // 不继承句柄
        0,                      // 无特殊标志
        NULL,                   // 使用父进程的环境变量
        NULL,                   // 使用父进程的工作目录
        &startupInfo,           // 启动信息
        &processInfo            // 进程信息
    )) {
        _tprintf(_T("ERROR: %d\n"), GetLastError());
        return 1;
    }

    // 等待新进程结束
    WaitForSingleObject(processInfo.hProcess, INFINITE);

    // 关闭进程和线程句柄
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);

    return 0;
}

int main() {
    // 创建线程
    HANDLE hThread = CreateThread(NULL, 0, OpenNotepadThread, NULL, 0, NULL);

    if (hThread == NULL) {
        _tprintf(_T("ERROR: %d\n"), GetLastError());
        return 1;
    }

    // 等待线程结束
    WaitForSingleObject(hThread, INFINITE);

    // 关闭线程句柄
    CloseHandle(hThread);

    return 0;
}

在上述代码中,我定义了一个名为 OpenNotepadThread 的函数,它是一个线程函数,用于打开记事本应用程序。该函数的参数类型为 LPVOID,表示一个指向任意类型的指针。然后创建进程信息结构体 STARTUPINFOPROCESS_INFORMATION,并对其进行了初始化。

接下来调用 CreateProcess 函数用于创建一个新的进程,返回进程和主线程的句柄等信息。它的参数包括可执行文件名、命令行参数、进程句柄和线程句柄是否可继承等信息。如果创建进程失败,会打印错误代码。

调用 WaitForSingleObject 函数用于等待一个对象的状态变为可信,即等待进程结束。它的参数包括要等待的对象句柄和等待的时间,这里使用 INFINITE 表示无限等待,直到进程结束。

当进程结束后,需要调用 CloseHandle 函数关闭进程和线程的句柄,释放资源。


测试效果


image-20230920011044280

😄