《windows核心编程》第4章 进程

一、进程基本概念

1、进程:一个进程就是一个正在运行的程序,一个程序可以产生多个进程。进程包含下面两个东西

进程内核对象一个内核对象被系统用来管理某个进程,内核对象就是代表这个进程。这个内核对象中,还包含了进程的一些策略,策略信息是系统怎么给进程分配时间、怎么调内核的优先级

● 地址空间:地址空间中包含了可执行代码、动态链接库模块代码、数据、程序动态内存分配获取的内存。

2、进程的特性

进程是静态的,进程中真正执行代码的是线程,所以进程必须有一个主线程primarythread。两种方法退出进程,第一如果进程内所有线程退出,那么进程自动销毁;第二使用函数ExitProcess函数强制退出当前进程

把进程比喻成工厂,把工厂中的人比作线程,工厂给人提供资源(场地、加工设备、原材料),主线程是随进程产生(工厂产生的时候同时产生它的厂长)。厂长工作第一件事就是招人,线程也是一样,产生后,就生成其他线程开始干活了。

二、WINDOW程序执行流程

Windows程序分两种,一个是控制台程序CUI、一个是图形界面程序GUI。如果我们选错了,可以在下图相应的位置修改项目属性。

我们所编写的windows程序中,真正第一个被执行的函数是:

WinMainCRTStartup // GUI_ASCII字符类型C运行时库的入口函数

wWinMainCRTStartup // GUI_UNICODE字符类型C运行时库的入口函数

mainCRTStartup // CUI_ASCII字符类型C运行时库的入口函数

WmainCRTStartup // CUI_UNICODE字符类型C运行时库的入口函数

C运行时库函数,主要完成以下任务

1. 获取进程命令行指针;

2. 获取进程环境变量指针;

3. 初始化C/C++运行时库的全局变量,如果你包含了头stdlib.h,那么你就可以访问这些变量;

4. 初始化malloc函数的内存堆;

5. 为C++全局类,调用构造函数。

C语言库函数,例如malloc函数最终会调用windowsAPI函数,我们直接调用virtualAlloc windowsAPl函数,效率会更高

程序执行流程

-> Kernerl调用XXXCRTStartup函数

-> XXXCRTStartup函数调用main函数

-> main函数退出

