VC 编程 杂货

使用SkinMagic Toolkit美化界面
[准备工作]

  1、将SkinMagicTrial.dll放置在调试目录

  2、设置库文件目录,在项目[连接器]的[附加依赖项]中加入库SkinMagicTrial.lib

  3、在项目的stdafx.h文件中加入头文件 #include "SkinMagicLib.h"

  [创建过程]

  1、初始化SkinMagic库:

  要使用SkinMagic,这一步必不可少。在应用程序类的InitInstance()函数中行加入如下代码(粗体部分):

  CWinApp::InitInstance();

  VERIFY( 1 == InitSkinMagicLib(AfxGetInstanceHandle(), NULL ,

  NULL, NULL ));

  说明:

  int InitSkinMagicLib( //初始化SkinMagic工具库
  HINSTANCE hInstance, //应用程序句柄
  char* szApplication , //皮肤文件中定义的应用程序名,置为NULL即可
  char* szRegCode, //SkinMagic的使用注册码。若无置为NULL
  char* szReserved2 //保留位,为NULL
  );

  2、调入皮肤文件:

  皮肤的调用有两种方法,一是直接从皮肤文件中调用,另一种方法是从资源文件中调用,分别说明如下:

  1)从皮肤文件中调用皮肤:紧接上句,加入如下代码

  VERIFY( 1 == LoadSkinFile("corona.smf"));

  2)从资源文件中调用皮肤:

  VERIFY(1 == LoadSkinFromResource(NULL,"FUTURA","skin"));

  说明: int LoadSkinFromResource(
  HMODULE hModule, //包含皮肤文件的模块句柄,若NULL表面在本模块中
 char* lpSkinName , //皮肤资源的名称
  char* lpType); //资源的类型

  3、为窗口添加皮肤:

  1)为标准窗口(拥有标题栏、系统菜单、可变大小等特征,比如文档/视图结构和有菜单的对话框)添加皮肤,通常用于主窗口。在应用程序类的InitInstance()函数的底部加入如下代码:

  VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));

  m_pMainWnd->ShowWindow(SW_SHOW);

  m_pMainWnd->UpdateWindow();

  说明:

  int SetWindowSkin(
  HWND hWnd, //要使用皮肤的窗口句柄
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );

  2)为对话框添加皮肤

  在对话框显示之前调用,通常在应用程序初始化函数中调用

  VERIFY( 1 == SetWindowSkin( m_pMainWnd->m_hWnd , "MainFrame" ));

  VERIFY( 1 == SetDialogSkin( "Dialog" ) );

  m_pMainWnd->ShowWindow(SW_SHOW);

  m_pMainWnd->UpdateWindow();

  说明:

  int SetDialogSkin(
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );
  使用该函数后,以后程序创建的对话框都将使用该皮肤,但对话框大小不可变。

 


 3)为单个对话框窗口添加皮肤,例如在对话框视图中:重载对话框视图的创建函数OnCreate,加入如下代码:

  VERIFY( 1 == SetSingleDialogSkin( m_hWnd, "Dialog" ) );

  EnableWindowScrollbarSkin( m_hWnd , SB_BOTH );

  说明:

  int SetSingleDialogSkin(
  HWND hWnd, //要使用皮肤的窗口句柄
  char* lpSkinName //为skinFrameWnd对象指定的名称
  );
  int EnableWindowScrollbarSkin( //为滚动条添加皮肤
  HWND hWnd, //要使用皮肤的窗口句柄
  int* fnBar //要使用皮肤的滚动条,SB_BOTH表明是横竖都是用皮肤
  );

  4、释放SkinMagic资源
 
  重载应用程序的ExitInstance()函数,添加如下代码:

  ExitSkinMagicLib();

如何有效地使用对话框
Q:如何在对话框中加入工具条在 OnInitDialog 中加入下面代码:
BOOL CYourDlg::OnInitDialog()
{
       CDialog::OnInitDialog();	

       // Create the toolbar. To understand the meaning of the styles used, you 
       // can take a look at the MSDN for the Create function of the CToolBar class.
	   
       ToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_TOOLTIPS |CBRS_FLYBY | CBRS_BORDER_BOTTOM);

      // I have assumed that you have named your toolbar''s resource as IDR_TOOLBAR1.
      // If you have given it a different name, change the line below to accomodate 
      // that by changing the parameter for the LoadToolBar function.
	  
      ToolBar.LoadToolBar(IDR_TOOLBAR1);
  
      CRect rcClientStart;
      CRect rcClientNow;
      GetClientRect(rcClientStart);


      // To reposition and resize the control bar

     RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,0, reposQuery, rcClientNow);
     CPoint ptOffset(rcClientNow.left - rcClientStart.left,rcClientNow.top-rcClientStart.top);

     CRect rcChild;
     CWnd* pwndChild = GetWindow(GW_CHILD);

     while (pwndChild)
     {
       pwndChild->GetWindowRect(rcChild);
       ScreenToClient(rcChild);
       rcChild.OffsetRect(ptOffset);
       pwndChild->MoveWindow(rcChild, FALSE);
       pwndChild = pwndChild->GetNextWindow();
     } 
	  CRect rcWindow;
     GetWindowRect(rcWindow);
     rcWindow.right += rcClientStart.Width() - rcClientNow.Width();
     rcWindow.bottom += rcClientStart.Height() - rcClientNow.Height();
     MoveWindow(rcWindow, FALSE);	

     // And position the control bars
     RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
  
     return TRUE;  // return TRUE  unless you set the focus to a control
  }

Q:如何改变对话框的形状?

可用下面一些函数:
CreatePolygonRgn
CreateRectRgn
CreateRoundRectRgn 等.
  CRgn m_rgn;  // Put this in your dialog''s header file. i.e. a member variable

  // This Gets the size of the Dialog: This piece of code is to be placed in the
  // OnInitDialog Function of your dialog.

  CRect rcDialog
  GetClientRect(rcDialog);

  // The following code Creates the area and assigns it to your Dialog
  m_rgn.CreateEllipticRgn(0, 0, rcDialog.Width(), rcDialogHeight());
  SetWindowRgn(GetSafeHwnd(), (HRGN) m_rgn, TRUE);
Q:如何实现非客户区移动?

可用下面二种方法
// Handler for WM_LBUTTONDOWN message

  void CYourDialog::OnLButtonDown(UINT nFlags, CPoint point)
  {
     CDialog::OnLButtonDown(nFlags, point);
     PostMessage( WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM( point.x, point.y));
  }

  // Handler for WM_NCHITTEST message

  LONG CYourDialog::OnNcHitTest( UINT uParam, LONG lParam )
  {  
     int xPos = LOWORD(lParam);
     int yPos = HIWORD(lParam);
     UINT nHitTest = CDialog::OnNcHitTest(CSize(xPos, yPos));
     return (nHitTest == HTCLIENT) ? HTCAPTION : nHitTest;
  }

Q:如何使对话框初始为最小化状态?

在 OnInitDialog 中加入下面代码:
SendMessage(WM_SYSCOMMAND, SC_MAXIMIZE, NULL);
Q:如何限定对话框大小范围?

在 WM_SIZING中加入下面代码:
void CYourDialog::OnSizing(UINT fwSide, LPRECT pRect) 
  {
     if(pRect->right - pRect->left <=200)
     	pRect->right = pRect->left + 200;
	
     if(pRect->bottom - pRect->top <=200)
     	pRect->bottom = pRect->top + 200;

     CDialog::OnSizing(fwSide, pRect);
  }
Q:如何在对话框中加入状态条?

定义 CStatusBar 变量:
CStatusBar m_StatusBar;
定义状态条指定状态:
static UINT BASED_CODE indicators[] =
  {
     ID_INDICATOR_CAPS,
     ID_INDICATOR_NUM
  };
在 OnInitDialog 中加入下面代码:
  m_StatusBar.CreateEx(this,SBT_TOOLTIPS,WS_CHILD|WS_VISIBLE|CBRS_BOTTOM,AFX_IDW_STATUS_BAR);
 
  // Set the indicators namely caps and nums lock status
  m_StatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT));

  CRect rect;
  GetClientRect(&rect);
		
  m_StatusBar.SetPaneInfo(0,ID_INDICATOR_CAPS,SBPS_NORMAL,rect.Width()/2);
  m_StatusBar.SetPaneInfo(1,ID_INDICATOR_NUM,SBPS_STRETCH ,rect.Width()/2);

  RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,ID_INDICATOR_NUM);

  m_StatusBar.GetStatusBarCtrl().SetBkColor(RGB(180,180,180));
如何手工编写.mak文件?
最简单的.mak文件格式如下:

     myprogram.exe : myprogram.obj
        $(LINKER) $(GUIFLAGS) -OUT: myprogram.exe myprogram.obj $(GUILIBS)
     myprogram.obj : myprogram.c myprogram.h
        $(CC) $(CFLAGS) myprogram.c

     .mak 文件包含一个或多个段,每段以一个左顶格行开始,首先列出的是一个目标
  文件,后跟一个冒号,再跟一个或多个目标文件的依赖文件。这一行后面跟一行或多
  行缩进命令,这些命令根据依赖文件来创建目标文件。 
      任何一个依赖文件的最后一次修改日期和时间比目标文件的最后一次修改日期和
  时间晚,则会执行缩进的命令行,以创建最新的目标文件。

      .mak文件的分析是从底向上的,因为大多数底部行的目标文件是上部行目标文件
  的依赖文件。

       你应该在你的Autoexec.bat 文件中设置如下语句:
           CALL /Micros~1/BIN/VCVARS32.BAT 
       其中Micros~1是你的VC所在的目录,VCVARS32.BAT是VC命令行编译的必需的Dos
  环境变量。

       其中(LINKER) .....都是设置的环境变量宏,这样做的原因只是为了节省.mak
  文件的复杂程度,因为我们编译的时候多半都是需要差不多的命令行。
      因此还需要以下的批处理文件:(假设命令为C.bat)
rem C.bat

rem -----------------------------------------------------
SET CC=cl
SET CFLAGS=-c -DSTRICT -G3 -Ow -W3 -Zp -Tp
SET CFLAGSMT=-c -DSTRICT -G3 -MT -Ow -W3 -Zp -Tp
SET LINKER=link
SET GUIFLAGS=-SUBSYSTEM:windows
SET DLLFLAGS=-SUBSYSTEM:windows -DLL
SET GUILIBS=-DEFAULTLIB:user32.lib gdi32.lib winmm.lib comdlg32.lib comctl32.lib
SET RC=rc
SET RCVARS=-r -DWIN32 
    
rem -------------------------------------------------------     

      以上批处理摘自《window95程序设计》,差不多包含了所必须的编译命令。  

     你同样应该在你的Autoexec.bat 文件中执行这个批处理文件。同时避免环境变量
  空间的溢出,你必须扩大你的环境变量空间,用如下命令行:

       shell=C:/windows/command.com /e:4096(见本版1188封信)

     这样你自己应该可以写自己的比较简单的.mak文件了。
如何与资源管理器互动剪切/拷贝/粘贴文件
一.本文将向读者介绍下面两个问题的解决方案:
1,用户在资源管理器(Windows Explorer)中剪切/拷贝(Cut/Copy)文件,然后在自己的应用程序中进行粘贴(Paste)操作;
2.用户在自己的应用程序中剪切/拷贝文件,在资源管理其中粘贴操作。

二.本文中的代码编写工具及测试环境:
1,VC6.0, Platform SDK(无须MFC);
2.Windows 2000。

三.概述
    我们知道,在Windows中可以通过剪贴板(Clipboard)来共享和传递数据,比如在资源管理器(Windows Explorer)中可以剪切/拷贝/粘贴文件。同样我们也可以在自己的应用程序中通过剪贴板来完成这些工作,从而提高我们自己的应用程序与Windows操作系统之间的互操作性。但我们如何才能与资源管理器之类的应用程序共享和传递数据呢?本文提供的方法是:使用Windows本身提供的一些数据结构和API,通过剪贴板来实现数据共享和传递。

四.实现方法
    首先,Windows在剪切/拷贝文件时并不是把文件名称写入剪贴板,而是在剪贴板中放入了一个DragAndDrop文件对象,并写入了一个状态值来标识操作类型(移动/拷贝,剪切其实就是移动,如果你剪切之后并没有粘贴,那么该文件依然存在而不会被删除)。依据这个知识,我们首先来看看在应用程序中如何识别出Windows 资源管理器的剪切/拷贝动作。

在使用剪贴板前,我们首先要打开它:
BOOL OpenClipboard(HWND hWnd);
参数 hWnd 是打开剪贴板的窗口句柄,成功返回TRUE,失败返回FALSE。      
之后,可以用GetClipboardData来得到剪贴板中的数据:
HANDLE GetClipboardData(UINT uFormat);      
uFormat是所需要数据的格式,例如本文拖放对象的格式为CF_HDROP。而表明该拖放对象类型(Move/Copy)的数据格式并不是Windows标准的剪贴板数据结构,而是一个简单的DWORD指针。我们可以通过下面的语句来注册一下数据类型 :
UINT uDropEffect=RegisterClipboardFormat("Preferred DropEffect");      
这里返回的uDropEffect就是我们将要代入GetClipboardData函数的该数据结构的代码,
GetClipboardData函数返回是一个句柄,这只是Windows为了统一性而做的工作,我们可以根据需要来转换成相应的数据形式,比如我们的uDropEffect就 是一个DWORD指针。
前面我已经说过在剪贴板中放的是一个拖放对象,因此我们可以通过如下语句得到该对象:
HDROP hDrop = HDROP( GetClipboardData( CF_HDROP));      
如果确实存在一个hDrop对象,我们应该取得uDropEffect的数据,以便我们处理后面的文件:
DWORD dwEffect=*((DWORD*)(GetClipboardData( uDropEffcet)));      
关于这个值的含义,我们只要包含一下"OLEIDL.H"头文件即可,在该头文件中5种状态的定义而本文只关注:
#define	DROPEFFECT_COPY	( 1 )
#define	DROPEFFECT_MOVE	( 2 )      
因此,我们可以通过
if(dwEffect & DROPEFFECT_COPY)
  CopyFile(....);
else (dwEffect & DROPEFFECT_MOVE)
  MoveFile(...);      
来完成剪切/拷贝操作。
在我们取得uDropEffect状态之后,我们需要得到文件列表,得到拖放对象中的文件列表可以通过DragQueryFile来实现:
UINT DragQueryFile(HDROP hDrop, UINT iFile,LPTSTR lpszFile,UINT cch);      
第二个参数是文件序列号,可以通过将iFile置为-1的方法来得到文件数量。
最后我们给出完整的例子:
#include <Shellapi.h>
#include <oleidl.h>

....

  UINT uDropEffect=RegisterClipboardFormat("Preferred DropEffect");

	if( OpenClipboard( hWnd)) {
		HDROP hDrop = HDROP( GetClipboardData( CF_HDROP));
		if( hDrop) {
			DWORD dwEffect,*dw;
			dw=(DWORD*)(GetClipboardData( uDropEffect));
			if(dw==NULL)
				dwEffect=DROPEFFECT_COPY;
			else
				dwEffect=*dw;
	    
			char Buf[4096];
			Buf[0] = 0;
			UINT cFiles = DragQueryFile( hDrop, (UINT) -1, NULL, 0);
			POINT Point;
			char szFile[ MAX_PATH];
			for( UINT count = 0; count < cFiles; count++ ) {
			    DragQueryFile( hDrop, count, szFile, sizeof( szFile));
				lstrcat(Buf,szFile);
				lstrcat(Buf,"/n");
			}
	
			if(dwEffect & DROPEFFECT_MOVE) {
				MessageBox(NULL,Buf,"Move Files",MB_OK);
			} else	if(dwEffect & DROPEFFECT_COPY) {
					MessageBox(NULL,Buf,"Copy Files",MB_OK);
			}

			CloseClipboard();
		}
	}      
    在这个例子中,我并没有进行文件操作,只是简单的显示一个消息框,实际应用时,需要使用MoveFile和CopyFile函数来完成,本文不做讨论。
    知道如何识别其他程序的剪切/拷贝 文件的动作后,我们对该操作的数据结构已经很了解了,要想让其他程序能识别我们的剪切/拷贝 文件动作其实就是将以上数据结构放入剪贴板的过程。
