/we4263
'function' : member function does not override any base class virtual member function。/we4264
'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
# Enable and treat as errors the following warnings to easily detect virtual function signature failures:
# 'function' : member function does not override any base class virtual member function
# 'virtual_function' : no override available for virtual member function from base 'class'; function is hidden
target_compile_options(trinity-compile-option-interface
INTERFACE
/we4263
/we4264)
Visual Studio 2019 提供了非常强大的性能分析工具,可以方便对位 CPU、内存问题。 通过按 F5 在 Visual Studio 中开始调试时,默认情况下会出现“诊断工具”窗口。 要手动打开该窗口, 请选择“Debug” > “Windows” > “Show Diagnostic Tools(诊断工具)”。“诊断工具”窗口显示有关事件、进程内存和 CPU 使用情况的信息。
以 HelloWorld.exe 为例,假设它依赖 MyMath.dll,而我们又希望 MyMath.dll 放到 math 子目录,目录结构如下:
/AppDir
|--- HelloWorld.exe
| |
|--- math
|--- math.manifest
|--- MyMath.dll
SxS Manager 加载顺序。Side-by-side searches the WinSxS folder.
\\<appdir>\<assemblyname>.DLL
\\<appdir>\<assemblyname>.manifest
\\<appdir>\<assemblyname>\<assemblyname>.DLL
\\<appdir>\<assemblyname>\<assemblyname>.manifest
反调试手段 IsDebuggerPresent
!analyze
命令。自动分析崩溃的详细信息,分析异常崩溃的 dump 文件,第一步就是要执行 !analyze -v 异常调用栈。
同步对象 | 支持等超时 | 进程锁 | 递归锁 | |
---|---|---|---|---|
CriticalSection 临界区 | 否 | 否 | 是 | 同一个进程内,实现互斥。 |
Mutex 互斥量 | 是 | 是 | 是 | 可以跨进程,实现互斥。 |
Event 事件 | 是 | 是 | N/A | 实现同步,可以跨进程。 |
Semaphore 信号量 | 是 | 是 | N/A | 可用资源计数的功能 |
ATL: Active Template Library(活动模板库) ATL 是 ActiveX Template Library 的缩写,它是一套 C++ 模板库。 ATL 是一个产生 C++/COM 代码的框架,就如同 C 语言是一个产生汇编代码的框架。
There is a theoretical limit of 65,536 GDI handles per session.
However, the maximum number of GDI handles that can be opened per session is usually lower, since it is affected by available memory.
There is also a default per-process limit of GDI handles.
To change this limit, set the following registry value: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Windows\GDIProc
essHandleQuota
This value can be set to a number between 256 and 65,536.
所有的 GDI 对象,在使用完成后,都需要调用删除,否则就会出现泄露问题 DeleteObject、ReleaseDC
GDI+ is an API that is exposed through a set of C++ classes. programmers of new applications should use GDI+ for all their graphics needs because GDI+ optimizes many of the capabilities of GDI and also provides additional features.
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "Gdiplus.lib")
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, PSTR, INT iCmdShow)
{
// Initialize GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// working with GDI+.
// Shutdown GDI+
GdiplusShutdown(gdiplusToken);
}
函数调用过程中有 ldr 锁,建议尽量不要做逻辑。 下面逻辑会触发 ldr 锁,确认不能在 dllmain 里面做以下事情:
字符集 vs 字符编码
BOOL IsUTF8(const char* _s, BOOL* is_ascii, char** invalid_point, int max_len)
{
const u_char* s = (const u_char*)_s;
const u_char* sv_s = s;
char* _invalid_point;
BOOL tmp;
if (!is_ascii) {
is_ascii = &tmp;
}
if (!invalid_point) {
invalid_point = &_invalid_point;
}
*is_ascii = TRUE;
while (*s && max_len-- > 0) {
if (*s >= 0x80) {
*is_ascii = FALSE;
*invalid_point = (char*)s;
if (*s <= 0xdf) {
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
}
else if (*s <= 0xef) {
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
}
else if (*s <= 0xf7) {
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
if (--max_len <= 0 || (*++s & 0xc0) != 0x80) return FALSE;
}
else return FALSE;
}
s++;
}
*invalid_point = NULL;
return TRUE;
}
BOOL StrictUTF8(char* s)
{
char* invalid_point = NULL;
if (!IsUTF8(s, NULL, &invalid_point) && invalid_point) {
*invalid_point = 0;
return TRUE;
}
return FALSE;
}
int U8Len(const char* s, int max_size)
{
if (max_size == -1) {
max_size = INT_MAX;
}
if (max_size == 0) {
return 0;
}
const char* sv_s = s;
int len;
int cur_size = 0;
for (len = 0; *s; len++) {
if (*s <= 0x7f) {
if ((cur_size += 1) > max_size) break;
}
else if (*s <= 0xdf) {
if ((cur_size += 2) > max_size) break;
if ((*++s & 0xc0) != 0x80) return -1;
}
else if (*s <= 0xef) {
if ((cur_size += 3) > max_size) break;
if ((*++s & 0xc0) != 0x80) return -1;
if ((*++s & 0xc0) != 0x80) return -1;
}
else if (*s <= 0xf7) {
if ((cur_size += 4) > max_size) break;
if ((*++s & 0xc0) != 0x80) return -1;
if ((*++s & 0xc0) != 0x80) return -1;
if ((*++s & 0xc0) != 0x80) return -1;
}
else return -1;
}
return len;
}
int u8cpyz(char* d, const char* s, int max_len)
{
int len = strncpyz(d, s, max_len);
if (StrictUTF8(d)) {
return (int)strlen(d);
}
return len;
}
/*
nul 文字を必ず付与する strncpy かつ return は 0 を除くコピー文字数
*/
int strncpyz(char* dest, const char* src, int num)
{
char* sv_dest = dest;
if (num <= 0) return 0;
while (--num > 0 && *src) {
*dest++ = *src++;
}
*dest = 0;
return (int)(dest - sv_dest);
}
(American Standard Code for Information Interchange) 编码起源于电报码,1960 年 10 月 6 日,美国标准协会(ANSI)的 X3.2 小组委员 会举行第一次会议,开始了关于 ASCII 标准制定。第一版 ASCII 标准于 1963 年发 布,在 1967 年经历了重大修改,在 1986 年期间经历了最近一次更新。ASCII 开始 基于英语字母表,将指定的 128 个字符编码成 7 位的二进制整数。其中 95 个编码 字符是可以打印的,包括数字 0-9,小写字母 a-z,大写字母 A-Z,还有一些标点 符号。
ISO-8859-1 编码是单字节编码(8bit),向下兼容 ASCII,其编码范围是 0x00-0xFF,0x00-0x7F 之间完全和 ASCII 一致,0x80-0x9F 之间未定义, 0xA0-0xFF 之间主要是拉丁字母和符号。 此字符集广泛应用于欧洲地区国家,支持英语、丹麦、德语、西班牙语、 法语、冰岛语等。Windows 补全该编码中的未使用字符,形成自己的编码 Windows-1252(code page 1252)
Unicode 是一种计算机行业统一编码标准,其起源是为了解决各国编码之 间的不兼容问题,创始人之一 Joe Becker 说为其取名‘Unicode’是为了表 明这是一种唯一的 (unique),统一的 (unified),通用的 (universal ) 编码。 Unicode 最初设计使用 16 位二进制来表示所有语言中的字符,因为当时所 有报纸和杂志的现代文字总和小于 2^14 = 16,384 。1991 年 3 月,Unicode 联 盟在加利福尼亚成立,10 月,发布 Unicode 1.0。
Unicode 的发展
Unicode 定义了两种类型的编码方法:Unicode Transformation Format (UTF) 和 Universal Coded Character Set (UCS)
UTF-16(16-bit Unicode Transformation Format),是一种可变长编码,用 1 或 2 个 16 位(2 字节)代码单元进行编码。UTF-16 是从早期的 16 位定长编 码 UCS-2 发展而来,由于 16 位最多只能编码 65,536 个字符,随着需要使用的 字符越来越多,这明显不够用。于是 IEEE(Institute of Electrical and Electronics Engineers,电气与电子工程师协会)提出了 UCS-4 方案,用 4 字 节编码一个字符。Unicode 联盟觉得这个方案扯淡,因为这会浪费很多磁 盘空间,而且很多厂商已经投入大量资源基于 2 字节进行技术开发。最终 UTF-16 作为一个折中方案在 Unicode 2.0 标准中发布。
BMP:
SP:
假设 Unicode 码点 U,范围是(U+10000 ~ U+10FFFF)
Unicode 字符:U+10437
先减去 0x10000,结果为 0x0437,转成 20 位二进制:0000000001 0000110111
高位:0x0001 + 0xD800 = 0xD801
低位:0x0037 + 0xDC00 = 0xDC37
所以 UTF-16 编码为:0xD801DC37
UTF-16 的一个代码单元是 2 字节,由于大多数通信协议和存储协议都是以字 节单位,于是在存储 UTF-16 序列的时候就需要选择字节序。为了便于识别 字节序,UTF-16 引入了 BOM(Byte Order Mark),插入在第一个实际代码 值之前。U+FEFF(零宽度不间断空格,啥也不是)表示大端,U+FFFE(非 字符)表示小端。UTF-16 规定如果缺少 BOM 头,应假设字节序为大端。
但事实上,因为 windows 默认使用小端,所以很多软件也默认使用小端, 平台的力量还是大。
特点:
BOM (Byte Order Mark)
UTF-8 | 0xEF 0xBB 0xBF |
UTF-16 BE | 0xFE 0xFF |
UTF-16 LE | 0xFF 0xFE |
UTF-32 BE | 0x00 0x00 0xFE 0xFF |
UTF-32 LE | 0xFF 0xFE 0x00 0x00 |
UTF-8 是一种可变长编码,使用 1-4 字节(8 位)编码所有 Unidcode 字符。与 ASCII 兼容,Unicode 前 128 个字符与 ASCII 是一一对应的,UTF-8 使用一字 节对其进行编码,并且在二进制值上与 ASCII 一样。UTF-8 已经成为了万维 网上的主导编码,截止到 2020 年,95% 网页使用 UTF-8 编码。IMC 推荐所有 的 e-mail 程序使用 UTF-8 展示和创建邮件,W3C 推荐将 UTF-8 作为 XML 和 HTML 的默认编码。
UTF-8 编码的最小代码值单元是 1 字节,所以不存在字节序问题。但是很多 Windows 程序(Windows 记事本)会在以 UTF-8 保存的文件最前加上 0xEF, 0xBB, 0xBF,这个通常称为 BOM 头。
优势:
劣势:
ANSI 编码没有明确定义,但通常被用来指代 Windows 代码页(Windows code page),在英文系统上通常指 Windows-1252 代码页,在简体中文系统通常 指 Windows-936(在不同的 windows 系统上,可能指代不同的代码页)。事实上, “ANSI 编码”这个名字是个错误用法,应为 ANSI 从来没有发表过这样的标准。
Windows-1252 : 别名 CP-1252,用一个字节,总共编码 256 个字符,在英文和一些西方的 Windows 系统上作为默认编码。
Windows-936 : 别名 CP-936,是 Windows 系统为简体中文设计的编码,最初只覆盖 GB2312 中 的字符,但是随着 Window95 的发布,被扩展到覆盖大部分的 GBK。
第一部分:使用 1 个字节编码,0x00-0x80 和 0xff。总共编码 130 个字符, 前 128 个字符和 ASCII 兼容。
第二部分:使用 2 个字节编码,一个 lead byte(首字节),一个 trail byte(尾字节),lead byte 范围:0x81-0xfe(125),trail byte 范围: 0x40-0xfe(190),总共编码字符:23750
GBK 亦采用双字节表示,总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。总计 23940 个码位,共收入 21886 个汉字和图形符号,其中汉字(包括部首和构件)21003 个,图形符号 883 个。 from
同一个编码文件里,怎么区分 ASCII 和中文编码呢?从 ASCII 表我们知道标准 ASCII 只有 128 个字符,0~127 即 0x00~0x7F(0111 1111)。 所以区分的方法就是,高字节的最高位为 0 则为 ASCII,为 1 则为中文。from
gbk 和 utf8 都是单字节编码,所以 strcpy 这种以 0(null)作为结束符的程序逻辑都是没问题的。 标准 ASCII 是 128 个,范围是 0x00~0x7F (0000 0000~0111 0000) ,最高位为 0。最高位为 1 作为扩展。
MultiByteToWideChar // UTF-16 转 UTF-8 或者 ANSI
WideCharToMultiByte // UTF-16 转 UTF-8 或者 ANSI
CA2W // MultiByteToWideChar
CW2A // WideCharToMultiByte
CStringA sa;
CString s = sa; // 危险(ANSI)
#include <iostream>
#include <atlconv.h>
#include <atlstr.h>
int test() {
// 舒服,内存在堆上。默认:CP_THREAD_ACP,可以指定编码。
// 大字符串内存申请在 AtlConvAllocMemory 堆上。
std::string test3 = std::string(ATL::CW2A(L"中文字符", CP_UTF8));
std::wstring test4 = std::wstring(ATL::CA2W("中文字符"));
return 0;
}
int main()
{
USES_CONVERSION; // 使用 CP_THREAD_ACP
// 危险:使用了 alloca,这个内存申请在栈上。
std::string test1 = W2A(L"我是宽字节"); // 转化成默认
std::wstring test2 = A2W("我是窄字节"); // 危险,这玩意在栈上。
return 0;
}
将使用标准 C 的 mbstowcs 方法和 wcstombs 方法,且配合标准 C 的 setlocale 方法,这也是利用标准库跨平台的做法, 但是过程没法直接转成自定义的编码,需要额外转码。所以在 Windows 平台开发的话不推荐。 iconv
ATL 中的 W2A\A2W,使用的都是线程的代码页。
在开发配置文件时,建议使用 UTF-8 编码格式,这样在中文、英文系统中都不会有读写问题。否则,如果只是使用 CP_THREAD_ACP,当操作系统语言变更时,就会出现读写乱码的问题。
INI 文件读写 API:WritePrivateProfileString
from 根据文件是否已经存在以及(如果存在)文件的内容编码方式,可能会出现问题。
创建方法:先 utf-16-le
编码,再加上 BOM 头 \xff\xfe
。
inifile = os.path.join(uploaddir, "books.ini")
booksini = b"\xff\xfe" + booksini.encode("utf-16-le")
writefile(inifile, booksini)
#openTextFile(inifile)
Windows 记事本中输入“连”,ctrl+s 保存后再打开。 文件内容 16 进制为:0xC1AC,转成二进制:1100 0001 1010 1100,命中 UTF-8 的两字节编码规则 (110xxxxx 10xxxxxx),被识别成 UTF-8 编码。 当做 UTF-8 解码,得到 U+6C,但是不在 U+0080-U+07FF 之间,被当做错误 编码。
ANSI 编码,但是二进制能命中 UTF8 规则,但是又找不到 UTF8 对应的字符。
Windows 目录中 system.dat、user.dat 和 config.pol 文件 (regedit.exe)
除了 HKEY_CURRENT_USER 之外的所有支持文件都保存在 C:\Windows\System32\config。 HKEY_CURRENT_USER 的支持文件存储在您的个人资料文件夹中,%UserProfile%\Ntuser.dat
显示类型(在编辑器中) | 数据类型 | 说明 |
---|---|---|
REG_SZ | 字符串 | 文本字符串 |
REG_MULTI_SZ | 多字符串 | 含有多个文本值的字符串 |
REG_BINARY | 二进制数 | 二进制值,以十六进制显示 |
REG_DWORD | 双字 | 一个 32 位的二进制值 |
REG_QWORD | 四字 | 一个 64 位的二进制值 |
UAC 是 User Account Control 的缩写,其中文翻译为用户帐户控制,是微软在 Windows Vista 和 Win7 中引用的新技术,主要功能是进行一些会影响系统安全的操作时,会自动触发 UAC,用户确认后才能执行,从而保证系统安全。
触发 UAC 提示的操作:
最低的级别(0 级):关闭 UAC 功能(必须重新启动后才能生效)。在该级别下,如果是以管理员登录,则所有操作都将直接运行而不会有任何通知,包括病毒或木马对系统进行的修改。在此级别下,病毒或木马可以任意连接访问网络中的其他电脑、甚至与互联网上的电脑进行通信或数据传输。可见如果完全关闭 UAC 并以管理员身份登录,将严重降低系统安全性。此外,如果是以标准用户登录,那么安装、升级软件或对系统进行修改和设置,将直接被拒绝而不弹出任何提示,用户只有获得管理员权限才能进行。可见完全关闭 UAC 并以标准用户登录,各种操作和设置也非常不方便,因此建议不要选择该级别。
比默认级别稍低的级别(1 级):该级别不启用安全桌面,也就是说有可能产生绕过 UAC 更改系统设置的情况。不过一般情况下,如果是用户启动某些程序而需要对系统进行修改,可以直接运行,不会产生安全问题。但如果用户没有运行任何程序却弹出提示窗口,则有可能是恶意程序在试图修改系统设置,此时应果断选择阻止。该级别适用于有一定系统经验的用户。
默认级别(2 级):在默认级别下,只有在应用程序试图改变计算机设置时才会提示用户,而用户主动对 Windows 进行更改设置则不会提示。同时,在该模式下将启用安全桌面,以防绕过 UAC 更改系统设置。可以看出,默认级别可以既不干扰用户的正常操作,又可以有效防范恶意程序在用户不知情的情况下修改系统设置。一般的用户都可以采用该级别设置。
最高级别(3 级):在高级级别下,“始终通知”(即完全开启),在该级别下,用户安装应用程序、对软件进行升级、应用程序在任何情况下对操作系统进行更改、更改 Windows 设置等情况,都会弹出提示窗口(并启用安全桌面),请求用户确认。由此可见该级别是最安全的级别,但同时也是最“麻烦”的级别,适用于多人共用一台电脑的情况下,限制其他标准用户,禁止其随意更改系统设置。
这里需要说到一个概念:Host-based Intrusion Prevention System HIPS,基于主机的入侵防御系统。HIPS 是一种系统控制软件,它能监控电脑中文件的运行,对文件的调用,以及对注册表的修改。HIPS 可以分为 3D,AD(Application Defend)–应用程序防御体系、RD(Registry Defend) 注册表防御体系、FD(File Defend)文件防御体系。UAC 可以理解为一个基础的 HIPS 防御系统。杀毒软件的更新永远要追着病毒来,这种滞后性使得病毒有机会利用时间差攻击系统;而对杀毒软件而言,亡羊补牢就意味着重大的失败。这种情况下,要谋求更高的安全性,HIPS 成为了当然之选。
经常有人抱怨 NT6.x 系统的兼容性多么多么差,身边也有人因为“不兼容”而将系统换回 XP。但从我个人的使用看,几乎所有的软件都能很好的工作于 NT6.x 上。区别在哪?排除掉确实因为软件开发导致的兼容性问题的话,普通用户只会双击运行程序,而我在一次尝试失败后会根据程序行为对其进行手动提权。刚从 XP 过来的用户,估计不会有那么一套权限划分的意识,UAC 在某种程度上而言也就削弱了系统对普通用户的兼容性。但我们如果能够根据程序行为对需要的程序进行手动提权,所谓“兼容性问题”也就迎刃而解了。对用户水平更高的需求?也许吧。但计算机从来都是与永无休止的学习联系在一起的。而如果程序的开发能进一步的规范化,内置提权申请的程序就不存在被 UAC 影响兼容性的问题了。
ref ref 目前公开的方法中,有以下几种方法绕过 UAC:
针对新版 win10 有效的绕过 UAC 的几种方案 [UAC-bypass.md]
Bypass UAC by hijacking a DLL located in the Native Image Cache https://github.com/AzAgarampur/byeintegrity-uac
服务和管理员、普通用户无法进行消息通信,需要做特殊过滤 ChangeWindowMessageFilter
C:\Users\当前登录计算机用户名\AppData\Local\VirtualStore\Program Files\下目录的的文本文件
,需要加特殊过滤。用作开机标识的注册表
进程就好比工厂里的车间,它代表 CPU 所能处理的单个任务。一个工厂可以有一到多个车间。
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);
WAIT_OBJECT_0 + nCount – 1
WAIT_TIMEOUT
WAIT_FAILED
HANDLE 内核对象 进程、线程、作业 文件、控制台的输入输出流、错误流互斥量、信号量、事件可等待的计时器
void InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
void DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
void EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
void LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);
BOOL InitializeCriticalSectionAndSpinCount(
LPCRITICAL_SECTION lpCriticalSection,
DWORD dwSpinCount);
由于将线程切换到等待状态(切换内核模式)的开销较大,因此为了提高关键段的性能,Microsoft 将旋转锁合并到关键段中,这样 EnterCriticalSection() 会先用一个旋转锁不断循环,尝试一段时间才会将线程切换到等待状态。 旋转次数一般设置为 4000。
读写锁 SRWLock
VOID InitializeSRWLock(PSRWLOCK SRWLock);
// 写入者线程申请写资源
VOID AcquireSRWLockExclusive(PSRWLOCK SRWLock);
VOID ReleaseSRWLockExclusive(PSRWLOCK SRWLock);
// 读取者线程申请读资源
VOID AcquireSRWLockShared(PSRWLOCK SRWLock);
VOID ReleaseSRWLockShared(PSRWLOCK SRWLock);
不常用,读取者可以并发执行。
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPCTSTR lpName);
HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
BOOL SetEvent(HANDLE hEvent);
BOOL ResetEvent(HANDLE hEvent);
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName);
HANDLE OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount);
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName);
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName);
ReleaseMutex
HANDLE CreateFileMapping(
HANDLE hFile, // 物理文件句柄
LPSECURITY_ATTRIBUTES lpAttributes, // 安全设置
DWORD flProtect, // 保护设置 PAGE_READWRITE | SEC_COMMIT
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
LPCTSTR lpName // 共享内存名称
);
LPVOID WINAPI MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
SIZE_T dwNumberOfBytesToMap
);
HANDLE CreateNamedPipe(
LPCTSTR lpName, // 指向管道名称的指针
DWORD dwOpenMode, // 管道打开模式
DWORD dwPipeMode, // 管道模式
DWORD nMaxInstances, // 最大实例数
DWORD nOutBufferSize, // 输出缓存大小
DWORD nInBufferSize, // 输入缓存大小
DWORD nDefaultTimeOut, // 超时设置
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全属性指针
);
服务器的实现过程
客户端实现过程
unsigned long _beginthreadex(
void *security,
unsigned stack_size,
unsigned(_stdcall *start_address)(void *),
void *argilist,
unsigned initflag,
unsigned *threaddr // 用来接收线程 ID
);
_beginthreadex 是一个 C/C++ 运行库函数。和 CreateThread 一样创建线程。 但是它们两者是有区别的。
CComPtr & CComQIPtr。
推荐使用 std::make_shared 来生成 std::shared_ptr
行为等价于:
placement new
int main() {
std::string* p = new std::string("test");
void p1 = operator new(strlen("test"));
std::string* p2 = new(p1) std::string("test");
return 1;
}
inline void* __CRTDECL operator new(
size_t _Size,
_Writable_bytes_(_Size) void* _Where) noexcept {
(void) _Size;
return _Where;
}
什么时候用:
注意:
int main() {
Test* p = new Test[10];
delete[] p;
}
Test::`vector deleting destructor':
push offset Test::~Test (0B115FAh)
mov eax, dword ptr [this]
mov ecx, dword ptr [eax-4] // 往前移 4 字节,取出数组数目。
push ecx
push 8
new(std::nothrow)
注意:
LPVOID VirtualAlloc(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
MEM_RESERVE 0x00002000
Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk. You can commit reserved pages in subsequent calls to the VirtualAlloc function. To reserve and commit pages in one step, call VirtualAlloc with MEM_COMMIT | MEM_RESERVE.
Other memory allocation functions, such as malloc and LocalAlloc, cannot use a reserved range of memory until it is released.
实例:已知一 PE 文件某全局变量的地址求该 PE 文件的基地址
实例:将分配的内存修改为读写状态
BOOL VirtualProtect(
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect
);
注意:
单进程模式应用:
from C/C++ 实现 PE 文件特征码识别,打开 PE 文件映射:
在读取 PE 结构之前,首先要做的就是打开 PE 文件到内存,这里打开文件我们使用了 CreateFile() 函数该函数可以打开文件并返回文件句柄, 接着使用 CreateFileMapping() 函数创建文件的内存映像,最后使用 MapViewOfFile() 读取映射中的内存并返回一个句柄, 后面的程序就可以通过该句柄操作打开后的文件了。
IsBadReadPtr
IsBadWritePtr
IsBadCodePtr
VirtualAllocEx
WriteProcessMemory
应用程序频繁地在堆上分配和释放内存,会导致性能的损失。并且会使系统中出现大量的内存碎片,降低内存的利用率。 默认的分配和释放内存算法自然也考虑了性能,然而这些内存管理算法的通用版本为了应付更复杂、更广泛的情况,需要做更多的额外工作。 而对于某一个具体的应用程序来说,适合自身特定的内存分配释放模式的自定义内存池可以获得更好的性能。
https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
超过 MAX_PATH 怎么办?
\\?\D:\very long path
什么是路径的 domain
DWORD GetShortPathNameA(
[in] LPCSTR lpszLongPath,
[out] LPSTR lpszShortPath,
[in] DWORD cchBuffer
);
缓冲区刷进磁盘的时机:
WriteFileEx function (fileapi.h)
BOOL WriteFileEx(
[in] HANDLE hFile,
[in, optional] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[in, out] LPOVERLAPPED lpOverlapped,
[in] LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
Nodejs = libuv + v8 跨平台异步 I/O 封装,在 Windows 上实现是 IOCP,在 Linux 上是 epoll
Windows 平台上与 kqueue (FreeBSD) 或者 (e)poll(Linux) 等内核事件通知相应的机制是 IOCP。 libuv 提供了一个跨平台的抽象,由平台决定使用 libev 或 IOCP。 libuv 异步文件系统: https://luohaha.github.io/Chinese-uvbook/source/filesystem.html
使用 API 写的问题,失败了不能确定是否破坏了其他项目的内容
注意 64 位的精度
IconToBitmap 并保留 Alpha 通道。
float KFunction::GetCurrentDpi() {
DWORD dwDpi = 96;
do {
KRegister2 reg;
if (reg.Open(HKEY_CURRENT_USER,
L"Control Panel\\Desktop\\WindowMetrics") &&
reg.Read(L"AppliedDPI", dwDpi)) {
break;
}
} while (FALSE);
return dwDpi;
}
HBITMAP CCPdfmenushell::_IconToBitmap(int iconResId)
{
float fdpi = KFunction::GetCurrentDpi() * 1.f / 96;
int nIconSize = int(fdpi * 16 + 0.5f);
HICON hIcon = (HICON)::LoadImage((HINSTANCE)&__ImageBase,
MAKEINTRESOURCE(iconResId),
IMAGE_ICON,
nIconSize,
nIconSize,
LR_DEFAULTCOLOR);
ICONINFO info = { 0 };
if (hIcon == NULL || !GetIconInfo(hIcon, &info) || !info.fIcon) {
if (hIcon) { ::DestroyIcon(hIcon); }
return NULL;
}
INT nWidth = 0;
INT nHeight = 0;
if (info.hbmColor != NULL) {
BITMAP bmp = { 0 };
GetObject(info.hbmColor, sizeof(bmp), &bmp);
nWidth = bmp.bmWidth;
nHeight = bmp.bmHeight;
}
if (info.hbmColor != NULL) {
DeleteObject(info.hbmColor);
info.hbmColor = NULL;
}
if (info.hbmMask != NULL) {
DeleteObject(info.hbmMask);
info.hbmMask = NULL;
}
if (nWidth <= 0 || nHeight <= 0) {
if (hIcon) { ::DestroyIcon(hIcon); }
return NULL;
}
INT nPixelCount = nWidth * nHeight;
HDC dc = GetDC(NULL);
INT* pData = NULL;
HDC dcMem = NULL;
HBITMAP hBmpOld = NULL;
bool* pOpaque = NULL;
HBITMAP dib = NULL;
BOOL bSuccess = FALSE;
if (dc == NULL) {
if (hIcon) { ::DestroyIcon(hIcon); }
return NULL;
}
do {
BITMAPINFOHEADER bi = { 0 };
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = nWidth;
bi.biHeight = -nHeight;
bi.biPlanes = 1;
bi.biBitCount = 32;
bi.biCompression = BI_RGB;
dib = CreateDIBSection(dc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (VOID**)&pData, NULL, 0);
if (dib == NULL) break;
memset(pData, 0, nPixelCount * 4);
dcMem = CreateCompatibleDC(dc);
if (dcMem == NULL) break;
hBmpOld = (HBITMAP)SelectObject(dcMem, dib);
::DrawIconEx(dcMem, 0, 0, hIcon, nWidth, nHeight, 0, NULL, DI_MASK);
pOpaque = new (std::nothrow) bool[nPixelCount];
if (pOpaque == NULL) break;
for (INT i = 0; i < nPixelCount; ++i) {
pOpaque[i] = !pData[i];
}
memset(pData, 0, nPixelCount * 4);
::DrawIconEx(dcMem, 0, 0, hIcon, nWidth, nHeight, 0, NULL, DI_NORMAL);
BOOL bPixelHasAlpha = FALSE;
UINT* pPixel = (UINT*)pData;
for (INT i = 0; i < nPixelCount; ++i, ++pPixel) {
if ((*pPixel & 0xff000000) != 0) {
bPixelHasAlpha = TRUE;
break;
}
}
if (!bPixelHasAlpha) {
pPixel = (UINT*)pData;
for (INT i = 0; i < nPixelCount; ++i, ++pPixel) {
if (pOpaque[i]) {
*pPixel |= 0xFF000000;
} else {
*pPixel &= 0x00FFFFFF;
}
}
}
bSuccess = TRUE;
} while (FALSE);
if (pOpaque != NULL) {
delete[]pOpaque;
pOpaque = NULL;
}
if (dcMem != NULL) {
SelectObject(dcMem, hBmpOld);
DeleteDC(dcMem);
}
ReleaseDC(NULL, dc);
if (!bSuccess) {
if (dib != NULL) {
DeleteObject(dib);
dib = NULL;
}
}
if (hIcon) { ::DestroyIcon(hIcon); }
return dib;
}
支持托管控件绘制,该控件是 IBkWindowPaintHook 替代解决方案 强烈建议不使用 BkWindowPaintHook
如何选择开源协议 世界上的开源协议有上百种(有兴趣的读者请猛击 这里 了解),很少有人能彻底搞清它们之间的区别,即使在最流行的六种开源协议 —— GPL、BSD、MIT、Mozilla、Apache 和 LGPL —— 之中做选择,也很复杂。
乌克兰程序员 Paul Bagwell 画了一张分析图,说明应该怎么选择开源协议,大家看了一目了然,真是清爽。 图片来自于阮一峰博客:如何选择开源协议