• 60.在用户环境中如何确定系统显示元素的颜色?
    调用SDK函数GetSysColor可以获取一个特定显示元素的颜色。下例说明了如何在MFC函数CMainFrameWnd:: OnNcPaint中调用该函数设置窗口标题颜色。

    void CMiniFrameWnd:: OnNcPaint ()
    {

    dc.SetTextColor (:: GetSysColor (m_bActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT))

    61.如何查询和设置系统参数?
    在Windows 3.1 SDK中介绍过SDK函数SystemParametersInfo,调用该函数可以查询和设置系统参数,诸如按键的重复速率设置、鼠标双击延迟时间、图标字体以及桌面覆盖位图等等。

    //Create a font that is used for icon titles.
    LOGFONT stFont
    ∶: SystemParametersInfo (SPIF_GETICONTITLELOGFONT, sizeof (LOGFONT), &stFont, SPIF_SENDWININICHANGE)
    m_font.CreateFontIndirect (&stFont)

    //Change the wallpaper to leaves.bmp.
    ∶ : SystemParametersInfo (SPI_SETDESKWALLPAPER, 0, _T (" forest.bmp"), SPIF_UPDATEINIFILE)
    62.如何使用一个预定义的Windows光标?
    调用CWinApp:: LoadStandardCursor并传送光标标识符。
    BOOL CSampleDialog:: OnSetCursor (CWnd* pWnd,
    UINT nHitTest, UINT
    message)
    {
    //Display wait cursor if busy.
    if (m_bBusy)
    {
    SetCursor (AfxGetApp () ->LoadStandardCursor (IDC_WAIT))
    return TRUE
    }

    return CDialog:: OnSetCursor (pWnd. nHitTest,message)
    }
    63.如何确定当前屏幕分辨率?
    调用SDK函数GetSystemMetrics,该函数可以检索有关windows显示信息,诸如标题大小、边界大小以及滚动条大小等等。

    //Initialize CSize object with screen size.
    CSize sizeScreen (GetSystemMetrics (SM_CXSCREEN),
    GetSystemMetrics (SM_CYSCREEN))
    64.如何检索原先的Task Manager应用程序使用的任务列表?
    原先的Task Manager应用程序显示顶层窗口的列表。为了显示该列表,窗口必须可见、包含一个标题以及不能被其他窗口拥有。调用CWnd:: GetWindow可以检索顶层窗口的列表,调用IsWindowVisible、GetWindowTextLength以及GetOwner可以确定窗口是否应该在列表中。下例将把TaskManager窗口的标题填充到列表中。

    void GetTadkList (CListBox&list)
    {
    CString strCaption
    //Caption of window.

    list.ResetContent ()
    //Clear list box.

    //Get first Window in window list.
    ASSERT_VALID (AfxGetMainWnd ())
    CWnd* pWnd=AfxGetMainWnd () ->GetWindow (GW_HWNDFIRST)

    //Walk window list.
    while (pWnd)
    {
    // I window visible, has a caption, and does not have an owner?
    if (pWnd ->IsWindowVisible()
    && pWnd ->GetWindowTextLength ()
    &&! pWnd ->GetOwner ())
    {

    //Add caption o window to list box.

    pWnd ->GetWindowText (strCaption)

    list.AddString (strCaption)
    }
    //Get next window in window list.
    pWnd=pWnd ->GetWindow(GW_HWNDNEXT)
    }
    }
    65.如何确定Windows和Windows系统目录?
    有两个SDK函数可以完成该功能。GetWindowsDirectory和GetSystemDirectory,下例说明了如何使用这两个函数:

    TCHAR szDir [MAX_PATH]
    //Get the full path of the windows directory.
    ∶ : GetWindowsDirectory (szDir, MAX_PATH)
    TRACE ("Windows directory %s\n", szDir)
    //Get the full path of the windows system directory.
    ∶ : GetSystemDirectory (szDir, MAX_PATH)
    TRACE ("Windows system directory %s\n", szDir)
    66.在哪儿创建临文件?
    调用SDK函数GetTemPath可以确定临时文件的目录,该函数首先为临时路径检测TMP环境变量:如果没有指定TMP,检测TMP环境变量,然后返回到当前目录。下例说明了如何创建一个临时文件。


    //get unique temporary file.
    CString strFile
    GetUniqueTempName (strFile)
    TRY
    {
    //Create file and write data.Note that file is closed
    //in the destructor of the CFile object.
    CFile file (strFile,CFile ::modeCreate | CFile:: modeWrite)

    //write data
    }

    CATCH (CFileException, e)
    {
    //error opening file
    }
    END_CATCH


    Void GetuniqueTempName (CString& strTempName)
    {
    //Get the temporary files directory.
    TCHAR szTempPath [MAX_PATH]
    DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath)
    ASSERT (dwResult)

    //Create a unique temporary file.
    TCHAR szTempFile [MAX_PATH]
    UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempfile)
    ASSERT (nResult)

    strTempName=szTempFile
    }
    67.如何访问桌面窗口?
    静态函数CWnd:: GetDesktopWindow 返回桌面窗口的指针。下例说明了MFC函数CFrameWnd::BeginModalStae是如何使用该函数进入内部窗口列表的。

    void CFrameWnd::BeginModalSt
  • 31.如何创建和使用无模式对话框?
    MFC将模式和无模式对话封装在同一个类中,但是使用无模式对话需要几个对话需要几个额处的步骤。首先,使用资源编辑器建立对话资源并使用ClassWizard创建一个CDialog的派生类。模式和无模式对话的中止是不一样的:模式对话通过调用CDialog : : EndDialog 来中止,无模式对话则是调用CWnd: : DestroyWindow来中止的,函数CDialog : : OnOK和CDialog : : OnCancel调用EndDialog ,所以需要调用DestroyWindow并重置无模式对话的函数。
    void CSampleDialog : : OnOK ( )
    {
    // Retrieve and validate dialog data .
    if (! UpdateData (TRUE) )
    {
    // the UpdateData rountine
    will set focus to correct item TRACEO (" UpdateData failed during dialog termination .\n")
    return
    }

    //Call DestroyWindow instead of EndDialog .
    DestroyWindow ( )
    }

    void CSampleDialog : : OnCancel ( )
    {
    //Call DestroyWindow instead of EndDialog .
    DestroyWindow ( )
    }

    其次,需要正确删除表示对话的C++对象。对于模式对来说,这很容易,需要创建函数返回后即可删除C++对象;无模式对话不是同步的,创建函数调用后立即返回,因而用户不知道何时删除C++对象。撤销窗口时工作框调用CWnd : : PostNcDestroy,可以重置该函数并执行清除操作,诸如删除this指针。
    void CSampleDialog : : PostNcDestroy ( )
    {
    // Declete the C++ object that represents this dialog.
    delete this

    最后,要创建无模式对话。可以调用CDialog : : DoModal创建一个模式对放,要创建一个无模式对话则要调用CDialog: : Create。下面的例子说明 了应用程序是如何创建无模式对话的: 象;无模式对话不是同步的,创建函数调用后立即返回,
    void CMainFrame : : OnSampleDialog ( )
    {
    //Allocate a modeless dialog object .
    CSampleDilog * pDialog =new CSampleDialog
    ASSERT_VALID (pDialog) Destroy ( )

    //Create the modeless dialog . represents this dialog.
    BOOL bResult = pDialog -> Creste (IDD_IDALOG)
    ASSERT (bResult )
    }
    32.如何在对话框中显示一个位图?
    这要归功于Win 32先进的静态控件和Microsoft的资源编辑器,在对话框中显示位图是很容易的, 只需将图形控件拖到对话中并选择适当属性即可,用户也可以显示图标、位图以及增强型元文件。
    33.如何改变对话或窗体视窗的背景颜色?
    调用CWinApp : : SetDialogBkColor可以改变所有应用程序的背景颜色。第一个参数指定了背景颜色,第二个参数指定了文本颜色。下例将应用程序对话设置为蓝色背景和黄色文本。
    BOOL CSampleApp : : InitInstance ( )
    {


    //use blue dialog with yellow text .
    SetDialogBkColor (RGB (0, 0, 255 ), RGB ( 255 ,255 , 0 ) )


    }

    需要重画对话(或对话的子控件)时,Windows向对话发送消息WM_CTLCOLOR,通常用户可以让Windows选择绘画背景的刷子,也可重置该消息指定刷子。下例说明了创建一个红色背景对话的步骤。

    首先,给对话基类增加一人成员变量
    CBursh :class CMyFormView : public CFormView
    {


    private :
    CBrush m_ brush // background brush


    }

    其次, 在类的构造函数中将刷子初始化为所需要的背景颜色。
    CMyFormView : : CMyFormView ( )
    {
    // Initialize background brush .
    m_brush .CreateSolidBrush (RGB ( 0, 0, 255) )
    }

    最后,使用ClassWizard处理WM_CTLCOLOR消息并返回一个用来绘画对话背景的刷子句柄。注意:由于当重画对话控件时也要调用该函数,所以要检测nCtlColor参量。
    HBRUSH CMyFormView : : OnCtlColor (CDC* pDC , CWnd*pWnd , UINT nCtlColor
    )

    {
    // Determine if drawing a dialog box . If we are, return +handle to
    //our own background brush . Otherwise let windows handle it .
    if (nCtlColor = = CTLCOLOR _ DLG )
    return (HBRUSH) m_brush.GetSafeHandle ( )
    return CFormView : : OnCtlColor (pDC, pWnd , nCtlColor
    )
    }
    34.如何获取一个对话控件的指针?
    有两种方法。其一,调用CWnd: : GetDlgItem,获取一个CWnd*指针调用成员函数。下例调用GetDlgItem,将返回值传给一个CSpinButtonCtrl*以便调用CSpinButtonCtrl : : SetPos 函数:
    BOOL CSampleDialog : : OnInitDialog ( )
    {
    CDialog : : OnInitDialog ( )

    //Get pointer to spin button .
    CSpinButtonCtrl * pSpin - ( CSpinButtonCtrl *) GetDlgItem(IDC_SPIN)
    ASSERT _ VALID (pSpin)
    //Set spin button’s default position .
    pSpin -> SetPos (10)

    return TRUE
    }

    其二, 可以使用ClassWizard将控件和成员变量联系起来。在ClassWizard中简单地选择Member Variables标签,然后选择Add Variable …按钮。如果在对话资源编辑器中,按下Ctrl键并双击控件即可转到Add Member Variable对话。
  • 1.如何获取应用程序的实例句柄?
    应用程序的实例句柄保存在CWinApp m_hInstance 中,可以这么调用AfxGetInstancdHandle获得句柄。
    Example: HANDLE hInstance=AfxGetInstanceHandle()
    2.如何通过代码获得应用程序主窗口的指针?
    主窗口的指针保存在CWinThread::m_pMainWnd中,调用AfxGetMainWnd实现。
    AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED)
    //使程序最大化
    3.如何在程序中获得其他程序的图标?
    两种方法:
    (1) SDK函数 SHGetFileInfo 或使用 ExtractIcon获得图标资源的 handle;
    (2) SDK函数 SHGetFileInfo 获得有关文件的很多信息,如大小图标,属性, 类型等。
    Example(1):
    在程序窗口左上角显示 NotePad图标.
    void CSampleView:
    OnDraw(CDC * pDC)
    {
    if( :: SHGetFileInfo(_T("c:\\pwin95\\notepad.exe"),0,
    &stFileInfo,sizeof(stFileInfo),SHGFI_ICON))
    {
    pDC ->DrawIcon(10,10,stFileInfo.hIcon)
    }
    }
    Example(2):同样功能,Use ExtractIcon Function
    void CSampleView:: OnDraw(CDC *pDC)
    {
    HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T
    ("NotePad.exe"),0)
    if (hIcon &&hIcon!=(HICON)-1)
    pDC->DrawIcon(10,10,hIcon)
    }
    说明: 获得notepad.exe的路径正规上来说用GetWindowsDirectory函数得到, 如果是调用 win95下的画笔,应该用访问注册表的方法获得其路径,要作成一个比较考究的程序,考虑应该全面点.
    4.如何编程结束应用程序?
    这是个很简单又是编程中经常要遇到的问题。
    向窗口发送 WM_CLOSE消息,调用 CWnd::OnClose成员函数.允许对用户提示是否保存修改过的数据.
    Example: AfxGetMainWindow()->SendMessage(WM_CLOSE)

    还可以创建一个自定义的函数 Terminate Window
    void Terminate Window(LPCSTR pCaption)
    {
    CWnd *pWnd=Cwnd::FindWindow(NULL,pCaption)

    if (pWnd)

    pWnd ->SendMessage(WM_CLOSE)
    }

    说明: FindWindow函数不是提倡的做法,因为它无法处理标题栏自动改变,比如我们要检测 Notepad是不是已运行而事先不知道Notepad的标题栏,这时FindWindow就无能为力了,可以通过枚举 windows任务列表的办法来实现。在机械出版社"Windows 95 API开发人员指南"一书有比较详细的介绍,这里就不再多说乐。
    5.怎样加载其他的应用程序?
    三个SDK函数 winexec, shellexecute,createprocess可以使用。
    WinExec最简单,两个参数,前一个指定路径,后一个指定显示方式.后一个参数值得说一下,比如泥用 SW_SHOWMAXMIZED方式去加载一个无最大化按钮的程序,就是Neterm,calc等等,就不会出现正常的窗体,但是已经被加到任务列表里了。

    ShellExecute较 WinExex灵活一点,可以指定工作目录,下面的Example就是直接打开 c:\temp\1.txt,而不用加载与 txt文件关联的应用程序,很多安装程序完成后都会打开一个窗口,来显示Readme or Faq,我猜就是这么作的啦.

    ShellExecute(NULL,NULL,_T("1.txt"),NULL,_T("c:\\temp"),SW_SHOWMAXMIZED)

    CreateProcess最复杂,一共有十个参数,不过大部分都可以用NULL代替,它可以指定进程的安全属性,继承信息,类的优先级等等.来看个很简单的Example:
    STARTUPINFO stinfo
    //启动窗口的信息
    PROCESSINFO procinfo //进程的信息

    CreateProcess(NULL,_T("notepad.exe"),NULL,NULL.FALSE,
    NORMAL_PRIORITY_

    CLASS,NULL,NULL, &stinfo,&procinfo)
    6.确定应用程序的路径
    Use GetModuleFileName 获得应用程序的路径,然后去掉可执行文件名。
    Example:
    TCHAR
    exeFullPath[MAX_PATH] // MAX_PATH在API中定义了吧,好象是
    128
    GetModuleFileName(NULL,exeFullPath,MAX_PATH)
    7.获得各种目录信息
    Windows目录: Use "GetWindowsDirectory"
    Windows下的system目录: Use "GetSystemDirectory"
    temp目录: Use "GetTempPath"
    当前目录: Use "GetCurrentDirectory"

    请注意前两个函数的第一个参数为目录变量名,后一个为缓冲区后两个相反。
    8.如何自定义消息
    (1) 手工定义消息,可以这么写
    #define WM_MY_MESSAGE(WM_USER+100),
    MS 推荐的至少是 WM_USER+100

    (2)写消息处理函数,用
    WPARAM,LPARAM返回LRESULT.
    LRESULT CMainFrame::OnMyMessage(WPARAM wparam,LPARAM lParam)

    {
    temp目录: Use "GetTempPath"
    //加入你的处理函数 irectory"
    }
    9.如何改变窗口的图标?
    向窗口发送 WM_SECTION消息。
    Example:
    HICON hIcon=AfxGetApp() ->LoadIcon(IDI_ICON)
    ASSERT(hIcon)
    AfxGetMainWnd() ->SendMessage(WM_SECTION,TRUE,(LPARAM)hIcon)
    10.如何改变窗口的缺省风格?
    重载 CWnd:: PreCreateWindow 并修改CREATESTRUCT结构来指定窗口风格和其他创建信息.
    Example: Delete "Max" Button and Set Original
    Window’s Position and Size

    BOOL CMainFrame:: PreCreateWindow
    (CREATESTRUCT &cs)
    {
    cs.style &=~WS_MAXINIZEMOX

    cs.x=cs.y=0
    cs.cx=GetSystemMet
  • // DynamicCreateButtonDlg.h : header file
    //

    #if !defined(AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_)
    #define AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    #include "TextBtn.h"

    /////////////////////////////////////////////////////////////////////////////
    // CDynamicCreateButtonDlg dialog

    class CDynamicCreateButtonDlg : public CDialog
    {
    // Construction
    public:
    CDynamicCreateButtonDlg(CWnd* pParent = NULL); // standard constructor

    // Dialog Data
    //{{AFX_DATA(CDynamicCreateButtonDlg)
    enum { IDD = IDD_DYNAMICCREATEBUTTON_DIALOG };
    int m_nCntofDBtn;
    //}}AFX_DATA

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CDynamicCreateButtonDlg)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    //}}AFX_VIRTUAL

    // Implementation
    protected:
    HICON m_hIcon;

    // Generated message map functions
    //{{AFX_MSG(CDynamicCreateButtonDlg)
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    afx_msg void On_Btn_Create();
    afx_msg void On_Btn_Erase();
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    private:
    CTextBtn* m_btn;
    };

    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.

    #endif // !defined(AFX_DYNAMICCREATEBUTTONDLG_H__BF0B7061_9F30_484D_92DA_97435414E97A__INCLUDED_)

    // DynamicCreateButtonDlg.cpp : implementation file
    //

    #include "stdafx.h"
    #include "DynamicCreateButton.h"
    #include "DynamicCreateButtonDlg.h"

    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif

    #define ID_MYBTN 1978

    /////////////////////////////////////////////////////////////////////////////
    // CAboutDlg dialog used for App About

    class CAboutDlg : public CDialog
    {
    public:
    CAboutDlg();

    // Dialog Data
    //{{AFX_DATA(CAboutDlg)
    enum { IDD = IDD_ABOUTBOX };
    //}}AFX_DATA

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CAboutDlg)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    //}}AFX_VIRTUAL

    // Implementation
    protected:
    //{{AFX_MSG(CAboutDlg)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
    };

    CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
    {
    //{{AFX_DATA_INIT(CAboutDlg)
    //}}AFX_DATA_INIT
    }

    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CAboutDlg)
    //}}AFX_DATA_MAP
    }

    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
    //{{AFX_MSG_MAP(CAboutDlg)
    // No message handlers
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    /////////////////////////////////////////////////////////////////////////////
    // CDynamicCreateButtonDlg dialog

    CDynamicCreateButtonDlg::CDynamicCreateButtonDlg(CWnd* pParent /*=NULL*/)
    : CDialog(CDynamicCreateButtonDlg::IDD, pParent)
    {
    //{{AFX_DATA_INIT(CDynamicCreateButtonDlg)
    m_nCntofDBtn = 0;
    //}}AFX_DATA_INIT
    // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
    }

    void CDynamicCreateButtonDlg::DoDataExchange(CDataExchange* pDX)
    {
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CDynamicCreateButtonDlg)
    DDX_Text(pDX, IDC_EDIT1, m_nCntofDBtn);
    //}}AFX_DATA_MAP
    }

    BEGIN_MESSAGE_MAP(CDynamicCreateButtonDlg, CDialog)
    //{{AFX_MSG_MAP(CDynamicCreateButtonDlg)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_BN_CLICKED(IDC_BUTTON1, On_Btn_Create)
    ON_BN_CLICKED(IDC_BUTTON2, On_Btn_Erase)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    /////////////////////////////////////////////////////////////////////////////
    // CDynamicCreateButtonDlg message handlers

    BOOL CDynamicCreateButtonDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_
  • #ifndef GLOBAL_H
    #define GLOBAL_H

    #ifndef BIF_NEWDIALOGSTYLE
    #define BIF_NEWDIALOGSTYLE 0x0040
    #endif

    #ifndef BIF_USENEWUI
    #define BIF_USENEWUI 0x0050
    #endif

    // 初始化文件夹设定用的回调函数
    int CALLBACK _SHBrowseForFolderCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
    {
    if(uMsg == BFFM_INITIALIZED)
    ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);

    return 0;
    }


    bool g_fSelectFolderDlg(CString* lpstrFolder,CString strIniFolder,bool bAvailNewFolder)
    {
    bool ret;
    char lpszPath[MAX_PATH];
    LPMALLOC lpMalloc;
    BROWSEINFO sInfo;
    LPITEMIDLIST lpidlRoot;
    LPITEMIDLIST lpidlBrowse;

    if(lpstrFolder == NULL)
    return false;

    if(::SHGetMalloc(&lpMalloc) != NOERROR)
    return false;

    ret = false;

    if(strIniFolder != "")
    {
    if(strIniFolder.Right(1) == "\\")
    strIniFolder = strIniFolder.Left(strIniFolder.GetLength() - 1); //删除末尾的"\\"

    }

    ::SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &lpidlRoot); //取得选定的文件夹名


    ::ZeroMemory(&sInfo, sizeof(BROWSEINFO));
    sInfo.pidlRoot = lpidlRoot;
    sInfo.pszDisplayName = lpszPath;
    sInfo.lpszTitle = _T("僼僅儖僟偺慖戰");
    sInfo.ulFlags = BIF_RETURNONLYFSDIRS;
    if(bAvailNewFolder == true)
    sInfo.ulFlags |= BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_USENEWUI;
    sInfo.lpfn = _SHBrowseForFolderCallbackProc;
    sInfo.lParam = (LPARAM)strIniFolder.GetBuffer(0);

    lpidlBrowse = ::SHBrowseForFolder(&sInfo); //显示文件夹选择对话框

    if(lpidlBrowse != NULL)
    {
    if(::SHGetPathFromIDList(lpidlBrowse,lpszPath)) //取得文件夹名
    {
    *lpstrFolder = "";
    *lpstrFolder = lpszPath;

    if(*lpstrFolder != "")
    {
    if(lpstrFolder->Right(1) != "\\")
    *lpstrFolder += "\\"; //在末尾时附加"\\"
    }
    }

    ret = true;
    }

    if(lpidlBrowse != NULL)
    ::CoTaskMemFree(lpidlBrowse);
    if(lpidlRoot != NULL)
    ::CoTaskMemFree(lpidlRoot);

    lpMalloc->Release();

    return ret;
    }

    #endif

    void CSHFolderDlg::OnClick_SHBrowseForFolder()
    {
    if(g_fSelectFolderDlg(&m_strFolder,m_strFolder,false))
    {
    UpdateData(false);
    }
    }
  • #if !defined(AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_)
    #define AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // AutoCompleteCombo.h : header file
    //

    /////////////////////////////////////////////////////////////////////////////
    // CAutoCompleteCombo window

    class CAutoCompleteCombo : public CComboBox
    {
    // Construction
    public:
    CAutoCompleteCombo();

    // Attributes
    public:

    // Operations
    public:

    // Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CAutoCompleteCombo)
    public:
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    //}}AFX_VIRTUAL

    // Implementation
    public:
    virtual ~CAutoCompleteCombo();

    // Generated message map functions
    protected:
    //{{AFX_MSG(CAutoCompleteCombo)
    afx_msg void OnEditupdate();
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
    private:
    BOOL m_bAutoComplete;
    };

    /////////////////////////////////////////////////////////////////////////////

    //{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.

    #endif // !defined(AFX_AUTOCOMPLETECOMBO_H__5FC0B924_A76A_4C95_AC30_EFC12F83BCBE__INCLUDED_)

    // AutoCompleteCombo.cpp : implementation file
    //

    #include "stdafx.h"
    #include "AutoCompleteCombo.h"

    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif

    /////////////////////////////////////////////////////////////////////////////
    // CAutoCompleteCombo

    CAutoCompleteCombo::CAutoCompleteCombo()
    {
    m_bAutoComplete=TRUE;
    }

    CAutoCompleteCombo::~CAutoCompleteCombo()
    {
    }


    BEGIN_MESSAGE_MAP(CAutoCompleteCombo, CComboBox)
    //{{AFX_MSG_MAP(CAutoCompleteCombo)
    ON_CONTROL_REFLECT(CBN_EDITUPDATE, OnEditupdate)
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    /////////////////////////////////////////////////////////////////////////////
    // CAutoCompleteCombo message handlers

    BOOL CAutoCompleteCombo::PreTranslateMessage(MSG* pMsg)
    {
    // TODO: Add your specialized code here and/or call the base class
    if(pMsg->message==WM_KEYDOWN)
    {
    m_bAutoComplete=TRUE;

    int nVirtKey=(int)(pMsg->wParam);

    if(nVirtKey==VK_DELETE || nVirtKey==VK_BACK)
    {
    m_bAutoComplete=FALSE;
    }
    }

    return CComboBox::PreTranslateMessage(pMsg);
    }

    void CAutoCompleteCombo::OnEditupdate()
    {
    // TODO: Add your control notification handler code here
    if(!m_bAutoComplete) return;

    // Get current text concept in the combobox
    CString strText;
    GetWindowText(strText);
    int nLength=strText.GetLength();

    // Get selected text range
    DWORD dwCurSel=GetEditSel();
    WORD dStart=LOWORD(dwCurSel);
    WORD dEnd=HIWORD(dwCurSel);

    // Serach String
    if(SelectString(-1,strText)==CB_ERR)
    {
    SetWindowText(strText);

    if(dwCurSel!=CB_ERR)
    {
    SetEditSel(dStart,dEnd);
    }
    }

    // Select Text
    if(dEnd<nLength && dwCurSel!=CB_ERR)
    {
    SetEditSel(dStart,dEnd);
    }
    else
    {
    SetEditSel(nLength,-1);
    }
    }

    /////////////////////////////////////////////////////////////////////////////
    // CTestAutoCompleteComboDlg message handlers

    BOOL CTestAutoCompleteComboDlg::OnInitDialog()
    {
    CDialog::OnInitDialog();

    // Add "About..." menu item to system menu.

    // IDM_ABOUTBOX must be in the system command range.
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
    CString strAboutMenu;
    strAboutMenu.LoadString(IDS_ABOUTBOX);
    if (!strAboutMenu.IsEmpty())
    {
    pSysMenu->AppendMenu(MF_SEPARATOR);
    pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
    }
    }

    // Set the icon for this dialog. The framework does this automatically
    // when the application’s main window is not a dialog
    SetIcon(m_hIcon, TRUE); // Set big icon
    SetIcon(m_hIcon, FALSE); // Set small icon

    // TODO: Add extra initialization here
    m_combo.AddString("America");
    m_combo.AddString
  • 学好VC++的十大良好习惯



    欢迎光临阿蒙的VC++专业技术网站: http://www.vchome.net/



    每到年底各大媒体就争先恐后热火朝天地搞总结,什么十大人物,十大品牌,十大美女,十大帅哥等等五花八门乱七八糟的让人充满好奇充满怀疑,这事确实让人有点郁闷,就如同男足国家队的国产教练如沈墙扶们每一次踢球失败后都要说这么一句:我们回去后要好好总结,下次会打得更好! 这话听了几十年了,耳朵都生虫了,但还是无法看到中国猪球队有象人样的表现.因此,总结在某一程度上来说只不过是一种形式罢了,总结不代表就能改过原有的不足,也不代表就能进步了,甚至有点俗不可耐,尽管如此,阿蒙亦明知故俗,前人说过了入乡了就要随俗,因此你生活在这种环境里,你无法对这些无聊无趣的东东置之不理,除非你是天才,天才往往在非天才的人看来是很怪异的,处处与现实格格不入,可阿蒙不是天才,所以还得赶快总结,要不就离题,又被大家骂了,:)



    (一)充分利用MSDN,因为我个人觉得它胜过任何一本编程参考书;

    MSDN是 Microsoft 当前提供的有关编程信息的最全面的资源,它包含微软最新的技术数据库,加上易学易用的全文检索功能,让您迅速找到任何您需要的技术参考数据,让您随时拥有与全世界菁英同步的技术,掌握最丰富的程序开发资源。我经常收到很多朋友的EMAILS,他们所提的问题往往都非常的简单,MSDN完全可以解答这些问题,但他们好象不太喜欢用,这是让我郁闷的地方,是因为英文不好呢,还是没有学会充分利用各种资源来解决问题的方法呢?



    (二)提高英文水平,养成多上英文网站多看英文资料多买老外原版英文书;

    有关程序员与英文水平的讨论已太多太多, 我个人认为要成为程序员,高中的英语水平够了,甚至不懂英语的一些人,也同样可以成为较好的程序员,因为开发工具的发展将是越来越傻瓜,但如果你是仅仅满足于能运用某种工具开发某个软件模块,那是没话说了.真正热衷技术肯干钻研乐于接受挑战的程序员是不满足于现状的,他们总感觉有太多的未知,于是总在不停地学习,如今信息技术发展得太快,而大部分的技术最先出现的时候都是英文版本的,要几个月或者几年以后才有中文版本的书出来,因此要想跟上步伐,一定要努力提高自己的英文水平,这样才能同步跟上信息技术。你可能担心自己的英语水平不行,没关系,刚开始多查字典,"万事开头难",必须有持之以恒的精神,不久你就会发现计算机英语其实很容易的。何况很多

    英文技术站点确实比国内做得好啊!比如http://www.codeguru.com, http://www.codeproject.com, http://www.programmersheaven.com 等等.



    (三)加强自我管理,善于作自我总结,分析自已的优点及缺点。

    中国境内百分之八十以上的领导人在百分之八十以上的场合的讲话中都有类似的观点,所以在这里我是不多说了,反正这一条用在什么行业什么地方都不会有错的,人生最大的敌人不是就是自已吗?管好自已认清自已,那还有什么搞不定的?



    (四)养成良好的文档习惯

    程序员大多都不喜欢写文档,我以前也是特讨厌,在我的思想里,所谓的文档就是一些废话,一句话硬是用十句话来代替的无聊透顶,就如同部分中文系男生的爱情表白,明明就是"我爱你"三个字,他硬是把月亮啊太阳啊大海啊高山啊石头啊天使啊乱七八糟的都拉上关系了,尽管听起来浪漫,但在我认为不实用,:), 甚至太肉麻了,一个男子汉干嘛这么罗里罗嗦的......良好的文档是正规研发流程中非常重要的环节,一个好的程序是先写好设计文档再进行编程的,在设计文档的指导下,才能写出安全的代码。如果你不写文档,一开始就写程序,这样你就不会按已设计好的路线走,而是想到哪写到哪。小功能还好说,要是大功能,就容易混乱甚至失控.那么如何写文档呢?其实我认为没有统一的标准,虽然国家及一些NB的人总结了很多的模板,但每个人的习惯不同,如果你不加以修改或创新,就套用某个标准,我相信写起来会很吃力及说不清的难受,因此我觉得只要能将你的设计思想及实现算法或步骤描述清楚就是好的文档,我强烈建议广大程序员朋友们在写文档时要善于用图表来说明你的思想,我们不是作家,也可能作文都经常性地不及格,写出五官端正的文章对我们来说可能不容易啊!好好地利用VISIO,ROSE或别的工具来表达你的思想吧!



    (五)代码风格要规范,严谨,效率要高。

    这个不用说了,所以一定要记住了!不过,这一点有时可能与人的性格有关,如果你是经常丢三落四经常胡子长长经常钮扣扣错经常吃个快餐要一个小时的人,那你在CODING的时候可千万要注意了,CODING是CODING,生活是生活,不要写出的程序也是那样就不好了!



    (六)掌握好跟踪调试技巧.

    跟踪调试程序是一件繁琐而又复杂的事情,所以掌握必要的调试策略及技巧却可以使这些工作变得轻松起来.强烈建议你去看一下老美Everett N.McKay及Mike Wooding写的书<<Debugging Windows Programs>>,你一不定受益匪浅.



    (七)养成自我测试的习惯

    测试工作应由测试工程师来做,但在你写完一个模块或一个软件时,还是要自已先测试一下,保证不要出现一些低级的错误,何况这些错误让测试工程师看到了,狂扁你一顿,你很没FACES的.



    (八
  • 使用VC6.0实现窗口的任意分割
    南京邮政局计算机中心 张中庆

    一、关于CSplitterWnd类
    我们在使用CuteFtp或者NetAnt等工具的时候,一般都会被其复杂的界面所吸引,在这些界面中窗口被分割为若干的区域,真正做到了窗口的任意分割。 那么我们自己如何创建类似的界面,也实现窗口的任意的分割呢 ?在VC6.0中这就需要使用到CSplitterWnd类。CSplitterWnd看上去像是一种特殊的框架窗口,每个窗口都被相同的或者不同的视图所填充。当窗口被切分后用户可以使用鼠标移动切分条来调整窗口的相对尺寸。虽然VC6.0支持从AppWizard中创建分割窗口,但是自动加入的分割条总是不能让我们满意,因此我们还是通过手工增加代码来熟悉这个类。
    CSplitterWnd的构造函数主要包括下面三个。


    BOOL Create(CWnd* pParentWnd,int nMaxRows,int nMaxCols,SIZE sizeMin,CCreateContext* pContext,DWORD dwStyle,UINT nID);
    功能描述:该函数用来创建动态切分窗口。 参数含义:pParentWnd 切分窗口的父框架窗口。 nMaxRows,nMaxCols是创建的最大的列数和行数。 sizeMin是窗格的现实大小。 pContext 大多数情况下传给父窗口。 nID是字窗口的ID号. BOOL CreateStatic(CWnd* pParentWnd,int nRows,int nCols,DWORD dwStyle,UINT nID)
    功能描述:用来创建切分窗口。 参数含义同上。 BOOL CreateView (int row,int col,CruntimeClass* pViewClass,SIZE sizeinit,CcreateContext* pContext);
    功能描述:为静态切分的窗口的网格填充视图。在将视图于切分窗口联系在一起的时候必 须先将切分窗口创建好。
    参数含义:同上。
    从CSplitterWnd源程序可以看出不管是使用动态创建Create还是使用静态创建CreateStatic,在函数中都调用了一个保护函数CreateCommon,从下面的CreateCommon函数中的关键代码可以看出创建CSplitterWnd的实质是创建了一系列的MDI子窗口。
    DWORD dwCreateStyle = dwStyle & ~(WS_HSCROLL|WS_VSCROLL);
    if (afxData.bWin4)
    dwCreateStyle &= ~WS_BORDER; //create with the same wnd-class as MDI-Frame (no erase bkgnd)
    if (!CreateEx(0, _afxWndMDIFrame, NULL, dwCreateStyle,
    0, 0, 0, 0,pParentWnd->m_hWnd, (HMENU)nID, NULL))
    return FALSE; // create invisible


    二、创建嵌套分割窗口
    2.1创建动态分割窗口
    动态分割窗口使用Create方法。下面的代码将创建2x2的窗格。
    m_wndSplitter.Create(this,2,2,CSize(100,100),pContext);

    但是动态创建的分割窗口的窗格数目不能超过2x2,而且对于所有的窗格,都必须共享同一个视图,所受的限制也比较多,因此我们不将动态创建作为重点。我们的主要精力放在静态分割窗口的创建上。
    2.2创建静态分割窗口
    与动态创建相比,静态创建的代码要简单许多,而且可以最多创建16x16的窗格。不同的窗格我们可以使用CreateView填充不同的视图。
    在这里我们将创建CuteFtp的窗口分割。CuteFtp的分割情况如下:
    -----------------------
    | CCuteFTPView |
    -----------------------
    |CView2 |CView3 |
    -----------------------
    | CView4 |
    -----------------------

    创建步骤:
    ▲ 在创建之前我们必须先用AppWizard生成单文档CuteFTP,生成的视类为 CCuteFTPView.同时在增加三个视类或者从视类继承而来的派生类CView2,CView3 CView4.
    ▲ 增加成员:
    在Cmainfrm.h中我们将增加下面的代码:


    CSplitterWnd wndSplitter1;
    CSplitterWnd wndSplitter2;
    ▲ 重载CMainFrame::OnCreateClient()函数: BOOL CMainFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext)
    { //创建一个静态分栏窗口,分为三行一列
    if(m_wndSplitter1.CreateStatic(this,3,1)==NULL)
    return FALSE;
    //将CCuteFTPView连接到0行0列窗格上
    m_wndSplitter1.CreateView(0,0,RUNTIME_CLASS(CCuteFTPView),CSize(100,100), pContext);
    m_wndSplitter1.CreateView(2,0,RUNTIME_CLASS(CView4),CSize(100,100),pContext);
    //将CView4连接到0行2列
    if(m_wndSplitter2.CreateStatic(&m_wndSplitter,1,2,WS_CHILD|WS_VISIBLE,
    m_wndSplitter.IdFromRowCol(1, 0))==NULL)
    return FALSE; //将第1行0列再分开1行2列
    //将CView2类连接到第二个分栏对象的0行0列
    m_wndSplitter2.CreateView(0,0,RUNTIME_CLASS(CView2),CSize(400,300),pContext);
    //将CView3类连接到第二个分栏对象的0行1列
    m_wndSplitter2.CreateView(0,1,RUNTIME_CLASS(CView3),CSize(400,300),pContext);
    return TRUE;
    }
    2.3实现各个分割区域的通信
    ■有文档相连的视图之间的通信
    由AppWizard生成的CCuteFTPView是与文档相连的,同时我们也让CView2与文档相连,因此我们需要修改CCuteFTPApp的InitInstance()函数,我们将增加下面的部分。
    AddDocTemplate (new
  • VC编程规范
    1.基本要求
    1.1 程序结构清析,简单易懂,单个函数的程序行数最好不得超过100行或者在VC编辑窗口3屏范围内,以控制在VC编辑窗口一屏之内为佳.
    1.2 打算干什么,要简单,直接了当,代码精简,避免垃圾程序.
    1.3 尽量使用标准库函数和公共函数,以及存在于标准库函数中的应避免重复设计函数.
    1.4 不要随意定义全局变量,尽量使用局部变量.
    1.5 多使用括号以避免二义性. 如if( (conditionA) && (conditionB==B) ) return;
    1.6 双目操作符两边应该加上一个空格.
    1.7 仔细考虑逻辑分枝,不能出现逻辑错误.

    2.可读性要求
    2.1 可读性第一,效率第二.
    2.2 保持注释与代码完全一致.
    2.3 每个源程序文件,都有文件头说明,说明规格见附件中<<C++ 编程规范规范>>.
    2.4 每个函数,都有函数头说明,说明规格见附件中<<C++ 编程规范规范>>.
    2.5 主要变量(结构、联合、类或对象)定义或引用时,注释能反映其含义.
    2.7 常量定义(DEFINE)有相应说明.
    2.8 处理过程的每个阶段都有相关注释说明.
    2.9 在典型算法前都有注释.
    2.10 利用缩进来显示程序的逻辑结构,缩进量一致并以Tab键为单位,定义Tab为 4个
    空格或者8个空格.
    2.11 循环、分支层尽量控制在三层内,最好不要超过五层.
    2.12 注释可以与语句在同一行,也可以在上行.
    2.13 空行和空白字符也是一种特殊注释.
    2.14 一目了然的语句不加注释.
    2.15 注释的作用范围可以为:定义、引用、条件分支以及一段代码.
    2.16 注释行数(不包括程序头和函数头说明部份)应占总行数的 1/5 到 1/3 .

    3. 结构化要求
    3.1 禁止出现两条等价的支路.
    3.2 禁止goto语句.
    3.3 用 if 语句来强调只执行两组语句中的一组.禁止 else goto 和 else return.
    3.4 用 switch…case 实现多路分支.
    3.5 避免从循环引出多个出口.
    3.6 函数只有一个出口.
    3.7 条件中不得有赋值语句.
    3.8 避免不必要的分支.
    3.9 不要轻易用条件分支去替换逻辑表达式.

    4. 正确性与容错性要求
    4.1 程序首先是正确,其次是效率,然后是优美。
    4.2改一个错误时可能产生新的错误,因此在修改前首先考虑对其它程序的影响.
    4.3所有变量在调用前必须被初始化.
    4.4对所有的用户输入,必须进行合法性检查.
    4.5不要比较浮点数的相等,
    如: 10.0 * 0.1 == 1.0 , 不可靠
    4.6程序与环境或状态发生关系时,必须主动去处理发生的意外事件,如文件能否
    逻辑锁定、打印机是否联机等.
    4.7单元测试也是编程的一部份,提交联调测试的程序必须通过单元测试.

    5. 可重用性要求
    5.1 重复使用的完成相对独立功能的算法或代码应抽象为公共控件或类.
    5.2 公共控件或类应考虑OO思想,减少外界联系,考虑独立性或封装性.
    5.3 公共控件或类应建立使用模板.


    附:C++ 编程规范
    1.适用范围
    本标准适用于利用Visual C++ ,Borland C++进行软件程序开发的人员.

    2.变量命名
    命名必须具有一定的实际意义,形式为xAbcFgh,x由变量类型确定,Abc、Fgh表示连续意
    义字符串,如果连续意义字符串仅两个,可都大写.如OK.

    具体样例:

    BOOL类型 bEnable;
    ch * char chText
    c * 类对象 cMain(对象实例)
    h * Handle(句柄) hWnd
    i * int
    n * 无符号整型
    p * 指针
    sz,str * 字符串
    w WORD
    x,y 坐标

    Char或者TCHAR类型 与Windows API有直接联系的用szAppName[10]形式否则用
    FileName[10]形式,单个字符也可用小写字母表示;

    Int类型 nCmdShow;
    LONG类型 lParam;
    UINT类型 uNotify;
    DWORD类型 dwStart;
    PSTR类型 pszTip;
    LPSTR类型 lpCmdLine
    LPTSTR类型 lpszClassName;
    LPVOID类型 lpReserved
    WPARAM类型 wParam,
    LPARAM类型 lParam
    HWND类型 hDlg;
    HDC类型 hDC;
    HINSTANCE类型 hInstance
    HANDLE类型 hInstance,
    HICON类型 hIcon;
    int iTmp
    float fTmp
    DWORD dw
    String str
    m_ 类成员变量 m_nVal, m_bFlag
    g_ 全局变量 g_nMsg, g_bFlag

    局部变量中可采用如下几个通用变量:nTemp,nResult,I,J,i,j(一般用于循环变量).
    其他资源句柄同上

    3.常量命名和宏定义
    常量和宏定义必须具有一定的实际意义;
    常量和宏定义在#include和函数定义之间;
    常量和宏定义必须全部以大写字母来撰写,中间可根据意义的连续性用下划线连接,每一
    条定义的右侧必须有一简单的注释,说明其作用;

    资源名字定义格式:
    菜单:IDM_XX或者CM_XX
    位图:IDB_XX
    对话框:IDD_XX
    字符串:IDS_XX
    DLGINIT:DIALOG_XX
    ICON:IDR_XX

    4.函数命名
    函数原型说明包括引用外来函数及内部函数,外部引用必须在右侧注明函数来源: 模
    块名及文件名, 如是内部函数,只要注释其定义文件名;

    第一个字母必须使用大写字母,要求用大小写字母组合规范函数命名,必要时可用下划线
    间隔,示例如下:
    void UpdateDB_Tfgd (TRACK_NAME); //Module Name :r01/sdw.c
    void PrintTrackData (TRACK_NAME); //Module Name :r04/tern.c
    void ImportantPoint (void); //Module Name :r01/sdw.c
    void ShowChar (int , int , chtype); //Local Module
    void ScrollUp_V (int , int);
  • 微软公司软件开发模式简介
    ——摘自北京大学出版社出版的《微软的秘密》一书

      北京大学出版社96年底所出的《微软的秘密》一书是目前我所见到的对微软公司软件产品开发过程介绍的最专业、最深入的一本书。通过本书,我们可以看到微软公司是如何对科学地对软件产品开发进行有效地管理,我想这些经验对于中国的广大软件开发人员,尤其是关心中国软件产业发展的各位朋友是大有益处的。所以特将此书中涉及软件产品开发的部分内容摘录出来(第四章“产品定义与开发过程”),与大家共同分享。本文作为摘录,自然是挂一漏万,所以建议大家若有时间还是找来原书一读。

      在产品定义与开发过程中,微软件遵循着一种可称之为“靠改进特性与固定资源来激发创造力”的战略。该战略可分为五个原则:

      一、将大项目分成若干里程碑式的重要阶段,各阶段之间有缓冲时间,但不进行单独的产品维护。

      二、运用想象描述和对特性的概要说明指导项目。

      三、根据用户行为和有关用户的资料确定产品特性及其优先顺序。

      四、建立模块化的和水平式的设计结构,并使项目结构反蚋产品结构的特点。

      五、靠个人负责和固定项目资源实施控制。

    原则一:将大项目分成若干里程碑式的重要阶段,各阶段之间有缓冲时间,但不进行单独的产品维护。
      项目进度安排与里程碑

      微软通常采用“同步-稳定产品开发法”。典型项目的生命周期包括三个阶段:计划阶段完成功能的说明和进度表的最后制定,开发阶段写出完整的的源代码,稳定化阶段完成产品,使之能够批量生产。这三个大阶段以及阶段间内在的循环方法与传统的“瀑布”式开发方式很不相同,后者是由需求、详尽设计、模块化的代码设计与测试、集成测试以及系统测试组成的。而微软的三个阶段更像是风险驱动的、渐进的“螺旋”式的生命周期模型。

      计划阶段的产品是想象性描述与说明文件,用来解释项目将做什么和息么做。在管理人员拟定进度表、开发员写出代码之前,这些东西都促进了人们对设计问题的思考与。讨论开发阶段围绕三次主要的内部产品发布来进行;称定化阶段集中于广泛的内部与外部测试。在整个产品生产周期中,微软都使用了缓冲时间的概念。缓冲时间使开发组能够对付意外的困难和影响到时间进度的变故,它也提供了一种手段,可以缓和及时发货与试图精确估计发货时间之间的矛盾。

      在开发和稳定化阶段的所有时间中,一个项目通常会将2/3的时间用于开发,1/3的时间用于稳定化。(Office部门副总裁曾这样概述通常的进度:“一般说来,在总的进度表中,用一半的时间写出产品,留下另一半的时间调试或应付意外事故。这样,如果我有一个两年的项目,我会用一年来完成事先想好的东西……如果事情有点麻烦,我便去掉我认为不太重要的特性。”)这种里程碑式的工作过程使微软的经理们可以清楚地了解产品开发过程进行到了哪一步,也使他们在开发阶段的后期有能力灵活地删去一些产品特性以满足发货时期的要求。

      计划阶段

      计划阶段是在一个项目的生命周期中,所有于开发前进行的计划所占用的时间。计划阶段产生出想象性描述、市场营销计划、设计目标、一份最初的产品说明、为集成其他组开发的构件而规定的接口标准、最初的测试计划、一个文档策划(印刷品和联机帮助形式的)以及一份可用性问题清单。计划阶段从想象性描述开始。想象性描述来自产品经理以及各产品单位的程序经理;它是对产品作业的市场营销设想,包括了对况争对手产品的分析以及对示来版本的规划。想象性描述也可能讨论在前一次版本中发现面必须解决的问题以及应添加的生要功能。所有这些都基于对顾客和市场的分析以及从产品支持服务组处得到的资料。

      说明文件从一个大纲开始,然后定义出新的或增加的产品特性,并对其赋以不同的优先级。说明文件只是产品特性的一个预备性概览;从开始开发到项目完成它要增加或变化20% - 30%。虽然在生命周期的后期说明变化一般较小,但越到后期,开发员就越是必须具充分的理由来作改变。

      通常程序经理使用VB创建项目原型。他们也开展设计可行性研究以了解设计中的取舍情况,尽快做出涉及产品说明的决定。

      对于重要产品的说明需由公司高层领导进行复审。对于不太生要的产品,则由部分经理去完成。

      开发阶段

      开发阶段的计划对三四个主要的里程碑版本都个咖分配一组特性,规定出特性的细节和技术上的相关性,记录下单个开发员的任务以及对进度的估计。在开发阶段中,开发员在功能性说明的指导下写源代码,测试员写出测试项目组以栓查产品的特性与工作范围是否正常,用户教育人员则编写出文档草案。

      当测试员发现错误时,开发员并不是留待以后处理,而是马上改正,并在整个开发阶段内使测试不断地、自动地进行。这就改善了产品的稳定性并且使版本发布日期更易估计。当达到项目中的一定阶段点后(40%时),开发员就试图“锁定”产品的主要功能要求或特性,从此只允许小的改动。如果在此点之后开发员想作大的改动,他们必须与程序经理以及开发经理,问题也许还要征求产品部门
  • From:http://www.yangning.com/cgi-bin/topic.cgi?forum=1&topic=77&replynum=last#bottom

    四大要素:
    1.程序的基本工作原理。这些原理性的东西是整个程序的灵魂,学术界搞得所谓的科研,就是这种东西。
    2.基本工作原理的实现方案,即程序的设计方案、核心技术、关键技术等。基本工作原理跟实现方案虽然本质是一致的,但是,在形式上是不同的。因为不同的底层技术供应商把基本原理包装到了不同的产品(函数)中了,所以,程序的架构师需要搞出一套实际的实现方案出来,把工作原理产品化。

    3.项目的整体设计结构。即应该如何将各个模块有效的组织在一起。程序的总体结构设计跟上一条“基本工作原理的实现方案”是不同的,上一条是抓重点,抓关键,而这一条是抓整体,抓大局。因为一个完善的商业化产品仅仅有核心功能是不够的,还必须要提供一系列的辅助功能,这些辅助功能从技术上来讲,是很简单的,但是,内容却很多,很杂,所以,需要好好进行一番规划设计才行。

    4.项目难点。可能存在于第1条中,也可能存在于第2、3条中。难点不一定就是很核心很关键的东西,有时候一个很小的,很不起眼的东西,却很难处理。

    一个原则:层层推进进行分析设计。
    大项目都是由小的模块组成的, 每个模块都包括上述四大要素,所以,整体和局部是一个层层推进,逐级放大的关系。

    分析大型的软件源码,或者是看书,搞研究,其实都是应该按照上述原则进行。
  • 实现和IE浏览器交互的几种方法的介绍
    浙江大学计算机系
    胡朝晖 陈奇 俞瑞钊
    ---- 1.引言

    ---- 如何实现对IE浏览器中对象的操作是一个很有实际意义问题,通过和IE绑定的DLL我们可以记录IE浏览过的网页的顺序,分析用户的使用行为和模式。我们可以对网页的内容进行过滤和翻译,可以自动填写网页中经常需要用户填写的Form内容等等,我们所有的例子代码都是通过VC来表示的,采用的原理是通过和IE对象的接口的交互来实现对IE的访问。实际上是采用COM的技术,我们知道COM是和语言无关的一种二进制对象交互的模式,所以实际上我们下面所描述的内容都可以用其他的语言来实现,比如VB,DELPHI,C++ Builder等等。

    ---- 2.IE实例遍历实现

    ---- 首先我们来看系统是如何知道当前有多少个IE的实例在运行。

    ---- 我们知道在Windows体系结构下,一个应用程序可以通过操作系统的运行对象表来和这些应用的实例进行交互。但是IE当前的实现机制是不在运行对象表中进行注册,所以需要采用其他的方法。我们知道可以通过ShellWindows集合来代表属于shell的当前打开的窗口的集合,而IE就是属于shell的一个应用程序。

    ---- 下面我们描述一下用VC实现对当前 IE实例的进行遍历的方法。IShellWindows是关于系统shell的一个接口,我们可以定义一个如下的接口变量:

    SHDocVw::IShellWindowsPtr m_spSHWinds;
    然后创建变量的实例:
    m_spSHWinds.CreateInstance
    (__uuidof(SHDocVw::ShellWindows));
    通过IShellWindows接口的方法GetCount
    可以得到当前实例的数目:
    long nCount = m_spSHWinds- >GetCount();
    通过IShellWindows接口的方法Item
    可以得到每一个实例对象
    IDispatchPtr spDisp;
    _variant_t va(i, VT_I4);
    spDisp = m_spSHWinds->Item(va);
    然后我们可以判断实例对象是不是
    属于IE浏览器对象,通过下面的语句实现:
    SHDocVw::IWebBrowser2Ptr spBrowser(spDisp);
    assert(spBrowser != NULL)
    ----在得到了IE浏览器对象以后,我们可以调用IWebBrowser2Ptr接口的方法来得到当前的文档对象的指针: MSHTML::IHTMLDocument2Ptr spDoc(spBrowser->GetDocument());

    ---- 然后我们就可以通过这个接口对这个文档对象进行操作,比如通过Gettitle得到文档的标题。

    ---- 我们在浏览网络的时候,一般总会同时开很多IE的实例,如果这些页面都是很好的话,我们可能想保存在硬盘上,这样,我们需要对每一个实例进行保存,而如果我们采用上面的原理,我们可以得到每一个IE的实例及其网页对象的接口,这样就可以通过一个简单的程序来批量的保存当前的所有打开的网页。采用上面介绍的方法实现了对当前IE实例的遍历,但是我们希望得到每一个IE实例所产生的事件,这就需要通过DLL的机制来实现。

    ---- 3.和IE相绑定的DLL的实现

    ---- 我们介绍一下如何建立和IE进行绑定的DLL的实现的过程。为了和IE的运行实例进行绑定,我们需要建立一个能够和每一个IE实例进行绑定的DLL。IE的启动过程是这样的,当每一个IE的实例启动的时候,它都会在注册表中去寻找这个的一个CLSID,具体的注册表的键位置为:

    HKEY_LOCALL_MACHINE\SOFTWARE\Microsoft\Windows
    \CurrentVersion\Explorer\Browser Helper Objects
    ---- 当在这个键位置下存在CLSIDs的时候,IE会通过使用CoCreateInstance()方法来创建列在该键位置下的每一个对象的实例。注意对象的CLSIDs必须用子键而非名字值的形式表现,比如{DD41D66E-CE4F-11D2-8DA9-00A0249EABF4} 就是一个有效的子键。我们使用DLL的形式而非EXE的形式的原因是因为DLL和IE实例运行在同一个进程空间里面。每一个这种形式的DLL必须实现接口IObjectWithSite,其中方法SetSite必须被实现。通过这个方法,我们自己的DLL就可以得到一个指向IE COM对象的IUnknown的指针,实际上通过这个指针我们就可以通过COM对象中的方法QueryInterface来遍历所有可以得到的接口,这是COM的基本的机制。当然我们需要的只是IWebBrowser2这个接口。

    ---- 实际上我们建立的是一个COM对象,DLL只不过是COM对象的一种表现形式。我们建立的COM对象需要建立和实现的方法有:

    ----1. IOleObjectWithSite接口的方法SetSite必须实现。实际上IE实例通过这个方法向我们的COM对象传递一个接口的指针。假设我们有一个接口指针的变量,不妨设为:

    ----CComQIPtr< IWebBrowser2, &IID_IWebBrowser2 > m_myWebBrowser2;

    ---- 我们就可以在方法SetSite中把这个传进来的接口指针赋给m_myWebBrowser2。 2. 在我们得到了指向IE COM对象的接口后,我们需要把自己的DLL和IE实例所发生的事件相关连,为了实现这个目的,需要介绍两个接口:

    ----(1) IConnectionPointContainer。这里使用这个接口的目的是用来根据它得到的IID来建立和DLL的一个特定的连接。比如我们可以进行如下的定义:

    CComQIPtr< IConnectionPointContainer,
    &IID_IConnectionPointContainer >
    spCPContainer(m_myWebBrowser2);
    ----然后,我们需要把所有IE中发生的
  • 一、调试版本与发布版本

      有时程序能在调试版本运行但不能运行于发布版本,反之也有可能。一般说来,一个发布版本意味着某些类型的优化,而一个调试版本则没有优化。下面我们来看看它们的区别:

    1、特别针对调试版本的编译选项

    (1)/MDd,/MLd或者/MTd

      调试版本的运行时刻库有调试符号,使用了调试堆,调试堆的目的是发现内存破坏和内存泄漏,并且向用户报告源代码的哪个地方出了问题。特性:

    .调试版本的运行时刻库对内存的分配作了跟踪,允许用户检查内存泄漏。

    .在刚分配的内存里写上0xCD的字节模式,用0xCD来填充刚分配的内存,有助于发现数据未被初始化的错误。

    .在被释放的内存写上0xDD的字节模式,有助于发现已被释放的内存。

    .在缓冲区的两边分配了四字节的保护数据,并用0xFD的字节模式作初始化,来检查写内存的上溢出和下溢出。

    .在每个内存分配的地方对源代码文件名和行号作了记录,有助于用户在源代码中对内存分配进行定位。

    (2)/Od

      这个选项用来关闭优化开关。因为未被优化的代码直接对应于源代码,所以比优化后的代码更容易读懂。未被优化的代码编译和链接会更快,会有更短的调试周期。而由于优化,发布版本不见得会比调试版本运行得好,优化代码要求编译器做一些假设,去除冗余,但有时这个假设是错误的,并且去掉的冗余也有可能隐藏错误。如发布版本的帧指针(EBP寄存器)省略(FPO)隐藏了函数原型不匹配的错误;在同步异常模式(只能由throw语句抛出,编译器默认,由/GX编译选项设置)下,异常处理程序可能被优化掉,会阻止程序中的C++异常处理代码安全地捕获结构异常,在这种情况下,你必须使用异步异常模式(采取任何指令都会产生异常的机制,由/Eha编译选项设置)。

    (3)/D “_DEBUG”

      打开条件编译调试代码开关。只有这个符号被定义,调试代码才会被编译,MFC使用_DEBUG符号来确定到底链接的是哪个版本的MFC类库。在调试版本中,内联默认情况下是被关闭的。

    (4)/ZI

      创建编辑继续(Edit and Continue)的程序数据库。这个选项会打开/GF编译选项,/GF编译选项会消除重复字符串,并将字符串放到只读内存。编辑继续功能需要获取存储在PDB文件里的特殊信息来使得代码的修改对调试器有效。如果被修改文件对应的信息不在PDB文件里,编辑继续功能就不能进行,而且在调试过程中对代码的任何修改都会出现下面的提示信息“One or more files are out of date or do not exist.”。

    (5)/GZ

    在调试版本中用来发现那些在发布版本里才发现的错误。其作用如下:

    .用0xCC模式初始化自动(本地)变量。

    .在通过函数指针调用函数时,检查栈指针,确认是否有调用规则不匹配。

    .在函数最后检查栈指针是否被改变。

    (6)/Gm

      打开最小化重新链接开关,减少链接时间。

    2、特别针对发布版本的编译选项

    (1)/MD,/ML或者/MT

      使用发布版本的运行时刻库。

    (2)/O1或者/O2

      打开优化开关,使得程序会最小或说速度会最快,优化器还可能发现代码中潜在的错误,而这些错误可能会被调试版本掩盖。

    (3)/D “NDEBUG”

      关闭条件编译调试代码开关。

    (4)/GF

      消除重复字符串并将它们放到只读内存中以避免被错误地修改。

    (5)/Zi

    创建包含调试符号的程序数据库。

      如果一个错误只发生在发布版本里,除非你是个汇编高手,否则你需要调试符号来提示你到底程序出现了什么问题,调试符号保存在程序的数据库文件(PDB)中。Visual C++的AppWizard默认情况下没有为发布版本创建调试符号。为创建调试符号,打开工程设置对话框,选择Win32 Release,在C/C++标签里选择Common类,在调试信息里,如果是发布版本选择Program Database,如果是调试版本选择Program Database for Edit and Continue(编辑继续选项与优化链接不相容,不适于发布版本)。在Link标签里选择Debug类,然后选择Debug Info和Microsoft format选项,最好不要选择Separate types选项,这样所有的调试信息才会被合并到单独的一个PDB文件中。对于发布版本,选择Link标签,在Project options对话框的最后加上“/OPT:REF”,这个选项使得不被引用的函数和数据不会出现在可执行文件中,避免了文件的无谓增大。对于调试版本不要使用这个选项,它会关闭增量链接(incremental linking)。

    二、Visual C++编辑器的“设置”菜单

      当你打开或新建一个包含至少一个工程的Workspace后,Visual C++的Project菜单中的“Settings…”命令就变为有效,选择它或者按下热键Alt+F7后,便可调出工程设置对话框,这里面的选项将影响整个工程的建立和调试过程,因此很重要。

      在这个对话框中,左上方的下拉列表框用于选择一种工程配置,包括有Win32 Debug、Win32 Release和All Configurations(指前两种配置一起),某些选项在不同的工程配置中有不同的缺省值。左边的树形视图给出了当前工程所有的文件及分类情况。下面我们就以Win32 Debug为例来看看与工程有关的的四个主要选项卡的各自功能与含义(一共有
  • 为了更好地对程序调试,可以使用如下方法:使用断言、使用跟踪语句、使用异常和返回值。

    一、断言

    1、基本概念

      断言是一种让错误在运行时候自我暴露的简单有效实用的技术。它们帮助你较早较轻易地发现错误,使得整个调试过程效率更高。

      断言是布尔调试语句,用来检测在程序正常运行的时候某一个条件的值是否总为真,它能让错误在运行时刻暴露在程序员面前。使用断言的最大好处在于,能在更解决错误的发源地的地方发现错误。断言具有以下特征:

    .断言是用来发现运行时刻错误的,发现的错误是关于程序实现方面的。

    .断言中的布尔表达式显示的是某个对象或者状态的有效性而不是正确性。

    .断言在条件编译后只存在于调试版本中,而不是发布版本里。

    .断言不能包含程序代码。

    .断言是为了给程序员而不是用户提供信息。

      使用断言最根本的好处是自动发现许多运行时产生的错误,但断言不能发现所有错误。断言检查的是程序的有效性而不是正确性,可通过断言把错误限制在一个有限的范围内。当断言为假,激活调试器显示出错代码时,可用Call Stack命令,通过检查栈里的调用上下文、少量相关参数的值以及输出窗口中Debug表的内容,通常能检查出导致断言失败的原因。_ASSERTE宏(属于C运行时间库)还能在断言失败时显示出失效断言。下面我们讨论一下MFC库中的断言。

    2、MFC库中的断言

    (1) ASSERT(布尔表达式)

    用MFC时最好选择ASSERT宏,它的优点是即使出现了WM_QUIT消息也能显示断言失效消息框。

    (2) VERIFY(布尔表达式)

    VERIFY宏中的布尔表达式在发布版本中被保留下来。VERIFY宏简化了对函数返回值的检查,一般用来检查Windows API的返回值。由于VERIFY宏里的布尔表达式在发布版本里保留了下来,因此最好尽量不要使用这个宏以实现程序代码和调试代码的完全分离。

    (3 )ASSERT_VALID(指向CObject派生类对象的指针)

    ASSERT_VALID宏通过调用重载的AssertValid函数来确定指向CObject派生类对象的指针是否有效。无论你什么时候从CObject派生类中得到一个对象,在对这个对象做任何操作之前都应该调用ASSERT_VALID宏。

    (4) ASSERT_KINDOF(类名, 指向CObject派生类对象的指针)

    这个宏用来验证指向CObject派生类对象的指针是否从某个特殊类中派生,在调用它之前先调用ASSERT_VALID宏。只有在很特殊的场合下才用得到,如检测编译器可能错过的对象类型问题。

    此外,还有两个没有正式文件的ASSERT宏的变种:ASSERT_POINTER(指针,指针类型),ASSERT_NULL_OR_POINTER(指针,指针类型)。

    3、什么时候使用断言

      把断言看作一种简单的制造栅栏的方法,这种栅栏能使错误在穿过自己时暴露。

    .检查函数的输入

    .检查函数的输出

    .检查对象的当前状态

    .坚持逻辑变量的合理性和一致性

    .检查类中的不变量

    公有成员函数比私有和保护的成员函数需要更全面的断言。

      不正确地使用断言会导致错误。断言应该检测那些在程序正常运行的时候永远都不可能出现的状态。断言是用来揭示错误的,而不是用来纠正运行时刻错误的。

    4、断言与防御性编程(Defensive Programming)

      断言在调试的时候向程序员揭示运行时刻错误(调试版本里),而防御性编程使用户在运行程序(发布版本里)时,当出现意外情况时程序仍能继续工作。实际上,防御性的编程要求程序在检测到意外时返回一个“安全”的值(比如布尔函数返回false,指针和句柄返回空值),一个错误代码或者抛出一个异常来解决问题。特定的防御性编程技术包括:处理无效函数参数和数据、出现问题的时候程序失败、检查临界函数返回的错误代码以及处理异常。需要防御性编程的标准问题包括:错误的输入数据、内存或者硬盘空间不够、不能打开一个文件、外部设备不能访问、网络连接不上或者甚至在程序中还有错误,目的是保持程序的运行状态。如果你的程序是防御性的,别忘了使用断言。如果你使用断言,也别忘了防御性编程。这两种技术最好结合在一起使用。

    二、跟踪语句

    1、基本概念

      跟踪语句(trace statements)可使程序执行,并使程序员可对可变值进行查看。它们提供了一个用于观察的程序,并且独立于一个交互式的调试器,但是最具有特色的是它们常用于对调试器提供的信息进行补充。在VC中,跟踪消息通常输出到输出窗口中的Debug标签,也可以重新输出到一个文件中。跟踪语句的特性如下:

    .跟踪语句用于报告代码中重要的运行事件。

    .跟踪语句的编译通常是有条件的,并只存在于调试版本中,而在发布版本中不被编译。

    .跟踪语句不能包含程序代码或对程序代码有间接的影响作用。

    .跟踪语句的目的是向程序员提供信息,而不是向用户。

    跟踪语句也是调试语句,它可以执行程序,并且在运行中程序员可以查看变量。跟踪语句对于那些使用交互式调试器很难调试的程序是很有效的。

    跟踪语句和断言的区别如下:

    .跟踪语句是无条件的,断言是有条件的布尔语句。

    .跟踪语句用于显示程序执行和变量值,不直接显示bug,断言用于显示出bug。

    .跟踪语句将信息输出到调试窗口
  • 编写易于调试的VC代码


    一 程序的设计

      要避免错误,首先要从好的设计开始。对于程序的设计,需考虑到程序的两个特性:

      1简单性

      大多数常见的错误来源于程序设计中不必要的复杂成分。一个好的设计应该反映问题本身的要求,而不必为了刻意追求“满足将来的需要”而添加不必要的特性。实际上,简单优雅的设计比那些复杂的设计更能迎合未来的需求。

      2 耦合性

      耦合(decoupling)性用来衡量不同对象之间的依赖程度。松耦合的程序易于理解和实现,易于测试和维护,且这种程序包含错误的可能性小,错误也较容易发现和清除。

    二 编程风格

      编程风格是个人问题,有很大的随意性。一个好的编程风格不仅让代码易理解,也易于调试。好的编程风格包括:

      1 清晰地书写代码

      如果没有必要,尽量不要使用语言中的高级特性,因为这些特性不易于理解和调试。使用大多数程序员都能理解的语言成分来书写代码不易犯错且易于理解和维护。

      2 编写结构良好的代码

      当程序崩溃时所能得到的最基本的调试信息是源代码文件、问题所在行的行号和一个调用栈(call stack)。调用栈是调试程序时最有帮助的部分,它提供错误出现的上下文,也就是带参数的函数调用序列。你书写的代码结构越好,调用栈就能给你越多信息。

      3 使用良好的标识符

      一个好名字能使你的代码更容易被理解和维护。流行的匈牙利命名法(Hungarian Notation)实际上是把标识符的意义和表示方法结合起来。现在,匈牙利命名法表现出不少的局限性,匈牙利命名法过于看重前缀的作用,对一个变量的表达信息不完整,实际上并没有传递多少有用信息,它使代码难于阅读,难以维护。一个好的命名传统是指示出变量的作用域以便在需要的时候检查它的定义,并明确地指出一个变量是全局的、局部的还是成员数据。依赖变量的定义比依赖匈牙利前缀更加有用和可靠。

      好的名字能够用平常的语言概括出该标识符所代表的实体的含义。在选择类、函数、变量的名字时可以考虑以下几个原则:

        取简单的描述性名字,好的名字能简要地概括出这个标识符代表的含义。

        避免简写,简写使标识符难于阅读和记忆,尽量使用混合大小写的完整的单词。

        避免相似性的文字,避免混淆。

        避免采用一般的或随机产生的名字,而应采用有实际意义的名字。如欲从按钮类派生位图按钮,取一个CBitmapButton,而不是CMyButton。

      4 用简单的语句行

      在VC中,一行可写多个语句。但调试是面向行的,过于复杂的行难于调试。因此,从调试的角度出发,每一个语句都应独自成行。

      5 使用统一的排列

      统一的排列方式使类、变量的定义和语句更加明显。

      6 用括号使书写清晰

      你不一定能都记住各种运算符的优先级和结合律,而使用多余的括号并不影响编译后的代码。因此,如果你不能确定是否需要括号时,请加上它。

      7 使用好的注释

      用好的注释能使你的代码不易出错,而且便于其他程序员阅读,便于理解和维护。

    三 编写程序时应注意的问题

      1 充分利用VC++的特性

      可用下列技术来充分利用VC++的编译器的特性:

      (1)用const代替#define来创建常量;

      (2)用enum代替#define来创建常量集合;

      (3)用内联(inline)函数代替#define;

      这三种技术用C++而不是C预处理。使用预处理的问题在于编译器对于预处理器所作的事情一无所知,因此无法用数据类型检查错误和不一致的地方。预处理的名字不在符号表里,因此也不能用调试工具来检查预处理常量。相似地,预处理宏被编译进去,不能用调试工具跟踪。编译器能充分了解const、enum和inline语句,从而能在编译的时候对出现的问题发出警告。

      但预处理在很多调试代码中起重要作用。调试代码经常需要从非调试代码里面得到不同的行为,而最有效的办法就是让预处理为调试创建不同的代码。

      (4)用new和delete代替malloc和free;

      在创建对象、类型的安全性和灵活性方面。使用new/delete比malloc/free要好。另外,new可被重载,提供了更大的灵活性。

      (5)用输入输出流(iostreams)代替stdio。

      使用C++输入输出流(<<和>>)而不使用C标准输入输出库(printf/sprintf和scanf/sscanf),有利于安全性和扩展性。从调试的角度来看,标准输入输出函数的最大问题在于编译器不能对控制流参数进行任何类型检测,而输入输出流的任何问题都能在编译时检测出来。

      2 使用头文件

      要在头文件中声明所有共享的外部符号,而且保留函数原型中的参数名。把所有的共享定义放在头文件中,不要在.cpp文件里面看到extern关键字。

      3 初始化变量

      在使用变量之前一定要把它们初始化。在初始化之前就使用变量肯定会产生错误。通常不需对对象进行初始化,对对数据成员应在构造函数中初始化。必须明确地为在栈中和堆中分配的数组和数据结构进行初始化。对于对象,应该初始化每个需要初始化的数据成员。因为变量的使用是由优