-> exit函数被调用(真正调用的是crt0dat.c中的static void __cdecl doexit(int code int quick, int retcaller);

exit函数主要做以下事情

1. 调用_onexit函数注册的所有函数;

2. 调用C++销毁函数销毁所有的全局和静态类对象;

3. 如果_CRTDBG_LEAK_CHECK_DF标志被设置,那么调用CrtDumpMemoryLeaks函数,列出泄露内存;

4. 调用ExitProcess函数,系統杀死当前进程

如果我们直接调用ExitProcess,那么前面的内存释放工作都不会做,所以可以会存在一些问题

三、程序运行系统和版本

#include <stdio.h>

#include <windows.h>

//操作系统版本

#define WINXP 51

#define WINXP2600 512600

#define WIN7 61

#define WIN77600 617600

#define WIN77601 617601

#define WIN8 62

#define WIN89200 629200

#define WIN81 63

#define WIN819600 639600

#define WIN10 100

#define WIN1010240 10010240

#define WIN1010586 10010586

#define WIN1014393 10014393

int main(void)

{

//定义变量

typedef LONG(__stdcall *fnRtlGetVersion)(PRTL_OSVERSIONINFOW lpVersionInformation);

fnRtlGetVersion pRtlGetVersion;

HMODULE hNtdll;

LONG ntStatus;

ULONG    dwMajorVersion = 0;

ULONG    dwMinorVersion = 0;

ULONG    dwBuildNumber = 0;

RTL_OSVERSIONINFOW VersionInformation = { 0 };

DWORD OsVersion;

do

{

hNtdll = GetModuleHandle(L"ntdll.dll");

if (hNtdll == NULL)break;

pRtlGetVersion = (fnRtlGetVersion)GetProcAddress(hNtdll, "RtlGetVersion");

if (pRtlGetVersion == NULL)break;

VersionInformation.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOW);

ntStatus = pRtlGetVersion(&VersionInformation);

if (ntStatus != 0)break;

dwMajorVersion = VersionInformation.dwMajorVersion;

dwMinorVersion = VersionInformation.dwMinorVersion;

dwBuildNumber = VersionInformation.dwBuildNumber;

if (dwMajorVersion == 5 && dwMinorVersion == 1 && dwBuildNumber == 2600)

OsVersion = WINXP2600;

else if (dwMajorVersion == 5 && dwMinorVersion == 1)

OsVersion = WINXP;

else if (dwMajorVersion == 6 && dwMinorVersion == 1 && dwBuildNumber == 7601)

OsVersion = WIN77601;

else if (dwMajorVersion == 6 && dwMinorVersion == 1 && dwBuildNumber == 7600)

OsVersion = WIN77600;

else if (dwMajorVersion == 6 && dwMinorVersion == 1)

OsVersion = WIN7;

else if (dwMajorVersion == 6 && dwMinorVersion == 2 && dwBuildNumber == 9200)

OsVersion = WIN89200;

else if (dwMajorVersion == 6 && dwMinorVersion == 2)

OsVersion = WIN8;

else if (dwMajorVersion == 6 && dwMinorVersion == 3 && dwBuildNumber == 9600)

OsVersion = WIN819600;

else if (dwMajorVersion == 6 && dwMinorVersion == 3)

OsVersion = WIN81;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 10240)

OsVersion = WIN1010240;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 10586)

OsVersion = WIN1010586;

else if (dwMajorVersion == 10 && dwMinorVersion == 0 && dwBuildNumber == 14393)

OsVersion = WIN1014393;

else if (dwMajorVersion == 10 && dwMinorVersion == 0)

OsVersion = WIN10;

else

{

return FALSE;

}

} while (FALSE);

printf("%d\n", OsVersion);

getchar();

getchar();

return 0;

}

四、进程句柄

1、进程句柄,要和进程内核对象的句柄区分开来,进程句柄也可以叫做模块句柄。每个可执行文件或者DLL文件被装入到某个进程的地址空间,都会有一个唯一的实例句柄,来表示装入后的可执行文件或者DLL,此时把这个可执行文件或者DLL叫做进程地址空间中的一个模块。

进程句柄(HINS)在程序很多地方都被使用,尤其是在装入某一个资源的时候,下面代码是装入图标资源的时候使用进程句柄hInstance:

LoadIcon(

HINSTANCE hInstance;

PCTSTR pszIcon);

2、获得进程句柄的两种方法

方法1:WinMain函数的参数

WinMain函数的第一个参数就是当前可执行程序的进程句柄

方法2:使用GetModuleHandle函数

在DLL中调用GetModuleHandle返回的不是DLL模块的地址,而是当前进程的模块地址

这个函数只检查本进程地址空间,不检查别的进程的地址空间

3、有进程句柄获取进程模块的路径和名字

DWORD GetModuleFileName (

HMODULE hInstance,

PTSTR pszPath,

DWORD cchPath);

五、进程命令行参数

方法1:入口函数传入参数

方法2:GetCommandLine();

六、环境变量操作

6.1 获取当前环境变量

方法1:使用全局变量

方法2:放在函数里

环境变量被保存放在注册表中

这个注册表键下放的是系统环境变量:HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSer \ Control \ Session Manager \ Environment

这个注册表键下放的是当前登陆用户自己的环境变量:HKEY_CURRENT_USER \ Environment