在我们这个例子中,往剪贴板中放的数据必须是内存对象:HGLOBAL。这个对象可以通过GlobalAlloc来生成。然后使用GlobalLock就可以得到该对象的内存地址,继而往里面写 数据。实际上在Win32中由于进程拥有独立的内存空间,因而常规的内存分配已经不需要GlobalLock了,看看MSDN就知道该函数主要就是为DDE和剪贴板服务的。
    根据前面的知识,要想让其他程序识别出我们的剪切/拷贝动作我们必须往剪贴板中放两项数据,现在就让我们来为DropEffect准备数据吧,同样我们需要先注册该数据格式:
uDropEffect=RegisterClipboardFormat("Preferred DropEffect");      
然后分配内存对象并得到指针:
hGblEffect=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE,sizeof(DWORD));
dwDropEffect=(DWORD*)GlobalLock(hGblEffect);      
注意往剪贴板中放的数据必须使用GMEM_MOVEABLE标志,最后我们设置数据并解除锁定:
if(COPY)
  *dwDropEffect=DROPEFFECT_COPY;
else 
 *dwDropEffect=DROPEFFECT_MOVE;
GlobalUnlock(hGblEffect);      
这样我就为DropEffect准备还数据了,等一会儿我们连同文件拖放对象一起放入剪贴板。建立文件拖放对象的方法与DropEffect基本相同,只是文件拖放对象有特殊的数据结构 而不象DropEffect那样简单,该对象数据结构如下:
+----------------------------+
|  DROPFILES  |  Files List  |
+----------------------------+      
DROPFILES是拖放对象的头数据,该结构在shlobj.h中定义:
typedef struct _DROPFILES {
    DWORD pFiles; 
    POINT pt; 
    BOOL fNC; 
    BOOL fWide; 
} DROPFILES, FAR * LPDROPFILES;       
    pFiles指针是以对象首地址为参照的文件列表(上图中的Files List项)的offset量。通常该值等于DROPFILES结构的长度(我还没见过例外);pt表明文件拖放的位置坐标,在这个例子里我们忽略为0; fNC表明pt值是否为客户区坐标(FALSE表明是屏幕坐标);fWide表明Files List是否包含unicode,作为中国人,我们当然要设其为TRUE。DROPFILES结构之后紧跟Files List,Files List是一组宽字符串,之间以0相隔,比如:"文件1/0文件2/0..."
我们可以通过MultiByteToWideChar函数将常规的字符串转换成宽字符串。下面就是生成拖放对象的代码:
uDropFilesLen=sizeof(DROPFILES);
dropFiles.pFiles =uDropFilesLen;
dropFiles.pt.x=0;
dropFiles.pt.y=0;
dropFiles.fNC =FALSE;
dropFiles.fWide =TRUE;

uGblLen=uDropFilesLen+uBufLen<<1+8;
//uBufLen是文件名字符传组的长度,由于要转换成宽字符,因此长度要乘2

hGblFiles= GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE, uGblLen);
szData=(char*)GlobalLock(hGblFiles);
memcpy(szData,(LPVOID)(&dropFiles),uDropFilesLen);
//将DROPFILES copy到头部

szFileList=szData+uDropFilesLen;
//得到存放文件列表的首地址

MultiByteToWideChar(CP_ACP,MB_COMPOSITE,
	lpBuffer,uBufLen,(WCHAR *)szFileList,uBufLen);
	
GlobalUnlock(hGblFiles);      
现在我们就可以将上面两组数据放入剪贴板中了,注意在写数据前应先清空剪贴板。为了方便大家使用,下面我给出实现此功能的独立的函数:
VOID CutOrCopyFiles(char * lpBuffer,UINT uBufLen,BOOL bCopy)      
lpBuffer是包括所有准备剪切/拷贝的文件名称的缓冲区;uBufLen是lpBuffer的长度;bCopy决定该操作是Copy还是Cut,TRUE为Copy,FALSE为Cut。例如我们可以这样调用该函数:
char szFiles[]="c://1.txt/0c://2.txt/0";
CutOrCopyFiles(szFiles,sizeof(szFiles),FALSE);      
来剪切文件,或者使用:
CutOrCopyFiles(szFiles,sizeof(szFiles),TRUE);      
来拷贝文件。
#include <Shellapi.h>
#include <Shlobj.h>
#include <oleidl.h>      
......

VOID CutOrCopyFiles(char *lpBuffer,UINT uBufLen,BOOL bCopy)
{
	UINT uDropEffect;
	DROPFILES dropFiles;
	UINT uGblLen,uDropFilesLen;
	HGLOBAL hGblFiles,hGblEffect;
	char *szData,*szFileList;

	DWORD *dwDropEffect;

	uDropEffect=RegisterClipboardFormat("Preferred DropEffect");
	hGblEffect=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE,sizeof(DWORD));
	dwDropEffect=(DWORD*)GlobalLock(hGblEffect);
	if(bCopy)
		*dwDropEffect=DROPEFFECT_COPY;
	else 
		*dwDropEffect=DROPEFFECT_MOVE;
	GlobalUnlock(hGblEffect);

	uDropFilesLen=sizeof(DROPFILES);
	dropFiles.pFiles =uDropFilesLen;
	dropFiles.pt.x=0;
	dropFiles.pt.y=0;
	dropFiles.fNC =FALSE;
	dropFiles.fWide =TRUE;

	uGblLen=uDropFilesLen+uBufLen*2+8;
	hGblFiles= GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE, uGblLen);
	szData=(char*)GlobalLock(hGblFiles);
	memcpy(szData,(LPVOID)(&dropFiles),uDropFilesLen);
	szFileList=szData+uDropFilesLen;

	MultiByteToWideChar(CP_ACP,MB_COMPOSITE,
			lpBuffer,uBufLen,(WCHAR *)szFileList,uBufLen);
	
	GlobalUnlock(hGblFiles);

	if( OpenClipboard(NULL) )
	{
		EmptyClipboard();
		SetClipboardData( CF_HDROP, hGblFiles );
		SetClipboardData(uDropEffect,hGblEffect);
		CloseClipboard();
	}
}      
希望以上内容对你有所帮助。
    本文附上一个Demo工程,编译后生成CutCopy.exe程序,该程序的使用方法如下:
    启动程序后,可使用Windows 资源管理器等程序剪切/拷贝文件,然后点程序中的[CheckClipboard],Demo程序将分析剪贴板中的内容,并弹出消息框告知是Copy Files还是Cut Files,并给出文件列表.用户点[OK]关闭消息框后,文件列表 将被放入文本框中,此时用户可以通过[Cut]/[Copy]按钮来改变剪贴板中的属性。
    同时,用户可以通过[Browser]来选择若干文件到文本框中,然后点[Cut]/[Copy]进行操作,之后,用户既可以通过[CheckClipboard]检查剪贴板中的内容也可以通过在Windows 资源管理器等程序中进行粘贴(Paste)来检查其是否正确。
VC中一些控件的小技巧
1.让List Control有Check Box
用SetExtendedStyle方法可以设置,看代码:
(CListCtrl m_ListCtrl;)

DWORD dwStyle = m_ListCtrl.GetStyle();
dwStyle |= LVS_EX_CHECKBOXES;
m_ListCtrl.SetExtendedStyle(dwStyle);

2.让List Box有Check Box
首先,声明Control变量的时候用CCheckListBox代替CListBox。
然后,将List Box的Has Strings属性改为True;把Ower Draw属性改为Fixed。

3.Spin Control与Edit Control的绑定
首先,要让Spin Control的Tap Order紧跟着Edit Control(就是说,Spin Control的Tap Order是Edit Control的Tap Order加1)。
然后,设置Spin Control的Auto Buddy和Set Buddy Integer属性为True。

4.可以设置背景和文字颜色的Static控件
从CStatic类继承一个自己的Static类,相应WM_CTLCOLOR消息。下面是代码:
.h文件:

class ClxStatic : public CStatic
{
    DECLARE_DYNAMIC(ClxStatic)
public:
    ClxStatic();
    virtual ~ClxStatic();

    afx_msg HBRUSH CtlColor(CDC* /*pDC*/, UINT /*nCtlColor*/);

    void SetBackColor(COLORREF clrBack);
    void SetTextColor(COLORREF clrText);

protected:
    DECLARE_MESSAGE_MAP()

    CBrush m_brushBack;
    COLORREF m_clrBack;
    COLORREF m_clrText;
};

.cpp文件:

IMPLEMENT_DYNAMIC(ClxStatic, CStatic)
ClxStatic::ClxStatic()
{
    m_clrBack = ::GetSysColor(COLOR_BTNFACE);
    m_clrText = ::GetSysColor(COLOR_BTNTEXT);
}

ClxStatic::~ClxStatic()
{
}

void ClxStatic::SetBackColor(COLORREF clrBack)
{
    m_clrBack = clrBack;
}

void ClxStatic::SetTextColor(COLORREF clrText)
{
    m_clrText = clrText;
}

BEGIN_MESSAGE_MAP(ClxStatic, CStatic)
    ON_WM_CTLCOLOR_REFLECT()
END_MESSAGE_MAP()

HBRUSH ClxStatic::CtlColor(CDC* pDC, UINT /*nCtlColor*/)
{
    // TODO:  Change any attributes of the DC here
    m_brushBack.Detach();
    m_brushBack.CreateSolidBrush(m_clrBack);

    pDC->SetBkColor(m_clrBack);
    pDC->SetTextColor(m_clrText);

    // TODO:  Return a non-NULL brush if the parent's handler should not be called
    //return NULL;
    return (HBRUSH)m_brushBack.GetSafeHandle();
}

5.在Static控件中显示BMP
首先,给Static控件添加一个Control变量(ID要改了以后才能添加变量,也就是说ID不能为IDC_STATIC),本例为m_staticTest。
然后,用ModifyStyle函数修改Static控件的Style,让它可以显示图片:

m_staticTest.ModifyStyle(0, SS_BITMAP | SS_CENTERIMAGE);

最后,就是Load文件显示出来:

CRect rect;
m_staticTest.GetWindowRect(&rect);

//  下面的方法是按照Static控件的大小显示bmp,如果要安装图片实际大小显示,用这个方法Load图片:
//  HBITMAP hBmp = (HBITMAP)::LoadImage(0, _T("D://test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
HBITMAP hBmp = (HBITMAP)::LoadImage(0, _T("D://test.bmp"), IMAGE_BITMAP, rect.Width(), rect.Height(), LR_LOADFROMFILE);

m_staticTest.SetBitmap(hBmp);
DeleteObject(hBmp);
显示BMP图像文件
简述实现步骤如下:

 

1、通过点击浏览按钮选择BMP图像文件
点击浏览按钮打开文件对话框选择BMP图像文件,得到文件所在的路径目录。
关键代码如下:
char szFilter[] = "BMP Files (*.bmp)|*.bmp|All Files (*.*)|*.*||";
CFileDialog dlg( TRUE,"BMP",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter );
if(dlg.DoModal() == IDOK)
{
    CString strPathName = dlg.GetPathName();                
}

2、加载BMP文件到内存
通过得到的BMP图像文件路径目录,加载BMP图像文件到内存中。
关键代码如下:
BOOL CShowBMPDlg::LoadShowBMPFile(const char *pPathname)
{
    CFile file;
    if( !file.Open( pPathname, CFile::modeRead) )
        return FALSE;
    DWORD            m_nFileLen;
    m_nFileLen = file.GetLength();
    m_pBMPBuffer = new char[m_nFileLen + 1];
    if(!m_pBMPBuffer)
        return FALSE;
    if(file.ReadHuge(m_pBMPBuffer,m_nFileLen) != m_nFileLen)
        return FALSE;
    return TRUE;
}

3、将内存中的BMP文件内容转换到HBITMAP
将内存中的BMP文件内容转换成位图句柄。
关键代码如下:
HBITMAP CShowBMPDlg::BufferToHBITMAP()
{
    HBITMAP                hShowBMP;
    LPSTR                hDIB,lpBuffer = m_pBMPBuffer;
    LPVOID                lpDIBBits;
    BITMAPFILEHEADER     bmfHeader;
    DWORD                bmfHeaderLen;
    
    bmfHeaderLen = sizeof(bmfHeader);
    strncpy((LPSTR)&bmfHeader,(LPSTR)lpBuffer,bmfHeaderLen);

    if (bmfHeader.bfType != (*(WORD*)"BM")) return NULL;
    hDIB = lpBuffer + bmfHeaderLen;
    BITMAPINFOHEADER &bmiHeader = *(LPBITMAPINFOHEADER)hDIB ;
    BITMAPINFO &bmInfo = *(LPBITMAPINFO)hDIB ;

    lpDIBBits=(lpBuffer)+((BITMAPFILEHEADER *)lpBuffer)->bfOffBits;
    CClientDC dc(this);
    hShowBMP = CreateDIBitmap(dc.m_hDC,&bmiHeader,CBM_INIT,lpDIBBits,&bmInfo,DIB_RGB_COLORS);
    return hShowBMP;
}

4、在屏幕上显示BMP图像
双缓冲实现BMP位图显示在屏幕上。
关键代码如下:
void CShowBMPDlg::DrawImage(int x, int y, CDC *pDC)
{
    HBITMAP OldBitmap;
    CDC MemDC;
    MemDC.CreateCompatibleDC(pDC);
    OldBitmap=(HBITMAP)MemDC.SelectObject(m_hBitmap);

    CRect rect;
    GetClientRect(&rect);    
    pDC->BitBlt(x,y,rect.Width(),rect.Height(),&MemDC,0,0,SRCCOPY);
    MemDC.SelectObject(OldBitmap);
}

自己动手制作一款简洁实用的图片浏览器
软件DIY,说白了就是利用现有的类库、组件,快速构造出自己的程序出来。就象你找来各种电脑配件,然后把它们装配成整机一样;但也有不一样的地方,电脑配件即使是旧的二手配件,怎么说也得花一点钱,而软件的类库、组件,你有时侯可以免费得到。

我想要DIY的,是一款简洁实用的图片浏览器。众所周知,ACDSee是最流行的图片浏览器,确实很不错。但作为一名程序员,我想,要是我也能制作出一款简洁实用的图片浏览器,可以在我开发的软件中有图片浏览功能,那该多好。

而我确实DIY出了这么一个图片浏览器,她简洁实用,小巧玲珑,取名为:SimpleBrowse。现将制作过程介绍如下,与大家分享,并期望能起到抛砖引玉的作用。

第一步:定规格

1.样子要象ACDSee一样,左边是目录树,右边是文件列表,显示图片文件的缩图。

2.缩图要好看,浏览速度要快,能浏览的图片格式要多。

3.简洁实用,制作难度不要太大。

4.在Win98,WinNT下都能用。

第二步:选材料

由于界面主要分为两大部分,即左边的目录树和右边的文件列表,故材料也就主要是这两大件。

1.左边目录树

从http://www.codejockeys.com/kstowell/上,我们可以得到一个免费的MFC扩展类库CJ60Lib.dll,这个类库包含有许多用于界面设计的类,其中有一个CShellTree类,可以显示和Windows Explorer左边目录树一样的效果,正好符合我想要的,选定了它。

