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


    更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验


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


    头文件


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

    CreateThread


    CreateThread Windows API 中的一个函数,用于创建一个新的线程。

    这就是我们程序的核心函数,其函数的原型如下:

    HANDLE CreateThread(
      LPSECURITY_ATTRIBUTES   lpThreadAttributes,
      SIZE_T                  dwStackSize,
      LPTHREAD_START_ROUTINE  lpStartAddress,
      LPVOID                  lpParameter,
      DWORD                   dwCreationFlags,
      LPDWORD                 lpThreadId
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    参数说明:

    • lpThreadAttributes:指向 SECURITY_ATTRIBUTES 结构的指针,用于指定新线程的安全性。可以设置为 NULL,表示使用默认的安全性。
    • dwStackSize:指定新线程的堆栈大小。可以设置为 0 0 0,表示使用默认的堆栈大小。
    • lpStartAddress:指向线程函数的指针,表示新线程的入口点。线程函数的原型为DWORD WINAPI ThreadProc(LPVOID lpParameter),其中lpParameter为传递给线程函数的参数。
    • lpParameter:传递给线程函数的参数,可以是任意类型的指针。
    • dwCreationFlags:指定线程的创建标志。可以设置为 0 0 0,表示无特殊标志。
    • lpThreadId:指向DWORD类型变量的指针,用于接收新线程的标识符。

    即:CreateThread 函数创建一个新的线程,并返回该线程的句柄。如果创建线程成功,返回值为线程的句柄;否则返回值为 NULL

    句柄

    • 有趣的是,在 Windows 里并没有进程层次的概念,所有进程的地位都是相同的。
    • 在创建进程时,父进程会得到一个特别令牌(句柄),用于控制子进程。
    • 该令牌是可以传递的,即父进程有权将该令牌传递给其他进程,以至于不存在了进程层次的概念。

    注意

    • 新线程的入口点是通过 lpStartAddress 参数指定的线程函数。线程函数在新线程中执行,可以执行各种任务。
    • 线程函数的返回值是一个 DWORD 类型的值,表示线程的退出码。
    • 通过 CreateThread 函数创建的线程是可执行的,它可以并发地与其他线程执行,但线程的执行顺序和调度由操作系统决定。
    • 在使用CreateThread函数创建线程后,需要使用 CloseHandle 函数关闭线程句柄,以释放资源。

    实现代码


    #include 
    #include 
    #include 
    
    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("WRONGING:%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("WRONGING:%d\n"), GetLastError());
            return 1;
        }
    
        // 等待线程结束
        WaitForSingleObject(hThread, INFINITE);
    
        // 关闭线程句柄
        CloseHandle(hThread);
    
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

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

    接下来调用 CreateProcess 函数用于创建一个新的进程,返回进程的句柄和线程的句柄。它的参数包括可执行文件名、命令行参数、进程句柄和线程句柄是否可继承等信息。如果创建进程成功,返回值为 0 0 0;否则返回值为 1 1 1

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

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


    测试效果


    image-20230920011044280

    🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗🤗

  • 相关阅读:
    HCIP---VLAN
    玩转NAS | 打造一个动态网关,部署OpenResty - Nginx与Lua的强强联合
    ISR实现RDN图像增强
    Zookeeper
    基于CentOS7搭建Linux, Nginx, MySQL, PHP环境
    关于js_Element对象_属性和获取元素位置的方法
    Spring学习笔记(二)Spring的控制反转(设计原则)与依赖注入(设计模式)
    Unity ECS 内存分配器原理详解
    为爱出发,与善同行丨纬创软件2023北京善行者圆满收官
    无视Win11 TPM/英特尔芯片等配置,强制升级Win11
  • 原文地址:https://blog.csdn.net/LYS00Q/article/details/133062090