《Windows核心编程》阅读小记 1

第一部分

第一章 错误处理


windows常用返还值类型
 
VOID:该函数的运行不可能失败。Windows函数的返回值类型很少是VOID
BOOL:如果函数运行失败,那么返回值是0,否则返回的是非0值。最好对返回值进行测试,以确定它是0还是非0。不要测试返回值是否为TRUE
HANDLE:如果函数运行失败,则返回值通常是NULL,否则返回值为HANDLE,用于标识你可以操作的一个对象。注意,有些函数会返回一个句柄值INVALID_HANDLE_VALUE,它被定义为-1。函数的PlatformSDK文档将会清楚地说明该函数运行失败时返回的是NULL还是INVALID_HANDLE_VALID
PVOID:如果函数运行失败,则返回值是NULL,否则返回PVOID,以标识数据块的内存地址
LONG/DWORD:这是个难以处理的值。返回数量的函数通常返回LONG或DWORD。如果由于某种原因,函数无法对想要进行计数的对象进行计数,那么该函数通常返回0或-1(根据函数而定)。如果调用的函数返回了LONG/DWORD,那么请认真阅读PlatformSDK文档,以确保能正确检查潜在的错误


1.1 DWORD GetLastError();

当函数返回时,它的返回值就能指明一个错误已经发生。若要确定这是个什么错误,请调用GetLastError函数:返回值相关文件WinError.h:想查看某错误时,请立即调用GetLastError(),否则对应的错误值将会被其他成功执行的函数更改。:调试时在Watch窗口输入@err,hr也可以查看错误值

1.2 向用户显示错误值

DWORD FormatMessage(
  DWORD dwFlags,      // source and processing options 指明文本源和处理方式(是否生成内存用以存储返回的字符串)
  LPCVOID lpSource,   // pointer to  message source ?
  DWORD dwMessageId,  // requested message identifier 查询的错误号
  DWORD dwLanguageId, // language identifier for requested message 语言信息
  LPTSTR lpBuffer,    // 返回对应的文本
  DWORD nSize,        // maximum size of message buffer ?
  va_list *Arguments  // pointer to array of message inserts ?
);


 

第二章
尽量使用Unicode字符格式,当编译时加上_UNICODE开关即可


用TCHAR代替char
用_T("xxx")或_TEXT("XXX")代替普通常量"xxx"
函数使用尽量不使用带有ANSI和Unicode提示的函数,如CreateWindowExW和CreateWindowExA,应该用CreateWindowEx


