跳至主要內容

snippet

chanchaw大约 3 分钟cpp

获取窗口句柄

// 根据窗口标题获取窗口句柄
HWND caclhWnd = FindWindowA(NULL, "计算器");
// 通过窗口类获取窗口句柄,要通过 spy++ 获取窗口类
HWND caclhWnd = FindWindowA("CalcFrame", NULL);

制作链表

下面代码缓冲区 pServStatus 中保存请求得到的系统服务对象集合,最后通过 for 循环制作链表,并在之后释放缓冲区

CServItem *CServConfig::EnumServList()
{
	SC_HANDLE hSCM = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
	if (!hSCM) {
		return NULL;
	}

	DWORD dwBytesNeeded = 0, dwRealBytes = 0, dwCount = 0, hResume = 0;// 返回的服务列表需要的存储空间的字节数量,服务的个数
	/*
		第一次调用本函数设置第四个参数、第五个参数依次为NULL,0。是为了通过第7,8个参数先取得返回数据需要的长度和数量
		之后第二次调用本函数则为第四个参数、第五个参数传入精确的值最终获取准确的返回数据
		第四个参数NULL:原本应该传入一个缓冲区地址,用于接收 EnumServicesStatus 返回的数据,这里传入NULL是为了通过第7,8个参数先取得返回数据需要的长度和数量
		第五个参数0:   缓冲区长度,设置0是为了通过第7,8个参数先取得返回数据需要的长度和数量
	 */
	ENUM_SERVICE_STATUS *pServStatus = NULL;// 枚举得到的系统服务都保存该指针指向的内存区域
	BOOL bRet = EnumServicesStatus(hSCM, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &dwBytesNeeded, &dwCount, &hResume);
	if (!bRet && GetLastError() == ERROR_MORE_DATA) {
		dwRealBytes = dwBytesNeeded;
		pServStatus = new ENUM_SERVICE_STATUS[dwRealBytes + 1];// 因为结尾会多出一个 \0,类似字符串的结尾,所以长度多出一个字节
		ZeroMemory(pServStatus, dwRealBytes + 1);// 用0填充一块内存区域,初始化内存块

		// 再次调用EnumServicesStatus时使用前面初始化好的缓冲区指针以及缓冲区的长度填充4,5参数位置
		bRet = EnumServicesStatus(hSCM, SERVICE_WIN32, SERVICE_STATE_ALL, pServStatus, dwRealBytes, &dwBytesNeeded, &dwCount, &hResume);
		if (!bRet) {// 没有成功获取到服务则要关闭服务管理器句柄,然后返回给用户
			CloseServiceHandle(hSCM);
			return NULL;
		}
	}
	else {
		CloseServiceHandle(hSCM);
		return NULL;
	}

	// 把枚举得到的系统服务构建成为链表
	// 下面3个变量依次是:前一个元素的指针,后一个元素的指针,头元素的指针
	CServItem *pServPre = NULL, *pServNext = NULL, *pServHeader = NULL;
	pServPre = pServNext;
	for (DWORD dwIdx = 0; dwIdx < dwCount; dwIdx++)
	{
		pServNext = new CServItem();
		pServNext->m_strServName = pServStatus[dwIdx].lpServiceName;
		pServNext->m_strServDispName = pServStatus[dwIdx].lpDisplayName;
		pServNext->m_dwServStatus = pServStatus[dwIdx].ServiceStatus.dwCurrentState;
		pServNext->m_pNext = NULL;
		
		if (pServHeader == NULL) pServHeader = pServNext;
		if (pServPre == NULL) pServPre = pServNext;
		else pServPre->m_pNext = pServNext;
		pServPre = pServNext;
		// 上面3行代码也可以写作如下
		// (pServPre == NULL) ? (pServPre = pServNext) : (pServPre->m_pNext = pServNext, pServPre = pServNext);
	}
	// 释放申请的内存区域(保存系统服务数据的内存区域),并返回链表的头元素
	delete [] pServStatus;
	return pServHeader;
}

printf

十进制、十六进制

printf("调用技能传入参数:\r\n十进制:a:%d,b:%d\r\n十六进制:a:%X,b:%X\r\n",a,b,a,b);

cout

cout输出string
cout输出string

to_string

#include<sstream>
using namespace std;

// 类 Addr 的成员属性 addr 的数据类型是 UINT_PTR
string Addr::toString() {
	return std::to_string(addr);
}

打印函数地址

变量地址压入栈

通过 lea 取地址保存到寄存器,然后将寄存器中的数据压入栈,即实现将变量的地址压入栈

const char* role="player";
__asm {
    lea eax, role
    push eax
}

寄存器数据赋值变量

采用外平栈的方法,只有一个参数(长度是4字节),下面代码是将执行函数后的返回值(默认填充到 eax 中)赋值到 c++ 变量中

UINT_PTR retVal=0;
__asm {
    call 函数地址
    add esp,4
    mov retVal, eax
}

线程

取当前线程ID

DWORD currentThreadID=GetCurrentThreadId();

创建线程

下面调用的第四个参数 自定义逻辑 是回调函数,需要在前面先定义好

::CreateRemoteThread(当前进程句柄,0,0,自定义逻辑,0,0,0);

硬件相关

主板蜂鸣声

MessageBeep(1);