在你的程序中,如果你改变了环境变量,那么最好你发送一个消息:WM_SETTINGCHANGE。发这个消息时,不要使用某一个窗口的句柄,应该使用:HWND_BROADCAST作为窗口句柄。使用SendMessage或者SendMessageTimeout函数来发送这个消息,IParam参数应该设置成"Environment"。

#include <windows.h>

#include <stdio.h>

#include <fcntl.h>

#include <io.h>

int main()

{

wchar_t buf[1024];

DWORD i = GetEnvironmentVariableW(TEXT("Path"), buf, 1024);

_setmode(_fileno(stdout), _O_U16TEXT);

wprintf(TEXT("str: %s\n"), buf);

//SetEnvironmentVariable(L"asd", L"qwe");

getchar();

return 0;

}

6.2 改变本进程的环境变量

修改系统环境变量要改注册表

6.3 解析系统变量中的%

七、进程目录操作

7.1 GetCurentDirectory获取进程的当前目录

DWORD WINAPI GetCurrentDirectory(

___in DWORD nBufferLength,

___out LPTSTR IpBuffer

);

nBufferLength: lpBuffer指针指向内存块的大小 (单位TCHAR) ;

lpBuffer: 接收当前路径的内存块。

7.2 SetCurrentDirectory设置进程当前目录

_chdir: 设置当前目录

7.3 代码测试

#include <windows.h>

#include <stdio.h>

#include <fcntl.h>

#include <io.h>

int main()

{

_setmode(_fileno(stdout), _O_U16TEXT);

wchar_t* szPath[MAX_PATH];

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)szPath);

wprintf(TEXT("%s\n"), szPath);

wchar_t* str = TEXT("D:\\ico");

SetCurrentDirectoryW(str);

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)szPath);

wprintf(TEXT("%s\n"), szPath);

getchar();

return 0;

}

7.4 GetFullPathName补齐目录

DWORD WINAPI GetFullPathName(

__in LPCTSTR lpFileName,

__in DMORD nBufferLength,

__out LPTSTR lpBuffer,

__out LPTSTR *lpFilePart

);

lpFileName: 文件名

NBufferlength: 获取全路径的内存大小( TCHAR )

lpBuffer: 内存指针

lpFilePart: 文件名最后一个元素,在lpBuffer中的位置。

上面生成的路径,实际上我磁盘中有D:\\ico,但是没有D:\\ico\\myIco


八、创建进程

8.1 用到的函数

BOOL WINAPI CreateProcess (

__in_opt LPCTSTR lpApplicationName,

__inout_opt LPTSTR lpCommandLine ,

__in_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,

__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,

__in BOOL bInheritHandles,

__in DWORD dwCreationFlags,

_in_opt LPVOID lpEnvironment,

__in_opt LPCTSTR lpCurrentDirectory,

__in LPSTARTUPINFO IpStartupInfo,

__out LPPROCESS_INFORMATION lpProcessInformation

);

简单使用如下,我们关闭控制台,计算机没有被关闭

8.2 参数1:lpApplicationName

被执行的模块的名称,这个模块可以是一个windows应用程序,也可以是其他类型的模块(例如MS-DOS或者OS/2)。如果要运行这些类型的模块,操作系统必须有支持这些模块运行的子系统

可以指定文件的全路径名和文件名,或者相对当前程序运行路径(前面讲了修改程序当前运行路径的函数)的文件名。参数要包含文件的拓展名

8.3 参数2:lpCommandLine

被执行的命令行参数。这个字符串的最大长度可以达到32768个字符,包括null结尾符。如果lpApplicationName是NULL,那么lpCommandLine参数中的可执行文件的名字被限定在MAT_PATH个字符之内。有命令行参数启动进程的方法如下

如果路径有空格,需要用引号括起来

8.4 参数3:lpProcessAttributes

进程的安全描述符

8.5 参数4:lpThreadAttributes

线程的安全描述符,参数3和参数4有专门的语言来编写

8.6 参数5:dwCreationFlags

