-
-
Save KuRRe8/1051ce47444f7b194d7d5e27f53f91fd to your computer and use it in GitHub Desktop.
Windows中包含很多约定的路径,比如%APPDATA% %USERPROFILE%,或者一些库路径,文档,图片,视频。在Windows编程实践中,
应该使用knownfolderid
来固定查找位置。
import ctypes
from ctypes import wintypes
import os
# Define GUID structure
class GUID(ctypes.Structure):
_fields_ = [
("Data1", wintypes.DWORD),
("Data2", wintypes.WORD),
("Data3", wintypes.WORD),
("Data4", wintypes.BYTE * 8)
]
# KnownFolderID GUIDs
FOLDERID_RoamingAppData = GUID(0x3EB685DB, 0x65F9, 0x4CF6, (ctypes.c_ubyte * 8)(0xA0,0x3A,0xE3,0xEF,0x65,0x72,0x9F,0x3D))
FOLDERID_LocalAppData = GUID(0xF1B32785, 0x6FBA, 0x4FCF, (ctypes.c_ubyte * 8)(0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91))
FOLDERID_ProgramData = GUID(0x62AB5D82, 0xFDC1, 0x4DC3, (ctypes.c_ubyte * 8)(0xA9,0xDD,0x07,0x0D,0x1D,0x49,0x5D,0x97))
FOLDERID_Documents = GUID(0xFDD39AD0, 0x238F, 0x46AF, (ctypes.c_ubyte * 8)(0xAD,0xB4,0x6C,0x85,0x48,0x03,0x69,0xC7))
FOLDERID_ProgramFiles = GUID(0x905E63B6, 0xC1BF, 0x494E, (ctypes.c_ubyte * 8)(0xB2,0x9C,0x65,0xB7,0x32,0xD3,0xD2,0x1A))
FOLDERID_ProgramFilesX64 = GUID(0x6D809377, 0x6AF0, 0x444B, (ctypes.c_ubyte * 8)(0x89,0x57,0xA3,0x77,0x3F,0x02,0x20,0x0E))
# SHGetKnownFolderPath prototype
SHGetKnownFolderPath = ctypes.windll.shell32.SHGetKnownFolderPath
SHGetKnownFolderPath.argtypes = [ctypes.POINTER(GUID), wintypes.DWORD, wintypes.HANDLE, ctypes.POINTER(ctypes.c_wchar_p)]
SHGetKnownFolderPath.restype = wintypes.HRESULT
def get_known_folder_path(folder_id):
path_ptr = ctypes.c_wchar_p()
hr = SHGetKnownFolderPath(ctypes.byref(folder_id), 0, 0, ctypes.byref(path_ptr))
if hr != 0:
raise ctypes.WinError(hr)
path = path_ptr.value
ctypes.windll.ole32.CoTaskMemFree(path_ptr)
return path
if __name__ == "__main__":
print("FOLDERID_RoamingAppData:", get_known_folder_path(FOLDERID_RoamingAppData))
print("FOLDERID_LocalAppData:", get_known_folder_path(FOLDERID_LocalAppData))
print("FOLDERID_ProgramData:", get_known_folder_path(FOLDERID_ProgramData))
print("FOLDERID_Documents:", get_known_folder_path(FOLDERID_Documents))
print("FOLDERID_ProgramFiles:", get_known_folder_path(FOLDERID_ProgramFiles))
print("FOLDERID_ProgramFilesX64:", get_known_folder_path(FOLDERID_ProgramFilesX64))
print("TEMP:", os.environ.get("TEMP"))#include <windows.h>
#include <shlobj.h>
#include <iostream>
#include <string>
void print_known_folder(const wchar_t* name, const KNOWNFOLDERID& folderId) {
PWSTR path = nullptr;
HRESULT hr = SHGetKnownFolderPath(folderId, 0, NULL, &path);
if (SUCCEEDED(hr)) {
std::wcout << name << L": " << path << std::endl;
CoTaskMemFree(path);
} else {
std::wcout << name << L": <Failed to get path>" << std::endl;
}
}
int main() {
print_known_folder(L"FOLDERID_RoamingAppData", FOLDERID_RoamingAppData);
print_known_folder(L"FOLDERID_LocalAppData", FOLDERID_LocalAppData);
print_known_folder(L"FOLDERID_ProgramData", FOLDERID_ProgramData);
print_known_folder(L"FOLDERID_Documents", FOLDERID_Documents);
print_known_folder(L"FOLDERID_ProgramFiles", FOLDERID_ProgramFiles);
print_known_folder(L"FOLDERID_ProgramFilesX64", FOLDERID_ProgramFilesX64);
wchar_t* temp = _wgetenv(L"TEMP");
std::wcout << L"TEMP: " << (temp ? temp : L"<not found>") << std::endl;
return 0;
}一个应用程序通常往注册表的HKCU\Software内写数据,规范是外层为公司名,内部是产品名,然后是自组织的设置条目。
import winreg
company = "MyExampleCompany"
product = "MyExampleProduct"
settings = {
"SettingA": 123,
"SettingB": "hello",
"SettingC": 1.23
}
key_path = fr"Software\\{company}\\{product}"
with winreg.CreateKey(winreg.HKEY_CURRENT_USER, key_path) as key:
for name, value in settings.items():
if isinstance(value, int):
winreg.SetValueEx(key, name, 0, winreg.REG_DWORD, value)
elif isinstance(value, float):
# 注册表没有 float 类型,通常以字符串形式存储
winreg.SetValueEx(key, name, 0, winreg.REG_SZ, str(value))
else:
winreg.SetValueEx(key, name, 0, winreg.REG_SZ, value)
print(f"Wrote settings to HKEY_CURRENT_USER\\{key_path}")#include <windows.h>
#include <string>
#include <iostream>
int main() {
const wchar_t* company = L"MyExampleCompany";
const wchar_t* product = L"MyExampleProduct";
std::wstring keyPath = std::wstring(L"Software\\") + company + L"\\" + product;
HKEY hKey;
LONG result = RegCreateKeyExW(
HKEY_CURRENT_USER,
keyPath.c_str(),
0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL
);
if (result != ERROR_SUCCESS) {
std::wcerr << L"Failed to create/open registry key!" << std::endl;
return 1;
}
DWORD settingA = 123;
std::wstring settingB = L"hello";
std::wstring settingC = L"1.23"; // float as string
RegSetValueExW(hKey, L"SettingA", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&settingA), sizeof(settingA));
RegSetValueExW(hKey, L"SettingB", 0, REG_SZ, reinterpret_cast<const BYTE*>(settingB.c_str()), (settingB.size() + 1) * sizeof(wchar_t));
RegSetValueExW(hKey, L"SettingC", 0, REG_SZ, reinterpret_cast<const BYTE*>(settingC.c_str()), (settingC.size() + 1) * sizeof(wchar_t));
RegCloseKey(hKey);
std::wcout << L"Wrote settings to HKEY_CURRENT_USER\\" << keyPath << std::endl;
return 0;
}Windows存在多组错误码体系,从调试码,到内核错误码,用户态错误码和C运行时错误码对应文件如下
| 类型 | 定义头文件 |
|---|---|
| Bug Code | bugcodes.h |
| NTSTATUS | ntstatus.h |
| Win32 Error | winerror.h |
| HRESULT | winerror.h, oaidl.h |
| errno | <errno.h> |
bugcodes.h
此代码一般出现在蓝屏BSOD或者黑屏中,
在内核中,bug码原型是
VOID KeBugCheckEx(
_In_ ULONG BugCheckCode,
_In_ ULONG_PTR Parameter1,
_In_ ULONG_PTR Parameter2,
_In_ ULONG_PTR Parameter3,
_In_ ULONG_PTR Parameter4
);BugCheckCode是主错误码,后面4个是附加信息。
可以使用windbg附加远程内核调试或者分析coredump文件
1: kd> !analyze -show 0x9F 0x3
DRIVER_POWER_STATE_FAILURE (9f)
A driver has failed to complete a power IRP within a specific time.
Arguments:
Arg1: 0000000000000003, A device object has been blocking an Irp for too long a time
Arg2: 0000000000000000, Physical Device Object of the stack
Arg3: 0000000000000000, nt!_TRIAGE_9F_POWER on Win7 and higher, otherwise the Functional Device Object of the stack
Arg4: 0000000000000000, The blocked IRP
本节未包含所有主错误代码的列表,但由于许多错误代码的潜在解决方法相同,因此最佳做法是按照以下步骤排查错误。有关停止错误代码的完整列表,请参阅 Bug 检查代码参考 。
以下部分列出了常见停止错误代码的常规故障排除步骤。
停止错误代码 0x00000141 或 0x00000117
联系列出的显示驱动程序的供应商以获取该驱动程序的适当更新。
停止错误代码 0x0000000D1
通过 Microsoft 更新目录网站为系统应用最新的累积更新,以应用驱动程序的最新更新。更新过时的网络驱动程序。虚拟化 VMware 系统通常运行“Intel(R) PRO/1000 MT 网络连接”(e1g6032e.sys)。您可以从英特尔下载驱动程序和软件网站下载此驱动程序。请联系硬件供应商更新网络驱动程序以获得解决方案。对于 VMware 系统,请使用 VMware 集成网络驱动程序,而不是英特尔的 e1g6032e.sys。例如,使用 VMware 类型 VMXNET、VMXNET2 或 VMXNET3。
停止错误代码 0x000000050
如果在停止错误消息中识别出驱动程序,请联系制造商获取更新。如果没有可用的更新,请禁用该驱动程序,并监控系统稳定性。运行 chkdsk /f /r 来检测并修复磁盘错误。在系统分区开始磁盘扫描之前,请重启系统。联系制造商获取他们可能提供的用于硬盘子系统的诊断工具。尝试重新安装最近安装或更新的任何应用程序或服务。崩溃可能是在系统启动应用程序并读取注册表以获取首选项设置时触发的。重新安装应用程序可以修复损坏的注册表项。如果问题仍然存在,并且您已运行最近的系统状态备份,请尝试从备份中还原注册表配置单元。
停止错误代码 c000021a {致命系统错误} Windows 子系统进程意外终止,状态为 0xc0000005。系统已关闭。
使用系统文件检查器工具修复丢失或损坏的系统文件。系统文件检查器允许用户扫描 Windows 系统文件中的损坏情况并恢复损坏的文件。有关更多信息,请参阅使用系统文件检查器工具 。
停止错误代码 0x000000024
此停止错误通常是由 NTFS 文件系统损坏或硬盘上的坏块(扇区)引起的。损坏的硬盘驱动程序(SATA 或 IDE)也会对系统读取和写入磁盘的能力产生不利影响。运行存储子系统制造商提供的任何硬件诊断程序。使用扫描磁盘工具验证是否存在文件系统错误。要执行此步骤,请右键单击要扫描的驱动器,选择“属性”,选择“工具”,然后选择“立即检查”按钮。更新 NTFS 文件系统驱动程序 (Ntfs.sys)。为出现问题的当前操作系统应用最新的累积更新。
停止错误代码 0x0000001E
如果停止错误消息中识别出某个驱动程序,请禁用或删除该驱动程序。禁用或删除最近添加的所有驱动程序或服务。
如果在启动过程中出现错误,并且系统分区使用 NTFS 文件系统格式化,则您可能能够使用安全模式在设备管理器中禁用该驱动程序。要禁用该驱动程序,请按照以下步骤操作:
转到设置 > 更新和安全 > 恢复 。 在高级启动下,选择立即重启 。 在您的电脑重新启动并进入 “选择选项” 屏幕后,选择 “疑难解答” > “高级选项” > “启动设置” > “重新启动” 。 电脑重启后,您将看到一个选项列表。按 4 或 F4 键以安全模式启动电脑。如果您想在安全模式下使用互联网,请按 5 或 F5 键选择 “带网络连接的安全模式” 选项。
停止错误代码 0x00000133
此停止错误代码是由故障驱动程序导致的,该驱动程序在特定条件下无法在规定的时间内完成工作。为了帮助缓解此错误,请从系统中收集内存转储文件,然后使用 Windows 调试器查找故障驱动程序。如果停止错误消息中识别出某个驱动程序,请禁用该驱动程序以隔离问题。请咨询制造商以获取驱动程序更新。在事件查看器中检查系统日志,查找其他可能有助于识别导致停止错误 0x133 的设备或驱动程序的错误消息。验证安装的任何新硬件是否与已安装的 Windows 版本兼容。例如,您可以在 Windows 10 规范中获取有关所需硬件的信息。如果已安装 Windows 调试器,并且您可以访问公共符号,则可以将 c:\windows\memory.dmp 文件加载到调试器中。然后,请参阅确定 Windows Server 2012 上 Bug Check 0x133 (DPC_WATCHDOG_VIOLATION) 错误的来源, 从内存转储中查找有问题的驱动程序。
ntstatus.h
该错误码一般由Windows系统弹窗报错,最常见的就是0xC0000005访问违例,一般是exe程序bug或者加载的dll版本不对(包括第三方库或者msvcrt这类标准库),导致指针访问到不可读或者不可写的区域。
还有一个常见的错误是STATUS_INVALID_IMAGE_FORMAT, 0xc000007b, 也是应用版本不匹配或者找不到关键DLL。
| 名称 | 值 | 含义 |
|---|---|---|
| STATUS_SUCCESS | 0x00000000 |
操作成功。 |
| STATUS_ACCESS_VIOLATION | 0xC0000005 |
进程访问无效内存地址(读写越界或空指针)。 |
| STATUS_INVALID_HANDLE | 0xC0000008 |
传入的句柄无效(文件、进程、线程句柄错误)。 |
| STATUS_INVALID_PARAMETER | 0xC000000D |
函数调用中传入的参数无效。常见于系统调用或驱动接口。 |
| STATUS_NO_MEMORY | 0xC0000017 |
系统或进程虚拟内存不足。常见于分配失败或页文件耗尽。 |
| STATUS_OBJECT_NAME_NOT_FOUND | 0xC0000034 |
指定的对象(文件、注册表键等)不存在。 |
| STATUS_ACCESS_DENIED | 0xC0000022 |
当前访问被拒绝(权限不足或句柄权限不符)。 |
| STATUS_CONFLICTING_ADDRESSES | 0xC0000018 |
映射或分配的虚拟地址区间冲突。 |
| STATUS_IN_PAGE_ERROR | 0xC0000006 |
内存页加载时发生 I/O 错误(例如磁盘读失败)。 |
| STATUS_ILLEGAL_INSTRUCTION | 0xC000001D |
CPU 执行非法指令(如代码段损坏或执行数据页)。 |
| STATUS_STACK_OVERFLOW_READ | 0xC0000228 |
栈溢出访问。常见于递归过深或错误调用栈展开。 |
winerror.h
由此开始的错误代码(但不一定是逻辑上的错误比如S_OK)都是用户态定义的错误。
一般该代码从WinApi返回的HRESUALT检查,对于错误,有可能静默,也有可能由程序显式弹窗。
该错误可以从Error Lookup小工具查询。
| Value | Name | Description |
|---|---|---|
| 0x00000000 | S_OK | Operation successful |
| 0x80004001 | E_NOTIMPL | Not implemented |
| 0x80004002 | E_NOINTERFACE | No such interface supported |
| 0x80004003 | E_POINTER | Pointer that is not valid |
| 0x80004004 | E_ABORT | Operation aborted |
| 0x80004005 | E_FAIL | Unspecified failure |
| 0x8000FFFF | E_UNEXPECTED | Unexpected failure |
| 0x80070005 | E_ACCESSDENIED | General access denied error |
| 0x80070006 | E_HANDLE | Handle that is not valid |
| 0x8007000E | E_OUTOFMEMORY | Failed to allocate necessary memory |
| 0x80070057 | E_INVALIDARG | One or more arguments are not valid |
<errno.h> <cerrno>
由C/C++运行时定义的错误代码,在原生WINAPI风格代码不常用,C风格代码常用,其他依赖于C/C++的库一般也有自己的错误码实现。
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *file = fopen("nonexistent_file.txt", "r");
if (file == NULL) {
printf("Error opening file: %s\n", strerror(errno));
return 1;
}
// 文件处理代码
fclose(file);
return 0;
}