将文本串视为字符数组,而不是chars数组或字节数组。
将通用数据类型(如TCHAR和PTSTR)用于文本字符和字符串。
将显式数据类型(如BYTE和PBYTE)用于字节、字节指针和数据缓存。
将TEXT宏用于原义字符和字符串。
执行全局性替换(例如用PTSTR替换PSTR)。
修改字符串运算问题。例如函数通常希望你在字符中传递一个缓存的大小,而不是字节。
sizeof(szBuffer),而应该传递(sizeof(szBuffer)/sizeof(TCHAR)

 

2.1该函数对两个U n i c o d e 字符串进行比较

int CompareString(
    LCID lcid,
    DWORD fdwStyle,
    PCWSTR pString1,
    int cch1,
    PCTSTR pString2,
    int cch2);


2.2 每次创建一个线程时,它就被赋予一种语言。函数将返回该线程的当前语言设置。LCID给上个函数使用

LCID GetThreadLocale();



2.3

PTSTR CharLower(PTSTR pszString);
PTSTR CharUpper(PTSTR pszString);


既可以转换单个字符,也可以转换以0 结尾的整个字符串


2.4 带长度,不解释

DWORD CharLowerBuff(
   /* pointer to buffer containing characters to process */
   PTSTR pszString, 
   /* number of bytes or characters to process */
   DWORD cchLength  
);
DWORD CharUpperBuff(
   /* pointer to buffer containing characters to process */
   LPTSTR lpsz,
   /* number of characters to process */
   DWORD cchLength
);



2.5不解释

BOOL IsCharAlpha(TCHAR ch);
BOOL IsCharAlphaNumeric(TCHAR ch);
BOOL IsCharLower(TCHAR ch);
BOOL IsCharUpper(TCHAR ch);



2.6不解释

DWORD IsTextUnicode(CONST PVOID pvBuffer, int cb,PINT pResult);



2.7在Unicode与ANSI之间转换,不解释

int MultiByteToWideChar(
    UINT CodePage,          //code page
    DWORD dwFlags,          //character-type options
    LPCSTR lpMultiByteStr,  //address of string to map
    int cchMultiByte,       //number of bytes in string
    LPWSTR lpWideCharStr,   //address of wide-character buffer
    int cchWideChar         //size of buffer
);

int WideCharToMultiByte(
  UINT CodePage,         // code page
  DWORD dwFlags,         // performance and mapping flags
  LPCWSTR lpWideCharStr, // address of wide-character string
  int cchWideChar,       // number of characters in string
  LPSTR lpMultiByteStr,  // address of buffer for new string
  int cchMultiByte,      // size of buffer
  LPCSTR lpDefaultChar,  // address of default for unmappable 
                         // characters
  LPBOOL lpUsedDefaultChar   // address of flag set when default 
                             // char. used
);


 

 

第三章 内核对象

3.1 什么是内核对象
 简单的说:
     1.创建内核对象的函数都有指向SECURITY_ATTRIBUTES结构的指针参数
     2.使用引用计数,当计数为0时,撤消该对象资源。所谓的引用是指:进程的句柄结构表里是否有该对象的引用
     3.多进程时,才容易理解引用计数。


3.2 安全性描述符的作用

typedef struct _SECURITY_ATTRIBUTES
{
   DWORD nLength,
   LPVOID lpSecurityDescriptor; //与安全性有关,其他?
   BOOL bInherttHandle;         //是否可继承(用于子进程等情况)
} SECURITY_ATTRIBUTES;



3.3 CloseHandle()函数


该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可 以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0 ,该内核便从内存中撤消该内核对象。


3.4跨越进程边界共享内核对象

3.4.1 使用继承性
 将SECURITY_ATTRIBUTES中的bInherttHandle置为true,然后用内核函数生成内核对象,表面该对象可继承
 让父进程生成子进程,CreateProcess中断bInheritHandles参数置为ture
 此时系统将父进程的句柄表中可继承的拷贝到子进程的句柄表中,除了拷贝句柄表项目外,系统还要递增内核对象的使用计数
 实际上,CreateProcess 函数返回后,父进程可以立即关闭对象的句柄,而不影响子进程对该对象进行操作的能力
 
3.4.2 改变句柄的标志
 也是用于父子进程

 BOOL SetHandleInformation(
   HANDLE hObject,
   DWORD dwMask,
   DWORD dwFlags);


3.4.3 命名对象
 共享跨越进程边界的内核对象的第二种方法是给对象命名
 例如

HANDLE CreateMutex(
   PSLCURITY_ATTRIBUTES psa,
   BOOL bInitialOwner,
   PCTSTR pszName);


   
   通过pszName参数设置对象名字,若为空,则为匿名对象。
   名字是唯一的,不同类项的内核对象,使用相同的名字则会报错
  
   使用:

进程A中..
   {
     ...
     HANDLE hMutexPronessA = CreateMutex(NULL, FALSE, "JeffMutex");
     ...
    }

    无关的进程B中//
   {
     ...
     HANDLE hMutexPronessB = CreateMutex(NULL, FALSE, "JeffMutex");
     ...
    }


 当ProcessB对CreateMutex的调用取得成功时,它并不实际创建一个互斥对象。相反,ProcessB只是被赋予一个与进程相关的句柄值,用于标识内核中现有的互斥对象。
 注意当你的多个内核对象拥有相同的名字时,有一个非常重要的细节必须知道。当ProcessB调用CreateMutex时,它将安全属性信息和第二个参数传递给该函数。如果已经存在带有指定名字的对象,那么这些参数将被忽略。应用程序能够确定它是否确实创建了一个新内核对象,而不是打开了一个现有的对象。方法是在调用Create*函数后立即调用GetLastError:

 另一种方法
 open*()类项的内核函数,不创建,直接打开,pszName不能为空,否则返回NULL


3.4.4 终端服务器的名字空间(不理解)
 服务程序的名字空间对象总是放在全局名字空间中。按照默认设置,在终端服务器中,应用程序的命名内核对象将放入会话的名字空间中。但是,如果像下面这样将“Global\”置于对象名的前面,就可以使命名对象进入全局名字空间:

HANDLEh=CreateEvenL(NULL,FALSE,FALSE,"Global\\MyName");


 也可以显式说明想让内核对象进入会话的名字空间,方法是将“Local\”置于对象名的前面:

HANDLEh=CreateEvent(NULL,FALSE,FALSE,"Local\\MyName");



3.4.5 复制对象句柄

BOOL DuplicateHandle(
   HANDLE hSourceProcessHandle,//源进程句柄
   HANDLE hSourceHandle,//源句柄,将要传递到目标进程中
   HANDLE hTargetProcessHandle,//目标进程句柄
   PHANDLE phTargetHandle,//返回与源句柄可以说是同样引用的东西。
   DWORD dwDesiredAccess,
   BOOL bInheritHandle,
   DWORD dwOptions);


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows下,可以使用bat批处理脚本来批量检测远程端口。Telnet是一种远程终端连接服务,可以通过它来连接到远程主机的特定端口。以下是一个使用Telnet命令在批处理脚本中批量检测远程端口的例子: @echo off setlocal set ip_list=file_path\ip_list.txt set port=80 for /f %%i in (%ip_list%) do ( telnet %%i %port% | find "Connected" > nul if errorlevel 1 ( echo %%i:%port% is closed ) else ( echo %%i:%port% is open ) ) pause 在以上示例中,首先我们定义了要检测的IP列表文件的路径,即ip_list.txt文件,然后我们设定了要检测的端口号,此处设定为80端口。接着使用for循环读取ip_list.txt文件中的每个IP地址。 在每次循环中,我们使用telnet命令连接到远程主机的指定端口。如果端口成功连接,则表示端口是开放的,telnet命令的输出会包含"Connected"关键字。我们使用find命令查找telnet命令的输出中是否包含"Connected"关键字。 如果find命令找到了"Connected"关键字,则说明端口是开放的,我们将显示"IP地址:端口号 is open"的消息。否则,即端口未连接,我们将显示"IP地址:端口号 is closed"的消息。 最后,我们使用pause命令来暂停脚本的运行,以便我们可以查看检测结果。 需要注意的是,Telnet命令在默认情况下可能未启用,需要手动在Windows设置中启用Telnet客户端功能。此外,Telnet命令在Windows 10及更高版本中可能已被淘汰,可以考虑使用其他工具如ncat或PowerShell来代替Telnet命令进行远程端口检测。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值