这里面存放创建时的标志。可以设置进程的优先级,并且规定进程是怎么创建的,详情阅读进程创建标志:

WinBase.h) (进程创建标志 - Win32 apps | Microsoft Learn

我们测试几个

首先建一个这样的项目:

测试CREATE_NEW_CONSOLE标志,如果不加这个标志,不会打开新的控制台

如果加了这个标志,就会重新打开一个控制台打印新进程的输出

进程优先级的标志

8.7 参数6:lpEnvironment

一个指向环境变量内存块的指针。如果这个参数是NULL,那么新进程使用父进程的环境变量。环境变量块保存的是一个NULL结尾的字符串,每个环境变量在改字符串中的形式为:name=value\0

8.8 参数7:lpCurrentDirectory

进程的当前目录

8.9 参数8:lpStartupInfo

启动信息。里面是一个指向STARTUPINFO或者STARTUPINEX结构的指针

8.10 参数9:lpProcessInformation

一个指向PROCESS_INFORMATION结构的指针。

typedef struct PROCESS_INFORMATION {

HANDLE hProcess; // 进程句柄

HANDLE hThread; // 主线程句柄

DWORD dwProcessld; // 进程 ID

DWORD dwThreadld; // 主线程 ID

} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

8.11 进程和进程内核对象的关系

进程内核对象的生命周期一定比进程生命周期长,进程没有退出代表进程的进程内核对象一定不会被销毁。当进程退出以后,进程内核对象才能被销毁。进程内核对象的引用计数为零时,销毁进程内核对象。当进程退出的时候,进程内核对象的状态发生变化。我们把进程推出后,代表该进程的内核对象的状态叫激发态

、结束进程

9.1 进程结束的方式

1、进程的主线程返回(最好的方式)

2、进程中的一个线程调用ExitProcess函数(不推荐)

3、其他进程中的某个线程调用TerminateProcess函数(不推荐)

4、进程中的所有线程执行完毕(这个很少出现)

9.2 方式1

进程的主线程返回(最好的方式)

主线程就是main、winmain函数代表的线程,也就是进程中的第一个线程。无论这个进程还有多少其他线程,只要主线程一退出,那么这个进程就结束了,其他线程自然就被系统杀死了。

#include <Windows.h>

#include <stdio.h>

DWORD WINAPI ThreadProc(__in LPVOID lpParameter)

{

wprintf(TEXT("Thread sleeping!\n"));

Sleep(INFINITE); // 让这个线程无限沉睡下去

return 0;

}

int main()

{

DWORD dwThreadID = 0;

CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwThreadID); // 创建一个线程

wprintf(TEXT("The sleeping thread ID is %d\n"), dwThreadID);

getchar();

return 0;

}

可以通过工具查看进程当前情况

9.3 方式2

进程中的一个线程调用ExitProcess函数(不推荐)

这个函数只能结束本进程,不能把其他的线程结束掉

9.4 方式3

其他进程中的某个线程调用TerminateProcess函数(不推荐)

只要你有足够的权限,你就可以使用这个TerminateProcess函数,结束系统中的任意进程。同时这个TerminateProcess函数是一个异步函数,下面是退出edge的代码

9.5 进程结束时的动作

1. 所有线程结束

2. 所有用户GDI对象被释放,所有内核对象被关闭。

例如画笔,背景刷,句柄,设备句柄,申请的内存等。

3. 退出码被设置。(退出码就是main或者WinMain的返回值,或者ExitProcess, TerminateProcess函数给出的返回值)

BOOL GetExitCodeProcess(

HANDLE hProcess,

PDWORD * pdwExitCode);

如果进程依然在运行,那么这个函数返回STILL _ACTIVE。

4. 进程内核对象的状态变为有信号状态。

WaitForSingleObject(

__in HANDLE hHandle,

__in DWORD dwMillionseconds);

5. 进程内核对象的计数减一 。

