??xml version="1.0" encoding="utf-8" standalone="yes"?> 2 消息cd 3 消息队列(Message Queues) 4 队列消息(Queued Messages)和非队列消息(Non-Queued Messages) 5 PostMessage(PostThreadMessage), SendMessage 6 GetMessage, PeekMessage 7 TranslateMessage, TranslateAccelerator 8(消息死锁( Message Deadlocks) 9 BroadcastSystemMessage
每个H口会有一个称为窗口过E的回调函数(WndProc)Q它带有四个参数Q分别ؓQ窗口句?Window Handle),消息ID(Message ID),和两个消息参?wParam, lParam), 当窗口收到消息时pȝ׃调用此窗口过E来处理消息。(所以叫回调函数Q?/p>
1) pȝ定义消息(System-Defined Messages)
在SDK中事先定义好的消息,非用户定义的Q其范围在[0x0000, 0x03ff]之间Q?可以分ؓ以下三类Q?br>
1>H口消息(Windows Message)
与窗口的内部q作有关Q如创徏H口Q绘制窗口,销毁窗口等。可以是一般的H口Q也可以是Dialog,控g{?br>
如:WM_CREATE, WM_PAINT, WM_MOUSEMOVE, WM_CTLCOLOR, WM_HSCROLL...
2>命o消息(Command Message)
与处理用戯求有养I 如单击菜单项或工h或控件时Q?׃产生命o消息?br>
WM_COMMAND, LOWORD(wParam)表示菜单,工具栏按钮或控g的ID。如果是控g, HIWORD(wParam)表示控g消息cd
3> 控g通知(Notify Message)
控g通知消息Q?q是最灉|的消息格式, 其Message, wParam, lParam分别为:WM_NOTIFY, 控gIDQ指向NMHDR的指针。NMHDR包含控g通知的内容, 可以L扩展?br>
2) E序定义消息(Application-Defined Messages)
用户自定义的消息Q?对于其范围有如下规定Q?br>
WM_USER: 0x0400-0x7FFF (ex. WM_USER+10)
WM_APP(winver>4.0): 0x8000-0xBFFF (ex.WM_APP+4)
RegisterWindowMessage: 0xC000-0xFFFF
Windows中有两种cd的消息队?br>
1) pȝ消息队列(System Message Queue)
q是一个系l唯一的QueueQ设备驱?mouse, keyboard)会把操作输入转化成消息存在系l队列中Q然后系l会把此消息攑ֈ目标H口所在的U程的消息队?thread-specific message queue)中等待处?br>
2) U程消息队列(Thread-specific Message Queue)
每一个GUIU程都会l护q样一个线E消息队列?q个队列只有在线E调用GDI函数时才会创建,默认不创?。然后线E消息队列中的消息会被送到相应的窗口过E?WndProc)处理.
注意Q?U程消息队列中WM_PAINTQWM_TIMER只有在Queue中没有其他消息的时候才会被处理QWM_PAINT消息q会被合q以提高效率。其他所有消息以先进先出QFIFOQ的方式被处理?/p>
1)队列消息(Queued Messages)
消息会先保存在消息队列中Q消息@环会从此队列中取消息q分发到各窗口处?br>
如鼠标,键盘消息?br>
2) 非队列消?NonQueued Messages)
消息会绕q系l消息队列和U程消息队列直接发送到H口q程被处?br>
如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSORQ?WM_WINDOWPOSCHANGED
注意: postMessage发送的消息是队列消息,它会把消息Post到消息队列中Q?SendMessage发送的消息是非队列消息Q?被直接送到H口q程处理
PostMessage:把消息放到指定窗口所在的U程消息队列中后立即q回?PostThreadMessageQ把消息攑ֈ指定U程的消息队列中后立卌回?br>
SendMessageQ直接把消息送到H口q程处理Q?处理完了才返回?/p>
PeekMessage会立卌?nbsp; 可以保留消息
GetMessage在有消息时返?nbsp;会删除消?/p>
TranslateMessage: 把一个virtual-key消息转化成字W消?character message)Qƈ攑ֈ当前U程的消息队列中Q消息@环下一ơ取出处理?br>
TranslateAccelerator: 快捷键对应到相应的菜单命o。它会把WM_KEYDOWN ?WM_SYSKEYDOWN转化成快捷键表中相应的WM_COMMAND 或WM_SYSCOMMAND消息Q?然后把{化后?WM_COMMAND或WM_SYSCOMMAND直接发送到H口q程处理Q?处理完后才会q回?/p>
假设有线EA和BQ?现在有以下下步骤
1) U程A SendMessagel线EB, A{待消息在线EB中处理后q回
2) U程B收到了线EA发来的消息,q进行处理, 在处理过E中QB也向U程A SendMessgaeQ然后等待从Aq回?br>
因ؓ此时Q?U程A正等待从U程Bq回Q?无法处理B发来的消息, 从而导致了U程A,B怺{待Q?形成死锁。多个线E也可以形成环Ş死锁?br>
可以使用 SendNotifyMessage或SendMessageTimeout来避免出现死锁?/p>
我们一般所接触到的消息都是发送给H口的, 其实Q?消息的接收者可以是多种多样的,它可以是应用E序(applications), 可安装驱?installable drivers), |络讑֤(network drivers), pȝU设备驱?system-level device drivers){,
BroadcastSystemMessageq个API可以对以上系l组件发送消?/p>
q种Ҏ的优Ҏ使h能够通过变量的名字来辨别变量的类?而不比去查找它的定义.遗憾的是,q种Ҏ不仅使变量名字非常绕?而且使改变变量类型的工作变得十分艰巨.在Windows3.1?整型变量?6为宽.如果我们在开始时采用了一个整型变?但是在通过30---40个函数的计算之后,发现采用整型变量宽度不够,q时我们不仅要改变这个变量的cd,而且要改变这个变量在q?0--40个函C的名?
因ؓ不切实际,除了一些顽固的WindowsE序员外已经没有人再使用"匈牙利表C法"?毫无疑问,在某U场合它依然存在,但大部分人现在已l抛弃它?一般而言,输入前缀是一U糟p的x,因ؓ它把变量于其cd紧紧地绑在了一?
对于30行以下的函数Q匈牙利Ҏ一般有优势?/p>
其是对界面~程Q有优势?/p>
但对于有强烈的算法要求、尤其是有很多抽象类型的C++E序Q匈牙利Ҏ直是一个灾难?/p>
看你用在什么地斏V?/p>
现在有了很好的IDE工具,?VC,SourceInsight{?
选中变量,会自动提C告诉你它的声明和定?q样
匈牙利命名法没有很大的必要?
无非是ZE序可读性较?
实际上良好的代码书写习惯比强制用匈牙利命名法更重要.
pȝ性。整体性。可L。分c要清楚。要有注释!
匈牙利命名法是微软推q的一U关于变量、函数、对象、前~、宏定义{各U类型的W号的命名规范。匈牙利命名法的主要思想是:在变量和函数名中加入前缀以增qh们对E序的理解。它是由微Y内部的一个匈牙利人发起用的Q结果它在微软内部逐渐行hQƈ且推q给了全世界的Windows开发h员。下面将介绍匈牙利命名法Q后面的例子里也会尽量遵守它和上面的代码风格。还是那句话Qƈ不是要求所有的读者都要去遵守Q但是希望读者作Z个现代的软g开发h员都去遵守它?/p>
a Array 数组
b BOOL (int) 布尔(整数)
by Unsigned Char (Byte) 无符号字W?字节)
c Char 字符(字节)
cb Count of bytes 字节?/p>
cr Color reference value 颜色(参??/p>
cx Count of x (Short) x的集?短整?
dw DWORD (unsigned long) 双字(无符号长整数)
f Flags (usually multiple bit values) 标志(一般是有多位的数?
fn Function 函数
g_ global 全局?/p>
h Handle 句柄
i Integer 整数
l Long 长整?/p>
lp Long pointer 长指?/p>
m_ Data member of a class 一个类的数据成?/p>
n Short int 短整?/p>
p Pointer 指针
s String 字符?/p>
sz Zero terminated String ?l尾的字W串
tm Text metric 文本规则
u Unsigned int 无符h?/p>
ul Unsigned long (ULONG) 无符号长整数
w WORD (unsigned short) 无符L整数
x,y x, y coordinates (short) 坐标?短整?/p>
v void I?/p>
有关目的全局变量用g_开始,cL员变量用m_Q局部变量若函数较大则可考虑用l_用以昄说明其是局部变量?/p>
前缀 cd 例子
g_ 全局变量 g_Servers
C cL者结构体 CDocumentQCPrintInfo
m_ 成员变量 m_pDocQm_nCustomers
VC常用前缀列表Q?/p>
前缀 cd 描述 例子
ch char 8位字W?nbsp; chGrade
ch TCHAR 16位UNICODEcd字符 chName
b BOOL 布尔变量 bEnabled
n int 整型Q其大小由操作系l决定) nLength
n UINT 无符h型(其大由操作pȝ军_Q?nbsp; nLength
w WORD 16位无W号整型 wPos
l LONG 32位有W号整型 lOffset
dw DWORD 32位无W号整型 dwRange
p * Ambient memory model pointer 内存模块指针Q指针变?nbsp; pDoc
lp FAR* 长指?nbsp; lpDoc
lpsz LPSTR 32位字W串指针 lpszName
lpsz LPCSTR 32位常量字W串指针 lpszName
lpsz LPCTSTR 32位UNICODEcd帔R指针 lpszName
h handle Windows对象句柄 hWnd
lpfn (*fn)() 回调函数指针 Callback Far pointer to CALLBACK function lpfnAbort
Windows对象名称~写Q?/p>
Windows对象 例子变量 MFCc?nbsp; 例子对象
HWND hWnd; CWnd* pWnd;
HDLG hDlg; CDialog* pDlg;
HDC hDC; CDC* pDC;
HGDIOBJ hGdiObj; CGdiObject* pGdiObj;
HPEN hPen; CPen* pPen;
HBRUSH hBrush; CBrush* pBrush;
HFONT hFont; CFont* pFont;
HBITMAP hBitmap; CBitmap* pBitmap;
HPALETTE hPalette; CPalette* pPalette;
HRGN hRgn; CRgn* pRgn;
HMENU hMenu; CMenu* pMenu;
HWND hCtl; CStatic* pStatic;
HWND hCtl; CButton* pBtn;
HWND hCtl; CEdit* pEdit;
HWND hCtl; CListBox* pListBox;
HWND hCtl; CComboBox* pComboBox;
2、解军_法:
在图形图象处理编E过E中,双缓冲是一U基本的技术。我们知?如果H体在响应WM_PAINT消息的时候要q行复杂的图形处理,那么H体在重l时׃q频的刷新而引起闪烁现象。解册一问题的有效方法就是双~冲技术?/p>
因ؓH体在刷新时Q总要有一个擦除原来图象的q程OnEraseBkgndQ它利用背景色填充窗体绘囑Q然后在调用新的l图代码q行重绘Q这样一擦一写造成了图象颜色的反差。当WM_PAINT的响应很频繁的时候,q种反差也就发明显。于是我们就看到了闪烁现象?/p>
我们会很自然的想刎ͼ避免背景色的填充是最直接的办法。但是那L话,H体上会变的一团糟。因为每ơ绘制图象的时候都没有原来的图象清除Q造成了图象的D留Q于是窗体重l时Q画面往往会变的ؕ七八p。所以单U的止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快Q于是我们想C使用BitBlt函数。它可以支持囑Ş块的复制Q速度很快。我们可以先在内存中作图Q然后用此函数将做好的图复制到前収ͼ同时止背景hQ这样就消除了闪?/font>。以上也是双缓冲绘囄基本的思\?/p>
Q、具体步骤: BOOL CDrawView::OnEraseBkgnd(CDC* pDC) int x=GetSystemMetrics(SM_CXSCREEN); return 0;
假设我们建立了一个Draw的工E,我们要在DrawView中进行绘图操作?br>
在双~冲Ҏ中,首先要做的是屏蔽背景h。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类QCDrawViewQ中d对这个消息的响应Q可以看到缺省的代码如下Q?/font>
{
//return CDrawView::OnEraseBkgnd(pDC);
return TRUE;
}
接下来是双缓冲的实现步骤Q?br>
Q?Q增加成员变?/font>Q在DrawView.h文g中)
//参数声明
CBitmap* m_pOldBitmap;
CBitmap* m_pMemBitmap; //声明内存中承载时图象的位图
CDC* m_pMemDC; //声明用于~冲作图的内存DC
Q?Q初始化变量Q在DrawView的构造函CQ?br>
m_pMemDC=new CDC();
m_pMemBitmap=new CBitmap();
Q?Q增加消息响应函?/font>WM_CREATEQ?font color=#000000>在DrawView.cpp?/font>Q?br>
int CDrawView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
int y=GetSystemMetrics(SM_CYSCREEN);
CDC* pDC=GetDC();
m_pMemDC->CreateCompatibleDC(pDC); //依附H口DC创徏兼容内存DC
m_pMemBitmap->CreateCompatibleBitmap(pDC,x,y); //创徏兼容位图
m_pOldBitmap=m_pMemDC->SelectObject(m_pMemBitmap); //位N进内存DC,原位图保存到m_pOldBitmap
CBrush brush(RGB(255,255,255));
m_pMemDC->FillRect(CRect(0,0,x,y),&brush); //讄客户景ؓ白色
ReleaseDC(pDC);
}
Q?Q修改OnDraw()函数(DrawView.cpp?
void CDrawView::OnDraw(CDC* pDC)
{
CDocument* pDoc = GetDocument();
CRect rc;
GetClientRect(&rc);
DrawSomething(); //在这个函数里你可以画你想ȝ东西
pDC->BitBlt(0,0,rc.Width(),rc.Height(),m_pMemDC,0,0,SRCCOPY);
//q里是内存里面的d复制到显C备的buffer?br> }
Q?Q自ql图函数(DrawView.cpp?
void DrawSomething()
{
m_pMemDC->Rectangle(0,0,100,100); //此处M个矩?br> Invalidate();
}
Q?Qdelete掉new的东?/font>Q在DrawView的析构函CQ?br>
delete m_pBitmap;
delete m_pMemDC;
]]>
我们会很自然的想刎ͼ避免背景色的填充是最直接的办法。但是那L话,H体上会变的一团糟。因为每ơ绘制图象的时候都没有原来的图象清除Q造成了图象的D留Q于是窗体重l时Q画面往往会变的ؕ七八p。所以单U的止背景重绘是不够的。我们还要进行重新绘图,但要求速度很快Q于是我们想C使用BitBlt函数。它可以支持囑Ş块的复制Q速度很快。我们可以先在内存中作图Q然后用此函数将做好的图复制到前収ͼ同时止背景hQ这样就消除了闪烁。以上也是双缓冲绘囄基本的思\?/p>
先按普通做囄Ҏq行~程。即在视cȝOnDraw函数中添加绘图代码。在此我们绘制若q同心圆Q代码如下:
CBCDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CPoint ptCenter;
CRect rect,ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
for(int i=20;i>0;i--)
{
ellipseRect.SetRect(ptCenter,ptCenter);
ellipseRect.InflateRect(i*10,i*10);
pDC->Ellipse(ellipseRect);
}
~译q行E序Q尝试改变窗口大,可以发现闪烁现象?/p>
在双~冲Ҏ中,首先要做的是屏蔽背景h。背景刷新其实是在响应WM_ERASEBKGND消息。我们在视类中添加对q个消息的响应,可以看到~省的代码如下:
BOOL CMYView::OnEraseBkgnd(CDC* pDC)
{
return CView::OnEraseBkgnd(pDC);
}
是调用父cȝOnEraseBkgnd函数Q我们屏蔽此调用Q只ȝ接return TRUE;卛_?/p>
下面是内存缓冲作囄步骤?/p>
CPoint ptCenter;
CRect rect,ellipseRect;
GetClientRect(&rect);
ptCenter = rect.CenterPoint();
CDC dcMem; //用于~冲作图的内存DC
CBitmap bmp; //内存中承载时图象的位图
dcMem.CreateCompatibleDC(pDC); //依附H口DC创徏兼容内存DC
bmp.CreateCompatibleBitmap(&dcMem,rect.Width(),rect.Height());//创徏兼容位图
dcMem.SelectObject(&bmp); //位N择q内存DC
dcMem.FillSolidRect(rect,pDC->GetBkColor());//按原来背景填充客户区Q不然会是黑?br> for(int i=20;i>0;i--) //在内存DC上做同样的同心圆图象
{
ellipseRect.SetRect(ptCenter,ptCenter);
ellipseRect.InflateRect(i*10,i*10);
dcMem.Ellipse(ellipseRect);
}
pDC->BitBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0,SRCCOPY);//内存DC上的图象拯到前?br> dcMem.DeleteDC(); //删除DC
bm.DeleteObject(); //删除位图
׃复杂的画图操作{入后収ͼ我们看到的是速度很快的复制操作,自然也就消除了闪烁现象?/p>
HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ); |
DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh, DWORD dwMoveMethod); |
BOOL CDirectAccessHDDlg::WriteSectors(BYTE bDrive, DWORD dwStartSector, WORD wSectors, LPBYTE lpSectBuff) // 对磁盘扇区数据的写入 { if (bDrive == 0) return 0; char devName[] = "\\\\.\\A:"; devName[4] ='A' + bDrive - 1; HANDLE hDev = CreateFile(devName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDev == INVALID_HANDLE_VALUE) return 0; SetFilePointer(hDev, 512 * dwStartSector, 0, FILE_BEGIN); DWORD dwCB; BOOL bRet = WriteFile(hDev, lpSectBuff, 512 * wSectors, &dwCB, NULL); CloseHandle(hDev); return bRet; } BOOL CDirectAccessHDDlg::ReadSectors(BYTE bDrive, DWORD dwStartSector, WORD wSectors, LPBYTE lpSectBuff) // 对磁盘扇区数据的d { if (bDrive == 0) return 0; char devName[] = "\\\\.\\A:"; devName[4] ='A' + bDrive - 1; HANDLE hDev = CreateFile(devName, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDev == INVALID_HANDLE_VALUE) return 0; SetFilePointer(hDev, 512 * dwStartSector, 0, FILE_BEGIN); DWORD dwCB; BOOL bRet = ReadFile(hDev, lpSectBuff, 512 * wSectors, &dwCB, NULL); CloseHandle(hDev); return bRet; } |
if (ReadSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分Z存在Q?, "错误", MB_OK | MB_IConERROR); return; } |
for (DWORD i = 0; i < dwSectorNum * 512; i++) { sprintf(cBuf, "%s%02X ", cBuf, bBuf[i]); if ((i % 512) == 511) sprintf(cBuf, "%s\r\nW?d扇区\r\n", cBuf, (int)(i / 512) + m_uFrom); if ((i % 16) == 15) sprintf(cBuf, "%s\r\n", cBuf); else if ((i % 16) == 7) sprintf(cBuf, "%s- ", cBuf); } |
![]() |
file.Open(fileDlg.GetPathName(), Cfile::modeCreate | Cfile::modeReadWrite); …… if (ReadSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分Z存在Q?, "错误", MB_OK | MB_IConERROR); return; } file.Write(bBuf, dwSectorNum * 512); file.Close(); |
file.Open(fileDlg.GetPathName(), Cfile::modeReadWrite); DWORD dwSectorNum = file.GetLength(); if (dwSectorNum % 512 != 0) return; dwSectorNum /= 512; unsigned char* bBuf = new unsigned char[dwSectorNum * 512]; file.Read(bBuf, dwSectorNum * 512); if (WriteSectors(uDiskID, m_uFrom, (UINT)dwSectorNum, bBuf) == FALSE) { MessageBox("所选磁盘分Z存在Q?, "错误", MB_OK | MB_IConERROR); return; } file.Close(); delete[] bBuf; |
unsigned char bBuf[512]; UINT i = 0; BOOL bRet = TRUE; while (m_bAllDisk){ memset(bBuf, 0xFF, sizeof(bBuf)); bRet = WriteSectors(uDiskID, i, 1, bBuf); memset(bBuf, 0, sizeof(bBuf)); bRet = WriteSectors(uDiskID, i, 1, bBuf); if (bRet == FALSE){ if (i == 0) MessageBox("所选磁盘分Z存在Q?, "错误", MB_OK | MB_IConERROR); else MessageBox("盘数据擦除完毕Q?, "错误", MB_OK | MB_IConERROR); return; } i++; } |
对于可以接收数据的控Ӟ如编辑控件来_UpdateData()函数臛_重要。当控g内容发生变化Ӟ对应的控件变量的值ƈ没有跟着变化Q同
P当控件变量值变化时Q控件内容也不会跟着变?br>UpdateData()函数是解决q个问题的?/p>
UpdateData(true);把控件内容装入控件变?br>UpdateData(false);用控件变量的值更新控?/p>
如:有编辑控件IDC_EDIT1Q对应的变量为字W串m_Edit1Q?br>1、修改变量值ƈ昄在控件中Q?br>m_Edit1 = _T("l果?0");
UpdateData(false);
2、读取控件的值到变量中:
用ClassWizard为IDC_EDIT1dEN_CHANGE消息处理函数Q?br>void CEditView::OnChangeEdit1()
{
UpdateData(true);
}
UINT_PTR SetTimer( HWND hWnd, // H口句柄 UINT_PTR nIDEvent, // 定时器IDQ多个定时器Ӟ可以通过该ID判断是哪个定时器 UINT uElapse, // 旉间隔,单位为毫U? TIMERPROC lpTimerFunc // 回调函数 );例如
SetTimer(m_hWnd,1,1000,NULL); //一?U触发一ơ的定时?/pre> 在MFCE序中SetTimer被封装在CWndcMQ调用就不用指定H口句柄了,例如:
UINT SetTimer(1,100,NULL);函数反回值就是第一个参数?Q表C此定时器的ID受?br>
W二个参数表C{待100毫秒旉再重新处理一ơ。第三个参数在这U方法中一般用NULL?br>注意Q?/strong>讄W二个参数时要注意,如果讄的等待时间比处理旉短,E序׃出问题了?br>
1.2 调用回调函数
此方法首先写一个如下格式的回调函数
void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime);然后再用SetTimer(1,100,TimerProc)函数来徏一个定时器Q第三个参数是回调函数地址?br>
二、多个定时器的实C应用
我们在安装定时器旉为其指定了IDQ用多个定时器Ӟ该ID发挥作用了?br>不用MFCӞ当接收到WM_TIMER消息QWPARAM wParam中的g是该定时器的ID
使用MFC时就更简单了Q我们ؓ其增加WM_TIME的消息处理函数OnTimer卛_Q请看如下例?void CTimerTestDlg::OnTimer(UINT nIDEvent) { switch (nIDEvent) { case 24: ///处理ID?4的定时器 Draw1(); break; case 25: ///处理ID?5的定时器 Draw2(); break; } CDialog::OnTimer(nIDEvent); }当你用回调函数时Q我们可以根据nTimerid的值来判断是哪个定时器Q例?void CALLBACK TimerProc(HWND hWnd,UINT nMsg,UINT nTimerid,DWORD dwTime) { switch(nTimerid) { case 1: ///处理ID?的定时器 Do1(); break; case 2: ///处理ID?的定时器 Do2(); break; } }三、取消定时器
不再使用定时器后Q我们应该调用KillTimer来取消定ӞKillTimer的原型如?br>BOOL KillTimer( HWND hWnd, // H口句柄 UINT_PTR uIDEvent // ID );在MFCE序中我们可以直接调用KillTimer(int nIDEvent)来取消定时器?br>
本文提供的例子代码在q行时就可以看到两个定时器都在工作,而且W一个在q行10ơ后׃被KILL掉?br>
]]>
Z方便理解Q先考虑一l情况下的线性插?
对于一个数列cQ我们假设c[a]到c[a+1]之间是线性变化的
那么对于点数x(a<=x<a+1)Qc(x)=c[a+1]*(x-a)+c[a]*(1+a-x);
q个好理解吧Q?
把这U插值方式扩展到二维情况
对于一个二l数lcQ我们假讑֯于Q意一个QҎi,c(a,i)到c(a+1,i)之间是线性变化的,c(i,b)到c(i,b+1)之间也是U性变化的(a,b都是整数)
那么对于点数的坐标(x,y)满(a<=x<a+1,b<=y<b+1)Q我们可以先分别求出c(x,b)和c(x,b+1):
c(x,b) = c[a+1]*(x-a)+c[a]*(1+a-x);
c(x,b+1) = c[a+1][b+1]*(x-a)+c[a][b+1]*(1+a-x);
好,现在已经知道c(x,b)和c(x,b+1)了,而根据假设c(x,b)到c(x,b+1)也是U性变化的Q所?
c(x,y) = c(x,b+1)*(y-b)+c(x,b)*(1+b-y)
q就是双U性插|
囑փ的双U性插值放大算法中Q目标图像中新创造的象素|是由源图像位|在它附q的2*2区域4个邻q象素的值通过加权q_计算得出的。双U性内插值算法放大后的图像质量较高,不会出现像素gq箋的的情况。然而次法h低通o波器的性质Q高频分量受损Q所以可能会使图像轮廓在一定程度上变得模糊?/span>
对于标准的双U性差值算法,X方向的线性插|
[通用1]
[通用2]
具体到我们所实现的算法中Q我们Q11、Q12、Q21、Q22为光栅上盔R的四点,即P只能落于q四点其中一点上?#916;col是当前像素离像素所属区域原点的水^距离Q比如图2Q各U不同的颜色代表一个区域,区域原点为区域左上角的像素?
δ R2 = Color Q22 −Color Q12 ?#916;col+Color Q12 ?56 (1)
δ R1 = Color Q21 −Color Q11 ?#916;col+Color Q11 ?56 (2)
其中Q?#916;col=(DestColNumber?(SrcWidth?)/DestWidth))&255Q?Color(X)表示点X的颜Ԍ具体法使用的是24位真彩色格式?/span>
做完X方向的插值后再做Y方向的插|对于一般情况,有:
[通用3]
而我们的具体法中,Y方向的线性插值方法如(3)所C?#916;row是当前像素离像素所属区域原点的垂直距离Q比如图2Q各U不同的颜色代表一个区域,区域原点为区域左上角的像素?
Color P = δ R2 ?56+ δ R2 −δ R1 ?#916;row ?6 (3)
其中Q?#916;row=(DestRowNumber?(SrcHeight?)/DestHeight))&255Q由于前面ؓ了便于计左UM16位,因此最后需要右U?6位保持匹配?/span>
c?C 伪码如下Q?/span>
我们以放大两倍ؓ例,说明选取Q12, Q22, Q11, Q21的过E。源囑փ3*3区域攑֤为目标区?*6区域。设以下为目标图像:
A |
A |
B |
B |
|
|
A |
A |
B |
B |
|
|
|
|
C |
C |
|
|
|
|
C |
C |
|
|
|
|
|
|
D |
D |
|
|
|
|
D |
D |
目标囑փA像素区域对应?span>Q21Q?span>Q22Q?span>Q11Q?span>Q12Q以U色区域为原点向右下Ҏ展的2*2区域?/span>
Q21 |
Q22 |
|
Q11 |
Q12 |
|
|
|
|
目标囑փB像素区域对应?span>Q21Q?span>Q22Q?span>Q11Q?span>Q12Q以蓝色区域为原点向右下Ҏ展的2*2区域?/span>
|
Q21 |
Q22 |
|
Q11 |
Q12 |
|
|
|
目标囑փC像素区域对应?span>Q21Q?span>Q22Q?span>Q11Q?span>Q12Q以l色区域为原点向右下Ҏ展的2*2区域?/span>
|
|
|
|
Q21 |
Q22 |
|
Q11 |
Q12 |
目标囑փD像素区域对应?span>Q21Q?span>Q22Q?span>Q11Q?span>Q12Q目标图像处于最后两行的边界情况Q将Q21Q?span>Q22Q?span>Q11Q?span>Q12q四个点的DZ栗?/span>
|
|
|
|
|
|
|
|
Q11=Q12=Q22=Q21 |
程囑֏边虚U框中ؓ相关q程的注解?/span>
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;
一、其它数据类型{换ؓ字符?/strong>
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串转换为其它数据类?/font>
strcpy(temp,"123");
三、其它数据类型{换到CString
使用CString的成员函数Format来{?例如:
四、BSTR、_bstr_t与CComBSTR
五、VARIANT 、_variant_t ?COleVariant
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. |
六、其它一些COM数据cd
七、ANSI与Unicode
UnicodeUCؓ宽字W型字串,COM里用的都是Unicode字符丌Ӏ?/p>
八、其?/strong>
九、注意事?/strong>
假如需要用到ConvertBSTRToString此类函数,需要加上头文gcomutil.h,q在setting中加入comsupp.lib或者直接加?pragma comment( lib, "comsupp.lib" )
2。在stdafx.cpp中加?
int myInt;
然后在stdafx.h中加?
extern int myInt
q样定义以后无论在什么文件中都是可见?
3。比较规范的是,先定义一个Glbs.hQ把所有的全局变量原始定义放进厅R然后定义一个Externs.hQ把你先前定义在Glbs.h中的变量都加上extern。注意:如果你在Glbs.h中设|了初|那么在Externs.h中就不要加g。然后调用时Q第一ơ调用的Qi nclude <Glbs.h>Q以后调用的Qi nclude <Externs.h>
另:
问:如何在VC++中用全局变量Q以使文档中的所有类都能讉K?br> {:把该变量攑ֈ该应用程序类的头文g中的attribute处。然后,在程序的M地方Q你都可以用下面的方法来讉K该变量:
CMyApp *app=QCMyApp*QAfxGet-AppQ)Q?br> app->MyGlobalVariable=…
用这个方法,不但可以定义全局变量Q也可以定义全局对象?br> 例如Q?br> MyClass MyObjectQ?br> CMyApp*app=QCMyApp*QAfxGet-AppQ)Q?br> app->MyObject.MyFunctionQ)Q?/font>
VC中用全局变量?U办法及防错措施
1. 对于全局变量存在和函CL问题Qؓ了在其他CPP文g中能够访问这些变量,必须在主文g的H文g中加上extern声明Q格式如下:
extern varibletype var; Q声明)
在主文g的CPP文g中定?br>varibletype var; Q定义)
例子:
AppWizard建立一个Test工程
那么在Test.h中声明extern CString cs;
在Test.app定义CString cs;
如果要定义整个工E的全局变量Q在M一个CPP文g中进行定义,然后在需要引用这个变量的文g中进行声明。如全局变量很多可以选择使用定义全局变量的。h文gQ在需要的地方直接include头文件即可,不需要写那么多extern了?br>2.应用E序cȝd文g处定义变量varibletype varQ然后,在程序的M地方Q都可以用下面的Ҏ来访问该变量Q?nbsp;
CClassApp *app=(CClassApp*)AfxGetApp();
app->var=
cM的,以上Ҏ也可以定义全局对象
例子:
AppWizard建立一个Test工程
那么在Test.h中声?CString cs;
使用的时候CTestApp *app=(CTestApp*)AfxGetApp();
app->cs="Global"
防错措施Q?br>若定义的函数和全局变量在多个文件包含且造成嵌套或多ơ调用的话,q样导致这个头文g每被包含依次Q函数或变量p重新定义一ơ,在链接编译时会导致重定义错误。ؓ此需要用一U被UCؓGuard macro的技术来保证不出错。在一个头文g开头加?nbsp;
#ifndef _MACRO_1_
#define _MACRO_1_
在文件末֢?nbsp;
#endif
打开VCQ+6.0Q徏立一个基于对话框的MFC应用E序SCommTest;
选择Project菜单下Add To Project子菜单中?Components and Controls…选项Q在弹出的对话框中双击Registered ActiveX Controls(E等一会,q个q程较慢Q,则所有注册过的ActiveX控g出现在列表框中?选择Microsoft Communications Control, version 6.0Q,单击Insert按钮它插入到我们的Project中来Q接受缺省的选项。(如果你在控g列表中看不到Microsoft Communications Control, version 6.0Q那可能是你在安装VC6时没有把ActiveX一w上Q重新安装VC6Q选上ActiveX可以了Q,
q时在ClassView视窗中就可以看到CMSCommcMQ(注意Q此cdClassWizard中看不到Q重构clw文g也一PQƈ且在控g工具栏Controls中出C电话图标Q如?所C)Q现在要做的是用鼠标此图标拖到对话框中Q程序运行后Q这个图标是看不到的?/span>
3.利用ClassWizard定义CMSCommcL制对?/span>
打开ClassWizardQ?gt;Member Viariables选项卡,选择CSCommTestDlgc,为IDC_MSCOMM1d控制变量Qm_ctrlCommQ这时你可以看一看,在对话框头文件中自动加入?/{{AFX_INCLUDES() Qi nclude "mscomm.h" //}}AFX_INCLUDES ?/span>
再打开ClassWizardQ?gt;Member Viariables选项卡,选择CSCommTestDlgc, 为IDC_EDIT_RXDATAdCString变量m_strRXDataQ?为IDC_EDIT_TXDATAdCString变量m_strTXData。说明: m_strRXData和m_strTXData分别用来攑օ接收和发送的字符数据?/span>
打开ClassWizardQ?gt;Message MapsQ选择cCSCommTestDlgQ选择IDC_MSCOMM1Q双L息OnCommQ将弹出的对话框中将函数名改为OnComm?/span>
q个函数是用来处理串口消息事件的Q如每当串口接收到数据,׃产生一个串口接收数据缓冲区中有字符的消息事Ӟ我们刚才d的函数就会执行,我们在OnComm()函数加入相应的处理代码就能实现自已想要的功能了。请你在函数中加入如下代码:
void CSCommTestDlg::OnComm()
{
// TODO: Add your control notification handler code here
VARIANT variant_inp;
COleSafeArray safearray_inp;
LONG len,k;
BYTE rxdata[2048]; //讄BYTE数组 An 8-bit integerthat is not signed.
CString strtemp;
if(m_ctrlComm.GetCommEvent()==2) //事ggؓ2表示接收~冲区内有字W?br> { ////////以下你可以根据自q通信协议加入处理代码
variant_inp=m_ctrlComm.GetInput(); //ȝ冲区
safearray_inp=variant_inp; //VARIANT型变量{换ؓColeSafeArray型变?br> len="safearray"_inp.GetOneDimSize(); //得到有效数据长度
for(k=0;k<len;k++)
safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数l?br> for(k=0;k<len;k++) //数l{换ؓCstring型变?br> {
BYTE bt=*(char*)(rxdata+k); //字符?br> strtemp.Format("%c",bt); //字W送入临时变量strtemp存放
m_strRXData+=strtemp; //加入接收~辑框对应字W串
}
}
UpdateData(FALSE); //更新~辑框内?br>}
到目前ؓ止还不能在接收编辑框中看到数据,因ؓ我们q没有打开串口Q但q行E序不应该有M错误Q不Ӟ你肯定哪儿没看仔l,因ؓ我是打开VC6对照着做一步写一行的Q运行试试。没错吧Q那么做下一步:
你可以在你需要的时候打开串口Q例如在E序中做一个开始按钮,在该按钮的处理函C打开串口。现在我们在d话框的CSCommTestDlg::OnInitDialog()打开串口Q加入如下代码:
// TODO: Add extra initialization here
if(m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(FALSE);
m_ctrlComm.SetCommPort(1); //选择com1
if( !m_ctrlComm.GetPortOpen())
m_ctrlComm.SetPortOpen(TRUE);//打开串口
else
AfxMessageBox("cannot open serial port");
m_ctrlComm.SetSettings("9600,n,8,1"); //波特?600Q无校验Q?个数据位Q?个停止位
m_ctrlComm.SetInputModel(1); //1Q表CZ二进制方式检取数?br>m_ctrlComm.SetRThreshold(1);
//参数1表示每当串口接收~冲Z有多于或{于1个字W时引发一个接收数据的OnComm事g
m_ctrlComm.SetInputLen(0); //讄当前接收区数据长度ؓ0
m_ctrlComm.GetInput();//先预ȝ冲区以清除残留数?/span>
现在你可以试试程序了Q将串口U接好后Q?span style="FONT-SIZE: 12pt; COLOR: #999999">不会接?ȝ看我写的串口接线基本ҎQ,打开
串口调试助手Qƈ串口设在com2Q选上自动发送,也可以等会手动发送。再执行你编写的E序Q接收框里应该有数据昄了?/span>先ؓ发送按钮添加一个单L息即BN_CLICKED处理函数Q打开ClassWizardQ?gt;Message MapsQ选择cCSCommTestDlgQ选择IDC_BUTTON_MANUALSENDQ双击BN_CLICKEDdOnButtonManualsend()函数Qƈ在函Cd如下代码Q?/span>
void CSCommTestDlg::OnButtonManualsend()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE); //d~辑框内?br>m_ctrlComm.SetOutput(COleVariant(m_strTXData));//发送数?br>}
q行E序Q在发送编辑框中随意输入点什么,单击发送按钮,啊!看看Q在另一端的串口调试助手Q或别的调试工具Q接收框里出C什么?/span>
如果你真是初ơ涉猎串口编E,又一ơ成功,那该说声谢谢我了Q因为我W一ơ做串口E序时可费劲了,那时|上的资料也不好找。开开玩笑Q谢谢你的支持,有什么好东西别忘了给我寄一份?/span>
说明Q?/span>
׃用到VC控gQ在没有安装VC的计机上运行时要从VC中把mscomm32.ocx、msvcrt.dll、mfc42.dll拷到Windows目录下的System子目录中Qwin2000为System32Qƈ再进行注册设|?/span>
3.1 static_cast
用法Qstatic_cast < type-id > ( expression )
该运符把expression转换为type-idcdQ但没有q行时类型检查来保证转换的安全性。它主要有如下几U用法:
①用于类层次l构中基cd子类之间指针或引用的转换?br> q行上行转换Q把子类的指针或引用转换成基c表C)是安全的Q?br> q行下行转换Q把基类指针或引用{换成子类表示Q时Q由于没有动态类型检查,所以是不安全的?br>②用于基本数据类型之间的转换Q如把int转换成charQ把int转换成enum。这U{换的安全性也要开发h员来保证?br>③把I指针{换成目标cd的空指针?br>④把Mcd的表辑ּ转换成voidcd?/font>
注意Qstatic_cast不能转换掉expression的const、volitale、或者__unaligned属性?/font>
3.2 dynamic_cast
用法Qdynamic_cast < type-id > ( expression )
该运符把expression转换成type-idcd的对象。Type-id必须是类的指针、类的引用或者void *Q?br>如果type-id是类指针cdQ那么expression也必L一个指针,如果type-id是一个引用,那么expression也必L一个引用?/font>
dynamic_cast主要用于cdơ间的上行{换和下行转换Q还可以用于cM间的交叉转换?br>在类层次间进行上行{换时Qdynamic_cast和static_cast的效果是一LQ?br>在进行下行{换时Qdynamic_casthcd查的功能Q比static_cast更安全?br>class B{
public:
int m_iNum;
virtual void foo();
};
class D:public B{
public:
char *m_szName[100];
};
void func(B *pb){
D *pd1 = static_cast<D *>(pb);
D *pd2 = dynamic_cast<D *>(pb);
}
在上面的代码D中Q如果pb指向一个Dcd的对象,pd1和pd2是一LQƈ且对q两个指针执行Dcd的Q何操作都是安全的Q?br>但是Q如果pb指向的是一个Bcd的对象,那么pd1是一个指向该对象的指针,对它q行Dcd的操作将是不安全的(如访问m_szNameQ,
而pd2是一个空指针?/font>
另外要注意:B要有虚函敎ͼ否则会编译出错;static_cast则没有这个限制?br>q是׃q行时类型检查需要运行时cd信息Q而这个信息存储在cȝ虚函数表Q?br>关于虚函数表的概念,详细可见<Inside c++ object model>Q中Q只有定义了虚函数的cL有虚函数表,
没有定义虚函数的cL没有虚函数表的?/font>
另外Qdynamic_castq支持交叉{换(cross castQ。如下代码所C?br>class A{
public:
int m_iNum;
virtual void f(){}
};
class B:public A{
};
class D:public A{
};
void foo(){
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast<D *>(pb); //compile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
在函数foo中,使用static_castq行转换是不被允许的Q将在编译时出错Q而?dynamic_cast的{换则是允许的Q结果是I指针?/font>
3.3 reinpreter_cast
用法Qreinpreter_cast<type-id> (expression)
type-id必须是一个指针、引用、算术类型、函数指针或者成员指针?br>它可以把一个指针{换成一个整敎ͼ也可以把一个整数{换成一个指针(先把一个指针{换成一个整敎ͼ
在把该整数{换成原类型的指针Q还可以得到原先的指针|?/font>
该运符的用法比较多?/font>
3.4 const_cast
用法Qconst_cast<type_id> (expression)
该运符用来修改cd的const或volatile属性。除了const 或volatile修饰之外Q?type_id和expression的类型是一L?br>帔R指针被{化成非常量指针,q且仍然指向原来的对象;
帔R引用被{换成非常量引用,q且仍然指向原来的对象;帔R对象被{换成非常量对象?/font>
Voiatile和constc试。D如下一例:
class B{
public:
int m_iNum;
}
void foo(){
const B b1;
b1.m_iNum = 100; //comile error
B b2 = const_cast<B>(b1);
b2. m_iNum = 200; //fine
}
上面的代码编译时会报错,因ؓb1是一个常量对象,不能对它q行改变Q?br>使用const_cast把它转换成一个常量对象,可以对它的数据成员L改变。注意:b1和b2是两个不同的对象?br>
VC目文g说明
.dsp
.dsw (developer studio worksapce )工作区文Ӟ重要性一般,因ؓ它信息不我,Ҏ恢复。它可以指向一个或多个.dsp文g
.cQ源代码文gQ按C语言用法~译处理?br>.cppQ源代码文gQ按C++语法~译处理?br>.rcQ资源文件?br>
以下文g在项目中是可丢弃的,有些文g删除后,VC会自动生成的?br>.clw ClassWizard信息文g,实际上是INI文g的格?有兴可以研I一?有时候ClassWizard出问?手工修改CLW文g可以解决.如果此文件不存在的话,每次用ClassWizard的时候绘提示你是否重?
.ncb 无编译浏览文?no compile browser)。当自动完成功能出问题时可以删除此文件。build后会自动生成?br>.opt 工程关于开发环境的参数文g。如工具条位|等信息Q?可丢?
.aps (AppStudio File),资源辅助文g,二进制格?一般不用去他.
.plg 是编译信息文?~译时的error和warning信息文gQ实际上是一个html文gQ?一般用处不?在Tools->Options里面有个选项可以控制q个文g的生? (建立日志文g)
.hpj (Help Project)是生成帮助文件的工程,用microsfot Help Compiler可以处理.
.mdp (Microsoft DevStudio Project)是旧版本的项目文?如果要打开此文件的?会提CZ是否转换成新的DSP格式.
.bsc 是用于浏览项目信息的,如果用Source Brower的话必Lq个文g.如果不用q个功能的话,可以在Project Options里面LGenerate Browse Info File,可以加快~译速度.
.map 是执行文件的映像信息U录文g,除非对系l底层非常熟?q个文g一般用不着.
.pch (Pre-Compiled File)是预~译文g,可以加快~译速度,但是文g非常?
.pdb (Program Database)记录了程序有关的一些数据和调试信息,在调试的时候可能有?
.exp 只有在编译DLL的时候才会生?记录了DLL文g中的一些信?一般也没什么用.