Win32 API 三两事 (一)
平日编程都是调用封装好的API,很多时候无法满足需要,今天学习下一些底层的API调用,在此做下笔记,所有内容来自网络,自己整理了下,持续更新...
1、APIENTRY,WINAPI,CALLBACK等是什么东西?
2、HINSTANCE 、HANDLE 、 HWND 、LPCTSTR是什么?
3、CWnd 、 CDC 与 HDC?
4、COLORREF 与 RGB 、LPVOID 与 LPCVOID?
5、char、wchar_t、TCHAR、WCHAR、std::string、std::wstring、CString、LPCTSTR、DWORD
6、SelectObject()和SelectStockObject()有何区别?
、
、
1、APIENTRY,WINAPI,CALLBACK等是什么东西?
通常在函数名前面(返回值后面)会有APIENTRY或WINAPI或CALLBACK修饰,这其实与调用约定有关.
调用约定
调用约定(Calling convention)决定以下内容:函数参数的压栈顺序,由调用者还是被调用者把参数弹出栈,以及产生函数修饰名的方法。MFC支持以下调用约定:
① _cdecl
按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于“C”函数或者变量,修饰名是在函数名前加下划线。对于“C++”函数,有所不同。
如函数void test(void)的修饰名是_test;对于不属于一个类的“C++”全局函数,修饰名是?test@@ZAXXZ。
这是MFC缺省调用约定。由于是调用者负责把参数弹出栈,所以可以给函数定义个数不定的参数,如printf函数。
② _stdcall
按从右至左的顺序压参数入栈,由被调用者把参数弹出栈。对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数,如函数int func(int a, double b)的修饰名是_func@12。对于“C++”函数,则有所不同。
所有的Win32 API函数都遵循该约定。
③ _fastcall
头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。由被调用者把参数弹出栈,对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数,如函数int func(int a, double b)的修饰名是@func@12。对于“C++”函数,有所不同。
未来的编译器可能使用不同的寄存器来存放参数。
④ thiscall
仅仅应用于“C++”成员函数。this指针存放于CX寄存器,参数从右到左压栈。thiscall不是关键词,因此不能被程序员指定。
⑤ naked call
采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。naked call不产生这样的代码。
naked call不是类型修饰符,故必须和_declspec共同使用,如下:
__declspec(naked) int func(formal_parameters)
{
// Function body
}
⑥ 过时的调用约定
原来的一些调用约定可以不再使用。它们被定义成调用约定_stdcall或者_cdecl。例如:
#define CALLBACK __stdcall
#define WINAPI __stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
.
2、HINSTANCE 、HANDLE 、 HWND 、LPCTSTR是什么?
HINSTANCE 是进程句柄;
HANDLE 是对象句柄;
HWND 是窗口的句柄。
LPCTSTR 一个指向常固定地址的可以根据一些宏定义改变语义的字符串
① 其实句柄是一个32位的整数,WINDOWS操作系统用来标志一个对象,是进程、图像图标资源等对象的ID。在Windows这样的多任务操作系统中,一个程序可以同时运行多个实例。不同的实例间需要彼此区别,句柄就是干这个的。
② 而在WinMain函数中,带有4个参数,分别是:hInstance, hPrevInstance, lpCmdLine, nShowCmd。hInstance是程序的当前实例的句柄。在Windows这样的多任务操作系统中,一个程序可以同时运行多个实例。不同的实例间需要彼此区别,句柄就是干这个的。
③ 微软喜欢将内核对象标识,称为句柄。如
进 程: HINSTANCE
文件句柄: HANDLE
窗口句柄: HWND
画笔句柄: HPEN等等。
④ LPCTSTRL实际是一个指向常固定地址的可以根据一些宏定义改变语义的字符串。L表示long指针, 这是为了兼容Windows 3.1等16位操作系统遗留下来的, 在win32中以及其他的32为操作系统中, long指针和near指针及far修饰符都是为了兼容的作用。没有实际意义。
P表示这是一个指针,C表示是一个常量T在Win32环境中, 有一个_T宏,这个宏用来表示你的字符是否使用UNICODE, 如果你的程序定义了UNICODE或者其他相关的宏,那么这个字符或者字符串将被作为UNICODE字符串,否则就是标准的ANSI字符串。STR表示这个变量是一个字符串。
所以LPCTSTR就表示一个指向常固定地址的可以根据一些宏定义改变语义的字符串。同样, LPCSTR就只能是一个ANSI字符串,在程序中我们大部分时间要使用带T的类型定义。LPCTSTR == const TCHAR *
LP和P在win32中是等效的,都是指针的意思。 PTSTR的定义 typedef LPWSTR PTSTR, LPTSTR; STR表示字符串。 问题就出在T上面. T是一个宏,当没定义unicode时为空,定义unicode后表示为宽字符。
所以当定义unicode后,PTSTR转换为PSTR(LPSTR,一样意思)就不能直接转换了,因为一个是unicode,一个是ascii 结论:unicode下,PTSTR转换为PSTR是个编码转换问题。 编码转换可以用MS的函数完成。
WideCharToMultiByte将unicode转换成ascii
MultiByteToWideChar将ascii转换成unicode
⑤ Win32 API 调用小例子
(注意,如果用VS类编译器,在新建项目时要选择"Win32项目",而不是控制台程序,如下图:)
// #include "stdafx.h" // #include "First.h" #include < windows.h > #include < tchar.h > int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(hInstance); UNREFERENCED_PARAMETER(nCmdShow); // 获取桌面的句柄。 HWND hWnd = GetDesktopWindow(); // 显示一行消息。 MessageBox(hWnd,_T( " 第一个应用程序 " ),_T( " 例子 " ), MB_OK); return 0 ; }
上面的例子将会会弹出一个小提示框,如图:
#define _tWinMain wWinMain
#define _tWinMain WinMain
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd );
#ifdef WPRFLAG mainret = wWinMain( #else /* WPRFLAG */ mainret = WinMain( #endif /* WPRFLAG */ (HINSTANCE) & __ImageBase, NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? StartupInfo.wShowWindow : SW_SHOWDEFAULT );
#include " stdafx.h " #include " First.h " int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(nCmdShow); // 使用应用程序句柄 const int MAXSIZE_APPBUF = 256 ; 19 TCHAR wAppTile[MAXSIZE_APPBUF]; 20 LoadString(hInstance,IDS_APP_TITLE,wAppTile,MAXSIZE_APPBUF); // 获取桌面的句柄。 HWND hWnd = GetDesktopWindow(); // 显示一行消息。 MessageBox(hWnd, _T( " 第一个应用程序 " ), wAppTile, MB_OK); return 0 ; }
.
3、CWnd 、 CDC 与 HDC?
① CWnd 是MFC的一个类,所有窗口类从其派生。
CWnd是提供窗口处理的一个类,里面有HWND m_hWnd成员,CWnd对象一般和一个窗口句柄绑定,但提供了很多窗口操作,如SetWindowText,GetWindowText,...。
② CDC 是MFC的DC的一个绘图类,所有跟绘图相关的操作都被封装在CDC类中。 CClientDC类、CWindowDC类皆派生自CDC类
HDC HDC是DC的句柄,API中的一个类似指针的数据类型
(MFC类的前缀都以C开头,H开头的大多是句柄,CDC等设备上下分类,都含有一个类的成员变量:m_nHdc;即HDC类型的句柄;MFC的类,是在用window API语句开发出来的有一定功能的小程序.(也可称为类).使用它的默认方法,就是,记住它的名字与参数)
③ HDC与CDC的转换
方法一:
方法二:HDC hdc; CDC cdc; // cdc到hdc hdc = cdc.GetSafeHdc(); // hdc到cdc cdc.Attach(hdc)
注意:/* CDC to hdc 用成员变量m_hDC hdc to CDC 用FromHandle */ CDC dc; HDC hDC = dc.m_hDC dc.FromHandle(hDC);
dc.FromHandle(hDC)产生一个dc,但是是临时的,mfc不保证系统在什么时候删除dc.
dc.Attach(hDC)是永久的,直到这个dc的生命正常结束。
④ 一些有关HDC与CDC使用的代码
// 使用HDC绘图 HDC hdc; hdc = ::GetDC(m_hWnd); MoveToEx(hdc,m_ptOrigin.x,m_ptOrigin.y,NULL); LineTo(hdc,point.x,point.y); ::ReleaseDC(m_hWnd,hdc);
//所有跟窗口相关的操作都被封装在CWnd类中 //所有跟绘图相关的操作都被封装在CDC类中// 使用CDC绘图 CDC * pDC = CWnd::GetDC(); // 此处直接使用CWnd类的GetDC,其返回值为CDC* // SDK中的同名函数使用返回的是HDC pDC -> MoveTo(m_ptOrigin); pDC -> LineTo(point); CWnd::ReleaseDC(pDC);
// 使用CClientDC绘图 /* CClientDC派生自CDC,在构造的时候调用GetDC, 在析构的时候调用ReleaseDC。使得我们不必 显示调用GetDC与ReleaseDC。 */ CClientDC dc( this ); dc.MoveTo(m_ptOrigin); dc.LineTo(point);
// 使用CWindowDC绘图 /* 同样派生自CDC,构造调用GetWindowDC, 析构时调用ReleaseDC()。 可以访问整个屏幕区域,包括客户区和非 客户区。 */ CWindowDC dc( this ); dc.MoveTo(m_ptOrigin); dc.LineTo(point);
// 通过GetDesktopWindow获得桌面DC /* 注意:用平台SDK同名函数获得的是句柄,CWnd中 的同名函数获得的是CWnd指针。可以通过该函数获得 桌面窗口的CWnd指针,使得我们可以对桌面进行操作。 */ CWindowDC dc(GetDesktopWindow()); dc.MoveTo(m_ptOrigin); dc.LineTo(point);⑤ DC 桌面画图实例
运行后界面如图:#include < windows.h > int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { HDC hdc; HWND hwnd; COLORREF clr; TCHAR szBuffer[] = TEXT( " Http://Www.ProgramLife.Net " ); hwnd = GetDesktopWindow(); // 获取桌面窗口句柄 hdc = GetWindowDC(hwnd); // 获取桌面窗口DC SetBkMode(hdc, TRANSPARENT); // 背景色透明 clr = SetTextColor(hdc, RGB( 255 , 0 , 0 )); // 设置颜色并输出文字 TextOut(hdc, 0 , 0 , szBuffer, lstrlen(szBuffer)); SetTextColor(hdc, RGB( 0 , 255 , 0 )); TextOut(hdc, 0 , 20 , szBuffer, lstrlen(szBuffer)); SetBkMode(hdc, OPAQUE); // 背景色不透明 SetTextColor(hdc, RGB( 0 , 0 , 255 )); TextOut(hdc, 0 , 40 , szBuffer, lstrlen(szBuffer)); SetTextColor(hdc, clr); // 还原颜色 ReleaseDC(hwnd, hdc); // 释放句柄DC Sleep( 5000 ); return 0 ; }.4、COLORREF 与 RGB 、LPVOID 与 LPCVOID ?
① COLORREF 与 RGB
RGB(r,g,b)是一个宏实际上它做得事是((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))rgb(r,g,b) = 一个整型值 = r + g * 256 + b*255*256 COLORREF 是 一 个 32-bit 整 型 数 值,它 代 表 了 一 种 颜 色。你 可以使 用 RGB 函 数 来 初 始 化 COLORREF 它的定义typedef DWORDCOLORREF; COLORREF变量有两种赋值方法●第一种COLORREF cf = RGB(,,);●第二种CColorDialog colorDialog; COLORREF color; if ( colorDialog.DoModal() == IDOK ) { color = colorDialog.GetColor(); }这 段 代 码 使 用 了 MFC 中 的 颜 色 对 话 框如何从 COLORREF中取出RGB分量值?可以使用宏 GetRValue、 GetGValue、 GetBValue他们的定义如下#define GetRValue(rgb) ((BYTE)(rgb)) #define GetGValue(rgb) ((BYTE)(((WORD)(rgb)) >> 8)) #define GetBValue(rgb) ((BYTE)((rgb)>>16))
.
5、char、wchar_t、TCHAR、WCHAR、std::string、std::wstring、CString、LPCTSTR、DWORD
① char、wchar_t
char : 8 bit 表示,最基本的类型。
wchar_t : typedef unsigned short wchar_t ,它是16位的。 wchar_t类型的字符串应该这样写:L”Hello”,因此可定义一个宽字符指针:wchar_t *pw = L”Hello”
::MessageBoxW(NULL,L " 你好 " ,L " OK " , 0 ); // L 表示为 wchar_t 型字符串 wchar_t * p = L”你好”; Setlocale(LC_ALL,””); // 必须有这句话,下面的才可以输出汉字 wcout << p << endl;
② TCHAR、WCHAR、CHAR
#ifdef UNICODE typedef wchar_t TCHAR; #else typedef unsigned char TCHAR; #endif typedef unsigned char CHAR; typedef unsigned wchar_t WCHAR;
由此看出,CHAR实际上是unsigned char,WCHAR为宽字符。而TCHAR根据是否支持unicode而不同。在程序使用sizeof(TCHAR),当默认设置时为1,当定义UNICODE为2
③ std::string、std::wstring
typedef basic_string < char > string ; typedef basic_string < wchar_t > wstring;
在C++中字符串类string是基于basic_string模板的在C++中定义了两个字符串:string和wstring
String 可以看做char[] 而wstring 使用的是wchar_t 类型,这是宽字符类型,用于满足非ASCII要求,wstring有要使用对应的wcout、wcin、wcerr等函数。实际上,string也可以使用中文,但是它将一个汉字写在两个char中,而如果将一个汉字看做一个单位wchar_t的话,那么在wstring中就只占一个单元,其他的非英文文字和编码也是如此,这样才真正的满足字符串操作的要求,尤其是国际化的要求。
④ CString
CString是基于TCHAR数据类型的对象。如果在你的程序中定义了符号_UNICODE,则TCHAR被定义为wchar_t,即16位组成。非_UNICODE模式下,CString对象由8位字符组成。当不使用_UNICODE时,CString是多字节字符集(MBCS,也被认为是双字节字符集,DBCS)。注意,对于MBCS字符串,CString仍然基于8位字符来计算,返回,以及处理字符串,并且你的应用程序必须自己解释MBCS的开始和技术字符。
⑤ DWORD
int、long是有符号的,而DWORD是无符号的;
typedef unsigned long DWORD
.
6、SelectObject()和SelectStockObject()有何区别?
SelectStockObject一般用来选vc本身定义的GDI对象。
SelectStockObject选择的是系统预定义的GDI对象
SelectObject选择你自己自定义的的GDI对象
而你自己建的东东,用完后要从HDC中选出来(选出也用SelectObject),delete掉,否
则致使资源泄露!
SelectObject是将GDI资源选入设备,后者将选择系统设备
因为GDI资源选入设备后需要释放,因此经常这样做。
...
CBrush brush( 0x123456 ) pDC -> SelectObject( & brush); // CBrush *pOldBrush=(CBrush *)pDC->SelectObject(&brush); pDC -> FillRect(rect,brush); pDC -> SelectStockObject(WRITE_BRUSH); // pDC->SelectObject(pOldBrush);
SelectObject is more powerful than SelectStockObject.
.
6、回调函数与API
回调函数参考:http://www.cnblogs.com/hicjiajia/archive/2011/02/15/1954897.html
其实Windows的很多API都采用了回调函数,例如EnumWindows。这里有一个例子,关于将窗体嵌入桌面,具体实现参照:http://www.cnblogs.com/hicjiajia/archive/2011/02/13/1953879.html
还没完呢....