6. 如果在进程中,直接调用ExitProcess或者TerminateProcess函数,那么C++类的销毁函数可能不会被调用!!!!

9.6 项目:自制CMD

#include <Windows.h>

#include <stdio.h>

int main()

{

WCHAR ws_Directory[MAX_PATH] ;

WCHAR str_Command[MAX_PATH] = { 0 };

STARTUPINFO start_info = { 0 };

PROCESS_INFORMATION info = { 0 };

start_info.dwFillAttribute = sizeof(start_info);

// 不区分大小写,比较输入的内容是否是quit,如果是就退出循环

while(CompareStringOrdinal(str_Command, 4, L"quit", 4, TRUE) != CSTR_EQUAL)

{

GetCurrentDirectoryW(MAX_PATH, (LPWSTR)ws_Directory);

wprintf(L"%s : ", ws_Directory);

_getws_s(str_Command, MAX_PATH);

CreateProcessW(NULL, str_Command, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&start_info, &info);

}

wprintf(L"press any key to quit\n");

return 0;

}

父进程在创建完子进程后,立即关闭子进程的进程句柄就可以断绝父子关系了

十、用户提权

10.1 简介

用户权限的提升:

UAC (User Account Control, 用户帐户控制) 是微软为提高系统安全而在Windows Vista中引入的新技术。要关闭它,可以进行如下操作:

Windows登录账户后给session一个token (令牌),计算机中每启动一个进程,这个进程就代表你去工作,计算机需要给进程令牌。如果没有以管理员权限运行,给的就是默认的令牌,就是削减过权限的filter token

10.2 查看程序是否有管理员权限

1、获取到的权限枚举

进程权限的检查:

// pElevationType:当前的权限类型:

typedef enum {

TokenElevationTypeDefault,

TokenElevationTypeFull,

TokenElevationTypeLimited

} TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE;

TokenElevationTypeDefault:进程以缺省的用户身份运行,或者没有使用UAC功能。用户没有启动UAC功能,那么程序使用的令牌一定不是Filter Token

TokenElevationTypeFull:进程的权限已经被提升,进程令牌没有使用filter令牌

TokenElevapionTypelimited:进程使用的令牌是Filter令牌。此时通过GetTokenInformation函数,并使用TokenLinkedToken标志,来获取原令牌。

1. 先通过GetTokenInformation函数来确定当前程序的令牌是否位Filter Token

2. 如果是,那么通过GetTokenInformation函数,来获得FilterToken 的原始令牌。

3. 在获得原始令牌后,通过CheckTokenMembership函数确认在原始令牌中有没有管理员的帐号。如果有,那么说明启动当前程序的账户是管理员帐号,否则就不是。CheckTokenMembership确认一个SID是否在一个Token中可用。

4. 如果不是Flter Token,那么直接使用IsUserAnAdmin函数,来确定启动当前程序的帐号

是否为管理员帐号。

#include <Windows.h>

#include <stdio.h>

#include <shlobj.h>

// 作用: 函数用于确认运行当前程序的账号是否为管理员账号

// 参数: pElevationType : 令牌提升类型

//       pIsAdmin : 是否是管理员

// 返回: 函数返回TRUE代表函数执行成功

BOOL GetProcessElevation(TOKEN_ELEVATION_TYPE * pElevationType, BOOL * pIsAdmin)

{

HANDLE hToken = NULL;

DWORD dwSize;

// 获得当前进程的令牌句柄

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))

return(FALSE);

BOOL bResult = FALSE;

// 看看这个令牌的权限提升类型是哪一种: TokenElevationTypeDefault、TokenElevationTypeFull、TokenElevapionTypelimited

if (GetTokenInformation(hToken, TokenElevationType, pElevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize))

{

// 创建一个管理员SID

BYTE adminSID[SECURITY_MAX_SID_SIZE];

dwSize = sizeof(adminSID);

CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);

