??xml version="1.0" encoding="utf-8" standalone="yes"?> 对于静态链接库(比较?Q?br />首先Q静态链接库的用需要库的开发者提供生成库?h头文件和.lib文g?br /> 对于动态链接库Q?br />动态链接库的用需要库的开发者提供生成的.lib文g?dll文g。或者只提供dll文g?br />首先我们必须先注意到DLL内的函数分ؓ两种Q? 因此q里衍生Z个问题: 我逐步展开问题?br />W一Q存在两U调用方式——动态调用和静态调?br />W二Q两U调用方式的库的生成q程和调用规?br />1〉动态调?br />
静态链接库与动态链接库都是׃n代码的方式,如果采用静态链接库Q则无论你愿不愿意,lib 中的指o都全部被直接包含在最l生成的 EXE 文g中了。但是若使用 DLLQ该 DLL 不必被包含在最l?EXE 文g中,EXE 文g执行时可以“动态”地引用和卸载这个与 EXE 独立?DLL 文g。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库Q而在动态链接库中还可以再包含其他的动态或静态链接库。静态链接库与静态链接库调用规则M比较如下?/p>
生成库的.h头文件中的声明格式如下:
extern "C" 函数q回cd 函数?参数?;
在调用程序的.cpp源代码文件中如下Q?br />#include "..\lib.h"
#pragma comment(lib,"..\\debug\\libTest.lib")
//指定与静态库一起链?br />
W二Q因为静态链接库是将全部指o都包含入调用E序生成的EXE文g中。因此如果用的是静态链接库Q那么也׃存在“导出某个函数提供给用户使用”的情况Q要想用得全要Q要不就都别要!:)
(1)DLL 导出函数Q可供应用程序调用;
(2)DLL 内部函数Q只能在 DLL E序使用Q应用程序无法调用它们?br />因此调用E序若想调用DLL中的某个函数p以某UŞ式或方式指明它到底想调用哪一个函数?/p>
W一Q如何调用?卌用的方式
W二Q不同调用方式对应的库的生成q程和调用规?/p>
生成库的.h头文件中的声明格式如下:
extern "C" 函数q回cd __declspec(dllexport) 函数?参数?;
在调用程序的.cpp源代码文件中按如下流E调用:
在main函数代码的开始处
定义需要的DLL模块的句柄和此DLL模块模块中需要调用的函数的函数指针?br />定义好后紧接着是标准的三部曲Q?br />取得需要的DLL模块------>q回模块句柄
LoadLibrary("DLL模块路径")
取得需要的函数地址——?gt;q回函数指针GetProcAddress(模块句柄Q?函数?)
从内存中卸蝲DLL模块——?gt;FreeLibrary(模块句柄)
2>静态调?br />在调用程序的.cpp源代码文件中按如下流E调用:
#include.........
告诉~译器与 DLL 相对应的.lib 文g所在的路径及文件名
#pragma comment(lib,"路径和文件名")
extern "C" 函数q回cd _declspec(dllimport) 函数?参数?分号
int main()
{
..............
}
未完Q待l?......
]]>
struct AFX_MSGMAP //消息映射?br />{
AFX_MSGMAP* pBaseMessageMap; //基类消息映射表的指针Q?br />AFX_MSGMAP_ENTRY* lpEntries; //消息入口表的指针Q?br />};
struct AFX_MSGMAP_ENTRY //消息映射入口?br />{
UINT nMessage; //消息Q?br />UINT nCode; //控g的通知码或WM_NOTIFY的通知?br />UINT nID; //控g的IDQ?0时ؓ Window 消息)
UINT nLastID; //最后一个控件的IDQ?br />UINT nSig; //信号cdQ?br />AFX_PMSG pfn; //消息函数的指针;
};
typedef void (CCmdTarget::*AFX_PMSG)(void); //消息映射函数Q?/p>
一、消息映图
1、DECLARE_MESSAGE_MAP()
展开Q?br />static AFX_MSGMAP_ENTRY _messageEntries[] //消息映射入口?
static AFX_MSGMAP messageMapQ?//消息映射表;
virtual AFX_MSGMAP* GetMessageMap() constQ?/q回消息映射 表的指针Q?/p>
2、BEGIN_MESSAGE_MAP(class_name,base_class_name)
①、定义GetMessageMap()函数Q代码如下:
{ return &class_name::messageMapQ}
②、初始化消息映射表,代码如下Q?br />AFX_MSGMAP class_name::messageMap={
&(base_class_name::messageMap)Q?br />&(class_name::_messageEntries)
}
③、初始化消息入口?br />AFX_MSGMAP_ENTRY class_name::_messageEntries[]={
//在这里添加消息和映射函数Q?/p>
3、END_MESSAGE_MAP()
{0,0,0,0,AfxSig_end,(AFX_PMSG)0}
} Q?//该括号和③相对应Q?/p>
二、消息类?br />1、命令消?WM_COMMAND)
所有派生自 CCmdTarget 的类都有资格接受WM_COMMAND?/p>
2、Window消息(WM_xxx)
所有派生自 CWnd 的类都有资格接受 WM_xxx?/p>
3、控件消?WM_NOTIFY)
控g向其父窗口通知消息?/p>
三、消息处?br />1、WM_xxx 消息处理
H口c?自n)处理→基cd理→CWnd∷DefWindowProc()处理Q?br />其所对应的宏一般ؓ在消?WM_ 前面加上 ON_?/p>
2、命令消息处?br />命o消息来自命o用户接口对象(菜单、加速键或工h按钮)发出的WM_COMMAND消息Q?br />㈠、WM_COMMAND消息
其所包含的类型和对应的宏如下Q?br />①、ON_COMMAND(ID,pfn)
标准的命令消息;
②、ON_COMMAND_EX(ID,pfn)
多个对象对同一个命?ID 的处理;
其函数的原型如下Q?br />afx_msg BOOL pfn(UINT nID)
说明Q?br />当返?TRUE 时表C已l处理,不用在消息处理链中l处理该命oQؓ FALSE 时表Cl在消息处理链中处理该命令?br />注意Q?br />其一Q在多对象处理中一定要使用该宏Q?br />其二Qpfn(UINT nID)(消息处理函数)q回值将其类型voidҎBOOLQ而且必须为FALSEQ?br />其三Q多个对象的处理是由高层向低层的q程Q即视图cZL架窗口类→应用程序类Q?/p>
③、ON_COMMAND_RANGE(nID,nLastID,pfn)
多个命o ID 提供相同的处理;
注意Q?br />其一Q确保nID、nLastID的值在 Resource.h 中是q箋的?br />其二Q一般在函数 pfn(UINT nID) 中加入参敎ͼ用来定那一个按钮点凅R?/p>
㈡、CN_UPDATE_COMMAND_UI消息
当菜单项、工h按钮{[命o用户接口对象]要更新其状态时所对应的消息,它所包含的类型和对应的宏如下Q?br />①、ON_UPDATE_COMMAND_UI(ID,pfn)
其中函数的原型如下:
afx_msg void pfn(CCmdUI* pCmdUI)
②、ON_UPDATE_COMMAND_UI_RANGE(nID,nLastID,pfn)
该函数可以处理一l[命o用户接口对象]的外观;
其中函数的原型如下:
afx_msg void pfn(CCmdUI* pCmdUI)
重要Q?br />CCmdUI 中的 m_nID 成员表示不同?IDQ因此可以利用它来进行区别处理?/p>
3、控件的通知消息
从控件和子窗口发送到父窗口的WM_COMMAND通知消息(卛_发送命令消息中加入控g的通知??br />注意Q在 Window9x 新控件中不再传送WM_COMMAND通知消息Q而是发?WM_NOTIFY 消息Q但Z兼容Q旧有的控gq是传送WM_COMMAND消息?br />例如Q?br />CEdit控g向父H口发?EN_CHANGE 通知代码的WM_COMMAND消息?br />注意Q框架像传送其?WM_ 消息一样传送通知消息Q但有一个例外,即由 [按钮] 控g发送的 BN_CLICKED 通知消息Q被作ؓ命o消息特别处理?br />㈠、WM_COMMAND 其所对应的宏如下Q?br />①、ON_CONTROL(通知? nID,fn)
②、ON_CONTROL_RANGE(通知? nFirstID,nEndID,fn)
注意Q?br />q两个宏的应用和 ON_COMMAND、ON_COMMAND_RANGE相同Q所不同的是在宏前面加入[通知码]?br />注意Q可以根据不同的控g的[通知码]z出特定的宏,其所z的宏一般ؓ?[通知码] 前面加上 ON_?br />㈡、WM_NOTIFY 其所对应的宏如下Q?br />①、ON_NOTIFY(通知? nID,fn)
其中函数的原型如下:
afx_msg void fn(NMHDR* pNotifyStruct,LRESULT* result)
其中l构Q?br />typedef struct tagNMHDR {
HWND hwndFrom; //发送通知消息的控件的句柄Q?br />UINT idFrom; //发送通知消息的控件的 IDQ?br />UINT code; //通知码;
} NMHDR;
②、ON_NOTIFY_EX(通知? nID,fn)
表示一个消息在多个对象的成员函Cq行处理?br />其中函数的原型如下:
afx_msg BOOL fn(UINT nID,NMHDR* pNotifyStruct,LRESULT* result)
说明Q?br />它必返?BOOL cd的数|其意义和 ON_COMMAND_EX 相同?/p>
③、ON_NOTIFY_RANGE(通知? nFirstID,nEnd,fn)
表示多个控g的通知消息在同一个函Cq行处理?br />其中函数的原型如下:
afx_msg void fn(UINT nID,NMHDR* pNotifyStruct,LRESULT* result)
说明Q?br />其意义和ON_COMMAND_RANGE相同?/p>
4、反消息处?br />父窗口在处理控gH口的通知消息WM_CTLCOLOR、WM_COMMAND、WM_NOTIFYӞ会把该消息{化ؓ反射消息Qƈ转交l控件子H口处理Q只有在控g子窗体不处理该消息时Q父H口才有Z处理?br />注意Q在cȝ属性对话框中的消息面可查反射消息(前面?="标志)
①、WM_CTLCOLOR_REFLECT反射消息
其所对应的宏如下Q?br />ON_WM_CTLCOLOR_REFLECT()
反射消息函数的原型:
HBRUSH class_name∷CtlColor(CDC* pDC,UINT nCtlColor)
{
return NULL;
}
该函数用来重|控件的色Q注意:必须 return CBrush才有效?/p>
5、自定义的窗口消?br />自定义窗口消息的消息标志都大于WM_USER(臛_是WM_USER+100Q因多控仉使用q一范围的WM_USER消息)
使用自定义的消息分ؓ二步Q?br />①、在 Resource.h 中定义消息标?br />#define WM_MYMSG (WM_USER+1000)
②、在消息映射表中加入消息映射?br />BEGIN_MESSAGE_MAP()
ON_MESSAGE(WM_MYMSG,fn)
END_MESSAGE_MAP()
说明Q?br />其对应的宏ؓ ON_MESSAGE()Q其成员函数的原型ؓQ?br />afx_msg LRESULT fn(WPARAM,LPARAM)
6、登记消?br />①、在pȝ中注册ƈ获取一个登记消息的消息标记
UINT RegisterWindowMessage(LPCTSTR)
说明Q?br />通过 API 函数来注册消息标讎ͼ其中 LPCTSTR 为用LL字符丌Ӏ例如:
UINT WM_MYMSG=RegisterWindowMessage("MYMSG");
其中 WM_MYMSG 是自定义无符h型的消息标记?/p>
②、在消息映射表中加入消息映射?br />BEGIN_MESSAGE_MAP()
ON_REGISTERED_MESSAGE(WM_MYMSG,fn)
END_MESSAGE_MAP()
说明Q?br />其对应的宏ؓ ON_REGISTERED_MESSAGE()Q其成员函数的原型ؓQ?br />afx_msg LRESULT fn(WPARAM,LPARAM)
注意Q登记消息可以实现跨q程的数据通讯?/p>
7、线E消?br />只有l承自CWinThreadcL能允许处理线E消息?br />①、定义线E的消息标记
有两U方法:
(1)、用自定义的消息标讎ͼ卻IWM_USERQ?br />(2)、用登记的消息标记Q即QRegisterWindowMessageQ?/p>
②、在CWinThreadl承cȝ消息映射表中d?br />ON_THREAD_MESSAGE(消息标记,fn) //自定义的消息Q?br />ON_REGISTERED_THREAD_MESSAGE(消息标记,fn) //登记?//消息
③、其函数的原型如下:
afx_msg void fn(WPARAM wPARAM,LPARAM lParam)
④、引发线E消?br />U程消息的引发必调?CWinThread cȝPostThreadMessage消息投递到U程消息队列中?br />注意Q可以通过 AfxGetApp() 函数获取一个全局的应用对象?br />PostThreadMessage(UINT,WPARAM,LPARAM)
8、WM_COPYDATA
操作pȝl护一块内存来理 WM_COPYDATA 消息Q该消息主要用于跨进E传递数据,传递的数据量达?232?br />①、定义一?COPYDATASTRUCT 数据l构
typedef struct tagCOPYDATASTRUCT {
DWORD dwData; //自定义的Ҏ数据Q?br />DWORD cbData; //以字节ؓ单位?lpData 的大;
PVOID lpData; //传送的数据内存块的指针Q?br />} COPYDATASTRUCT;
②、其所对应的宏
ON_WM_COPYDATA()
③、其所对应的函数的原型
afx_msg BOOL OnCopyData(CWnd*,COPYDATASTRUCT*)
说明Q?br />CWnd*Q发送该消息的窗口的指针Q?/p>
9、投递和发送消?br />通过向一个窗体投递或发送消息,可以间接地驱动窗体的消息q程?br />投?PostMessage)Q将消息攑ֈU程的消息队列中Q然后不{线E处理该消息q接返回到调用斏V?br />发?SendMessage)Q当一个线E向目标U程发送消息时Q该U程要一直等待,直到目标U程处理了该消息为止?br />①、投递消?br />BOOL CWnd∷PostMessage(UINT,WPARAM=0,LPARAM=0)
说明Q?br />CWndQ目标窗口;
该函数将一条消息放入到应用E序的消息队列,然后不等H口处理q接返回?/p>
②、发送消?br />LRESULT CWnd∷SendMessage(UINT,WPARAM=0,LPARAM=0)
说明Q?br />CWndQ目标窗口;
该函数将一条消息放入到应用E序的消息队列,{待H口处理后才q回?br />Z避免U程陷入怹{待状态,可以用SendMessageTimeout代替SendMessageQ?br />LRESULT SendMessageTimeout(HWND,UINT,WPARAM,LPARAM,UINT,UINT,PDWORD_PTR)
说明Q?br />HWNDQ窗口句柄;
UINTQ消息发送的选项QؓSMTO_BLOCKӞ可以防止U程?限等待,x据一定的时D回?br />UINTQ超Ӟ以毫Uؓ单位Q?br />PDWORD_PTRQ返回|
注意QCWnd没有对该函数的包装?/p>
③、投递和发送消?br />BOOL CWnd∷SendNotifyMessage(UINT,WPARAM,LPARAM)
说明Q?br />CWndQ目标窗口;
该消息具有SendMessage和PostMessage两种功能Q?br />当目标窗口和发送窗口ؓ同一个线E时Q则相当于SendMessage的功能;否则当不为同一个线E时Q则为PostMessage的功能?/p>
6-1、投递和发?WM_XXX 消息
在发送标准的 WINDOW 消息Ӟ只要该消息?ID、wParam、lParam参数攑֜ SendMessage()和PostMessage()函数的相应位|即可?/p>
6-2、投递和发送命令消息和控g的通知消息
在投递和发送命令消息时Q消息的 ID?WM_COMMADNQ而对于不同的菜单V加速键、控件则wParam、lParam的取g同?br />wParam分成低、高两部分,低部分ؓ菜单V加速键、控件的ID。高部分则:
菜单:0Q加速键Q?Q控Ӟ通知?br />lParamQ当控g时是控g的句柄,否则?NULL?/p>
对于wParam参数可以采用自定义宏Q?br />WPARAM MAKEWPARAM(WORD wLow,WORD wHigh)
6-3、投递和发送自定义的窗口消?br />在投递和发送自定义的窗口消息时Q参?wParam、lParam 没有特别的涵义,只和普通函数的形参一栯行数据的传递?br />注意Q?br />PostMessage ?SendMessage 是不同的Q前者投递后p回,而后者必ȝ到消息处理后再返回;所以在参数?[局部] ?[临时]Ӟ使用PostMessage函数会引发错?除非参数使用 指针Q则可避免错?Q而必M用SendMessage函数?br />6-4、投递和发送注册的H口消息
?6-3 基本一P但它要特别注意的问题是:在跨q程的处理消息时Q如果将消息PostMessage、SendMessage到某个进E?AQ则必须在进E?B 中获取进E?A 的窗口类名,q过H口cd获取H口的指针,最后再Ҏ指针调用 PostMessage、SendMessage 函数?br />注意Q在获取H口的指针时Q可以根据窗口类名或H口的标题?/p>
6-5、投递和发送WM_COPYDATA消息
SendMessage(消息标记,WPARAM,LPARAM)
其中Q?br />消息标记QWM_COPYDATAQ?br />WPARAMQ发送该消息的窗口句柄;
LPARAMQCOPYDATASTRUCTl构的指针,先通过(LPVOID)q行转换Q再通过(LPARAM)q行转换Q如下Ş式:
(LPARAM)(LPVOID)&cds
int i = 100;
long l = 2001;
float f=300.2;
double d=12345.119;
char username[]="E佩?;
char temp[200];
char *buf;
CString str;
_variant_t v1;
_bstr_t v2;
一、其它数据类型{换ؓ字符?
短整?int)
itoa(i,temp,10);///i转换为字W串攑օtemp?最后一个数字表C十q制
itoa(i,temp,2); ///按二q制方式转换
长整?long)
ltoa(l,temp,10);
点?float,double)
用fcvt可以完成转换,q是MSDN中的例子:
int decimal, sign;
char *buffer;
double source = 3.1415926535;
buffer = _fcvt( source, 7, &decimal, &sign );
q行l果:source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0
decimal表示数点的位置,sign表示W号:0为正敎ͼ1?
CString变量
str = "2008北京奥运";
buf = (LPSTR)(LPCTSTR)str;
BSTR变量
BSTR bstrValue = ::SysAllocString(L"E序?);
char * buf = _com_util::ConvertBSTRToString(bstrValue);
SysFreeString(bstrValue);
AfxMessageBox(buf);
delete(buf);
CComBSTR变量
CComBSTR bstrVar("test");
char *buf = _com_util::ConvertBSTRToString(bstrVar.m_str);
AfxMessageBox(buf);
delete(buf);
_bstr_t变量
_bstr_tcd是对BSTR的封装,因ؓ已经重蝲?操作W,所以很Ҏ使用
_bstr_t bstrVar("test");
const char *buf = bstrVar;///不要修改buf中的内容
AfxMessageBox(buf);
通用Ҏ(针对非COM数据cd)
用sprintf完成转换
char buffer[200];
char c = '1';
int i = 35;
long j = 1000;
float f = 1.7320534f;
sprintf( buffer, "%c",c);
sprintf( buffer, "%d",i);
sprintf( buffer, "%d",j);
sprintf( buffer, "%f",f);
二、字W串转换为其它数据类?
strcpy(temp,"123");
短整?int)
i = atoi(temp);
长整?long)
l = atol(temp);
点(double)
d = atof(temp);
CString变量
CString name = temp;
BSTR变量
BSTR bstrValue = ::SysAllocString(L"E序?);
...///完成对bstrValue的?
SysFreeString(bstrValue);
CComBSTR变量
CComBSTRcd变量可以直接赋?
CComBSTR bstrVar1("test");
CComBSTR bstrVar2(temp);
_bstr_t变量
_bstr_tcd的变量可以直接赋?
_bstr_t bstrVar1("test");
_bstr_t bstrVar2(temp);
三、其它数据类型{换到CString
使用CString的成员函数Format来{?例如:
整数(int)
str.Format("%d",i);
点?float)
str.Format("%f",i);
字符串指?char *){已l被CString构造函数支持的数据cd可以直接赋?
str = username;
对于Format所不支持的数据cdQ可以通过上面所说的关于其它数据cd转化到char *的方法先转到char *Q然后赋值给CString变量?
四、BSTR、_bstr_t与CComBSTR
CComBSTR 是ATL对BSTR的封装,_bstr_t是C++对BSTR的封?BSTR?2位指?但ƈ不直接指向字串的~冲区?
char *转换到BSTR可以q样:
BSTR b=_com_util::ConvertStringToBSTR("数据");///使用前需要加上comutil.h和comsupp.lib
SysFreeString(bstrValue);
反之可以使用
char *p=_com_util::ConvertBSTRToString(b);
delete p;
具体可以参考一Q二D落里的具体说明?
CComBSTR与_bstr_t对大量的操作W进行了重蝲Q可以直接进?,!=,=={操作,所以用非常方ѝ?
特别是_bstr_t,大家使用它?
五、VARIANT 、_variant_t ?COleVariant
VARIANT的结构可以参考头文gVC98\Include\OAIDL.H中关于结构体tagVARIANT的定义?
对于VARIANT变量的赋|首先lvt成员赋|指明数据cdQ再对联合结构中相同数据cd的变量赋|举个例子Q?
VARIANT va;
int a=2001;
va.vt=VT_I4;///指明整型数据
va.lVal=a; ///赋?
对于不马上赋值的VARIANTQ最好先用Void VariantInit(VARIANTARG FAR* pvarg);q行初始?其本质是vt讄为VT_EMPTY,下表我们列Dvt与常用数据的对应关系:
Byte bVal; // VT_UI1.
Short iVal; // VT_I2.
long lVal; // VT_I4.
float fltVal; // VT_R4.
double dblVal; // VT_R8.
VARIANT_BOOL boolVal; // VT_BOOL.
SCODE scode; // VT_ERROR.
CY cyVal; // VT_CY.
DATE date; // VT_DATE.
BSTR bstrVal; // VT_BSTR.
DECIMAL FAR* pdecVal // VT_BYREF|VT_DECIMAL.
IUnknown FAR* punkVal; // VT_UNKNOWN.
IDispatch FAR* pdispVal; // VT_DISPATCH.
SAFEARRAY FAR* parray; // VT_ARRAY|*.
Byte FAR* pbVal; // VT_BYREF|VT_UI1.
short FAR* piVal; // VT_BYREF|VT_I2.
long FAR* plVal; // VT_BYREF|VT_I4.
float FAR* pfltVal; // VT_BYREF|VT_R4.
double FAR* pdblVal; // VT_BYREF|VT_R8.
VARIANT_BOOL FAR* pboolVal; // VT_BYREF|VT_BOOL.
SCODE FAR* pscode; // VT_BYREF|VT_ERROR.
CY FAR* pcyVal; // VT_BYREF|VT_CY.
DATE FAR* pdate; // VT_BYREF|VT_DATE.
BSTR FAR* pbstrVal; // VT_BYREF|VT_BSTR.
IUnknown FAR* FAR* ppunkVal; // VT_BYREF|VT_UNKNOWN.
IDispatch FAR* FAR* ppdispVal; // VT_BYREF|VT_DISPATCH.
SAFEARRAY FAR* FAR* pparray; // VT_ARRAY|*.
VARIANT FAR* pvarVal; // VT_BYREF|VT_VARIANT.
void FAR* byref; // Generic ByRef.
char cVal; // VT_I1.
unsigned short uiVal; // VT_UI2.
unsigned long ulVal; // VT_UI4.
int intVal; // VT_INT.
unsigned int uintVal; // VT_UINT.
char FAR * pcVal; // VT_BYREF|VT_I1.
unsigned short FAR * puiVal; // VT_BYREF|VT_UI2.
unsigned long FAR * pulVal; // VT_BYREF|VT_UI4.
int FAR * pintVal; // VT_BYREF|VT_INT.
unsigned int FAR * puintVal; //VT_BYREF|VT_UINT.
_variant_t是VARIANT的封装类Q其赋值可以用强制类型{换,其构造函C自动处理q些数据cd?
使用旉加上#include
例如Q?
long l=222;
ing i=100;
_variant_t lVal(l);
lVal = (long)i;
COleVariant的用与_variant_t的方法基本一P请参考如下例子:
COleVariant v3 = "字符?, v4 = (long)1999;
CString str =(BSTR)v3.pbstrVal;
long i = v4.lVal;
六、其它一些COM数据cd
ҎProgID得到CLSID
HRESULT CLSIDFromProgID( LPCOLESTR lpszProgID,LPCLSID pclsid);
CLSID clsid;
CLSIDFromProgID( L"MAPI.Folder",&clsid);
ҎCLSID得到ProgID
WINOLEAPI ProgIDFromCLSID( REFCLSID clsid,LPOLESTR * lplpszProgID);
例如我们已经定义?CLSID_IApplication,下面的代码得到ProgID
LPOLESTR pProgID = 0;
ProgIDFromCLSID( CLSID_IApplication,&pProgID);
...///可以使用pProgID
CoTaskMemFree(pProgID);//不要忘记释放
七、ANSI与Unicode
UnicodeUCؓ宽字W型字串,COM里用的都是Unicode字符丌Ӏ?
ANSI转换到Unicode
(1)通过Lq个宏来实现Q例? CLSIDFromProgID( L"MAPI.Folder",&clsid);
(2)通过MultiByteToWideChar函数实现转换,例如:
char *szProgID = "MAPI.Folder";
WCHAR szWideProgID[128];
CLSID clsid;
long lLen = MultiByteToWideChar(CP_ACP,0,szProgID,strlen(szProgID),szWideProgID,sizeof(szWideProgID));
szWideProgID[lLen] = '\0';
(3)通过A2W宏来实现,例如:
USES_CONVERSION;
CLSIDFromProgID( A2W(szProgID),&clsid);
Unicode转换到ANSI
(1)使用WideCharToMultiByte,例如:
// 假设已经有了一个Unicode ?wszSomeString...
char szANSIString [MAX_PATH];
WideCharToMultiByte ( CP_ACP, WC_COMPOSITECHECK, wszSomeString, -1, szANSIString, sizeof(szANSIString), NULL, NULL );
(2)使用W2A宏来实现,例如:
USES_CONVERSION;
pTemp=W2A(wszSomeString);
八、其?
Ҏ息的处理中我们经帔R要将WPARAM或LPARAM{?2位数据(DWORD)分解成两?6位数据(WORD),例如Q?
LPARAM lParam;
WORD loValue = LOWORD(lParam);///取低16?
WORD hiValue = HIWORD(lParam);///取高16?
对于16位的数据(WORD)我们可以用同LҎ分解成高低两?位数?BYTE),例如:
WORD wValue;
BYTE loValue = LOBYTE(wValue);///取低8?
BYTE hiValue = HIBYTE(wValue);///取高8?
两个16位数据(WORDQ合?2位数?DWORD,LRESULT,LPARAM,或WPARAM)
LONG MAKELONG( WORD wLow, WORD wHigh );
WPARAM MAKEWPARAM( WORD wLow, WORD wHigh );
LPARAM MAKELPARAM( WORD wLow, WORD wHigh );
LRESULT MAKELRESULT( WORD wLow, WORD wHigh );
两个8位的数据(BYTE)合成16位的数据(WORD)
WORD MAKEWORD( BYTE bLow, BYTE bHigh );
从R(red),G(green),B(blue)三色得到COLORREFcd的颜色?
COLORREF RGB( BYTE byRed,BYTE byGreen,BYTE byBlue );
例如COLORREF bkcolor = RGB(0x22,0x98,0x34);
从COLORREFcd的颜色值得到RGB三个颜色?
BYTE Red = GetRValue(bkcolor); ///得到U颜?
BYTE Green = GetGValue(bkcolor); ///得到lK?
BYTE Blue = GetBValue(bkcolor); ///得到兰颜?
九、注意事?
假如需要用到ConvertBSTRToString此类函数,需要加上头文gcomutil.h,q在setting中加入comsupp.lib或者直接加?pragma comment( lib, "comsupp.lib" ) ?br />
消息的接?/font> 消息的处?/font> 首先QGetMessage从进E的ȝE的消息队列中获取一个消息ƈ它复制到MSGl构Q如果队列中没有消息Q则GetMessage函数等待一个消息的到来以后才返回?如果你将一个窗口句柄作为第二个参数传入GetMessageQ那么只有指定窗口的的消息可以从队列中获得。GetMessage也可以从消息队列中过滤消息只接受消息队列中落在范围内的消息。这时候就要利用GetMessageQPeekMessage指定一个消息过滤器。这个过滤器是一个消息标识符的范围或者是一个窗体句柄,或者两者同时指定。当应用E序要查找一个后入消息队列的消息是很有用。WM_KEYFIRST ?WM_KEYLAST 帔R用于接受所有的键盘消息?WM_MOUSEFIRST ?WM_MOUSELAST 帔R用于接受所有的鼠标消息? H口q程 switch (message) 消息分流?/font>
消息的接收主要有Q个函数QGetMessage、PeekMessage、WaitMessage?br /> GetMessage原型如下QBOOL GetMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax);该函数用来获取与hWnd参数所指定的窗口相关的且wMsgFilterMin和wMsgFilterMax参数所l出的消息D围内的消息。需要注意的是,如果hWnd为NULLQ则GetMessage获取属于调用该函数应用程序的MH口的消息,如果wMsgFilterMin和wMsgFilterMax都是Q,则GetMessagep回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除?br /> PeekMessage原型如下QBOOL PeekMessage(LPMSG lpMsg,HWND hWnd,UINT wMsgFilterMin,UINT wMsgFilterMax,UINT wRemoveMsgQ;该函数用于查看应用程序的消息队列Q如果其中有消息将其放入lpMsg所指的l构中,不过Q与GetMessage不同的是QPeekMessage函数不会{到有消息放入队列时才返回。同P如果hWnd为NULLQ则PeekMessage获取属于调用该函数应用程序的MH口的消息,如果hWnd=-1Q那么函数只q回把hWnd参数为NULL的PostAppMessage函数送去的消息。如果wMsgFilterMin和wMsgFilterMax都是Q,则PeekMessagep回所有可得到的消息。函数获取之后将删除消息队列中的除WM_PAINT消息之外的其他消息,至于WM_PAINT则只有在其处理之后才被删除?br /> WaitMessage原型如下QBOOL VaitMessage();当一个应用程序无事可做时Q该函数将控制权交l另外的应用E序Q同时将该应用程序挂P直到一个新的消息被攑օ应用E序的队列之中才q回?/p>
接下来我们谈一下消息的处理Q首先我们来看一下VC中的消息泵:
while(GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
然后TranslateAccelerator判断该消息是不是一个按键消息ƈ且是一个加速键消息Q如果是Q则该函数将把几个按键消息{换成一个加速键消息传递给H口的回调函数。处理了加速键之后Q函数TranslateMessage把两个按键消息WM_KEYDOWN和WM_KEYUP转换成一个WM_CHARQ不q需要注意的是,消息WM_KEYDOWN,WM_KEYUP仍然传递给H口的回调函数。 ?
处理完之后,DispatchMessage函数把此消息发送给该消息指定的H口中已讑֮的回调函数。如果消息是WM_QUITQ则GetMessageq回Q,从而退出@环体。应用程序可以用PostQuitMessage来结束自q消息循环。通常在主H口的WM_DESTROY消息中调用?br /> 下面我们举一个常见的例子来说明q个消息늚q用Q?br /> if (::PeekMessage(&msg, m_hWnd, WM_KEYFIRST,WM_KEYLAST, PM_REMOVE))
{
if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE)Q.Q?br /> }
q里我们接受所有的键盘消息Q所以就用WM_KEYFIRST ?WM_KEYLAST作ؓ参数。最后一个参数可以是PM_NOREMOVE 或?PM_REMOVEQ表C消息信息是否应该从消息队列中删除。 ?
所以这D小代码是判断是否按下了Esc键,如果是就q行处理?/p>
H口q程是一个用于处理所有发送到q个H口的消息的函数。Q何一个窗口类都有一个窗口过E。同一个类的窗口用同LH口q程来响应消息?pȝ发送消息给H口q程消息数据作为参C递给他,消息到来之后Q按照消息类型排序进行处理,其中的参数则用来区分不同的消息,H口q程使用参数产生合适行为?br /> 一个窗口过E不l常忽略消息Q如果他不处理,它会消息传回到执行默认的处理。窗口过E通过调用DefWindowProc来做q个处理。窗口过E必return一个g为它的消息处理结果。大多数H口只处理小部分消息和将其他的通过DefWindowProc传递给pȝ做默认的处理。窗口过E被所有属于同一个类的窗口共享,能ؓ不同的窗口处理消息。下面我们来看一下具体的实例Q?br /> LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
通常的窗口过E是通过一个switch语句来实现的Q这个事情很烦,有没有更便的Ҏ呢?有,那就是消息分器Q利用消息分器Q我们可以把switch语句分成更小的函敎ͼ每一个消息都对应一个小函数Q这样做的好处就是对消息更容易管理?br /> 之所以被UCؓ消息分流器,是因ؓ它可以对M消息q行分流。下面我们做一个函数就很清楚了Q?br /> void MsgCracker(HWND hWnd,int id,HWND hWndCtl,UINT codeNotify)
{
switch(id)
{
case ID_A:
if(codeNotify==EN_CHANGE)...
break;
case ID_B:
if(codeNotify==BN_CLICKED)...
break;
....
}
}
然后我们修改一下窗口过E:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
HANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);
HANDLE_MSG(hWnd,WM_DESTROY,MsgCracker);
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
在WindowsX.h中定义了如下的HANDLE_MSG宏:
#define HANDLE_MSG(hwnd,msg,fn) \
switch(msg): return HANDLE_##msg((hwnd),(wParam),(lParam),(fn));
实际上,HANDLE_WM_XXXX都是宏,例如QHANDLE_MSG(hWnd,WM_COMMAND,MsgCracker);被转换成如下定义:
#define HANDLE_WM_COMMAND(hwnd,wParam,lParam,fn)\
((fn)((hwnd),(int)(LOWORD(wParam)),(HWND)(lParam),(UINT)HIWORD(wParam)),0L);
好了Q事情到了这一步,应该一切都明朗了?br /> 不过Q我们发现在windowsx.h里面q有一个宏QFORWARD_WM_XXXXQ我们还是那WM_COMMANDZQ进行分析:
#define FORWARD_WM_COMMAND(hwnd, id, hwndCtl, codeNotify, fn) \
(void)(fn)((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
所以实际上QFORWARD_WM_XXXX消息参数进行了重新构造,生成了wParam && lParamQ然后调用了我们定义的函数?br /> 好了Q事情到q里也算是也D落了,明天我们再分析消息在MFC中的处理?/p>
]]>
最q突然又很有Ȁ情的开始看Jeff Prosise的那?Programming Windows with MFC, 2 ed."。尽是英文版的Q但是感觉这本书上手比喉l的那本所谓的 深入出MFC 要容易理解的多。候同学给ZU故弄玄虚故作深沉的感觉Q而Jeff Prosise的这本书才真正的U得上是深入出?/p>
管如此Q其中有关GDIl图中的坐标映射部分q是有一个问题没有搞清楚Q那是SetWindowOrg和SetViewportOrgq两个函数到底应该如何理解。潘爱民译的那本VC内幕没有讲清楚;Jeff Prosise的这本书没有讲清楚;MSDN上的东西看的也是一头雾_Charles Petzold的那本书q没有来得及看。因个问题,昨天晚上是带着遗憾的困惑入睡的?/p>
ȝ来说Q我对这两个函数的理解导致的l果是与实际E序q行的结果截然相反。依据MSDN上的解释Q有一个很严重的问题没有阐q清楚,那就是:所谓的SetWindowOrg(x, y)函数Q到底是表示set window origin to (x, y)q是set window origin as (x, y)Qto和as在执行的时候,其操作的效果是截然相反的?/p>
set window origin to (x, y)表示坐标原点设|到(x, y)Q即?x, y)作ؓ坐标原点Q此时原点坐标不再ؓ(0, 0)Q?br />set window origin as (x, y)表示原来的原点(0, 0)的坐标改?x, y)Q即所有点的坐标增?+x, +y)Q?/p>
现在我的理解是:应该?set window origin to (x, y)。这U理解基于以下几个前提:
1. 所有绘图语句中l出的坐标,全部是逻辑坐标Q即?window 中的坐标(相对于viewport所表示的设备坐标而言);
2. 所有用戯看到的点Q其讑֤坐标一定是位于(0, 0)?1024, 768)范围内;(假设昄器ؓ输出讑֤Q采用MM_TEXT映射方式Q且屏幕分L率ؓ1024*768);
3. 所谓?0,0)原点,原点的坐标一定就?0,0)”这U理解,是错误的Q?br />4. Viewport中的坐标表示讑֤坐标QWindow中的坐标表示逻辑坐标Q?br />5. 当在逻辑坐标中指定新的原点后Q在执行映射Ӟ讑֤坐标的原点一定要与逻辑坐标的新原点重合Q反q来也是一P即两个坐标系的原点一定要重合?/p>
下面举例说明Q?MM_TEXT映射模式)
(1)
CRect rect(0, 0, 200, 200);
dc.rectangle(rect);
上面的语句在屏幕的最左上角绘制一个正方Ş;(因ؓ此时逻辑坐标与设备坐标没有偏U?
(2)
dc.SetViewportOrg(100, 100);
CRect rect(0, 0, 200, 200);
dc.rectangle(rect);
设备坐标的原点讄?100, 100)Q即讑֤坐标的原点不?0, 0)处,而是?100, 100)处;此时若执行映的话,逻辑坐标的原?0, 0)需要与讑֤坐标的原?100, 100)重合(参考前?)Q那么此时绘制的矩Ş(0, 0, 200, 200)的坐?为逻辑坐标Q参考前?)在设备坐标中׃映射?100, 100, 300, 300)Q最l我们在昄器上看到的会是一个向右下方偏U?100, 100)的一个边长ؓ200的正方Ş(用户看到的点是在讑֤坐标中的Q参考前?)
(3)
dc.SetWindowOrg(100, 100);
CRect rect(0, 0, 200, 200);
dc.rectangle(rect);
逻辑坐标的原点设|到(100, 100)Q即逻辑坐标的原点不?0, 0)处,而是?100, 100)处;此时若执行映的话,讑֤坐标的原?0, 0)需要与逻辑坐标的原?100, 100)重合(参考前?)Q那么此时绘制的矩Ş(0, 0, 200, 200)的坐?为逻辑坐标Q参考前?)在设备坐标中׃映射?-100, -100, 100, 100)Q最l我们在昄器上看到的会是一个只?/4个大的矩Ş的一部分(事实上相当于向左上方偏移(100, 100)的一个边长ؓ200的正方Ş。注意:用户看到的点是在讑֤坐标中的Q参考前?)
【作? xqscorpion?