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;
}

上面的例子将会会弹出一个小提示框,如图:

从上面这段程序就可以看到,_tWinMain是应用程序的入口函数,这里是使用它的宏,定义在tchar.h头文件里,为什么要这样作宏定义的呢?由于Windows的应用程序要适应UNICODE和以前单字符的应用程序,由于Windows这两个API的定义是不一样的,如下:
UNICODE的定义:
#define _tWinMain wWinMain
单字符的定义:
#define _tWinMain WinMain
只要经过这样的宏定义后,就可以适应不同字符宽度的函数接口了。由于我是采用UNICODE编译的,所以这里使用wWinMain函数定义,下面再把它的定义找出来,如下:
int WINAPI wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd
);
这里要详细地解释一下函数wWinMain的参数,它有四个参数。
hInstance是当前应用程序的实例句柄,一般用来区分不同的资源使用。
hPrevInstance是以前Win98使用的句柄,在Win2000以后的操作系统里都是空值NULL
lpCmdLine是命令行参数,比如你在Windows开始菜单里运行一个程序,并添加参数在后面,就会传递给应用程序,后面再详细讨论。
nShowCmd是窗口的显示方式,比如最大化显示,最小化显示,还是正常显示。
 
Windows运行程序时,是通过运行库里的启动代码来调用wWinMain函数,它是在启动文件里如下调用:
#ifdef WPRFLAG
mainret
= wWinMain(
#else /* WPRFLAG */
mainret
= WinMain(
#endif /* WPRFLAG */
(HINSTANCE)
&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags
& STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
这就是操作系统传递给应用程序的值,现在就来演示使用第一个参数hInstance
请看下面的例子:
#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指针,使得我们可以对桌面进行操作。
*/
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 DWORD   COLORREF;
COLORREF变量有两种赋值方法
●第一种
COLORREF cf = RGB(,,);
●第二种
CColorDialog colorDialog;
COLORREF color;
if( colorDialog.DoModal() == IDOK )
{
color
= colorDialog.GetColor();
}
这 段 代 码 使 用 了 MFC 中 的 颜 色 对 话 框
如何从 COLORREF中取出RGB分量值?
可以使用宏GetRValueGetGValueGetBValue
他们的定义如下
#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()有何区别?

  SelectObject能选任何pen,brush,font等GDI对象,包括vc本身的和自己定义的   
  SelectStockObject一般用来选vc本身定义的GDI对象。   
  SelectStockObject选择的是系统预定义的GDI对象   
  SelectObject选择你自己自定义的的GDI对象  
说白了,Stock   Object就系统已创建好的,大家共用,不用你去delete,你也delete不了   
  而你自己建的东东,用完后要从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

还没完呢....


posted on 2011-02-15 11:02  hicjiajia  阅读(5293)  评论(0编辑  收藏  举报