if (*pElevationType == TokenElevationTypeLimited)

{

// 通过Filter Token来获得原始的Token

HANDLE hUnfilteredToken = NULL ;

// TokenLinkedToken标志,表示要获得Filter Token的原始Token

GetTokenInformation(hToken, TokenLinkedToken, (VOID*)&hUnfilteredToken, sizeof(HANDLE), &dwSize);

// 检查原始Token中,管理员账户adminSID是否被激活,如果被激活,那么说明启动这个程序的账号是管理员账号

// CheckTokenMembership函数结果被保存在pIsAdmin参数中,函数的返回值表示这个函数是否执行成功

if (CheckTokenMembership(hUnfilteredToken, &adminSID, pIsAdmin))

{

bResult = TRUE;

}

// 不要忘记关闭原始令牌

CloseHandle(hUnfilteredToken);

}

else

{

// 如果是原始令牌,只要IsUserAnAdmin就可以确定,启动当前程序的账号是否是管理员账号

*pIsAdmin = IsUserAnAdmin();

bResult = TRUE;

}

}

// 不要忘记关闭进程令牌

CloseHandle(hToken);

return(bResult);

}

int main()

{

TOKEN_ELEVATION_TYPE Type;

BOOL IsAdmin;

BOOL ret = GetProcessElevation(&Type, &IsAdmin);

if (ret)

{

wprintf(L"%d, Type\n");

if (IsAdmin)

wprintf(L"On Admin running!\n");

else

wprintf(L"On normal acount running!\n");

}

getchar();

return 0;

}

10.3 程序权限调整

#include <Windows.h>

#include <stdio.h>

#include <processthreadsapi.h>

// 作用: 函数将进程权限提升成具有调试权限的进程,这个权限应该是进程所能具有的最大权限

//       这个函数成功的前提,启动这个进程的账号必须是管理员账号

// 参数: fEnable : 为TRUE表示授予当前进程调试权限,为FALSE表示取消当前进程调试权限

// 返回: 函数返回TRUE代表函数调整权限成功

BOOL EnableDebugPrivilege(BOOL fEnable)

{

// 调试权限可以让该进程能够读取其他进程的信息

BOOL fok = FALSE;

HANDLE hToken;

// 获取当前进程的令牌

// 这个函数第一个参数是当前进程句柄,第二个参数是进程对获得的令牌有哪些操作权限

if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))

{

// 开始激活当前令牌的调试权限

TOKEN_PRIVILEGES tp; // 结构体,表示令牌权限

tp.PrivilegeCount = 1; // 我们启动调试权限,所以给1

// 查找调试权限的LVID,如果第一个参数是NULL,表示获取本地的某个权限的LVID

// 函数LookupPrivilegeValue获取本地系统的调试的LVID

LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

// 在tp.Privileges[0].Attributes属性中设置开启调试权限还是关闭调试权限

// Attributes=SE_SE_PRIVILEGE_ENABLED时,激活权限

// Attributes=0时,关闭权限

tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;

// AdjustTokenPrivileges激活或者关闭tp中给定的权限

AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

// 确定是否激活成功

fok = (GetLastError() == ERROR_SUCCESS);

CloseHandle(hToken);

}

return(fok);

}

int main()

{

if (EnableDebugPrivilege(TRUE))

{

wprintf(L"Enable Debug privilege is ok!\n");

}

else

{

wprintf(L"Enable Debug privilege is Failure!\n");

}

EnableDebugPrivilege(FALSE);

getchar();

return 0;

}

10.4 以管理员权限启动其他程序

SHELLEXECUTEINFO ShExecInfo = { 0 };

ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);

ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;

ShExecInfo.hwnd = NULL;

ShExecInfo.lpVerb = L"runas";

ShExecInfo.lpFile = L"cmd";

ShExecInfo.lpParameters = L"";

ShExecInfo.lpDirectory = NULL;