2.右边文件列表

这是关键的部分,它要求能创建、显示、管理图片文件的缩图,要求能浏览多种格式的图片文件,要求有较精美的缩图显示效果和较快的浏览速度,要求易于使用难度不大。泰来影像科技有限公司在图像处理应用软件开发方面有较深的造诣,推出了一个MFC扩展类库thl.dll,其中有一个CThumbListCtrl类,正是用来创建、显示图片缩图用的,选定了它,从http://www.thalia.com.cn/上获取之。

第三步:生成程序框架

象开发其他程序一样,用VC++6.0 AppWizard生成程序框架。

1.选菜单项File->New,到Projects面板,选取MFC AppWizard(exe),Project name为SimpleBrowse。OK确定后,进入一个向导中,共有6步。

2.Step 1,选single document,其它不动,用缺省值。

3.Step 2 of 6,不动,用缺省值。

4.Step 3 of 6,不动,用缺省值。

5.Step 4 of 6,选Internet Explorer ReBars,其它不动,用缺省值。

6.Step 5 of 6,选Windows Explorer,其它不动,用缺省值。

7.Step 6 of 6,不动,用缺省值。Finish确定后,即生成程序框架。

第四步:使用类库、组件

1.左边的目录树对应的类是CLeftView,修改之。

class CLeftView : public CView/*CTreeView*/
{
...
// Attributes
public:
CShellTree m_TreeCtrl;// use shell tree control
...
// Generated message map functions
protected:
//{{AFX_MSG(CLeftView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnSelchanged(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
...
};


(1)将其父类由CTreeView改为CView,原因后述。

(2)加入CShellTree类的成员变量m_TreeCtrl,目录树的具体内容就是由它实现的。

(3)增加消息响应函数OnCreate(),在其中把m_TreeCtrl创建起来。

(4)增加消息响应函数OnSize(),使m_TreeCtrl总是占满CLeftView的区域。

(5)增加消息响应函数OnItemexpanding(),在此响应展开目录的操作。如果CLeftView的父类是CTreeView的话,将不能得到希望的结果,这就是(1)中把父类改为CView的原因。

(6)增加消息响应函数OnSelchanged(),在此响应点击目录的操作。

具体修改请看源文件leftview.h和leftview.cpp,都很简单。

2.右边的文件列表对应的类是CSimpleBrowseView,修改之。

class CSimpleBrowseView : public CView/*CListView*/
{
...
// Attributes
public:
CThumbListCtrl m_ThumbListCtrl;
// use thumb list control
// Operations
public:
void FormatList(CString csPath);

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSimpleBrowseView)
protected:
virtual void OnInitialUpdate();
// called first time after construct
//}}AFX_VIRTUAL
...
// Generated message map functions
protected:
//{{AFX_MSG(CSimpleBrowseView)
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
...
};

(1)将其父类由CListView改为CView,原因和上面的类似。

(2)加入CThumbListCtrl类的成员变量m_ThumbListCtrl,文件列表的具体内容就是由它实现的。

(3)增加消息响应函数OnCreate(),在其中把m_ThumbListCtrl创建起来,并初始化为缩图显示风格。

(4)增加消息响应函数OnSize(),使m_ThumbListCtrl总是占满CSimpleBrowseView的区域。

(5)在OnInitialUpdate()中创建文件列表的栏目,调用CThumbListCtrl::BuildColumns()。

(6)增加函数FormatList(),它被CLeftView::OnSelchanged()调用,它则转而调用CThumbListCtrl::BrowseFolder()。点击左边的目录树,右边的文件列表跟着改变,就是通过这一个函数来实现的。

具体修改请看源文件simplebrowseview.h和simplebrowseview.cpp,都很简单。

3.其它事项

CShellTree和CThumbListCtrl都是在MFC扩展类库中实现的,要使用它们,必须:在编译阶段要有相应的.h文件,在连接阶段要有相应的.lib文件,在运行阶段要有相应的.dll文件。因此还要做一些配置:在leftview.h中加入#include "ShellTree.h",在simplebrowseview.h中加入#include "ThumbListCtrl.h";选菜单项Project->Settings,转到Link面板,在Object/library modules中加入CJ60Lib.lib和thl.lib;将CJ60Lib.dll,thl.dll和simplebrowse.exe放在同一目录下,注意thl.dll又使用到其它dll如Convert20.dll,JPEG.dll,TIFF60.dll等,也要放在同一目录下。

第五步:编译、运行

Rebuild All,Execute SimpleBrowse.exe,一款简洁实用的图片浏览器浮出水面...

大功告成!

(注意:一开始,我编译的是SimpleBrowse的Debug版本,发现不能正确运行,后来改为编译Release版本,就能正确运行了。可能是因为CJ60Lib.lib和thl.lib是Release版本的MFC扩展类库,需要配套的缘故。)
在对话框显示图片的多种方法
我们先从简单的开始吧.先分一个类:

(一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID)

(二) 动态载入图片(即只需要在程序中指定图片的路径即可载入)



为方便说明,我们已经建好一个基于对话框的工程,名为Ttest.

对话框类为CTestDlg

(一)    非动态载入图片.



方法1.先从最简单的开始,用picture 控件来实现.

步骤:

先在资源里Import一张图片,ID为IDB_BITMAP2

然后在对话框上添加一个picture控件,右键点击打开属性,

将type下拉框选择BITMAP,紧跟着下面就出现一个Image下拉框,

拉开就会看到所有已经载入好的图片,

选择你要的图片.运行程序即可看到.



方法2.通过背景图

同样如上,先载入一张图片,ID为IDB_BITMAP2

TestDlg.h中

CBrush m_brBk;//在public中定义



TestDlg.cpp中



在初始化函数OnInitDialog()中加入:

BOOL CTestDlg::OnInitDialog()

{

               CDialog::OnInitDialog();

CBitmap bmp;

bmp.LoadBitmap(IDB_BITMAP2);

m_brBk.CreatePatternBrush(&bmp);

bmp.DeleteObject();

.

.

.

          return TRUE;  // return TRUE  unless you set the focus to a control

}



在打开类向导,找到WM_CTLCOLOR消息,重载得对应函数OnCtlColor(),添加如下:

HBRUSH  CTestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)

{

               HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);

               

if (pWnd == this)

{

    return m_brBk;

}

       return hbr;

}



(二)    动态载入图片.

方法3 图像控件(本例用KoDak 图像编辑控件)

1.    首先应该保证系统中有这个控件。注意,它不能单独使用,必须和其他几个控件(特别是Imgcmn.dll)一同使用。如果没有,从别的机器上copy过来即可。这几个文件是Imgadmin.ocx,Imgcmn.dll,Imgedit.ocx,Imgscan.ocx,Imgshl.dll,Imgthumb.ocx,Imgutil.dll,把它们copy到windows/system目录下,然后用regsvr32.exe将它们分别注册。

2.    打开工程,进入资源管理器,在对话框上单击右键,单击Insert Activex control… 选择Kodak图象编辑控件,大小任意。

3.    在对话框上选中该控件,为其添加变量:m_ctrlPicture。。

4.    在BOOL CTestDlg::OnInitDialog()添加如下:

BOOL CTestDlg::OnInitDialog()

{

     CDialog::OnInitDialog();

     m_ctrlPicture.SetImage("aa.jpg");  //保证图像在工程目录下,也可以写绝对路径

     m_ctrlPicture.Display();



.

;

;

     return TRUE;  // return TRUE unless you set the focus to a control

                   // EXCEPTION: OCX Property Pages should return FALSE

}

编译运行就OK了,此种方法的好处就是可能针对多种图像格式.


方法4 通过CBitmap,HBITMAP,直接用OnPaint()绘制

首先在CTestDlg类中声明一个变量:   CBitmap  m_bmp;

然后我们在对话框中加入一个picture 标签,名为IDC_STATIC1

然后:

BOOL CDisplayPic::OnInitDialog()

{

       CDialog::OnInitDialog();

    if( m_bmp.m_hObject != NULL )//判断

        m_bmp.DeleteObject();

/载入图片

    HBITMAP hbmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),

        "c://aaa.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);

    if( hbmp == NULL )

        return FALSE;

///该断程序用来取得加载的BMP的信息

    m_bmp.Attach( hbmp );

    DIBSECTION ds;

    BITMAPINFOHEADER &bminfo = ds.dsBmih;

    m_bmp.GetObject( sizeof(ds), &ds );

    int cx=bminfo.biWidth;  //得到图像宽度

    int cy=bminfo.biHeight; //得到图像高度

    ///

/得到了图像的宽度和高度后,我们就可以对图像大小进行适应,即调整控件的大小,让它正好显示一张图片///

    CRect rect;

    GetDlgItem(IDC_STATIC1)->GetWindowRect(&rect);

    ScreenToClient(&rect);

    GetDlgItem(IDC_STATIC1)->MoveWindow(rect.left,rect.top,cx,cy,true);//调整大小



    return TRUE;  // return TRUE unless you set the focus to a control

                  // EXCEPTION: OCX Property Pages should return FALSE

}

图片加载成功了,标签大小也适应了,下面就是绘制绘制图像了,打开类向导,重载WM_PAINT消息

void CDisplayPic::OnPaint()

{

//以下三种情况任选一种会是不同效果(只能一种存在)///

    //CPaintDC dc(this);      //若用此句,得到的是对话框的DC,图片将被绘制在对话框上.

    CPaintDC dc(GetDlgItem(IDC_STATIC1)); //用此句,得到picture控件的DC,图像将被绘制在控件上  

    //  CDC dc;

    //  dc.m_hDC=::GetDC(NULL);  //若用此两句,得到的是屏幕的DC,图片将被绘制在屏幕上///

    CRect rcclient;

    GetDlgItem(IDC_STATIC1)->GetClientRect(&rcclient);

    CDC memdc;

    memdc.CreateCompatibleDC(&dc);  

    CBitmap bitmap;

    bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());

    memdc.SelectObject( &bitmap );



    CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC , 0);



    CDC maskdc;

    maskdc.CreateCompatibleDC(&dc);

    CBitmap maskbitmap;

    maskbitmap.CreateBitmap(rcclient.Width(), rcclient.Height(), 1, 1, NULL);

    maskdc.SelectObject( &maskbitmap );

    maskdc.BitBlt( 0, 0, rcclient.Width(), rcclient.Height(), &memdc,

        rcclient.left, rcclient.top, SRCCOPY);



    CBrush brush;

    brush.CreatePatternBrush(&m_bmp);

    dc.FillRect(rcclient, &brush);  

    

    

    dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(),

             &memdc, rcclient.left, rcclient.top,SRCPAINT);

    brush.DeleteObject();



    // Do not call CDialog::OnPaint() for painting messages

}



以上四种方法唯有KoDak可以支持多种图像,其它的只支持BMP
快捷菜单
 这里我们也不是手工编程,而是使用Visual Studio提供的构件工具Component Gallery(组件画廊)向框架窗口添加快捷菜单。有关Componet Gallery的概念参见第二课2.1.5节。选择Project->Add to Project->Component and Controls菜单,弹出Component and Controls Gallery对话框,选择Developer Studio Components目录,在该目录下选择Popup Menu构件,如图3-15所示。
>
按Insert按钮。弹出Poup Menu对话框,在Add popup menu to下拉列表框中选择CMainFrame,点OK按钮,关闭Popup Menu对话框。按Close按钮关闭Component and Controls Gallery对话框。编译运行Hello,弹出窗口后按右键,就弹出如图3-16所示的快捷菜单。菜单中包含三项:cut、copy、paste。因为没有对应的消息矗立函数,所有这些菜单都是灰色的、非活动的。
按Insert按钮。弹出Poup Menu对话框,在Add popup menu to下拉列表框中选择CMainFrame,点OK按钮,关闭Popup Menu对话框。按Close按钮关闭Component and Controls Gallery对话框。编译运行Hello,弹出窗口后按右键,就弹出如图3-16所示的快捷菜单。菜单中包含三项:cut、copy、paste。因为没有对应的消息矗立函数,所有这些菜单都是灰色的、非活动的。
>
现在,我们看看Component Gallery是如何实现快捷菜单的。首先看资源视图的菜单资源,Component Gallery在其中增加了一个ID为CG_IDR_POPUP_MAIN_FRAME的菜单,菜单中包含了刚才我们看到的三个菜单项:cut、copy、paste。切换到类视图,浏览CMainFrame类,可以看到CMainFrame增加了一个OnContextMenu的成员函数,它是CWnd的一个方法,用于处理鼠标右键单击消息,原型如下:

afx_msg void OnContextMenu(CWnd* pWnd,CPoint point);

  其中pWnd指向右键单击的窗口,它可以是一个本窗口的一个子窗口。比如,我们在工具条上单击右键时也弹出同样的菜单,工具条就是框架窗口的一个子窗口。OnContextMenu函数定义如清单3.6所示。

清单3.6 右键菜单

void CMainFrame::OnContextMenu(CWnd*, CPoint point)

{

// CG: This block was added by the Pop-up Menu component

{

if (point.x == -1 && point.y == -1){

//如果是键盘激活的快捷菜单,则在窗口左上角5,5的位置显示快捷菜单

CRect rect;

GetClientRect(rect);

ClientToScreen(rect);

point = rect.TopLeft();

point.Offset(5, 5);

}

//载入快捷菜单资源

CMenu menu;

VERIFY(menu.LoadMenu(CG_IDR_POPUP_MAIN_FRAME));

//取得本菜单的第一个子菜单

CMenu* pPopup = menu.GetSubMenu(0);

ASSERT(pPopup != NULL);

CWnd* pWndPopupOwner = this;

//如果当前窗口是一个子窗口,取其父窗口作为弹出菜单的拥有者

while (pWndPopupOwner->GetStyle() & WS_CHILD)

pWndPopupOwner = pWndPopupOwner->GetParent();

//在point.x,point.y处显示弹出式菜单并跟踪其选择项

pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y,pWndPopupOwner);

}

}

  一般的,我们都可以使用Component Gallery的Popup menu构件为某个窗口、对话框、视图等增加快捷菜单而无需手工编程。我们要做的只是编辑修改缺省的菜单为我们自己的快捷菜单,并用ClassWizard生成必要的成员函数,在加入自己的代码。如果确实要手工做的话,首先应当用菜单编辑器增加一个菜单,然后为对应的窗口添加OnContextMenu方法,OnContextMenu的定义可以参考上面的程序。
  Component Gallery的功能远不止向程序添加快捷菜单这一项,它还可以增加启动画面(Splash Window)、多页式对话框、口令检查对话框等多种功能。读者可以试着往Hello程序中添加Splash Window和口令对话框,体验一下Component Gallery的强大功能。
如何在vc对话框中加菜单
 
 
A     全部在资源管理器里做

先insert一个菜单资源比如是IDR_MENU1,打开你的对话框属性页,第一个tab是general,找到一个叫MENU的选框下拉框,选上IDR_MENU1,再重编译即可
B 在资源管理器中设计好一个菜单,然后……

在CMyDialog的函数OnInitDialog()中加入如下几行,IDR_MENU_MYMENU为你在资源管理器中设置好的menu资源的ID



CMenu * Menu=new CMenu();

    

    Menu->LoadMenu(IDR_MENU_MYMENU);

    SetMenu(Menu);
SetMenu就是将menu与当前窗口关联起来;  
  menu.Detach是将C++对象与菜单分离,使得menu对象析构时,菜单不被销毁。
