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;
}
这个例子是在前面的基础上修改的,主要添加了使用应用程序实例句柄。在第19行里定义了一个保存应用程序标题的缓冲区,然后在第20行里调用函数LoadString从应用程序的资源里加载字符串,它的第一个参数就使用到hInstance句柄。因此应用程序句柄是表示程序在资源上唯一的标识符.
.
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指针,使得我们可以对桌面进行操作。