ShExecInfo.nShow = SW_SHOW;

ShExecInfo.hInstApp = NULL;

ShellExecuteEx(&ShExecInfo);

十一、获取所有进程和进程中DLL

11.1 获取所有进程

#include <stdio.h>

#include <stdlib.h>

#include <Windows.h>

#include <TlHelp32.h>

int main(int argc, char* argv[])

{

HANDLE hProcessSnap = NULL;

PROCESSENTRY32 pe32 = {0};

//在使用这个结构前,先设置它的大小

pe32.dwSize = sizeof(PROCESSENTRY32);

//给系统内所有的进程拍个快照

hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);

if (hProcessSnap == INVALID_HANDLE_VALUE)

{

printf_s("CreatToolhelp32Snapshot error!\n");

return -1;

}

//遍历进程快照,轮流显示每个进程的信息

BOOL bRet = ::Process32First(hProcessSnap,&pe32);

while (bRet)

{

printf_s("进程名称:%ls\n",pe32.szExeFile);   //这里得到的应该是宽字符,用%ls或者%S,不然无法正常打印

printf_s("进程ID %u\n\n",pe32.th32ProcessID);

bRet = ::Process32Next(hProcessSnap,&pe32);

}

//不要忘记清除掉snapshot对象

::CloseHandle(hProcessSnap);

return 0;

}

11.2 获取所有DLL

class Dll

{

public:

Dll(std::wstring name, std::wstring path);

~Dll();

std::wstring getDllName(void) const;

std::wstring getDllPath(void) const;

private:

std::wstring dllName;

std::wstring dllPath;

};

bool GetDllFromProcess(std::vector<Dll>& dllVec, const DWORD& processId)

{

//创建系统快照,并返回进程句柄

HANDLE handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId);

if (handle == INVALID_HANDLE_VALUE)

return false;

//枚举进程模块

MODULEENTRY32 info;

info.dwSize = sizeof(MODULEENTRY32);

Module32First(handle, &info);

while (Module32Next(handle, &info) != FALSE)

{

dllVec.emplace_back(Dll(std::wstring(info.szModule), std::wstring(info.szExePath)));

}

CloseHandle(handle);

return true;

}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Windows编程是指在Windows操作系统上进行软件开的过程。它涵盖了各种应用程序的开发,包括桌面应用程序、游戏、设备驱动程序等。下面是Windows编程的些重要概念和技术: 1. Windows API:Windows API(Application Programming Interface)是一组函数和数据结构,用于与Windows操作系统进行交互。开发者可以使用Windows API来创建窗口、处理用户输入、绘制图形等。 2. 编程语言:Windows编程可以使用多种编程语言进行,如C++、C#、VB.NET等。其中,C++是最常用的语言之一,因为它提供了对Windows API的直接访问。 3. 窗口和消息循环:在Windows编程中,窗口是用户界面的基本元素。开发者可以使用Windows API创建和管理窗口,并通过消息循环处理用户输入和系统事件。 4. 图形界面库:为了简化Windows编程,许多图形界面库被开发出来,如MFC(Microsoft Foundation Classes)、WinForms、WPF(Windows Presentation Foundation)等。这些库提供了更高级的抽象和封装,使开发者能够更快速地创建用户界面。 5. 数据持久化:在Windows编程中,通常需要将数据保存到文件或数据库中。开发者可以使用各种数据库技术(如SQL Server、MySQL)或文件操作API(如Windows API的文件操作函数)来实现数据持久化。 6. 多线程编程:为了提高程序的性能和响应能力,Windows编程中常常使用多线程。开发者可以使用线程相关的API来创建和管理线程,并实现并发执行的功能。 7. 调试和测试:在Windows编程中,调试和测试是非常重要的环节。开发者可以使用各种调试工具(如Visual Studio)来定位和修复程序中的错误,并使用单元测试和集成测试来验证程序的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

herb.dr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值