类与头文件
Class   Header   file  
CAnimateCtrl   afxcmn.h  
CArchive   afx.h  
CArchiveException   afx.h  
CArray   afxtempl.h  
CAsyncMonikerFile   afxole.h  
CAsyncSocket   afxsock.h  
CBitmap   afxwin.h  
CBitmapButton   afxext.h  
CBrush   afxwin.h  
CButton   afxwin.h  
CByteArray   afxcoll.h  
CCachedDataPathProperty   afxctl.h  
CCheckListBox   afxwin.h  
CClientDC   afxwin.h  
CCmdTarget   afxwin.h  
CCmdUI   afxwin.h  
CColorDialog   afxdlgs.h  
CComboBox   afxwin.h  
CComboBoxEx   afxcmn.h  
CCommandLineInfo   afxwin.h  
CCommonDialog   afxdlgs.h  
CConnectionPoint   afxdisp.h  
CControlBar   afxext.h  
CCriticalSection   afxmt.h  
CCtrlView   afxwin.h  
CDaoDatabase   afxdao.h  
CDaoException   afxdao.h  
CDaoFieldExchange   afxdao.h  
CDaoQueryDef   afxdao.h  
CDaoRecordset   afxdao.h  
CDaoRecordView   afxdao.h  
CDaoTableDef   afxdao.h  
CDaoWorkspace   afxdao.h  
CDatabase   afxdb.h  
CDataExchange   afxwin.h  
CDataPathProperty   afxctl.h  
CDateTimeCtrl   afxdtctl.h  
CDBException   afxdb.h  
CDBVariant   afxdb.h  
CDC   afxwin.h  
CDHtmlDialog   afxdhtml.h  
CDialog   afxwin.h  
CDialogBar   afxext.h  
CDocItem   afxole.h  
CDockState   afxadv.h  
CDocObjectServer   afxdocob.h  
CDocObjectServerItem   afxdocob.h  
CDocTemplate   afxwin.h  
CDocument   afxwin.h  
CDragListBox   afxcmn.h  
CDumpContext   afx.h  
CDWordArray   afxcoll.h  
CEdit   afxwin.h  
CEditView   afxext.h  
CEvent   afxmt.h  
CException   afx.h  
CFieldExchange   afxdb.h  
CFile   afx.h  
CFileDialog   afxdlgs.h  
CFileException   afx.h  
CFileFind   afx.h  
CFindReplaceDialog   afxdlgs.h  
CFont   afxwin.h  
CFontDialog   afxdlgs.h  
CFontHolder   afxctl.h  
CFormView   afxext.h  
CFrameWnd   afxwin.h  
CFtpConnection   afxinet.h  
CFtpFileFind   afxinet.h  
CGdiObject   afxwin.h  
CGopherConnection   afxinet.h  
CGopherFile   afxinet.h  
CGopherFileFind   afxinet.h  
CGopherLocator   afxinet.h  
CHeaderCtrl   afxcmn.h  
CHotKeyCtrl   afxcmn.h  
CHtmlEditCtrl   afxhtml.h  
CHtmlEditCtrlBase   afxhtml.h  
CHtmlEditDoc   afxhtml.h  
CHtmlEditView   afxhtml.h  
CHtmlStream   afxisapi.h  
CHtmlView   afxhtml.h  
CHttpArgList   afxisapi.h  
CHttpConnection   afxinet.h  
CHttpFile   afxinet.h  
CHttpFilter   afxisapi.h  
CHttpFilterContext   afxisapi.h  
CHttpServer   afxisapi.h  
CHttpServerContext   afxisapi.h  
CImageList   afxcmn.h  
CInternetConnection   afxinet.h  
CInternetException   afxinet.h  
CInternetFile   afxinet.h  
CInternetSession   afxinet.h  
CIPAddressCtrl   afxcmn.h  
CLinkCtrl   afxcmn.h  
CList   afxtempl.h  
CListBox   afxwin.h  
CListCtrl   afxcmn.h  
CListView   afxcview.h  
CLongBinary   afxdb_.h  
CMap   afxtempl.h  
CMapPtrToPtr   afxcoll.h  
CMapPtrToWord   afxcoll.h  
CMapStringToOb   afxcoll.h  
CMapStringToPtr   afxcoll.h  
CMapStringToString   afxcoll.h  
CMapWordToOb   afxcoll.h  
CMapWordToPtr   afxcoll.h  
CMDIChildWnd   afxwin.h  
CMDIFrameWnd   afxwin.h  
CMemFile   afx.h  
CMemoryException   afx.h  
CMenu   afxwin.h  
CMetaFileDC   afxext.h  
CMiniFrameWnd   afxwin.h  
CMonikerFile   afxole.h  
CMonthCalCtrl   afxdtctl.h  
CMultiDocTemplate   afxwin.h  
CMultiLock   afxmt.h  
CMultiPageDHtmlDialog   afxdhtml.h  
CMutex   afxmt.h  
CNotSupportedException   afx.h  
CObArray   afxcoll.h  
CObject   afx.h  
CObList   afxcoll.h  
COccManager   afxocc.h  
COleBusyDialog   afxodlgs.h  
COleChangeIconDialog   afxodlgs.h  
COleChangeSourceDialog   afxodlgs.h  
COleClientItem   afxole.h  
COleCmdUI   afxdocob.h  
COleControl   afxctl.h  
COleControlContainer   afxocc.h  
COleControlModule   afxctl.h  
COleControlSite   afxocc.h  
COleConvertDialog   afxodlgs.h  
COleCurrency   afxdisp.h  
COleDataObject   afxole.h  
COleDataSource   afxole.h  
COleDBRecordView   afxoledb.h  
COleDialog   afxodlgs.h  
COleDispatchDriver   afxdisp.h  
COleDispatchException   afxdisp.h  
COleDocObjectItem   afxole.h  
COleDocument   afxole.h  
COleDropSource   afxole.h  
COleDropTarget   afxole.h  
COleException   afxdisp.h  
COleInsertDialog   afxodlgs.h  
COleIPFrameWnd   afxole.h  
COleLinkingDoc   afxole.h  
5月20日
PtInRect(point)
/
// CCDCView message handlers
void CCDCView::OnLButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 CClientDC dc(this);
 char str[100];
 int temp=10;
 sprintf(str, "There are %d", temp);
 
 if(m_position.PtInRect(point))
 {
  SetCapture();
  m_oldPoint=point;
  dc.TextOut(100,100, str);
 }
 
 CView::OnLButtonDown(nFlags, point);
}
void CCDCView::OnMouseMove(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if(GetCapture()==this)
 {
  CRect validRect=m_position;
  CSize size=point-m_oldPoint;
  m_position.OffsetRect(size);
  m_oldPoint=point;
  validRect.UnionRect(&validRect, &m_position);
  InvalidateRect(&validRect);
 }
 if(m_position.PtInRect(point))
  ::SetCursor(AfxGetApp()->LoadCursor(IDC_CROSS ));
 
 CView::OnMouseMove(nFlags, point);
}
void CCDCView::OnLButtonUp(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 if(GetCapture()==this)
  ::ReleaseCapture();
 
 CView::OnLButtonUp(nFlags, point);
}
测试分析报告(GB8567——88)

1.1编写目的

说明这份测试分析报告的具体编写目的,指出预期的阅读范围。

1.2背景

说明:

a.       被测试软件系统的名称;

b.       该软件的任务提出者、开发者、用户及安装此软件的计算中心,指出测试环境与实际运行环境 之间可能存在的差异以及这些差异对测试结果的影响。

1.3定义

列出本文件中用到的专问术语的定义和外文首字母组词的原词组。

1.4参考资料

列出要用到的参考资料,如:

a.  本项目的经核准的计划任务书或合同、上级机关的批文;

b.  属于本项目的其他已发表的文件

c.  文件中各处引用的文件、资料,包括所要用到的软件开发标准。列出这些文件的标题、文件编号、发表日期和出版单位,说明能够得到这些文件资料的来源。

2测试概要

用表格的形式列出每一项测试的标识符及其测试内容,并指明实际进行的测试工作内容与测试计划中预先设计的内容之间的差别,说明作出这种改变的原因。

3测试结果及发现

3.1测试1(标识符)

把本项测试中实际得到的动态输出(包括内部生成数据输出)结果同对于动态输出的要求进行比较,陈述其中的各项发现。

3.2测试2(标识符)

用类似本报告3.1条的方式给出第 2项及其后各项测试内容的测试结果和发现。

4对软件功能的结论

4.1功能1(标识符)

4.1.1能力

简述该项功能,说明为满足此项功能而设计的软件能力以及经过一项或多项测试已证实的能力。

4.1.2限制

说明测试数据值的范围(包括动态数据和静态数据),列出就这项功能而言,测试期间在该软件中查出的缺陷、局限性。

4.2功能2(标识符)

用类似本报告4.l的方式给出第2项及其后各项功能的测试结论。

......

5分析摘要

5.1能力

陈述经测试证实了的本软件的能力。如果所进行的测试是为了验证一项或几项特定性能要求的实现,应提供这方面的测试结果与要求之间的比较,并确定测试环境与实际运行环境之间可能存在的差异 对能力的测试所带来的影响。

5.2缺陷和限制

陈述经测试证实的软件缺陷和限制,说明每项缺陷和限制对软件性能的影响,并说明全部测得的性能缺陷的累积影响和总影响。

5.3建议

对每项缺陷提出改进建议,如:

a.  各项修改可采用的修改方法;

b.  各项修改的紧迫程度;

c.  各项修改预计的工作量;

d.  各项修改的负责人。

5.4评价

说明该项软件的开发是否已达到预定目标,能否交付使用。

6测试资源消耗

总结测试工作的资源消耗数据,如工作人员的水平级别数量、机时消耗等。

关于软件文档
 
 

文档的作用和分类

    软件文档(document)也称文件,通常指的是一些记录的数据 和数据媒体,它具有固定不变的形式,可被人和计算机阅读。它和 计算机程序共同构成了能完成特定功能的计算机软件(有人把源 程序也当作文档的一部分)。我们知道,硬件产品和产品资料在整 个生产过程中都是有形可见的,软件生产则有很大不同,文档本 身就是软件产品。没有文档的软件,不成其为软件,更谈不到软件 产品。软件文档的编制(documentation)在软件开发工作中占有突 出的地位和相当的工作量。高效率、高质量地开发、分发、管理和维 护文档对于转让、变更、修正、扩充和使用文档,对于充分发挥软 件产品的效益有着重要意义。

    然而,在实际工作中,文档在编制和使用中存在着许多问 题,有待于解决。软件开发人员中较普遍地存在着对编制文档不感 兴趣的现象。从用户方面看,他们又常常抱怨:文档售价太高、文 档不够完整、文档编写得不好、文档已经陈旧或是文档太多,难于 使用等等。究竟应该怎样要求它,文档应该写哪些,说明什么问 题,起什么作用?这里将给出简要的介绍。

图 文档桥梁作用

    文档在软件开发人员、软件管理人员、维护人员、用户以及计 算机之间的多种桥梁作用可从图9.2中看出。软件开发人员在各 个阶段中以文档作为前阶段工作成果的体现和后阶段工作的依 据,这个作用是显而易见的。软件开发过程中软件开发人员需制定 一些工作计划或工作报告,这些计划和报告都要提供给管理人员, 并得到必要的支持。管理人员则可通过这些文档了解软件开发项 目安排、进度、资源使用和成果等。软件开发人员需为用户了解软 件的使用、操作和维护提供详细的资料,我们称此为用户文档。以 上三种文档构成了软件文档的主要部分。我们把这三种文档所包 括的内容列在图6中。其中列举了十三个文档,这里对它们作 一些简要说明:

  • 可行性研究报告:说明该软件开发项目的实现在技术上、经 济上和社会因素上的可行性,评述为了合理地达到开发目标可供 选择的各种可能实施的方案,说明并论证所选定实施方案的理 由。
  • 项目开发计划:为软件项目实施方案制定出具体计划,应 该包括各部分工作的负责人员、开发的进度、开发经费的预算、所 需的硬件及软件资源等。项目开发计划应提供给管理部门,并作 为开发阶段评审的参考。
  • 软件需求说明书:也称软件规格说明书,其中对所开发软 件的功能、性能、用户界面及运行环境等作出详细的说明。它是用 户与开发人员双方对软件需求取得共同理解基础上达成的协议, 也是实施开发工作的基础。
  • 数据要求说明书:该说明书应给出数据逻辑描述和数据采 集的各项要求,为生成和维护 系统数据文卷作好准备。
  • 概要设计说明书:该说 明书是概要设计阶段的工作 成果,它应说明功能分配、模 块划分、程序的总体结构、输 入输出以及接口设计、运行设 计、数据结构设计和出错处理 设计等,为详细设计奠定基 础。
  • 详细设计说明书:着重 描述每一模块是怎样实现的, 包括实现算法、逻辑流程等。 ·用户手册:本手册详细 描述软件的功能、性能和用户 界面,使用户了解如何使用该软件。

    文档用户文档用户手册
    操作手册
    维护修改建议
    软件需求(规格)说明书
    开发文档软件需求(规格)说明书
    数据要求说明书
    概要设计说明书
    详细设计说明书
    可行性研究报告
    项目开发计划
    管理文档项目开发计划
    测试计划
    测试报告
    开发进度月报
    开发总结报告

    图 三种文档

  • 操作手册:本手册为操作人员提供该软件各种运行情况的 有关知识,特别是操作方法的具体细节。
  • 测试计划:为做好组装测试和确认测试,需为如何组织测试 制定实施计划。计划应包括测试的内容、进度、条件、人员、测试用 例的选取原则、测试结果允许的偏差范围等。
  • 测试分析报告:测试工作完成以后,应提交测试计划执行 情况的说明。对测试结果加以分析,并提出测试的结论意见。
  • 开发进度月报:该月报系软件人员按月向管理部门提交的 项目进展情况报告。报告应包括进度计划与实际执行情况的比较、 阶段成果、遇到的问题和解决的办法以及下个月的打算等。
  • 项目开发总结报告:软件项目开发完成以后,应与项目实 施计划对照,总结实际执行的情况,如进度、成果、资源利用、成本 和投入的人力。此外还需对开发工作作出评价,总结出经验和教 训。
  • 维护修改建议,软件产品投入运行以后,发现了需对其进 行修正、更改等问题,应将存在的问题、修改的考虑以及修改的影 响估计作详细的描述,写成维护修改建议,提交审批。 以上这些文档是在软件生存期中,随着各阶段工作的开展适 时编制。其中有的仅反映一个阶段的工作,有的则需跨越多个阶 段。表5给出了各个文档应在软件生存期中哪个阶段编写。这 些文档最终要向软件管理部门,或是向用户回答以下的问题:
    表9.2 软件生存期各阶段编制的文档

    阶段

    文档        

    可行性药酒与计划需求分析设计代码编写测试运行与维护
    可行性研究报告      
    项目开发计划      
    软件需求说明      
    数据要求说明      
    概要设计说明      
    星系设计说明      
    测试计划      
    用户手册      
    操作手册      
    测试分析报告      
    开发进度月报      
    项目开发总结      
    维护修改建议      


  • 哪些需求要被满足,即回答“做什么?”
  • 所开发的软件在什么环境中实现以及所需信息从哪里来, 即回答“从何处?”
  • 某些开发工作的时间如何安排,即回答“何时干?”
  • 某些开发(或维护)工作打算由“谁来干?”
  • 某些需求是怎么实现的?
  • 为什么要进行那些软件开发或维护修改工作?

    上述十三个文档都在一定程度上回答了这六个方面的问题。这可从表中看到。

表 文档所回答的问题

     所提问题

文档 

什么 何处 何时 如何 为何
可行性研究报告        
项目开发计划      
软件需求说明        
数据要求说明        
概要设计说明          
详细设计说明          
测试计划      
用户手册          
操作手册          
测试分析报告          
开发进度月报        
项目开发总结          
维护修改建议      

    至此,我们对文档的作用有了进一步的理解。每一个文档的任 务也是明确的,任何一个文档都此是多余的。

