现在的位置: 首页 > 综合 > 正文

Win32 API(一)

2013年04月13日 ⁄ 综合 ⁄ 共 6217字 ⁄ 字号 评论关闭
http://www.cnblogs.com/hicjiajia/archive/2011/02/15/1954942.html

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指针,使得我们可以对桌面进行操作。

抱歉!评论已关闭.