软件测试

1        软件测试和VSTS 测试工具

本部分介绍各种测试类型,以及它们在VSTS 2005中的应用。
 
在学习讨论之前,阿彪问大家:我有一个闷在心里好久的问题 bug到底翻译成什么最好?
杂曰:
    臭虫,缺陷,错误,地雷,应用程序异常,
    就用bug好了,大家都理解!
 
小强!小强!
 
大家回头一看,阿毛红着脸说:我们宿舍里有不少小强,每晚自习回去都要打小强。。。
众大笑。
阿彪:我倒是不反对用“小强”。
阿超:好是好,VSTS也支持改工作项的名字。就怕我们以后招进来名字中有“强”的同学。
阿彪:我觉得我可以为“小强”花一颗银弹,我们以后就把“小强 当作bug的同义词.
小飞:那我们怎么翻译“bug fix”?  翻译成“针对缺陷的修改”也太绕口了。
阿毛:我们是用拖鞋来打小强,所以不妨称之为“拖鞋”。
(大笑)
国栋:我反对把软件工程的术语生活化。。。
 
 
阿超:说到测试,大家肯定有不少了解,也保不准有一些误解,我们这个讨论就是要去伪存真,把大家的理解统一到一个水平上。大家知道的“测试方法”有多少?
 
杂曰:
           Black box Test, White box Test
           Code Coverage, Test Unit Test
           Functional Test, Structural Test
           System Test, Performance Test
           Stress Test, Load Test
           Acceptance Test, Regression Test
           Ad hoc Test, Integration Test
           Alpha/beta Test, Localization/Globalization Test
           Security Test, Accessibility Test, Scenario Test,
           Usability Test,Smoke Test
 
二柱:这么多!把我都忽悠得有点晕了。看来我国软件测试人才真是大有用武之地了。
小飞:这么多名词,是得学几年的,写程序的方法怎么没有这么多花头?
 
阿超:咱们还是一个一个来吧。 这么多名词只不过是从各个方面描述了软件测试,并不是说有这么多独立的测试方法,我们把它们分类处理就不难了。

1.1     从测试设计的方法分类

从测试设计的方法来看,我们知道有两类方法:
Black box (黑箱)
White box (白箱)
这是每个接触过软件测试的人都会给出的答案。但是这只是整个软件测试的入门。我们可以跳过去,直接讨论下面的内容。。。
 
问:我在网上看到有人争论黑箱测试和白箱测试哪一个是另一个的基础,还有那一个更难,那一个更有前途,等等。据说李村数码在搞“灰箱测试”,是不是更高级?能不能简单讲一讲?
阿超:大家都有这些问题么?
杂曰:[略去对此问题热烈的争论500 ]
阿超:听了大家的争论,看来我们的确得花不少时间统一认识.
 
第一个要注意的问题是,所谓黑箱/白箱,是软件测试设计的方法,不是软件测试的方法!注意“设计”二字。
 
黑箱:在设计测试的过程中,把软件系统当作一个“黑箱”,无法了解或使用系统的内部结构及知识。一个更准确的说法是“Behavioral Test Design, 从软件的行为,而不是内部结构出发来设计测试。
白箱:在设计测试的过程中,设计者可以“看到”软件系统的的内部结构,并且使用软件的内部知识来指导测试数据及方法的选择。“白箱”并不是一个精确的说法,因为把箱子涂成白色,同样也看不见箱子里的东西。有人建议用“玻璃箱”来表示。
 
在实际工作中,我们不应画地为牢,严格只用某一种方法来设计测试方法。在实际的测试中,当然是对系统了解的越多越好。所谓“灰箱”的提法,正是这一反映。有些人甚至希望我们全部忘记“箱子”和它们的颜色。
 
我们并不是要禁止懂得内部构造的人员来进行黑箱测试设计,只不过我们在设计时有意不考虑软件的内部结构。例如:在测试程序内部基本模块的时候(单元测试),我们通常是要求对程序结构非常了解的程序员来设计,这是因为内部模块的“行为”和程序的外部功能并没有直接的关系,而且内部基本模块的“行为”通常没有明确的定义。另一个例子是“可用性测试”,在设计此类测试的时候,我们没必要纠缠于程序的内部结构,而是着重于软件的界面和行为。但是软件可用性测试也需要很多专业知识。这也从一个侧面表明“黑箱”和 “白箱”没有简单的高低之分。
 
一旦测试用例写出来之后,我们大可以忘了它们是从那种颜色的箱子里出来的,用它就可以了。

1.2     功能测试

以下的测试术语都是主要测试软件的功能。在下表所列的测试中,测试的范围有小到大,测试者也由内到外从程序开发人员(单元测试)到 测试人员,到一般用户(Alpha/Beta 测试)。
 

测试名称
测试内容
Unit Test
单元测试在最低的功能/参数上验证程序的正确性
Functional Test
功能测试验证模块的功能
Integration Test
集成测试验证几个互相有依赖关系的模块的功能
Scenario Test
场景测试验证几个模块是否能够完成一个用户场景
System Test
系统测试对于整个系统功能的测试
Alpha/Beta Test
外部软件测试人员(Alpha/Beta 测试员)在实际用户环境中对软件进行全面的测试。
 
 
 
 
 
 

 
 

1.3     非功能测试

一个软件除了基本功能之外,还有很多功能之外的特性,这些叫“non-functional requirement, 或者“quality of service requirement- 服务质量需求。没有软件的功能,这些特性都无从表现出来,我们要在软件开发的适当阶段做这些测试。
比如:

测试名称
测试内容
Stress/load test
测试软件在负载情况下能否正常工作
Performance test
测试软件的效能
Accessibility test
测试软件辅助功能测试测试软件是否向残疾用户提供足够的辅助功能
Localization/Globalization Test
本地化/全球化测试
Compatibility Test
兼容性测试
Configuration Test
配置测试测试软件在各种配置下能否正常工作
Usability Test
可用性测试测试软件是否好用
Security Test
软件安全性测试

 

1.4     Unit Test单元测试

二柱:我们也试过用单元测试来保证质量,要求每人都要写,在签入代码前必须通过单元测试。但是搞了几个星期就不了了之。
 
大家七嘴八舌的列举了单元测试的问题:
-       有时单元测试报了错,再运行一次就好了,后来大家就不想花时间改错,多运行几次,有一次通过就行了。
-       单元测试中好多错都和环境有关,在别人的机器都运行不成功。
-       花在单元测试上的时间要比写代码的时间还多
-       单元测试中我们还要测试效能和压力,花了很多时间
-       我们都这么费劲地测了,那还要测试人员干什么?

1.4.1    用VSTS 单元测试

单元测试的基本构成
           Setup  //设置好环境,准备测试
           Test    // 测试
           Teardown  //打扫战场
例子:我们要写一个银行账户的类,那么它的单元测试应该怎么写?
谁自告奋勇上来表演一下写代码?小飞,好请上台。
 
小飞写了下面的代码:
定义interface IAccount
 
实现public class Account : IAccount
{
}
 
每个函数都使用临时代码
 
好,现在右键按住Account,就可以看到“Create Unit Tests”的菜单,选中之后,会出来新建Unit Tests的对话框:
每个函数都可以选中是否产生 单元测试。
 
//解释单元测试的结构
 
//一个缺省的单元测试
 
//修改过的单元测试
 
//运行单元测试
 
//单元测试的结果
 
//代码覆盖率

1.4.2    好的单元测试的标准

单元测试应该准确,快速地保证程序基本模块的正确性。下面是验证单元测试好坏的一系列标准:
1.4.2.1    单元测试应该在最低的功能/参数上验证程序的正确性
单元测试应该测试程序中最基本的单元如在C++/C#/Java中的类,在此基础上,可以测试一些系统中最基本的功能点(这些功能点由几个基本类组成),从面向对象的设计原理出发,系统中最基本的功能点也应该由一个类及其方法来表现。单元测试要测试API中的每一个方法,及其每一个参数。
1.4.2.2    单元测试必须由最熟悉代码的人(程序的作者)来写
代码的作者最了解代码的目的,特点,和实现的局限性。所以,没有比作者适合的人选。
问:如果我很忙,能不能让别人代劳单元测试?
答:如果忙到连单元测试都没有时间做,那么你也没有时间写好这个功能。在一些极限编程的方法中,是可以考虑让别人来做单元测试,但是,程序的作者还是要对单元测试负责。
 
最好是在设计的时候就写好单元测试,这样单元测试就能体现API的语义,如果没有单元测试,语义的准确性就不能得到保障,以后会产生歧义。
1.4.2.3    单元测试过后,机器状态保持不变
这样就可以不断地运行单元测试,如果单元测试创建了临时的文件或目录,应该在Teardown阶段把这些临时的文件或目录删除。
如果单元测试在数据库中创建或修改了记录,那么也许要删除这些记录,或者每一个单元测试使用一个新的数据库,这样保证单元测试不受以前单元测试实例的干扰。
1.4.2.4    单元测试要快 (一个测试运行时间是几秒钟, 而不是几分钟)
快,才能保证效率。因为一个软件中有几十个基本模块(类),每个模块又有几个方法,基本上我们要求一个类的测试要在几秒钟内完成。如果软件有相互独立的几个层次,那么在测试组中可以分类,如数据库层次,网络通信层次,客户逻辑层次,和用户界面层次,可以分类运行测试,比如我只修改了“用户界面”的代码,我只需运行“用户界面”的单元测试。
1.4.2.5    单元测试应该产生可重复,一致的结果
如果单元测试的结果是错的,那一定是程序出了问题,而且这个错误一定是可以重复的。
问:如果用随机数以增加测试的真实性,好么?
答:一般情况下不好,如果某个随机数导致程序出错,但是下一次运行又不能重复这一错误,于事无补。要注意我们还是要用随机数等办法“增加测试的真实性”,但是不是在单元测试中。单元测试不能解决所有问题,所以也不必期望它会发现所有的缺陷。
1.4.2.6    独立性,单元测试的运行/通过/失败不依赖于别的测试,可以人为构造数据,以保持单元测试的独立性。
程序中的各个模块都是互相依赖的,否则它们就不会出现在一个程序中。一般情况下,单元测试中的模块可以直接引用其它的模块,并期待其它的模块能返回正确的结果。
 
如果其它的模块很不稳定,或者其他模块运行比较费时(如进行网络操作),而且对于本模块的正确性并不起关键的作用。这时可以人为地构造数据以保证这个单元测试的独立性。
 
New Object
New user
Get user permission // go thru the server to get the correct permission, you can also mock the permission object.
Object.Test(user)
1.4.2.7    单元测试应该覆盖所有代码路径,包括错误处理路径,为了保证单元测试的代码覆盖率,单元测试必须测试公开的和私有的函数/方法。
单元测试必须覆盖所测单元的所有代码路径。
问:啊!这样岂不是要写很多啰里啰唆的测试方法?
答:对,因为程序中很多缺陷都是从这些啰里啰唆的错误处理中产生的。如果你的模块中某个错误处理路径很难到达,那你也许要想想是否可以把这个错误处理拿掉。
[大栓:这对于那些爱写复杂代码的人是一个很好的惩罚,不对,是一个很好的锻炼。]
[阿超:对,把单元测试的责任和代码作者绑定在一起后,代码作者就能更真切地体会到复杂代码的副作用,因为验证复杂代码的正确性要困难得多。要注意的一点是:100% 的代码覆盖率并不等同于100% 的正确性,请看下列例子]
 
e.g.  didn’t check return value.
e.g.  memory leak
1.4.2.8    单元测试应该集成到自动测试的框架中
另一个重要的措施是要把单元测试自动化,这样每个人都能很容易地运行,并且单元测试每天都可以运行。每个人都可以随时在自己机器上运行。团队一般是在每日构建中运行单元测试, 这样每个单元测试的错误就能及时发现并得到修改。
1.4.2.9    单元测试必须和产品代码一起保存和维护
单元测试必须和代码一起进行版本维护。如果不是这样,过了一阵,代码和单元测试就会出现不一致,而且所有代码的作者要花时间来确认哪些是程序出现的错误,哪些是由于单元测试更新滞后造成的错误。。。这样就失去了单元测试的意义,同时又给大家增加了负担,折腾多次以后,大家就会觉得维护单元测试是一件很费时费力的事。
 

1.5     BVT 构建验证测试 Build Verification Test

望文生义,构建验证测试是指在一个构建完成之后,团队自动运行的一套验证系统的基本功能的测试。大多数情况下,这一套系统都是在自动构建成功后自动运行的,有些情况下也会手工运行,但是由于构建是自动生成的,我们也要努力让BVT自动运行。
问:一个系统有这么多功能点,什么是基本,什么是不基本的?
答:第一,必须能安装;第二,必须能够实现一组核心场景(例如:对于字处理软件来说,必须能打开/编辑/保存一个文档文件,但是一些高级功能,如: 文本自动纠错, 则不在其中; 又如,网站系统,用户可以注册/上载/下载信息,但是一些高级功能,如删除用户,列出用户参与的所有讨论,则不在其中。
 
在运行BVT之前,可以运行所有的单元测试, 这样可以保证系统的单元测试和程序员的单元测试版本保持一致。不少情况下,开发人员修改了程序和单元测试,但是忘了把更新的单元测试也同时签入源代码库中。
 
通过BVT的构建可以称为:可测 self-test, 意思是说团队可以用这一版本进行各种测试因为它的基本功能都是可用的。通不过BVT的构建称为“不可测”(self-hosed
 
如果构建测试不能通过,那么自动测试框架会自动对每一个失败的测试产生一个bug (小强)。一般的做法这些小强都是有最高优先级,是开发人员要首先修改这些小强。大家知道维持每日构建,并产生一个可测的版本是软件开发过程质量控制的基础。对于导致问题的小强,我们该怎么办?
1. 找到导致失败的原因,如果原因很简单,程序员可以马上修改,然后直接提交。
2. 找到导致失败的原因的修改集,把此修改集剔出此版本(程序员必须修改好后再重新提交到源代码库中)。
3. 程序员必须在下一个构建开始前把此小强修理好。
 
方法1/2都可以使今天的构建成为“可测”,但是有时各方面的修改互相依赖,不能在短时间内解决所有问题,只能采用第三个办法。
 
问:有人提到一种“smoke test”,冒烟测试,是什么回事?
答:这事实上是一种基本验证测试,据说是从硬件设计行业流传过来的说法当年设计电路板的时候,很多情况下,新的电路板一插上电源就冒起白烟,烧坏了,如果插上电源后没有冒烟,那就是通过了“冒烟测试”,可以进一步测试电路板的功能了。我们正在讨论的BVT也是一种冒烟测试。

1.6     功能测试 functional test

测试团队拿到一个“可测”等级的构建,他们就会按照测试计划,测试各自负责的模块和功能,这个过程可能会产生总共10来个bug,也可能产生100个以上的bug,那么如何保证我们有效地测试了软件,同时我们在这一步怎样衡量构建的质量?
 
MSF敏捷模式中,我们建议还是采用场景来规划测试工作
在“基本场景”的基础上,我们把所有系统目前理论上支持的场景都列出来,然后按功能分类测试,如果测试成功,就在此场景中标明“成功”,否则,就标明“失败”,并且把失败的情况用一个(或几个)“小强”/bug来表示。
 
当所有的测试人员完成对场景的测试,我们自然地就得出了一个表:
 

场景ID
场景名
测试结果
Bug/小强id
3024
用户登录
成功
 
3026
用户按价格排序
失败
5032
3027
用户按名字搜索
失败
5033
 

 
这样我们就能很快地报告“功能测试56% 通过”等等。如果所有场景都能通过,(有些情况下可以把此标准从100% 降低到90% 左右)则这个构建的质量是“可用”,意味着这一个版本可以给用户使用。 这种情况下,客户,合作伙伴可以得到这样的版本这也是所谓“技术预览版”,或“社区预览版”的由来。
 
但是,有一个重要的问题要大家注意“可用”,并不是指软件都没有bug,而是指在目前的用户场景中,按照场景的要求进行的操作,都能得到预期的效果。
1. 目前还没有定义的用户场景中,程序质量如何,还未得而知。
a.     场景中没有考虑到多种语言设置
2. 不按照场景的要求进行的操作,结果如何,还未得而知。
a.     如:在某一场景中,场景规定用户可以在最后付款前取消操作,回到上一步,如果一个测试人员发现在反复 提交/取消同一访问多次,然后网页出现问题,这并不能说明用户场景失败,当然这个极端的bug也必须找出原因并在适当的时间改正。
 
这种测试有时也被称为“acceptance test, 因为如果构建通过了这样的测试,这一个构建就被测试团队“接受了”。 同时,还有对系统各个方面进行的“接收”测试,如测试系统的全球化,或者针对某一语言环境做的测试。
 

1.7       Ad hoc Test, Exploratory Test “探索式”的测试

 
“Ad Hoc” 原意是指 “特定的,一次性的”。
 
什么叫“特定”测试?或者“探索式”的测试?
就是为了某一个特定目的进行的测试,就这一次,以后一般也不会重复测试。在软件工程的实践中,“ad hoc”大部分是指随机进行的,探索性的测试。
 
比如:测试人员阿毛拿到了一个新的构建,按计划是进行模块A的功能测试,但是他灵机一动,想看看另一个功能B做得如何,或者想看看模块A在某种边界条件下会出现什么问题,于是他就“ad hoc”一把,居然在这一功能模块中发现了不少小强。
 
ad hoc”也意味着测试是尝试性的,“我来试试,在这个对话框中一通乱按,然后随意改变窗口大小,看看会出什么问题…” 如果没问题,那么以后也不会再这么做了。
 
一般情况下,测试人员不会花很多时间进行特定测试,但是在一些缺乏管理的团队中,很多时候测试人员不知道自己此时应该做什么,只好做一些看似“ad hoc 的测试,比如随机测试各个功能的各个方面。这些测试理论上都应该由测试管理人员规划好属于各个功能模块的测试用例中。
 
在一个团队中,“ad hoc”太多是一个管理不好的标志,因为“ad hoc”是指那些一时想到要做,但是以后也没有计划经常重复的测试计划。
 
问:我听说有人是“ad hoc”测试的高手,这是什么意思?
答:有很多测试人员会按部就班地进行测试,但是还有一些人头脑比较灵活,喜欢另辟蹊径,测试一些一般人不会想到的场景,这些人往往会发现更多的小强。开发人员对这样的“ad hoc”高手是又爱又恨。
 
问:同时看问题要分两方面,有些“ad hoc”发现的小强在正常使用软件中几乎不会出现,我们要不要花时间“ad hoc”?
答:现在一些成功的通用软件的用户以百万计,按部就班的测试计划很难包括很多实际的场景,这时,“ad hoc”测试能够发现重要的问题;另外一些风险很大的领域,例如安全性,一旦出了问题,威胁就会相当大,这时要多鼓励一些“ad hoc”测试,以弥补普通测试的不足。从这个意义上说,“ad hoc”测试可以用来衡量当前测试用例的完备性,如果你探索了半天,都没有发现什么在现有测试用例之外的问题,那就说明现有的测试用例是比较完备的。
 
ad hoc”测试的测试流程是不可重复的,因为它的测试都是“特定”测试,没法重复。由于这一原因,“ad hoc”测试不能自动化,就这一点而言,还达不到CMM的第二级可重复级。
 
作为管理人员来说,如果太多小强是在“ad hoc”出来的,那我们就要看看测试计划是否基于实际的场景,开发人员的代码逻辑是否完善,等等。同时,要善于把看似“ad hoc”的测试用例抽象出来,包括到以后的测试计划中。

1.8       Regression Test回归测试

问:我听说不少关于Regression Test的介绍,但是它到底是怎么“回归”法?回归到哪里去?我还是没搞懂。
答:Regress的英语定义是:return to a worse or less developed state.  是倒退,退化,退步的意思。
在软件工程中,如果一个模块或功能以前是正常工作的,但是在一个新的构建中出了问题,那这个模块就出现了一个“退步”- regression, 从正常工作的稳定状态退化到不正常工作的不稳定状态。
 
在一个模块的功能逐步完成的同时,和此功能有关的测试用例也同样在完善中。一旦有关的测试用例通过,我们就得到此模块的功能基准(baseline
 
在某某版本,某某模块的某某测试用例是通过的!
 
如果测试人员发现了在新的构建版本某个测试用例失败了,这就是一个“倒退”,在新版本上运行所有已通过的测试用例以验证没有“退化”情况发生,这个过程就是一个“regression test”.  如果这样的“倒退”是由于模块的功能发生了正常变化(由于设计变更的原因),那么测试用例的基准就要修改,以和新的功能保持一致。
 
针对一个bug fix (拖鞋),我们也要作Regression Test
a)     验证新的代码的确把缺陷改正了,
b)    同时要验证新的代码没有把模块的现有功能破坏,没有regression
 
所以我不也知道“回归测试”是如何的“回归”,我们可以理解为“回归到以前不正常的状态”。
 
回归测试最好要自动化,因为对于每一个构建都要运行所有回归测试,以保证尽早发现问题。
 

1.9       Scenario/integration/System Test  场景/集成/系统测试

在软件开发的一定阶段,我们要对一个软件进行全面和系统的测试,以保证软件的各个模块都能共同工作,在各方面都能满足用户的要求。这时的测试叫系统/集成测试。
问:什么时候做系统测试?是不是越快越好?
答:原则上是当一个模块稳定的时候,就可以把它集成到系统中,和整个系统一起进行测试,通常在软件产品需要阶段性发布的时候。
 
问:有一种叫Scenario Test, 是什么意思?
答:就是以场景为驱动的集成测试,关于“场景”,大家可以看专门的介绍。这一方法的核心思想是:当用户使用一个软件的时候,他/她并不会独立使用各个模块,而是把软件做为一个整体来使用的。我们在做场景测试的时候,就需要考虑在现实环境中用户使用软件的流程是什么样,然后模拟这个流程,看看软件能不能达到用户的需求。这样,能使软件符合用户使用的实际需求。
 
用一个数字照片编辑软件为例,这个软件的各个模块都是独立开发的,可是用户有一定的典型流程,如果这个流程走得不好,哪怕某个模块的质量再高,用户也不会满意。
 
1. 把照相机的储存卡插入电脑
2. 程序会跳出窗口提示用户导入照片
3. 导入照片
4. 对照片进行快速编辑
      a.     调整颜色
      b.     调整亮度,对比度
      c.     修改红眼
5. 把其中几幅照片用email发送
 
这里面每一步出了问题,都会影响用户对这一产品的使用,同时这里面各个模块的用户界面如果很不一致(即使是ok/cancel按钮的次序不同),用户使用起来也很不方便。这些问题都是在单独模块的测试中不容易发现的。

1.10    Performance Test 效能测试

用户使用软件,不光是希望软件能够提供一定的服务,而且还要求服务的质量要达到一定的水平,软件的效能, 是这些“非功能需求”,或者说“服务质量需求”的一部分。
 
效能测试要验证的问题是:
           软件在设计负载内能够提供令用户满意的服务质量。
 
   1.在设计负载内我们要定义什么是正常的设计负载
   2.令用户满意的服务质量我们要定义什么样的质量是令用户满意的
 
设计负载从需求说明出发,得出系统的正常负载。例如,一个购物网站,客户认为正常负载是每分钟20次客户请求。
用户满意的质量同一个购物网站,如果客户定义满意为:每个用户的请求都能在2秒钟内返回结果。
 
我们可以逐步细化
设计负载的细化,上面我们只提到“20次客户请求”,那这些客户请求到底是什么,我们可以按照请求发生的频率来分类:
   1. 用户登录 10%
   2. 用户查看某商品详情(50%
   3. 用户比较两种商品(10%
   4. 用户查看关于商品的反馈(20%
   5. 用户购买商品(5%
   6. 所有其他请求(5%

服务质量的细化有些请求,是要对数据作“写”操作,可以要求慢一些,比如“用户购买商品”,这一服务质量请求可以放宽为3秒钟。
 
除了用户体验到的“2秒钟页面刷新”目标外,效能测试还要测试软件内部各模块的效能,这要求软件的模块能报告自身的各种效能指标,通过perfmon  或其它测试工具表现出来。
 
和别的测试不同,效能测试对硬件要有固定的要求,而且最要每次测试在相同的机器和网络环境中进行,这样才能避免外部随机因素的干扰,得到精准的效能数据。
 
同时,效能测试的结果应该成为“发布指南”的一部份,给客户发布和改进系统提供参考。
 
VSTS中如何进行效能测试在以后会详细讲解。

1.11 Stress Test压力测试

压力测试严格地说不属于效能测试。压力测试要验证的问题是:
           软件在超过设计负载的情况在仍能够返回正常结果,而没有产生严重的副作用或崩溃。
 
问:为啥不要求软件在这种情况下仍然在2-3秒钟内返回结果?
答:因为我们做不到。
 
注意,我们在这一部分要求“正常结果”,啥叫“正常”?我们也要和客户达成一致。比如,同一个购物网站,所有请求都能在网络返回“超时”错误前返回,就可以认为是“正常”。 或者网站返回“系统忙,请稍候”,也是正常结果。但是,如果用户提交的请求一部分执行,另一部分没有执行;或者用户信息丢失,这些都是不正常的结果,应该避免。
 
那我们怎么增加负载?对于网络服务软件来说,主要有下面两个方面:
1. 沿着用户轴延长
我们用刚才的购物网站为例,正常的负载是20请求/分钟,如果有更多的用户登录,怎么办?那么负载就会变成3040 100请求/分钟,或更高
 
2. 沿着时间轴延长
做过网络服务的同事都知道,网络的负载有时间性,负载压力波峰和波谷相差很大,那么如果每时每刻负载都处于峰值,程序会不会垮掉?这就是我们要作的第二点,沿着时间轴延长。
 
与此同时,我们可以减少系统可用的资源,来增加压力。
 
注意,压力测试的重点是验证程序不崩溃或产生副作用。在超负载的情况下,我们的程序仍然能够正确地运行,而不会死机。在给程序加压的过程中,程序中的很多“小”问题就会被放大,暴露出来。 最常见的问题是
  • 内存/资源泄露,在压力下这会导致程序可用的资源枯竭,最后崩溃。
  • 一些平时认为“足够大/足够好”的算法实现会出现问题。
  • 进程/线程的同步死锁问题,在压力下一些小概率事件会发生,看似完备的程序逻辑也出现了问题。
 
VSTS中如何进行压力测试在以后章节中会详细讲解。
 

1.12 Alpha Test, Beta Test

在开发软件的过程中,开发团队希望让用户直接接触到最新版本的软件,以便从用户那里收集反馈,这时开发团队会在开发过程中让特定的用户(alpha/beta用户)使用正处于开发过程中的版本,用户会用特定的反馈渠道(email, BBS)与开发者讨论使用中发现的问题,等等。这种做法成功地让广大用户心甘情愿地替开发团队测试产品并提出反馈。最近有些开发团队增加了反馈的密度,不必再等到Alpha或者Beta发布,而是不断地把一些中间版本发布出去以收集反馈,美其名曰“技术预览版本”或“社区预览版本”。
 

1.13 Usability Test可用性测试

小燕问:作为测试人员,我们是不是也要作可用性测试?
答:测试人员,以及其他的团队成员都可以对软件的可用性提出意见,包括用bug的形式放在在TFS中。软件的可用性不神秘,就是让软件更好用,让用户更有效地完成工作。
 
但是我觉得“可用性测试”似乎更多地用来描述一套测试软件可用性的过程,这个过程一般不是由测试人员来主导的,而是有对软件设计及可用性有很多研究的“可用性设计师”来实行。网络上有许多关于这方面的文章,大家可以参考。

1.14 Bug Bash

问:我们已经讲得太多的测试了,好像微软还有一个叫“bug bash”的活动,是啥意思?
答:简而言之,就是大家一起来找小强的活动小强大扫除。一般是安排出一段时间(一天),所有测试人员(有时也加入其他角色)放下手里的事情,专心找某种类型的小强。然后结束时,统计并奖励找得最多的,和最厉害的小强的员工。这种活动,如果运用得好,有下面的作用:
  • 鼓励大家做探索试的测试,开阔思路。
  • 鼓励测试队伍学习并应用新的测试方法,例如在做完“软件安全性测试”培训后,立马做一个针对“安全性”的小强大扫除.
  • 找到很多小强,让开发人员忙一阵子
 
当然,小强大扫除也有一些副作用:
  • 扰乱正常的测试工作
  • 如果过分重视奖励,会导致一些数量至上,滥竽充数的做法。
 
两个细节是:
1. 一定要让“小强大扫除”有明确的目标,明了的技术支持。
2. 一定要让表现突出的人介绍经验,让别人学习。
 
要记住,最好的测试,是能够防止小强的出现。

2       总结和思考

2.1     十八般兵器

阿毛:超总, 我的脑袋好像装不下了听了这么多,我感觉像是身上扛着十八般兵器,累得半死,但是不知道什么时候,对哪一种敌人用哪一种兵器,能不能总结一下!
 
阿超:好,我们用软件开发的生命周期来说明一下不同的测试在不同阶段的使用:
远景和计划阶段
测试只是处于计划阶段,同时要收集用户对于软件非功能性的需求,如效能,可用性,国际化等。一些“小强大扫除”的类型也可以在这个时候初步安排。
开发阶段
开发人员要写单元测试, 测试人员要写BVT
           对于每一个成功的构建,测试人员要运行功能测试/场景测试,同时建立回归测试基准以便开始回归测试。各类测试人员要进行探索式测试。
           随着软件功能的逐步完善,测试人员要进行集成测试。这时,团队可以开展对程序非功能性特性的测试,如效能/压力测试,国际化/本地化测试,安全性测试,可用性,适用性测试等。在这个时候,可以考虑分析各个模块的代码覆盖率,以增加测试的有效性。根据计划,各种类型的“小强大扫除”会以适当的频率发生。
 
稳定阶段:
           到了一个开发阶段的尾声,这时测试团队就可以依据以前制定的验收标准,对软件逐项进行验收测试。按照测试计划,各个方面的测试都会宣布“测试完成”- 所有想到的测试都做了,所有问题都发现了。在此阶段,团队也可以把软件发布给外部进行alpha/beta测试。
           一般情况下,测试队伍要把迄今为止发现的所有小强都从新试一次,确保它们都在最后的版本中被清除了,没有一个“回归”出现。
 
发布阶段:
           测试队伍要把尽可能多的测试用例自动化,并为下一个版本的测试工作做好准备。
 

2.2     构建的质量

           1.编译失败
           2.可测/不可测
           3.可用/不可用
 
 

2.3     问题

2.3.1    如果连续几天都不能产生“可测”版本,怎么办?

提示:可以在下一个构建版本中限制签入的数量,只接受那些高等级的修改,在一般的做法中,导致“不可测”的小强的严重性是1,必须在下一个构建开始的时候得到改正。
VC学习笔记

VC学习笔记1:按钮的使能与禁止

用ClassWizard的Member Variables为按钮定义变量,如:m_Button1;

m_Button1.EnableWindow(true); 使按钮处于允许状态
m_Button1.EnableWindow(false); 使按钮被禁止,并变灰显示


VC学习笔记2:控件的隐藏与显示

用CWnd类的函数BOOL ShowWindow(int nCmdShow)可以隐藏或显示一个控件。

例1:
CWnd *pWnd;
pWnd = GetDlgItem( IDC_EDIT1 );    //获取控件指针,IDC_EDIT为控件ID号
pWnd->ShowWindow( SW_HIDE );    //隐藏控件

例2:
CWnd *pWnd;
pWnd = GetDlgItem( IDC_EDIT1 );    //获取控件指针,IDC_EDIT为控件ID号
pWnd->ShowWindow( SW_SHOW );    //显示控件

以上方法常用于动态生成控件,虽说用控件的Create函数可以动态生成控件,但这种控件很不好控制,所以用隐藏、显示方法不失为一种替代手段。


VC学习笔记3:改变控件的大小和位置

用CWnd类的函数MoveWindow()或SetWindowPos()可以改变控件的大小和位置。

void MoveWindow(int x,int y,int nWidth,int nHeight);
void MoveWindow(LPCRECT lpRect);
第一种用法需给出控件新的坐标和宽度、高度;
第二种用法给出存放位置的CRect对象;
例:
CWnd *pWnd;
pWnd = GetDlgItem( IDC_EDIT1 );    //获取控件指针,IDC_EDIT1为控件ID号
pWnd->MoveWindow( CRect(0,0,100,100) );    //在窗口左上角显示一个宽100、高100的编辑控件

SetWindowPos()函数使用更灵活,多用于只修改控件位置而大小不变或只修改大小而位置不变的情况:
BOOL SetWindowPos(const CWnd* pWndInsertAfter,int x,int y,int cx,int cy,UINT nFlags);
第一个参数我不会用,一般设为NULL;
x、y控件位置;cx、cy控件宽度和高度;
nFlags常用取值:
SWP_NOZORDER:忽略第一个参数;
SWP_NOMOVE:忽略x、y,维持位置不变;
SWP_NOSIZE:忽略cx、cy,维持大小不变;
例:
CWnd *pWnd;
pWnd = GetDlgItem( IDC_BUTTON1 );    //获取控件指针,IDC_BUTTON1为控件ID号
pWnd->SetWindowPos( NULL,50,80,0,0,SWP_NOZORDER | SWP_NOSIZE );    //把按钮移到窗口的(50,80)处
pWnd = GetDlgItem( IDC_EDIT1 );
pWnd->SetWindowPos( NULL,0,0,100,80,SWP_NOZORDER | SWP_NOMOVE );    //把编辑控件的大小设为(100,80),位置不变
pWnd = GetDlgItem( IDC_EDIT1 );
pWnd->SetWindowPos( NULL,0,0,100,80,SWP_NOZORDER );    //编辑控件的大小和位置都改变
以上方法也适用于各种窗口。


VC学习笔记4:什么时候设定视中控件的初始尺寸?

我在CFormView的视中加入了一个编辑控件,在运行时使它充满客户区,当窗口改变大小时它也跟着改变。
改变控件尺寸可以放在OnDraw()函数中,也可放在CalcWindowRect()函数中,当窗口尺寸发生变化时,它们都将被执行,且CalcWindowRect()函数先于OnDraw()函数,下例是在CalcWindowRect()函数中修改控件尺寸。
重载VIEW类的CalcWindowRect函数,把设定控件的尺寸的语句加入这个函数中。
例:
void CMyEditView::CalcWindowRect(LPRECT lpClientRect, UINT nAdjustType)
{
    // TODO: Add your specialized code here and/or call the base class

    CFrameWnd *pFrameWnd=GetParentFrame(); //获取框架窗口指针

    CRect rect;
    pFrameWnd->GetClientRect(&rect); //获取客户区尺寸

    CWnd *pEditWnd=GetDlgItem(IDC_MYEDIT); //获取编辑控件指针,IDC_MYEDIT为控件ID号
    pEditWnd->SetWindowPos(NULL,0,0,rect.right,rect.bottom-50,SWP_NOMOVE | SWP_NOZORDER); //设定控件尺寸,bottom-50是为了让出状态条位置。

    CFormView::CalcWindowRect(lpClientRect, nAdjustType);
}


VC学习笔记5:单选按钮控件(Ridio Button)的使用

一、对单选按钮进行分组:
每组的第一个单选按钮设置属性:Group,Tabstop,Auto;其余按钮设置属性Tabstop,Auto。

如:
Ridio1、Ridio2、Ridio3为一组,Ridio4、Ridio5为一组

设定Ridio1属性:Group,Tabstop,Auto
设定Ridio2属性:Tabstop,Auto
设定Ridio3属性:Tabstop,Auto

设定Ridio4属性:Group,Tabstop,Auto
设定Ridio5属性:Tabstop,Auto

二、用ClassWizard为单选控件定义变量,每组只能定义一个。如:m_Ridio1、m_Ridio4。

三、用ClassWizard生成各单选按钮的单击消息函数,并加入内容:

void CWEditView::OnRadio1()
{
    m_Ridio1 = 0;    //第一个单选按钮被选中
}

void CWEditView::OnRadio2()
{
    m_Ridio1 = 1;    //第二个单选按钮被选中
}

void CWEditView::OnRadio3()
{
    m_Ridio1 = 2;    //第三个单选按钮被选中
}

void CWEditView::OnRadio4()
{
    m_Ridio4 = 0;    //第四个单选按钮被选中
}

void CWEditView::OnRadio5()
{
    m_Ridio4 = 1;    //第五个单选按钮被选中
}

四、设置默认按钮:
在定义控件变量时,ClassWizard在构造函数中会把变量初值设为-1,只需把它改为其它值即可。
如:
//{{AFX_DATA_INIT(CWEditView)
m_Ridio1 = 0;    //初始时第一个单选按钮被选中
m_Ridio4 = 0;    //初始时第四个单选按钮被选中
//}}AFX_DATA_INIT


VC学习笔记6:旋转控件(Spin)的使用

当单击旋转控件上的按钮时,相应的编辑控件值会增大或减小。其设置的一般步骤为:
一、在对话框中放入一个Spin控件和一个编辑控件作为Spin控件的伙伴窗口,
设置Spin控件属性:Auto buddy、Set buddy integer、Arrow keys
设置文本控件属性:Number

二、用ClassWizard为Spin控件定义变量m_Spin,为编辑控件定义变量m_Edit,定义时注意要把m_Edit设置为int型。

三、在对话框的OnInitDialog()函数中加入语句:
BOOL CMyDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    
    m_Spin.SetBuddy( GetDlgItem( IDC_EDIT1 ) );    //设置编辑控件为Spin控件的伙伴窗口
    m_Spin.SetRange( 0, 10 );    //设置数据范围为0-10
    return TRUE;
}

四、用ClassWizard为编辑控件添加EN_CHANGE消息处理函数,再加入语句:
void CMyDlg::OnChangeEdit1()
{
    m_Edit = m_Spin.GetPos();    //获取Spin控件当前值
}

OK!


VC学习笔记7:程序结束时保存文件问题

在文档-视图结构中,用串行化自动保存文件在各种VC书上都有介绍。现在的问题是我不使用串行化,而是自己动手保存,当点击窗口的关闭按钮时,如何提示并保存文档。

用ClassWizard在文档类(CxxDoc)中添加函数CanCloseFrame(),再在其中加入保存文件的语句就可以了。
注:要保存的数据应放在文档类(CxxDoc)或应用程序类(CxxApp)中,不要放在视图类中。

例:
//退出程序
BOOL CEditDoc::CanCloseFrame(CFrameWnd* pFrame)
{
    CFile file;
    if(b_Flag)    //b_Flag为文档修改标志,在修改文档时将其置为True
    {
        int t;
        t=::MessageBox(NULL,"文字已经改变,要存盘吗?","警告",
                MB_YESNOCANCEL | MB_ICONWARNING);    //弹出提示对话框
        if(t==0 || t==IDCANCEL)
            return false;
        if(t==IDYES)
        {
            CString sFilter="Text File(*.txt)|*.txt||";
            CFileDialog m_Dlg(FALSE,"txt",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,(LPCTSTR)sFilter,NULL);    //定制文件对话框

            int k=m_Dlg.DoModal();    //弹出文件对话框
            if(k==IDCANCEL || k==0)
                return false;
            m_PathName=m_Dlg.GetPathName();    //获取选择的文件路径名
            
            file.Open(m_PathName,CFile::modeCreate | CFile::modeWrite);
            file.Write(m_Text,m_TextLen);    //数据写入文件
            file.Close();
        }
    }
    return CDocument::CanCloseFrame(pFrame);
}


VC学习笔记8:UpdateData()

对于可以接收数据的控件,如编辑控件来说,UpdateData()函数至关重要。当控件内容发生变化时,对应的控件变量的值并没有跟着变化,同样,当控件变量值变化时,控件内容也不会跟着变。
UpdateData()函数就是解决这个问题的。

UpdateData(true);把控件内容装入控件变量
UpdateData(false);用控件变量的值更新控件

如:有编辑控件IDC_EDIT1,对应的变量为字符串m_Edit1,
1、修改变量值并显示在控件中:
m_Edit1 = _T("结果为50");
UpdateData(false);
2、读取控件的值到变量中:
用ClassWizard为IDC_EDIT1添加EN_CHANGE消息处理函数,
void CEditView::OnChangeEdit1()
{
    UpdateData(true);
}
C++开发中常见问题

    1,简述VC6下如何进行程序的调试。

在主菜单"Build"中,有一个Start Build的子菜单,它下面包含了Go菜单(快捷键为F5),选择后,程序将从当前语句进入调试运行,直到遇到断点或程序结束。

将鼠标移动到要调试的代码行,单击鼠标右键选择“Insert/Remove Breakpoint”,或者按下F9,可以在该行上添加断点,此时断点代码行前面出现一个棕色的圈,再次选择将清除断点。进入调试状态后,Debug菜单将取代Build菜单出现在菜单栏中,它下面包含常用的调试操作,如Step Over,单步运行并不跟踪到调用的函数内部;其他还包括Step Into,Step Out, Stop Debugging等调试方法。

    2, 简述在VC6建立的工程中后缀为.cpp,.h,.rc,.dsp,.dsw的文件的作用是什么?

.cpp是源程序代码C++文件

.h是包含函数声明和变量定义的头文件

.rc是定义资源的资源脚本文件

.dsp是工程文件,记录当前工程的有关信息

.dsw是工作区文件,一个工作区可能包含一个或多个工程

    3, 已知一个对话框上有一个编辑框控件,ID为IDC_EDIT1,为其关联了CEdit类型的变量m_edit1,使用两种方法,说明如何改变编辑框内部的文本为"Hello",写出程序代码的片断。

第一种方法:m_edit1.SetSel(0,-1);           

             m_edit1.ReplaceSel("Hello");    

第二种方法:SetWindowText("Hello");      

    4, 简述使用Windows API编写的一个基本的Windows应用程序框架的结构。

Windows API编写的基本应用程序框架至少应该包含程序入口函数WinMain和窗口函数WndProc。在主函数WinMain里面包含窗口类的定义和注册,窗口的创建和显示以及消息循环。

    5, 消息在Windows中的数据类型是什么,它有哪些成员变量,各有什么含义

消息的数据类型是MSG,它是一个结构体,其成员变量主要包括hwnd,表示消息的窗口句柄;message代表消息的类型;wParam和lParam包含消息的附加信息,随不同的消息有所不同。

    6, Windows的鼠标消息的长参数lParam与字参数wParam的含义是什么

鼠标消息的长参数lParam的低字节包含了鼠标光标位置的x坐标值,lParam的高字节包含了鼠标光标位置的y坐标值;字参数wParam内包含了指示当前按下的各种虚键状态的值。

    7, 说明使用一个非模态对话框的注意问题和用到的Windows API函数

使用一个非模态对话框应该注意一定要在样式中包含WS_VISIBLE才能正常显示;创建对话框使用CreateDialog函数;消息循环部分应该使用IsDialogMessage过滤消息;关闭对话框使用函数DestroyWindow。

    8, 简述在MFC应用程序中UpdateData函数的作用及其参数含义与使用场合。

UpdateData只有一个BOOL类型的参数,UpdateData(FALSE)一般用于对话框控件连接的变量值刷新屏幕显示;UpdateData(TRUE)用于获取屏幕数据到对话框控件连接的变量中。

    9, 列举列表框控件能够接受的三个消息类型,并说明其作用

LB_ADDSTRING用于在列表框中加入一项字符串;LB_DIR用于在列表框中列出指定文件;LB_GETTEXT用于获取指定项的文本。

    10, 在一个对话框上添加了三个单选按钮,要使它们之间自动实现互斥,应该注意什么问题,在VC环境下如何操作?

要实现一组单选按钮的自动互斥,应该让它们的控件ID值连续,并设置第一个单选按钮的Group属性,其他的不设。

    11, 简述由一个文档类派生自己的文档类,并实现文档的存取需要哪些步骤。

首先为每一个文档类型从CDocument派生一个相应的文档类;然后为该文档类添加成员变量以保存数据;最后重载Serialize成员函数以实现文档数据的串行化。

    12, 列举视图类(CView)的三个子类,并简要说明其作用。

CScrollView类提供视图的滚动显示;CEditView类支持在视图中的文本编辑操作;CHtmlView类支持在视图中显示和操作html文件。

    13, Visual C++ 6.0如何进入调试状态,在调试状态下能够显示哪些调试窗口,列举三个,其作用分别是什么?

启动调试后,在View菜单的Debug Window子菜单下可以打开一些辅助调试的窗口

Watch:显示察看当前语句和前面语句中变量值的窗口

Call Stack:显示察看调用堆栈的窗口

Memory:显示察看内存中内容的窗口

    14, 说明位图资源的创建及显示过程的步骤,并给出相应的Windows API函数名。

首先定义位图句柄HBITMAP hBitmap;第二步使用LoadBitMap加载位图;第三步,调用CreateCompatibleDC向系统申请内存设备环境句柄,并调用函数SelectObject把位图选入内存设备环境;第四步,调用BitBlt函数将位图从内存设备环境输出到指定的窗口设备环境中,从而实现显示位图。

    15, 如何获取字体句柄从而实现字体的输出,并给出相应的Windows API函数名。

首先定义字体句柄变量HFONT hF;然后调用函数GetStockObject获取系统的字体句柄,或者调用CreateFont得到自定义的字体句柄;最后调用SelectObject把字体句柄选入设备环境。

    16, 列举三种按钮的类型,并说明其作用和创建方法之间的不同之处。

常用的按钮有普通按钮、单选按钮、复选框,和组框。普通按钮作用是帮助用户触发指定动作;单选按钮一般各选项之间存在互斥性;复选框用来显示一组选项供用户选择,各选项之间不存在互斥;组框主要用于把控件分成不同的组并加以说明.

    17, 要使一个静态控件显示一个位图并能接受用户输入,应该注意什么问题。

要使静态控件显示位图,必须设定其风格包含SS_BITMAP,并在创建静态控件窗口,即调用CreateWindow时指定并加载位图;要使静态控件能够接收用户输入,必须设定其风格包含SS_NOTIFY。

c++异常处理
#include<fstream.h>
#include<iostream.h>
#include<stdlib.h>
void main()
{ ifstream source("c:/abc.txt");  //打开文件
 char line[128];
 try //定义异常 
 {if (source.fail())
  throw "txt";  //抛掷异常
 }
 catch(char * s) //定义异常处理
 { cout<<"error opening the file "<<s<<endl;
  exit(1);
 }
 while(!source.eof())
 { source.getline(line, sizeof(line));
  cout<<line<<endl;}
 source.close();
}
///
 
5月19日
Debug
某年,某月,某日。
为某一个大型程序,增加一个大型功能。编译,运行,死机。

跟踪之,居然死在了如下语句:
CString str;
而且还极不稳定,这次调试死在n行,下次调试死在m行。但都是和内存申请有关。(由于程序很大,其中频繁地申请和释放内存,多处使用new和CString)

猜测:一定是内存不够啦,遂在某处调用函数得到当前剩余的物理内存数量并使用MessageBox显示。报告曰:自由物理内存还有100多M。鼠标按下OK键,程序居然不死了。恩???

删除MessageBox()函数—死!加上MessageBox()函数—不死!再删除–死,再加上–不死。晕倒!

捏呆呆郁闷不知道多少时间后,灵光闪烁……把多处的new/delete改写为GlobalAlloc()/GlobalFree(),一切OK。

事后原因分析:使用new和CString,频繁申请,释放内存,一定产生零碎内存块。当使用MessageBox的时候,系统接管程序的运行(因为它在等待着你按OK按纽),它这时候开始回收合并这些零碎的内存块。这样程序就没有问题了。而函数GlobalAlloc()/GlobalFree()本身就有回收合并零碎内存的功能。

友情提示:在频繁使用new,CString的场合,建议把某些(大)数据块的申请用GlobalAlloc替换。

VC实现BMP位图文件结构及平滑缩放
 
 
 

用普通方法显示BMP位图,占内存大,速度慢,在图形缩小时,失真严重,在低颜色位数的设备上显示高颜色位数的图形图形时失真大。本文采用视频函数显示BMP位图,可以消除以上的缺点。

 

一、BMP文件结构

1. BMP文件组成

BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。

2. BMP文件头

BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。

其结构定义如下:

typedef struct tagBITMAPFILEHEADER

{

WORDbfType; // 位图文件的类型,必须为BM

DWORD bfSize; // 位图文件的大小,以字节为单位

WORDbfReserved1; // 位图文件保留字,必须为0

WORDbfReserved2; // 位图文件保留字,必须为0

DWORD bfOffBits; // 位图数据的起始位置,以相对于位图

// 文件头的偏移量表示,以字节为单位

} BITMAPFILEHEADER;

3. 位图信息头

BMP位图信息头数据用于说明位图的尺寸等信息。

typedef struct tagBITMAPINFOHEADER{

DWORD biSize; // 本结构所占用字节数

LONGbiWidth; // 位图的宽度,以像素为单位

LONGbiHeight; // 位图的高度,以像素为单位

WORD biPlanes; // 目标设备的级别,必须为1

WORD biBitCount// 每个像素所需的位数,必须是1(双色),

// 4(16色),8(256色)或24(真彩色)之一

DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),

// 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一

DWORD biSizeImage; // 位图的大小,以字节为单位

LONGbiXPelsPerMeter; // 位图水平分辨率,每米像素数

LONGbiYPelsPerMeter; // 位图垂直分辨率,每米像素数

DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数

DWORD biClrImportant;// 位图显示过程中重要的颜色数

} BITMAPINFOHEADER;

4. 颜色表

颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:

typedef struct tagRGBQUAD {

BYTErgbBlue;// 蓝色的亮度(值范围为0-255)

BYTErgbGreen; // 绿色的亮度(值范围为0-255)

BYTErgbRed; // 红色的亮度(值范围为0-255)

BYTErgbReserved;// 保留,必须为0

} RGBQUAD;

颜色表中RGBQUAD结构数据的个数有biBitCount来确定:

当biBitCount=1,4,8时,分别有2,16,256个表项;

当biBitCount=24时,没有颜色表项。

位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:

typedef struct tagBITMAPINFO {

BITMAPINFOHEADER bmiHeader; // 位图信息头

RGBQUAD bmiColors[1]; // 颜色表

} BITMAPINFO;

5. 位图数据

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:

当biBitCount=1时,8个像素占1个字节;

当biBitCount=4时,2个像素占1个字节;

当biBitCount=8时,1个像素占1个字节;

当biBitCount=24时,1个像素占3个字节;

Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,一个扫描行所占的字节数计算方法:

DataSizePerLine= (biWidth* biBitCount+31)/8;

// 一个扫描行所占的字节数

DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数

位图数据的大小(不压缩情况下):

DataSize= DataSizePerLine* biHeight;

二、BMP位图一般显示方法

1. 申请内存空间用于存放位图文件 GlobalAlloc(GHND,FileLength);

2. 位图文件读入所申请内存空间中 LoadFileToMemory( mpBitsSrc,mFileName);

3. 在OnPaint等函数中用创建显示用位图

用CreateDIBitmap()创建显示用位图,用CreateCompatibleDC()创建兼容DC, 用SelectBitmap()选择显示位图。

4. 用BitBlt或StretchBlt等函数显示位图

5. 用DeleteObject()删除所创建的位图

以上方法的缺点是: 1)显示速度慢; 2) 内存占用大; 3) 位图在缩小显示时图形失真大,(可通过安装字体平滑软件来解决); 4) 在低颜色位数的设备上(如256显示模式)显示高颜色位数的图形(如真彩色)图形失真严重。

三、BMP位图缩放显示

用DrawDib视频函数来显示位图,内存占用少,速度快,而且还可以对图形进行淡化(Dithering)处理。淡化处理是一种图形算法,可以用来在一个支持比图像所用颜色要少的设备上显示彩色图像。BMP位图显示方法如下:

1. 打开视频函数DrawDibOpen(),一般放在在构造函数中

2. 申请内存空间用于存放位图文件

GlobalAlloc(GHND,FileLength);

3. 位图文件读入所申请内存空间中

LoadFileToMemory( mpBitsSrc,mFileName);

4. 在OnPaint等函数中用DrawDibRealize(),DrawDibDraw()显示位图

5. 关闭视频函数DrawDibClose(),一般放在在析构函数中

以上方法的优点是: 1)显示速度快; 2) 内存占用少; 3) 缩放显示时图形失真小,4) 在低颜色位数的设备上显示高颜色位数的图形图形时失真小; 5) 通过直接处理位图数据,可以制作简单动画。
四、CViewBimap类编程要点

 

1. 在CViewBimap类中添加视频函数等成员

HDRAWDIB m_hDrawDib; // 视频函数

HANDLEmhBitsSrc; // 位图文件句柄(内存)

LPSTR mpBitsSrc; // 位图文件地址(内存)

BITMAPINFOHEADER *mpBitmapInfo; // 位图信息头

2. 在CViewBimap类构造函数中添加打开视频函数

m_hDrawDib= DrawDibOpen();

3. 在CViewBimap类析构函数中添加关闭视频函数

if( m_hDrawDib != NULL)

{

DrawDibClose( m_hDrawDib);

m_hDrawDib = NULL;

}

4. 在CViewBimap类图形显示函数OnPaint中添加GraphicDraw()

voidCViewBitmap::OnPaint()

{

CPaintDC dc(this); // device context for painting

GraphicDraw( );

}

voidCViewBitmap::GraphicDraw( void )

{

CClientDC dc(this); // device context for painting

BITMAPFILEHEADER *pBitmapFileHeader;

ULONG bfoffBits= 0;

CPoint Wid;

// 图形文件名有效 (=0 BMP)

if( mBitmapFileType < ID_BITMAP_BMP ) return;

// 图形文件名有效 (=0 BMP)

// 准备显示真彩位图

pBitmapFileHeader= (BITMAPFILEHEADER *) mpBitsSrc;

bfoffBits= pBitmapFileHeader->bfOffBits;

// 使用普通函数显示位图

if( m_hDrawDib == NULL || mDispMethod == 0)

{

HBITMAP hBitmap=::CreateDIBitmap(dc.m_hDC,

mpBitmapInfo, CBM_INIT, mpBitsSrc+bfoffBits,

(LPBITMAPINFO) mpBitmapInfo,DIB_RGB_COLORS);

// 建立位图

HDC hMemDC=::CreateCompatibleDC(dc.m_hDC);// 建立内存

HBITMAP hBitmapOld= SelectBitmap(hMemDC, hBitmap); // 选择对象

// 成员CRect mDispR用于指示图形显示区域的大小.

// 成员CPoint mPos用于指示图形显示起始位置坐标.

if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))

mPos.x= mpBitmapInfo->biWidth - mDispR.Width() ;

if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))

mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();

if( mPos.x < 0 ) mPos.x= 0;

if( mPos.y < 0 ) mPos.y= 0;

if( mFullViewTog == 0)

{

// 显示真彩位图

::BitBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),

hMemDC,mPos.x,mPos.y, SRCCOPY);

} else {

::StretchBlt(dc.m_hDC,0,0, mDispR.Width(), mDispR.Height(),

hMemDC,0,0, mpBitmapInfo- >biWidth, mpBitmapInfo-

>biHeight, SRCCOPY);

}

// 结束显示真彩位图

::DeleteObject(SelectObject(hMemDC,hBitmapOld));

// 删 除 位 图

} else {

// 使用视频函数显示位图

if( mPos.x > (mpBitmapInfo- >biWidth - mDispR.Width() ))

mPos.x= mpBitmapInfo- >biWidth - mDispR.Width() ;

if( mPos.y > (mpBitmapInfo- >biHeight- mDispR.Height()))

mPos.y= mpBitmapInfo- >biHeight- mDispR.Height();

if( mPos.x < 0 ) mPos.x= 0;

if( mPos.y < 0 ) mPos.y= 0;

// 显示真彩位图

DrawDibRealize( m_hDrawDib, dc.GetSafeHdc(), TRUE);

if( mFullViewTog == 0)

{

Wid.x= mDispR.Width();

Wid.y= mDispR.Height();

// 1:1 显示时, 不能大于图形大小

if( Wid.x > mpBitmapInfo- >biWidth )

Wid.x = mpBitmapInfo- >biWidth;

if( Wid.y > mpBitmapInfo- >biHeight)

Wid.y = mpBitmapInfo- >biHeight;

DrawDibDraw( m_hDrawDib, dc.GetSafeHdc()

, 0, 0, Wid.x, Wid.y,

mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),

mPos.x, mPos.y, Wid.x, Wid.y, DDF_BACKGROUNDPAL);

} else {

DrawDibDraw( m_hDrawDib, dc.GetSafeHdc(),

0, 0, mDispR.Width(), mDispR.Height(),

mpBitmapInfo, (LPVOID) (mpBitsSrc+bfoffBits),

0, 0, mpBitmapInfo- >biWidth, mpBitmapInfo- >biHeight,

DDF_BACKGROUNDPAL);

}

}

return;

}

五、使用CViewBimap类显示BMP位图

1. 在Visual C++5.0中新建一个名称为mymap工程文件,类型为MFC AppWizard[exe]。在编译运行通过后,在WorkSpace(如被关闭,用Alt_0打开)点击ResourceView,点击Menu左侧的+符号展开Menu条目,双击IDR_MAINFRAME条目,进入菜单资源编辑,在'“查看(V)”下拉式菜单(英文版为View下拉式菜单)的尾部添加“ViewBitmap”条目,其ID为ID_VIEW_BITMAP。

2. 在Visual C++5.0中点击下拉式菜单Project- >Add To project- >Files...,将Bitmap0.h和Bitmap0.cpp添加到工程文件中。

3. 在Visual C++5.0中按Ctrl_W进入MFC ClassWizard,选择类名称为CMainFrame,ObjectIDs: ID_VIEW_BITMAP,Messages选择Command,然后点击Add Fucction按钮,然后输入函数名为OnViewBimap。在添加OnViewBimap后,在Member functions: 中点击OnViewBimap条目,点击Edit Code按钮编辑程序代码。代码如下:

void CMainFrame::OnViewBitmap()

{

// TODO: Add your command handler code here

CViewBitmap *pViewBitmap= NULL;

pViewBitmap= new CViewBitmap( "BITMAP.BMP", this);

pViewBitmap- >ShowWindow( TRUE);

}

并在该程序的头部添加#include "bitmap0.h",然后编译运行。

4. 找一个大一点的真彩色的BMP位图,将它拷贝到BITMAP.BMP中。

5. 运行时,点击下拉式菜单“查看(V)- >ViewBitmap”(英文版为View- > ViewBitmap)即可显示BITMAP.BMP位图。

六、CViewBimap类功能说明

1. 在客户区中带有水平和垂直滚动条。在位图大小大于显示客户区时,可以使用滚动条;在位图大小小于显示客户区或全屏显示时,滚动条无效。

2. 在客户区中底部带有状态条。状态条中的第一格为位图信息,第二格为位图显示方法,可以是使用普通函数或使用视频函数。在第二格区域内点击鼠标,可在两者之间接换。第三格为位图显示比例,可以是1;1显示或全屏显示。在第三格区域内点击鼠标,可在两者之间接换。在全屏显示时,如果位图比客户区小,则对位图放大; 如果位图比客户区大,则对位图缩小。

3. 支持文件拖放功能。可以从资源管理器中拖动一个位图文件到客户区,就可以显示该位图。

程序调试通过后,可以找一个较大的真彩色位图或调整客户区比位图小,在全屏显示方式下,比较使用普通函数与使用视频函数的差别。可以看出,位图放大时两者差别不大,但在位图缩小时,两者差别明显; 使用视频函数时位图失真小,显示速度快。

还可以从控制面板中将屏幕显示方式从真彩色显示模式切换到256色显示模式,再比较使用普通函数与使用视频函数显示同一个真彩色位图的差别。现在可以体会到使用视频函数的优越性了吧。

在全屏显示时,位图的xy方向比例不相同,如要保持相同比例,可在显示程序中加以适当调整即可,读者可自行完成.

 

 
  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 雷达目标跟踪是指利用雷达技术获取目标的运动状态,并通过计算机算法在雷达图像中对目标进行跟踪。对于目标跟踪的实现,VC编程是一种常用的编程语言,可用于实现图像处理和计算机视觉算法。 VC编程是一种C++语言的变体,拥有许多图形操作函数和库,使其具有方便快捷的图像处理能力。在雷达目标跟踪方面,VC可以对雷达获得的目标信息进行处理,提取目标运动状态信息,并进行目标跟踪。 VC编程还提供了强大的计算机视觉算法库,包括卡尔曼滤波器、粒子滤波器、背景减除等算法。这些算法可以用于提高雷达目标跟踪的精度和实时性。 总之,VC编程是一种强大的编程语言,可以为雷达目标跟踪提供可靠的技术支持。通过VC编程实现雷达目标跟踪,可以提高目标检测和跟踪的精度和效率,为实现精确的雷达指挥控制提供有力保障。 ### 回答2: 雷达目标跟踪是指利用雷达技术对目标的运动状态进行跟踪,以实现目标的识别、定位和追踪等任务。在雷达目标跟踪中,可以采用各种算法和技术,如卡尔曼滤波、粒子滤波、神经网络等,来实现目标的跟踪。 其中,vc编程是一种常用的编程语言,可以应用于实现雷达目标跟踪功能。通过vc编程语言,可以实现数据处理、算法设计、界面设计等功能,使得雷达目标跟踪系统具有更高的可靠性和灵活性。在vc编程中,常用的开发平台包括VC++、MFC、QT等,可以根据具体需求选择合适的平台进行开发。 在实现雷达目标跟踪的过程中,vc编程可以帮助开发人员将雷达数据处理、目标检测、目标识别、追踪等功能进行集成,同时还能通过图形化界面展示和处理数据。此外,通过优化算法和程序设计,还可以提高系统的运行效率和稳定性,满足不同应用场景的需求。 综上所述,雷达目标跟踪vc编程是一个精细和复杂的过程,需要综合运用雷达技术、算法设计和vc编程等知识。只有通过对相关技术熟练掌握和实践,才能构建出高效、可靠和可控的雷达目标跟踪系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值