VC++ 知識小結(續)

1)當文檔被修改時,如何在標題上加上標誌'*'?
重載CDocument類的虛函數virtual SetModifiedFlag:
void CTest2Doc::SetModifiedFlag(BOOL bModified)
{
    CString strTitle = GetTitle();
    CString strDirtyFlag = " *"; // note space before the '*'
            // so we don't break Save As dialog
    if (!IsModified() && bModified)
    {
        SetTitle(strTitle + strDirtyFlag);
    }
    else if ( IsModified() && !bModified )
    {
        int nTitleLength = strTitle.GetLength();
        int nDirtyLength = strDirtyFlag.GetLength();
        SetTitle( strTitle.Left(nTitleLength - nDirtyLength) );
    }
    UpdateFrameCounts();
    CDocument::SetModifiedFlag(bModified);
}
(2)VC6.0對VC5.0的兼容性?
很不幸,vc6.0在調試模式對vc5.0不兼容,但發行模式沒有問題.原因在微軟改變了調試模式所用dll的格式,而保留了原文件名. 因此,不要在vc6.0中打開vc5.0的調試版本工程.
(3)打印和打印機的問題?
我碰到這麼一個問題:在打印方法中使用了MM_LOMETRIC模式,在LOGFONT結構中改變了字體的大小,但不知道173(或者對於屏幕而言是25)是從哪來的,它是自動的.然而當我用另外一個打印機時173並不適合.我想知道的是:我如何對所有的打印來調整這個數字.
我以前也碰到過類似的問題,我讓用戶改變字體(大小,顏色等等).這些改變在屏幕上看起來挺好,但是打印時太小(我的同事在程序包中加入一個放大類).原因非常簡單:打印機的分辨率可能是300dpi,而屏幕的分辨率則低得多.我是這麼解決的:在獲得屏幕字體信息後,我獲取屏幕字體的毫米級大小(使用LPtoDP,然後將模式變爲MM_LOMETRIC,調用DPtoLP),接着對打印機設定了相同的模式,再調用LPtoDP.切換回原來的模式之後,我調用了DPtoLP,這樣就得到了想要的字體高度和寬度. 在LOGFONT中使用這個值,並且帶有其它諸如下劃線,斜體等字體信息,我實現了用戶的要求.
(4)CRichEditCtrl滾動條的問題?
我使用了CRichEditCtrl控制來顯示某個文件中的數據(將該控制設置爲只讀).我已經設置了ES_MULTILINE | ES_AUTOVSCROLL,但當數據內容比控制顯示多的時候,滾動條並不出現,是不是因爲設置了只讀屬性而引起了其它的問題?
ES_AUTOVSCROLL | ES_AUTOHSCROLL屬性只在控制是可編輯時有效.你可心使用下面的滾動條風格來使滾動條出現:WS_VSCROLL | WS_HSCROLL,但是這樣一來,不管你的數據量有多大,滾動條總是會出現.
(5)從數據庫中讀大於32k的內容?
我在從數據庫中讀數據時碰到了問題.當數據欄包含超過32k的內容時,我就讀不出來,我試過ODBC::SQLGetData()也不行.
哪種類型的數據庫?MS SQL,SYBASE... 試試設置一下大小:
BOOL CGetBlobStmt::Execute(LPCTSTR stmt)
{
m_cbSize = 0;
m_size = 0;
LPBYTE
  lpData;
lpData = (LPBYTE)GlobalLock(m_hData);
m_retcode = SQLSetStmtOption(GetHandle(),SQL_MAX_LENGTH,m_dwBytesLeft);
m_retcode = SQLExecDirect(GetHandle(),(UCHAR*)stmt,SQL_NTS);
if (m_retcode == SQL_SUCCESS)
{
  m_retcode = SQLFetch(GetHandle());
  if (m_retcode == SQL_SUCCESS ||m_retcode == SQL_SUCCESS_WITH_INFO)
  {
   m_retcode = SQLGetData(GetHandle(),1,SQL_C_BINARY,lpData,254,&m_cbSize);
   while(m_retcode == SQL_SUCCESS_WITH_INFO)
   {
    lpData+= 254;
    m_retcode = SQLGetData(GetHandle(),1,SQL_C_BINARY,lpData,254,&m_cbSize);
   }
   GetError();
  }
}
GlobalUnlock(m_hData);
#if TESTDATA
TRACE("%ld",m_size);
#endif
SaveFile();
return RETVALUE;
}
(6)如何獲得CRichEditCtrl中字符的位置?
我想在CRichEditCtrl中使用右鍵菜單,因此想判定光標處字符的位置,請指點.
查看如下的幫助:
IRichEditOleCallback::GetContextMenu
EM_SETOLECALLBACK
(7)如何限制mdi子框架最大化時的大小?
用ptMaxTrackSize代替prMaxSize,如下所示:
void CChildFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
   // TOD Add your message handler code here and/or call default
   CChildFrame::OnGetMinMaxInfo(lpMMI);
   lpMMI->ptMaxTrackSize.x = 300;
   lpMMI->ptMaxTrackSize.y = 400;
}
(8)如何切換視口而不破壞它們?
我創建了一個帶有靜態分隔區的sdi應用程序,左邊顯示工作區,右過顯示左邊選取的東西.我想達到的是如果在分隔區之間進行切換,而不覆蓋或破壞原來的CView對象.
以下代碼是你所想要的:
class CExSplitterWnd : public CSplitterWnd
{
// Construction
public:
    CExSplitterWnd();
// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CExSplitterWnd)
    //}}AFX_VIRTUAL
// Implementation
    virtual ~CExSplitterWnd();
    BOOL AttachView(CWnd* pView, int row, int col);
    BOOL DetachView(int row, int col);
    // Generated message map functions
    //{{AFX_MSG(CExSplitterWnd)
        // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
CExSplitterWnd::CExSplitterWnd()
{
}
CExSplitterWnd::~CExSplitterWnd()
{
}
BOOL CExSplitterWnd::AttachView(CWnd* pView, int row, int col)
{
    //Make sure the splitter window was created
    if (!IsWindow(m_hWnd))
    {
        ASSERT(0);
        TRACE(_T("Create the splitter window before attaching windows to panes"));
        return (FALSE);
    }
    //Make sure the row and col indices are within bounds
    if (row >= GetRowCount() || col >= GetColumnCount())
    {
        ASSERT(0);
        return FALSE;
    }
    //Is the window to be attached a valid one
    if (pView == NULL || (!IsWindow(pView->m_hWnd)))
    {
        ASSERT(0);
        return FALSE;
    }
    pView->SetDlgCtrlID(IdFromRowCol(row, col));
    pView->SetParent(this);
    pView->ShowWindow(SW_SHOW);
    pView->UpdateWindow();
    return (TRUE);
}
BOOL CExSplitterWnd::DetachView(int row, int col)
{
    //Make sure the splitter window was created
    if (!IsWindow(m_hWnd))
    {
        ASSERT(0);
        TRACE(_T("Create the splitter window before attaching windows to panes"));
        return (FALSE);
    }
    //Make sure the row and col indices are
    //within bounds
    if (row >= GetRowCount() || col >= GetColumnCount())
    {
        ASSERT(0);
        return FALSE;
    }
    CWnd* pWnd = GetPane(row, col);
    if (pWnd == NULL || (!IsWindow(pWnd->m_hWnd)))
    {
        ASSERT(0);
        return FALSE;
    }
    pWnd->ShowWindow(SW_HIDE);
    pWnd->UpdateWindow();
    //Set the parent window handle to NULL so that this child window is not
    //destroyed when the parent (splitter) is destroyed
    pWnd->SetParent(NULL);
    return (TRUE);
}
(9)改變列表控制時發生閃爍現象?
我創建了一個簡單的對話框,在對話框中設置了一個列表控件,這個控件佔用了對話框的全部客戶區.對話框是可以改變大小的,因此我要保證列表控件在對話框中維持正確的位置,在對話框的ONSize()事件中我對列表控件使用了MoveWindow(),這起到了作用,但當用戶改變對話框的大小時,列表控件不停地閃爍.
要解決這個問題,在用MoveWindow之前,先用ShowWindow(SW_HIDE)隱藏列表控件,然後在MoveWindow之後用ShowWindow(SW_SHOW)來顯示列表控件.
(10)處理列表控件可見項的問題?
我在一個列表控件中加入了好多條目.我通過獲取某個條目是否可見或最後是哪個條目來進行處理.我看了CListCtrl::GetItem()的幫助,但是沒有找到如何判斷一個條目是否可見的方法.
如果你只想處理可見的條目,你可以用GetTopIndex.它返回最大可見條目的索引值,然後你再用GetCountPerPage來得到在可見區域的條目數.
(11)產生線程的問題?
我在使用CreateThread時碰到了問題.我想讓調用的函數和被調用的函數屬於同一個類,結果在我調用CreateThread時得到如下錯誤:
error C2440: 'type cast' : cannot convert from 'unsigned long (__stdcall Cdmi::*)(void *)' to 'unsigned long (__stdcall *)(void *)'
方法一:
(1)'unsigned long (__stdcall Cdmi::*)(void *)'是指向Cdmi某個成員函數的指針.
(2)'unsigned long (__stdcall *)(void *)'僅僅只是一個c形式函數的指針. 編譯器無法將(1)轉換爲(2)是因爲c++成員函數取第一個(隱藏)參數"this pointer"作爲成員函數,但當是一個靜態的成員時則例外.可按如下方法解決.
class XMyThread
{
public:
    void StartThread(void);
    virtual UINT ThreadFunction(void);
    static UINT __bogusthreadfunc(LPVOID lpparam);
};
void XMyThread::StartThread()
{
    AfxBeginThread(__bogusthreadfunc,this);
}
UINT XMyThread::ThreadFunction(void)
{
    //here you do all your real work
    return 0;
}
UINT XMyThread::__bogusthreadfunc(LPVOID lpparam)
{
     XMyThread* This = dynamic_cast(lpparam);
     return This->ThreadFunction();
}
for the sake of clairty, I did not add StopThread and I did not save the
CWinThread* returned by AfxBeginThread.
If you wanted a thread that does other things, simply derive from XMyThread
and override ThreadFunction()
example:
class XAnotherThread : public XMyThread
{
    virtual UINT ThreadFunction(void);
};
UINT XAnotherThread :: ThreadFunction(void)
{
    //do some other work here
    return 0;
}
方法二:Cdmi::MonitorFiles()是個靜態的成員函數.
(12)CFile使用了緩衝區嗎?
請告訴我CFile到底有沒有使用緩衝區來處理文件?
CFile沒有使用運行庫的I/O緩衝例程,從這個意義上講CFile並沒有使用緩衝.但是有可能操作系統在處理文件時使用了緩衝區,如果你完全不需要緩衝區,你可以設置FILE_FLAG_NO_BUFFERING.CFile工作在這種模式下的唯一的方法是CFile::Attach().
(13)DAO的密碼?
我創建了一個使用數據庫的mfc應用程序.用類模板生成CDaoRecordset直接打開數據庫(不通過ODBC),但問題是我如何打開有密碼保護的數據庫?
方法一:試試下面的代碼:
DAODBEngine* pDBEngine = AfxDaoGetEngine();
ASSERT(pDBEngine != NULL);
COleVariant varUserName (strUserName, VT_BSTRT);
COleVariant varPassword (strPassword, VT_BSTRT);
DAO_CHECK(pDBEngine->put_DefaultUser (V_BSTR(&varUserName));
DAO_CHECK(pDBEngine->put_DefaultPassword (V_BSTR(&varPassword));
方法二:你可以使用CDaoDatabase的Open方法來打開:
MyDaoDatabase->Open("C:\MyDatabaseFile.mdb",FALSE,FALSE,";PWD=MyPassWord");
btw:不要忘了PWD=前面的;號.
(14)如何知道CListBox什麼時候滾動了?
每次繪製列表框都要重繪某項,通過消息WM_CTLCOLOR從父窗口獲得DC顏色.因此每欠列表框的滾動你都可以用WM_CTLCOLOR來檢驗是否滾動.
HBRUSH CParentDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
   // is the control _the_ list box we're interested in?
   if( nCtlColor == CTLCOLOR_LISTBOX &&
      pWnd->GetDlgCtrlID() == IDC_LIST )
   {
      // if the top index changed, the list box has been scrolled
      int iTop = ((CListBox*)pWnd)->GetTopIndex();
      if( iTop != m_iTopOld )
      {
         // keeps tracking of the top index changes
         m_iTopOld = iTop;
         // process scrolling
         ...
      }
   }
   HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
   return hbr;
}
使用這種方法可以不必爲了實現這個功能而去產生一個繼承類.

(15)視口的不活動性如何處理?
有什麼方法使CListView成爲類似WM_DIASBLED的風格,或者使它和背景色一致.
方法一:你所要做的是處理CListView的WM_SETFOCUS消息,然後在TreeView中調用SetFocus,
這樣,ListView就永遠不會獲得焦點.
    afx_msg void CMyListView::OnSetFocus(CWnd* pOldWnd);
    {
        // assuming m_pwndTreeView points to the valid TreeView
        // on the left side
        m_pwndTreeView->SetFocus();
    }
方法二:重載PreTranslateMessage,然後當消息爲WM_LBUTTONDOWN或WM_RBUTTONDOWN時返回真即可.

(16)如何使用COleClientItem的IDispatch接口?
我創建了一個如何使用COleClientItem對象,我想使用它的自動化方法.有什麼方法來獲得IDispatch的接口?我試過以CCmdTarget爲基類的的GetIDispatch函數但卻出錯,我用過EnableAutomation和GetIDispatch,卻什麼也沒得到.
MSDN中有一篇關於這個的文章(TN039).如下的代碼也可能是你所需要的:
LPDISPATCH CMyClientItem::GetIDispatch()
{
    ASSERT_VALID(this);
    ASSERT(m_lpObject != NULL);
    LPUNKNOWN lpUnk = m_lpObject;
    Run(); // must be running
    LPOLELINK lpOleLink = NULL;
    if (m_lpObject->QueryInterface(IID_IOleLink,
        (LPVOID FAR*)&lpOleLink) == NOERROR)
    {
        ASSERT(lpOleLink != NULL);
        lpUnk = NULL;
        if (lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
        {
            TRACE0("Warning: Link is not connected!\n");
            lpOleLink->Release();
            return NULL;
        }
        ASSERT(lpUnk != NULL);
    }
    LPDISPATCH lpDispatch = NULL;
    if (lpUnk->QueryInterface(IID_IDispatch, &lpDispatch)
        != NOERROR)
    {
        TRACE0("Warning: does not support IDispatch!\n");
        return NULL;
    }
    ASSERT(lpDispatch != NULL);
    return lpDispatch;
}
(17)關於用戶自定義的消息使用?
我寫了一個基於MFC應用程序的對話框,在這個程序中,我創建了等待網絡傳輸數據的線程,一旦該線程接收到數據,它就傳送一個用戶自定義的消息到對話框,使對話框知道有數據過來.但是爲何在CMyDialog::PreTranslateMessage(MSG* pMsg)中能捕捉到WM_MYCMD這個消息,卻不能和OnMyCommand相映射?
將你所有自定義消息的基類設爲WM_APP,而不是WM_USER.

(18)在打開一個文檔時退出?
我有一個mdi程序,在打開文件的處理過程中,我想判斷該文檔是不是應用程序需要處理的文檔,因此,我檢測文檔中的某個數字是否符合要求,如何在發現不是該文檔時出現一個錯誤提示,然後不打開該文檔?
給文檔設定某個標誌,如果文檔不是所要的就設定它.然後OnOpenDocument中檢測,當發現標誌被設定後返回FALSE.

(19)在CListCtrl控件中多選擇項的刪除?
如何從在CListCtrl中刪除多個選擇項?
按如下方法處理:如果你的在CListCtrl是m_list,to_delete是個整數數組.
i=3D0;
POSITION pos=3Dm_list.GetFirstSelectedItemPosition();
if(pos)
  while(pos)
   to_delete[i++]=3Dm_list.GetNextSelectedItem(pos);
然後用刪除保存在to_delete中的項目,用GetSelectedCount來得到已選項的個數.
(20)工作線程的登錄狀態?
我使用循環刪除了用AfxBeginThread創建的線程的好幾個實例.每個線程打開一個iNET連接,打開一個URL並返回結果.我需要找出哪一個或者何時這些線程進入到登錄狀態.
按如下方法處理:(僞代碼)
    // Start Threads
    for( unsigned u = 0; u < NUMBER_OF_THREADS; u++ )
{
    ThreadHandleArray[ u ] = AfxBeginThread( ...... )->m_hThread;
}
DWORD count = NUMBER_OF_THREADS
DWORD dwWait;
while( count )
{
    dwWait = ::WaitForMultipleObjects( count, ThreadHandleArray, FALSE,
INFINITE );
    if( dwWait >= WAIT_OBJECT_0 && dwWait < ( WAIT_OBJECT_0 + count ) )
    {
        dwWait -= WAIT_OBJECT_0;
        // dwWait now has index to thread that completed do whatever
you want to do with it
        // set array back up for next wait
        if( dwWait != ( count - 1 ) )
            ThreadHandleArray[ dwWait ] = ThreadHandleArray[
count - 1 ];
            count--;
    }
}
(21)如何增加視圖中ActiveX控件的事件處理函數?
如果我在對話框中加入微軟的網絡瀏覽器,很容易通過類模板加入對事件的處理.但我現在在視圖中用m_pBrowser=new CWebBrowser2加入了網絡瀏覽器,我該如何對事件進行處理?
到[url]www.vcdj.com[/url](inet章節)去看看,有一篇文章名爲"Building a Webbrowser in a Afternoon".如下的代碼也可能是你所需要的:
#include // For AFX_EVENT def.
BOOL OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
    AFX_EVENT *pEvent = (AFX_EVENT *)pExtra;
    //If this is a control notification event.
    if (nCode == CN_EVENT)
    {
        // If we have information on this event.
        if (pEvent)
        {
            // Event DISPID is stored at pEvent->m_dispid
            // Event DISPPARAMS are stored at pEvent->m_pDispParams
            // Handle the event from here...
        }
    }
    return CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
(22)如何創建一個動態的Tree控件?
我想創建一個動態的tree控件,就象彈出窗口一樣,但它並不象想象中那麼容易.
方法一:用CreateWindow(SDK)創建風格爲WS_POPUP,WS_CAPTION和WS_TICKFRAME的窗口,並作爲父窗口.
方法二:創建一個包含Tree控件的對話框.
(23)SDI程序開始時不打開文檔?
我創建了一個SDI應用,但每次啓動時它都會打開一個文檔("untitled"),如何不讓它打開該文檔呢?
看看InitInstance函數中有沒有關於OnFileNew的調用,去掉它即可.
(24)List控件中整欄選擇?
我在處理List控件時碰到了麻煩,我想創建一個ListView,來依據Tree控件的選擇同時在ListView和ReportView中顯示列表的信息.以下是相關的代碼:
// Set full line select
ListView_SetExtendedListViewStyle(m_plstCustomers->GetSafeHwnd(),
LVS_EX_FULLROWSELECT);
按如下方法處理:
// -------------------- begin of snippet --------------------------------
bool CCommCtrlUtil32::ListCtrl_ModifyExtendedStyle(CListCtrl& p_rListCtrl,
                                   const DWORD p_dwStyleEx,
                                   const bool p_bAdd)
{
    HWND t_hWnd = p_rListCtrl.GetSafeHwnd();
    DWORD t_dwStyleEx = ListView_GetExtendedListViewStyle(t_hWnd);
    if(p_bAdd)
    {
        if(0 == (p_dwStyleEx & t_dwStyleEx))
        {
            // add style
            t_dwStyleEx |= p_dwStyleEx;
        }
    }
    else
    {
        if(0 != (p_dwStyleEx & t_dwStyleEx))
        {
            // remove style
            t_dwStyleEx &= ~p_dwStyleEx;
        }
    }
    ListView_SetExtendedListViewStyle(t_hWnd, t_dwStyleEx);
    return true;
}

(25)如何重載MRU文件?
我創建了一個應用程序可以載入圖象文件,但當我點擊FILE菜單下MRU文件列表時,卻不能從磁盤載入以前曾經打開過的文件.
下面是我所能想到的解決方案:
(1)在文檔類中定義一個成員函數(例如:CMyDoc::Reopen)來處理重新打開這個問題,指明參數和返回值.
(2)產生一個CMultiDocTemplate的繼承類(如CMyDocTemplate),定義一個構造函數,取和基類相同的參數,不做任何事,只是調用基類的構造函數.
(3)重載MatchDocType:
CMyDocTemplate::Confidence CMyDocTemplate::MatchDocType(
    LPCTSTR lpszPath,
    CDocument *&rpDocMatch
    )
{
    Confidence match = CMultiDocTemplate::MatchDocType(lpszPath, rpDocMatch);
    if(yesAlreadyOpen == match) // clear enough
    {
        ASSERT_KINDOF(CMyDoc, rpDocMatch);
        ((CMyDoc *) rpDocMatch)->Reopen(/* your parameters */);
        // you can take any other actions here...
    }
    return match;
}
當這個函數返回"yesAlreadyOpen"時,你的文檔框架將會被激活.
--------------------------------------------------

(26)CImageList控件中圖象橙色被顯示爲黃色?
我使用了一個CImageList控件來裝入位圖,用於TREE控件,其它的色彩都很正常就是橙色被顯示成爲黃色.
你只能使用系統指定的20種顏色(橙色不包括在內);當然,你也可以用下面的方法來裝載位圖資源而不受顏色數的限制.
HBITMAP LoadResourceBitmap(HINSTANCE hInstance, LPSTR lpString,
                           HPALETTE FAR* lphPalette)
{
    HRSRC hRsrc;
    HGLOBAL hGlobal;
    HBITMAP hBitmapFinal = NULL;
    LPBITMAPINFOHEADER lpbi;
    HDC hdc;
    int iNumColors;
    if (hRsrc = ::FindResource(hInstance, lpString, RT_BITMAP))
{
  hGlobal = ::LoadResource(hInstance, hRsrc);
  lpbi = (LPBITMAPINFOHEADER)LockResource(hGlobal);
  hdc = ::GetDC(NULL);
  *lphPalette = CreateDIBPalette ((LPBITMAPINFO)lpbi, &iNumColors);
  if (*lphPalette)
  {
   ::SelectPalette(hdc,*lphPalette,FALSE);
   ::RealizePalette(hdc);
  }
  hBitmapFinal = ::CreateDIBitmap(hdc,
       (LPBITMAPINFOHEADER)lpbi,
       (LONG)CBM_INIT,
       (LPSTR)lpbi + lpbi->biSize + iNumColors * sizeof(RGBQUAD),
                   (LPBITMAPINFO)lpbi,
                   DIB_RGB_COLORS );
  ::ReleaseDC(NULL,hdc);
// ::UnlockResource(hGlobal);
// ::FreeResource(hGlobal);
}
    return (hBitmapFinal);
}
// internally used by LoadResourceBitmap
HPALETTE CreateDIBPalette (LPBITMAPINFO lpbmi, LPINT lpiNumColors)
{
LPBITMAPINFOHEADER lpbi;
LPLOGPALETTE lpPal;
HANDLE hLogPal;
HPALETTE hPal = NULL;
int i;
lpbi = (LPBITMAPINFOHEADER)lpbmi;
if (lpbi->biBitCount <= 8)
  *lpiNumColors = (1 << lpbi->biBitCount);
else
  *lpiNumColors = 0; // No palette needed for 24 BPP DIB
if (lpbi->biClrUsed > 0)
  *lpiNumColors = lpbi->biClrUsed; // Use biClrUsed
if (*lpiNumColors)
{
  hLogPal = GlobalAlloc (GHND, sizeof (LOGPALETTE) +
   sizeof (PALETTEENTRY) * (*lpiNumColors));
  lpPal = (LPLOGPALETTE) GlobalLock (hLogPal);
  lpPal->palVersion = 0x300;
  lpPal->palNumEntries = *lpiNumColors;
  for (i = 0; i < *lpiNumColors; i++)
  {
   lpPal->pal
PalEntry.
peRed = lpbmi->bmiColors.rgbRed;
   lpPal->palPalEntry.peGreen = lpbmi->bmiColors.rgbGreen;
   lpPal->palPalEntry.peBlue = lpbmi->bmiColors.rgbBlue;
   if (i<=10 || i>=246)
    lpPal->palPalEntry.peFlags = PC_NOCOLLAPSE;
   else
    lpPal->palPalEntry.peFlags = 0;
  }
  hPal = CreatePalette (lpPal);
  GlobalUnlock (hLogPal);
  GlobalFree (hLogPal);
}
return hPal;
}
該函數也重載了位圖調色板,這個功能被CBitmap::LoadBitmap忽略了(它假定位圖只使用20種顏色).因此要保證在DC中有SelectPalette和RealizePalette.
(27)無法正確改變應用程序的圖標?

我有一個基於對話框的應用程序,在初始化時我使用了AfxGetApp()->LoadIcon(IDI_BRIEFCASE)來載入自己的圖標,當把程序拷貝到桌面上時,圖標是我所期望的.但在資源管理器中的圖標卻還是MFC的圖標.
資源管理器僅使用16x16的小圖標,可能你在資源編輯器中只修改了32x32的標準圖標.你需要重建16x16的小圖標.
(28)工具條狀態的問題?

在應用程序中我創建了三個工具條,我想讓它們在應用程序啓動的時候排成一行正好在主菜單的下面,我該如何去做?
在VC CDs上有一個例子:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//other stuff here...
    EnableDocking(CBRS_ALIGN_ANY);
    DockControlBar(&m_wndToolBar,AFX_IDW_DOCKBAR_TOP);
    DockControlBarLeftOf(&m_wndListToolBar,&m_wndToolBar);
    return 0;
}
void CMainFrame::DockControlBarLeftOf(CToolBar* Bar,CToolBar* LeftOf)
{
    CRect rect;
    DWORD dw;
    UINT n;
    // get MFC to adjust the dimensions of all docked ToolBars
    // so that GetWindowRect will be accurate
    RecalcLayout();
    LeftOf->GetWindowRect(&rect);
    rect.OffsetRect(1,0);
    dw=LeftOf->GetBarStyle();
    n = 0;
    n = (dw & CBRS_ALIGN_TOP) ? AFX_IDW_DOCKBAR_TOP :n;
    n = (dw & CBRS_ALIGN_BOTTOM && n==0) ? AFX_IDW_DOCKBAR_BOTTOM :n;
    n = (dw & CBRS_ALIGN_LEFT && n==0) ? AFX_IDW_DOCKBAR_LEFT :n;
    n = (dw & CBRS_ALIGN_RIGHT && n==0) ? AFX_IDW_DOCKBAR_RIGHT :n;
    // When we take the default parameters on rect, DockControlBar will dock
    // each Toolbar on a seperate line. By calculating a rectangle, we in effect
    // are simulating a Toolbar being dragged to that location and docked.
    DockControlBar(Bar,n,&rect);
}
(29)在SDI應用程序中使用Active控件?

我剛瞭解到如何在MFC應用程序中使用Active控件,文檔上說只能在視圖爲CFormView和CDialog時使用,但要是其它的情況該怎麼辦呢?
你可以在你應用程序的任何地方使用Active控件,而不僅僅侷限於CFormView和CDialog爲視圖基類的情況.DevStudio通過資源編輯器和對話框模板來使得在上述兩個條件下使用Active控件更容易.因此,你也可以在任何視圖中使用Active控件,條件是你直接操縱該控件,創建它並手工的佈置好它的位置(這也是DevStudio爲你所做的事).
(30)有RichEdit控件的對話框無法正常顯示?
我在對話框中放置了一個RichEdit控件,但是對話框卻無法正常顯示.
在你的應用程序InitInstance()中調用了::AfxInitRichEdit()嗎?
(31)DLL中的模板成員函數?

在一個DLL中,我在自己創建的類中使用了模板成員函數來代替預處理宏.但出現以下錯誤:
  error C2664: 'double Data::extract(double &)' : cannot convert parameter 1
  from 'class CArray' to 'double &'
爲什麼在匹配模板定義時它要尋找一個DOUBLE參數?
我覺得你可能是在表達成員函數(內聯)時出現了問題,請參照下面的示例:
   class AFX_EXT_CLASS Data : public CObject //This is not a template
   {
   public:
      Data();
      Data(BYTE * buffer,int size);
      template
      Data(const CArray& array);
      template
      CArray& extract(CArray& array)
      {
         CArchive ar(&buffer, CArchive::store);
         ar >> array;
      };
      double extract(double&);
                 (...)
   private:
      CMemFile buffer;
   }
(32)CFormView中的上下文幫助?

我想在基於CFormView類的SDI應用程序中加入真正的上下文幫助,但沒有成功.
你應該重載CMyFormView類的OnHelpHitTest函數:
LRESULT CMyFormView::OnHelpHitTest(WPARAM, LPARAM lParam)
{
    LRESULT lResult = (LRESULT)0x00;
    CWnd* pWndChild = ChildWindowFromPoint(CPoint(lParam),
CWP_ALL|CWP_SKIPINVISIBLE);
    if (pWndChild && ::IsWindow(pWndChild->m_hWnd))
    {
        lResult = ::GetWindowLong(pWndChild->m_hWnd, GWL_ID);
        if (lResult)
            lResult += HID_BASE_COMMAND;
    }
    if (lResult == (LRESULT)0x00)
        lResult = ::GetWindowLong(m_hWnd, GWL_ID) + HID_BASE_RESOURCE;
    return lResult;
}
然後你就可以使用平時用的幫助文件了,但你要保證有正確的前綴,請參照
TN028:Context-Sensitive Help Support.
例如:
ID_SOME_MENU_ITEM_OR_COMMAND_BUTTON
IDR_SOME_WINDOW_OR_DIALOG
IDP_PROMPT
IDW_CONTROL_THAT_IS_NOT_A_COMAND_BUTTON
你要確認你所使用的控件的ID包含在文件resource.hm中.

(33)CArchive類的WriteObject函數問題?

誰知道在使用CArchive類的WriteObject函數時,如何避免將類名寫入文件嗎?
WriteObject函數不僅寫入了類名,而且還寫入PID(請查看TN02),如果你只想寫進一個文本文件,並且你也想用串行化,你可以使用文件指針(用GetFile)來存儲字符串.或者,你可以使用CFILE類來處理這個問題,如果是文本文件,你也可以用CStdioFile類.
(34)RegisterWindowMessage中的BroadcastSystemMessage如何處理?

我想用BroadcastSystemMessage來在兩個進程之間通訊,我從一個進程發送了一個用RegisterWindowMessage註冊過的消息,但在目的進程中卻沒有收到該消息.
我認爲你應該在兩個進程的最高級窗口中都註冊該消息.請看下例:
static UINT sBroadcastCommand = ::RegisterWindowMessage( _T("BroadcastCommand"));
BEGIN_MESSAGE_MAP( Gui_Top_Level_MainFrame, Gui_MainFrame )
    ON_REGISTERED_MESSAGE( sBroadcastCommand, onBroadcastCommand )
END_MESSAGE_MAP()
LRESULT Gui_MainFrame :: onBroadcastCommand( UINT aMsg, LPARAM lParam )
{
    your code...
}
然後發送進行應該包含:
While the sending process would contain:
static UINT sBroadcastCommand = ::RegisterWindowMessage( _T("BroadcastCommand"));
void Someclass :: someMethod( void )
    {
    ::PostMessage( (HWND)HWND_BROADCAST,
                                sBroadcastCommand, 0,
                                yourMessageId );
    }
(35)CListCtrl中選擇變化時如何獲得通知?

我在Report View中使用了一個CListCtrl(自繪製類型),我想知道什麼時候選擇項發生了改變.
在選擇項變化時,可以使用按鈕有效或失效,按如下操作:
  加入LVN_ITEMCHANGED消息處理.
void CYourClassNameHere::OnItemchangedEventList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
*pResult = 0;
if (pNMListView->uChanged == LVIF_STATE)
{
  if (pNMListView->uNewState)
   GetDlgItem(IDC_DELETE)->EnableWindow(TRUE);
  else
   GetDlgItem(IDC_DELETE)->EnableWindow(FALSE);
}
}
(36)如何向ATL-COM對象傳送一個數組?

我想創建一個函數來向ATL-COM對象傳送數組.
如下代碼的方法用於ACTIVEX中,可能對ATL-COM也有啓發吧.
CoInitialize(NULL);
CLSID m_clsid;
USES_CONVERSION;
::CLSIDFromString(T2OLE("ROUNDANALOG.RoundAnlgAARCtrl.1"), &m_clsid);
IDispatch FAR* pObj = (IDispatch FAR*)NULL;
CString str = "UpdateControl";
BSTR bstr = str.AllocSysString();
HRESULT hr = CoCreateInstance(m_clsid, NULL, CLSCTX_ALL, IID_IDispatch,
(void**)&pObj);
SafeArrayAccessData(psa, (void**)&bstrArray);
bstrArray[0] = str.AllocSysString();
bstrArray[1] = str.AllocSysString();
SafeArrayUnaccessData(psa);
VARIANTARG* pvars = new VARIANTARG[1];
VariantInit(&pvars[0]);
pvars[0].vt = VT_ARRAY|VT_BYREF|VT_BSTR;
pvars[0].pparray = &psa;
DISPID dispid;
hr = pObj->GetIDsOfNames(IID_NULL, &bstr, 1,LOCALE_USER_DEFAULT, &dispid);
DISPPARAMS disp = {pvars, &dispid, 1,1};
hr = pObj->Invoke(dispid, IID_NULL,
LOCALE_USER_DEFAULT,DISPATCH_PROPERTYPUT,&disp,NULL, NULL, NULL);
delete[] pvars;
pObj->Release();
CoUninitialize();
在你的控制中建立如下並變量參考:
void CRoundAnlgAARCtrl::SaveFunc(const VARIANT FAR& var)
{
// TOD Add your dispatch handler code here
ASSERT(var.vt == VT_ARRAY | VT_BYREF | VT_BSTR);
SAFEARRAY* psa = *var.pparray;
}

(37)如何選擇CTreeCtrl中的節點文本進行編輯?

在向CTreeCtrl中加入一項後,有什麼方法可以編輯該節點的文本呢?
首先設置你的CcompTreeCtrl具有TVS_EDITLABELS屬性.在設計時用控件屬性來設置在運行時用GetStyle()/SetStyle()成員函數來設置.然後請看下述代碼:
HTREEITEM CCompTreeCtrl::AddSet()
{
static int setCnt =3D 1;
HTREEITEM hItem;
CString csSet;
//create text for new note: New Set 1, New Set 2 ...
csSet.Format( _T( "New Set %d" ), setCnt++ );
hItem =3D InsertItem( csSet, IMG_CLOSEDFOLDER, IMG_CLOSEDFOLDER );
if( hItem !=3D NULL )
           EditLabel( hItem );
return hItem;
}
(38)如何改變默認的光標形狀?

我試着將光標改變爲其它的形狀和顏色,但卻沒有變化.
在對話框/窗口/你需要的地方加上對WM_SETCURSOR消息的處理.
BOOL MyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    // TOD Add your message handler code here and/or call default
    ::SetCursor(AfxGetApp()->LoadCursor(IDC_MYCURSOR));
    return TRUE;
    //return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
你沒有成功的原因是因爲窗口類光標風格不能爲NULL.

(39)如何用鍵盤滾動分割的視口?

我的問題是當我用鼠標滾動分割窗口時,視口滾動都很正常,但用鍵盤時,卻什麼也沒有發生.
在你的視圖繼承類中加入如下兩個函數,假定該類爲CScrollerView:
void CScrollerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
        BOOL processed;
        for (unsigned int i=0;i< nRepCnt&&processed;i++)
                processed=KeyScroll(nChar);
        if (!processed)
           CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
}
BOOL CScrollerView::KeyScroll(UINT nChar)
{
        switch (nChar)
                {
                case VK_UP:
                        OnVScroll(SB_LINEUP,0,NULL);
                        break;
                case VK_DOWN:
                        OnVScroll(SB_LINEDOWN,0,NULL);
                        break;
                case VK_LEFT:
                        OnHScroll(SB_LINELEFT,0,NULL);
                        break;
                case VK_RIGHT:
                        OnHScroll(SB_LINERIGHT,0,NULL);
                        break;
                case VK_HOME:
                        OnHScroll(SB_LEFT,0,NULL);
                        break;
                case VK_END:
                        OnHScroll(SB_RIGHT,0,NULL);
                        break;
                case VK_PRIOR:
                        OnVScroll(SB_PAGEUP,0,NULL);
                        break;
                case VK_NEXT:
                        OnVScroll(SB_PAGEDOWN,0,NULL);
                        break;
                default:
                        return FALSE; // not for us
                             // and let the default class
                             // process it.
                }
   return TRUE;
}

(40)如何在線程中處理狀態條?
在我的應用程序CWnd的繼承中有指針指向狀態條,用pStatusBar->SetPaneText(0,status,TRUE)在狀態條上顯示一些文本都很正常.但在第二個線程中調用該函數卻不行,出現hwnd警告.
當你傳送一個CWnd的指針到另外一個線程時,m_hWnd將爲空.我的辦法是用PostThreadMessage傳送消息到狀態條的父類,讓它對狀態條進行處理.

(41)如何阻止WINDOWS關閉?

我有一個應用程序會不停地工作.當該程序正常運行時,該如何避免用戶關掉系統?是不是該用WM_QUERYENDSESSION.
是的,在你的主框架窗口類中使用.
// in the class header
afx_msg BOOL OnQueryEndSession( WPARAM wReserved, LPARAM lEndReason );
// in the Message Map
ON_MESSAGE( WM_QUERYENDSESSION, OnQueryEndSession )
// in the class body
BOOL CMainFrame::OnQueryEndSession( WPARAM wReserved, LPARAM lEndReason )
{
    if( lEndReason =3D=3D ENDSESSION_LOGOFF ) {
        // user is logging off
    else
        // Windows is going down
    return( bCanExit );
}
(42)如何使一個按鈕Disable?

我使用下面代碼來Disable一個爲ID_BUTTON的按鈕,爲什麼會沒有變化.
GetDlgItem(IDC_BUTTON)->EnableWindow(FALSE);
CWnd類中的EnableWindow函數用來Enable或Disable一個窗口類的對象,因爲CButton類繼承於類CWnd,所以你可以使用來操作一個按鈕.Enable一個基於窗口類的對象可以用以下代碼:
      pWnd->EnableWindow(TRUE);
Disable一個對象可用
      pWnd->EnableWindow(FALSE);
其中pWnd爲一個指向窗口對象的指針VC++中消息WM_ENABLE告訴窗口它正在Disable或Enable,但它並不能使一個窗口Enable或Disable.
(43)怎樣從MFC擴展動態鏈結庫(DLL)中顯示一個對話框?

我在過去的幾天中試着在DLL中定義的函數中顯示一個對話框,可是已經在DLL中定義好的對話框資源,在常規DLL調用時,我可以正常的顯示出來,爲什麼在擴展DLL中同樣的資源我卻不能顯示.
當你在DLL中使用資源時,有些小細節需要注意,首先,在DLL運行時,必須保存DLL的實例,可以通過AfxInitExtensionModule
static AFX_EXTENSION_MODULE extensionDLL;
extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID)
{
   if (dwReason == DLL_PROCESS_ATTACH)
      {
      // Extension DLL one-time initialization
      if (!AfxInitExtensionModule(extensionDLL, hInstance))
         return false;
      }
   return(true);
}
然後,每次使用DLL資源時,你必須改變資源的句柄,使其指向DLL,並保存exe的資源,以便以後正確恢復
void get_DLL_resource(void)
{
   /* this function changes the resource handle to that of the DLL */
   //這個函數改變資源句柄使其指向DLL
   if (resource_counter == 0)
      {
      save_hInstance = AfxGetResourceHandle();
      AfxSetResourceHandle(extensionDLL.hModule);
      }
   resource_counter++;
}
接着你需要其它函數來恢復資源句柄
void reset_DLL_resource(void)
{
   /* this function restores the resource handle set by
'get_DLL_resource()' */
   if (resource_counter > 0)
      resource_counter--;
   if (resource_counter == 0)
      AfxSetResourceHandle(save_hInstance);
}
接下來一點非常重要,只要有可能就必須恢復資源句柄,否則,你將會遇到許多問題.原因是可執行文件必須重畫工具條等等,比如說,如果用戶移動DLL的對話框,如果資源句柄仍然爲DLL的資源,程序就崩潰了,我發現最好恢復句柄的時機在對話框的OnInitDialog()中,這時對話框的模板等已經讀出了.
(44)想隱藏用戶界面怎麼辦?

我編了一個小巧而有趣的工具,當用戶使用時我不想讓它顯示出任何用戶界面。聽聽各位有辦法可將視關閉。
你可以註冊一個新的窗口類型,它擁有除了WS_VISBLE屬性外的任何屬性,類似CFrameWnd,在PreCreateWindow方法中實現。另外,你能在OnCreate方法中通過設置m_nCmdShow爲SW_HIDE來實現,具體方法如下:
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
        return -1;
    // hide our app
    AfxGetApp()->m_nCmdShow = SW_HIDE;
    return 0;
}

(45)如何實現SDI與MDI的轉換?

我想將一個編好的SDI應用程序轉換爲MDI,很明顯要有多處的改變。
你可以這樣做:建立一個繼承於CMDIChidWnd的類,不防設爲CChldFrm.在CWinApp中作如下變化。

InitInstance()
{
. ...
    //instead of adding CSingleDocTemplate
    // Add CMultiDocTemplate.
    pDocTemplate = new CMultiDocTemplate(
           IDR_MAINFRAME,
           RUNTIME_CLASS(CSDIDoc),
           RUNTIME_CLASS(CChldFrm),
// For Main MDI Frame change this frame window from
// CFrameWnd derivative ( i.e. CMainFrame )
// to your CMDIChildWnd derived CChldFrm.
           RUNTIME_CLASS(CSDIView));
/// After this it is required to create the main frame window
// which will contain all the child windows. Now this window is
// what was initially frame window for SDI.
    CMainFrame* pMainFrame = new CMainFrame;
    if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
            return FALSE;
     m_pMainWnd = pMainFrame;
.....
}
在從CMDIFrameWnd中繼承的類CMainFrame代替CFramWnd後,所有的類都將從CMDIFrame繼承,而不是CFrameWnd,編譯運行後你就會發現程序已經從SDI變換到MDI。
注意:在CMainFram中必須將構造函數從private改爲public.否則會出錯。



(46) CDC中的豎排文本?

在OnDraw成員函數中我想讓文本豎直對齊,但CDC類似乎不支持該處理
方法一:如果你的豎直對齊是指旋轉文本的話,下面的代碼會對你有幫助:該代碼檢查一個Check box控制,查看文本是否需要旋轉.
// m_pcfYTitle is a CFont* to the selected font.
// m_bTotateYTitle is a bool (==TRUE if rotated)
void CPage1::OnRotateytitle()
{
LOGFONT lgf;
m_pcfYTitle->GetLogFont(&lgf);
m_bRotateYTitle=
        ((CButton*)GetDlgItem(IDC_ROTATEYTITLE))->GetCheck()>0;
// escapement is reckoned clockwise in 1/10ths of a degree:
lgf.lfEscapement=-(m_bRotateYTitle*900);
m_pcfYTitle->DeleteObject();
m_pcfYTitle->CreateFontIndirect(&lgf);
DrawSampleChart();
}
注意如果你從CFontDialog中選擇了不同的字體,你應該自己設定LOGFONT的lfEscapement成員.將初始化後的lfEscapement值傳到CFontDialog中.
方法二:還有一段代碼可參考:
LOGFONT LocalLogFont;
strcpy(LocalLogFont.lfFaceName, TypeFace);
LocalLogFont.lfWeight = fWeight;
LocalLogFont.lfEscapement = Orient;
LocalLogFont.lfOrientation = Orient;
if (MyFont.CreateFontIndirect(&LocalLogFont))
   {
   cMyOldFont = cdc->SelectObject(&MyFont);
   }
(47)如何激活變灰的彈出菜單?

在設計菜單時設定爲GRAYED的菜單項,如何在運行時激活它?
請看下面的示例代碼:
void CMyView::OnRButtonDown(UINT nFlags, CPoint point)
{
CScrollView::OnRButtonDown(nFlags, point);
CMenu *menu, *popup;
menu = new CMenu();
// load menu from resource file
menu->LoadMenu( IDR_POPUPMENU );
popup = menu->GetSubMenu(0); // item 0 is DUMMY
UINT nEnable;
nEnable = MF_BYCOMMAND|MF_GRAYED;
if( your test )
{
  nEnable = MF_BYCOMMAND|MF_ENABLED;
}
popup->EnableMenuItem( ID_YOUR_ID, nEnable );
//display menu
ClientToScreen(&point);
popup->TrackPopupMenu(
   TPM_LEFTALIGN | TPM_RIGHTBUTTON,
   point.x, point.y, this );
delete menu;
}
(48)線程消息?

如何正確地在線程之間傳送消息?
下面的代碼將會幫你的忙:
void CThread::OnUserOpen( WPARAM wParm, LPARAM lParm )
{
    UNUSED( wParm ) ;
    UNUSED( lParm ) ;
    AfxMessageBox("User Open", MB_OK|MB_ICONEXCLAMATION);
}
當然,也別忘了以下聲明:
class CThread : public CWinThread
{
     DECLARE_DYNCREATE(CThread)
protected:
     CThread(); // protected constructor used by dynamic creation
     afx_msg void OnUserOpen( WPARAM wParm, LPARAM lParm );
(49)TreeCtrl控制的顯示速度太慢?

我從CTreeCtrl繼承了一個TREE控制類,重載主要是爲了改寫每個節點的文本.我在 OnPaint函數中寫了一些代碼,但這嚴重地影響了TREE控制的滾動速度.
OnPaint函數
1.可見節點,對於GetFirstVisibleItem和GetNextVisibleItem來講,是:
  a.根節點;b.父節點已展開的節點;因此,"可見"意味着"沒有被未展開的父節點隱藏".當節點滾動到客戶外時,它對上述兩個函數來講仍是可見的.
2.當TREE的內容改變時,它默認只將變爲可見的節點重繪.另外其它已經是可見的節點沒有必要重繪,TREE只是滾動DC的位圖而已.
上面的意思是不要繪製你不需要看的節點,那會導致速度降低.建議,測試節點矩形是否在客戶區,使得只有需要繪製的節點纔會被繪製.
void CIndentTree::OnPaint()
{
   CPaintDC dc(this); // device context for painting
   HTREEITEM hItem = NULL;
   DRAWITEMSTRUCT dis;
   CRect rc;
   // redraw only visible items with indentation
   for(
      hItem = GetFirstVisibleItem();
      hItem; hItem = GetNextVisibleItem( hItem ) )
   {
      if( !GetItemRect( hItem, rc, FALSE ) )
         continue;
      if( rc.top <= dc.m_ps.rcPaint.bottom &&
         rc.bottom > dc.m_ps.rcPaint.top &&=20
         rc.left <= dc.m_ps.rcPaint.right &&
         rc.right > dc.m_ps.rcPaint.left )
      {
         dis.hwndItem = (HWND)hItem;
         dis.rcItem = rc;
         OnDrawItem(0, &dis, &dc);
      }
   }
}
OnDrawItem函數
1.刪掉如下代碼:
      IMAGEINFO* pinfo = new IMAGEINFO;
      ...
      delete pinfo;
沒有必要使用動態的IMAGEINFO變量,你可以將其定義爲堆棧變量.
2.GetItemState和GetItemText都是使用的GetItem,因此,你只需調用一次, 就可以從節點獲得你要的所有信息.
(50)關於工具條?

我需要在程序中做一個FLAT工具條,於是我加入一個變量m_wndToolBar. 在程序主體窗口的OnCreate()函數中修改工具條狀態(0,TBSTYLE_FLAT). 在NT中運行正常,爲什麼在95中工具條變得透明?
在COMCTL32.DLL中的舊版本中有些小bug,繪畫時會帶來一些問題, 你可以使用一些定製代碼,在[url]www.codeguru.com[/url]站點上有下載,如果你使用的是6.0版本,你也可以使用下列代碼(摘自我的mainfrm.cpp文件)
m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP |
CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
用CreateEx替換ModifyStyle在同一尺寸的工具條中有些不同((CreateEx 建立的略小些),試一下,如果你仍然有這個問題,檢查一下COMCTL32.DLL的版本使用標準按鈕替換FLAT.
(51)關於線程消息?

真奇怪,OnUserOpen()函數和OnUserOpen(WPARAM, LPARAM)函數竟然不是一個函數,編譯器在查到OnUserOpen(WPARAM, LPARAM)時,不會調用OnUserOpen 莫非有人在消息映象做了什麼手腳?
其實,這是MFC嚴密的表現,處理時,通過函數指針來調用,而該指針是由發生的事件所決定的.如果句柄不正確定義的話,調用當然是非法的

(52)關於控件的焦點?

有誰能給我一個有效的方法:當一個控件失去焦點時,怎樣管理才能使對話框的焦點進入到正確的控件.
我有一個可運行的程序來實現,不一定很全面,但能工作.
const int WM_VALIDATE_PARAMS WM_APP + 101;
void CMyDlg::OnKillfocusName()
{
    PostMessage(WM_VALIDATE_PARAMS, ED_NAME, 0L);
}
void CMyDlg::OnKillfocusAddress()
{
    PostMessage(WM_VALIDATE_PARAMS, ED_ADDRESS, 0L);
}
bool CMyDlg::OnValidateParams(WPARAM rcId, LPARAM)
{
        switch( rcId )
        {
        case ED_NAME:
        if( !validateName() )
            m_edName.SetFocus();
        break;
        case ED_ADDRESS:
        if( !validateAddress() )
            m_edAddress.SetFocus();
        break;
    default:
        break;
    }
    return true;
}
上面的代碼可以在用戶使用TAB鍵或鼠標操縱時,使用焦點跳至下一個控制.當你想DISABLE一個控件或重設焦點時,會有些問題,特別是在Killfocus事件中。
(53)如何捕獲鍵盤按鍵?

在CTabCtrl的子對話框怎樣才能捕獲ALT+0組合鍵
可以在PreTranslateMessage中截取鍵盤消息。

(54)怎樣實現3D效果?

在對話框中怎樣實現Edit和Listboxes控件的3D效果?(環境95/NT VC5.0)
1). 使用帶WS_EX_CLIENTEDGE標誌的::CreateWindowEx來替換::CreateWindow 或者用CWnd::CreateEx替換CWnd::Create.
2).在建立控件之後,調用ModifyStyleEx(0, WS_EX_CLIENTEDGE).

(55)怎樣建立客戶CSocket?

我有一個客戶socket想在socket中建立一個局域聯接.我使用下列順序:
CSocket* m_pSocket;
m_pSocket = new CSMSSocket(this);
m_pSocket->Create();
m_pSocket->Bind(m_intHostPort, m_strHostIPAddress);
m_pSocket->Connect(lpszAddress, nPort);
但每次Windows Socket都定向到別的端口,怎樣才能定向到同一個端口(環境:95/NT VC5.0).
1).如果你想用Client Socket,你就不能在connect()之前調用bind(),因爲局域端口地址由TCP/IP設置,我們不可能知道下一次將使用那一個端口,我想我們不必這做.
2).看一下Create()的幫助,裏面告訴我們必須給Create()指定一個端口值, 缺省的情況爲0,也就是由Window爲我們選擇一個端口,通過Create()將會自動捆綁. 3).我不認爲你應該完成所有的工作,但想總是用一個相同的端口來連接遠程機器是一個不正確的想法.
問題出在端口數/地址結合必須唯一,如果你想在Create()中指一個固定的端口數,你只能與遠程機器建一個單個連接.在你所寫的代碼中是允許局域端口數可變化,可以打開多個連接來取得相同的地址.在偵聽(listening)Socket中有許多理由使用一個固定端口,但在連接(connecting Socket中我想沒有太多的必要.
(56)Disable一個非模態對話框的客戶區?

我在OCX(對象連接和嵌入客戶控制程序)有一個非模態對話框.它有一個菜單以及工具條.現在我想Disable客戶區(只是客戶區,例如:設置特殊變量時顯示一個等待光標,區域裏的所有控制都不可以處理)但在客戶區的所有控制要看上去沒有變化(也就是不可以Disable)
可以這樣試一下,建立一個子窗口,覆蓋對話框的全部的用戶區域,用WS_EX_TRANSPAPENT 透明類型,然後調用函數EnableWindow(FALSE),使用SetClassLong或者別的方法,在子窗口調用"忙"光標,這時光標就正確了,但對話框中的菜單還能正常使用.(說白了就是建立一個透明的子窗口蓋住所有的用戶區域,然後Disable該透明窗口,在這個窗口中設置光標爲"忙") 這個方法我沒有試過,但在一些老的Windows的書介紹過這種方法.
(57)關於使用SetClassLong和SetCapture問題

我用SetClassLong設置對話框光標時遇到了一些問題,當我使用SetCapture捕獲鼠標時,
光標形狀並沒有變化時,以下爲原代碼:
void CMouseMoveSimDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
  myDragging = TRUE;
  myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR,
   (long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_SELECTCURSOR )));
  SetCapture(m_hWnd);
CDialog::OnLButtonDown(nFlags, point);
}
如果移去SetCapture這一行,光標就會正確的設置,但它就不能正確的捕獲鼠標消息.那兒出問題了(環境NT4.0 VC6.0)?
1).如果我沒有記錯的話,SetClassLong隻影響調用它以後的建立的窗口.可以使用 SetWindowLong來改變已存在的窗口的屬性.(爲什麼要用SetClassLong來改變光標形狀, 爲什麼不在消息WM_SETCURSOR中替換.)
2).我也不清楚問題出在那兒,但下面的方法可以克服SetCapture帶來的問題,它是從我的程序裏面提出來的:

void CScribbleView::OnLButtonDown(UINT nFlags, CPoint point)
{
    ........
    SetCapture(); // Capture the mouse until button up
    myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR,
(long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1)));
SetCursor(LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1)));
    ........
}
void CScribbleView::OnLButtonUp(UINT nFlags, CPoint point)
{
    if( GetCapture() != this )
        return;
    ........
    ReleaseCapture();
    SetClassLong( m_hWnd, GCL_HCURSOR, (long)myhPrevCursor);
    return;
}

(58)動畫控件?

我在對話框中使用一個動畫控件,通常我都是用CAnimated的open成員函數,並加上avi的文件名來使用動畫控件,怎樣在資源文件加入一個avi文件,作爲資源使用?
1).簡單,將avi文件引入資源,按你的喜歡來決定是屬於那一種類型的,通過ID來代替文件的名字,這樣你就可以使用了.
2).在資源窗口中單擊右鍵,在彈出的菜單中選擇"Import".這時會打開文件選擇框,選擇所要的文件,這時系統將會詢問自定義資源類型,輸入avi.一個AVIS的資源組將會創立,你所選的avi文件將會出現在該組中並擁有一個ID.
3).手動在資源文件中加入一個AVI資源說明,比如:
//在這手工編輯資源文件
IDR_ANIMATION AVI res\animate.avi
(59)錯誤聲明的消息?

我給一個視發送一條消息
pView->SendMessage (MY_MESSAGE, wparam, lparam);
消息聲明被認爲是不正確的
afx_msg void OnMyMessage();
高手一看就知道這是一條命令錯誤的聲明對象,正確的聲明應該爲:
afx_msg LERSULT OnMyMessage (WPAPRAM wparam, LPARAM lparam);
因爲我不使用參數,程序工作也很好,所以我不知道爲什麼會有這種錯誤,該過程處理完之後也沒有任何錯誤的信息出現.但現在release版本中有一個奇怪的現象(debug版本中沒有)程序會非正常終止,通常這現象發生在SendMessage()返回之後。爲什麼?
1.相信問題是出在錯誤的堆棧上,"thiscall"調用後就應該清除堆棧,調用者調用時將兩個參數壓入堆棧,但參數卻沒有被清除.如果你真的不需要WPARAM,LPARAM,也不需要返回值的話,你可以使用ON_MESSAGE_VOID 消息聲明.在afxpriv.h中定義,是非文檔的,意思就是它不會有什麼提示或可能中斷程序, 另外,需要注意一下線程消息,注意線程消息是可變的,它們將返回void,沒有LRESULT,同樣的聲明.
2.如果你不使用WParam和LParam,爲什麼不在視中定義一個用戶函數來處理自己想做的?
(59)怎樣模擬鼠標動作?

這是困擾我多時的一個問題,怎樣才能實現模擬鼠標的動作,就是說要使一個程序實現鼠標的單擊,雙擊,拖放等功能.我認爲必須要實現相應的消息傳遞,但每次都不成功.
比如說,我想關閉記事本窗口,可以傳送WM_lBUTTONDOWN和WM_LBUTTONUP(X,Y值爲記事本的右上角關閉按鈕的位置)給記事本窗口,但窗口並沒有關閉.當然,我也知道關閉一個窗口可以通過傳送WM_QUIT或WM_CLOSE來實現,但鼠標的消息爲什麼會丟失?
請教各位大師,怎樣模式模擬實現鼠標的動作,或者給我一些怎樣發送消息來關閉窗口的建議(不是WM_CLOSE或WM_QUIT)
1).試一下window hooks,你可以使用SetWindowsHookEx和JournalPlayback來處理鼠標事件.
2).你可以使用文檔中的SendInput(),它能實現模擬鍵盤或鼠標事件.如果你使用NT,那也可以用老的函數像mouse_event(),keyb_event等,在Win98中,SendInput()一樣可以使用.
3).抱歉不能給你一個滿意的回答,你可以在網站
[url]http://www.microsoft.com/enable/dev/tooldev.htm[/url] 中找到一篇關於模擬輸入的文章.
4).在NT中可以使用mouse_event()傳遞事件,文檔上說這種方法已經過時了,那麼你可以用 SendInput()替換,但找不到關於此函數的使用說明,所以我依然使用mouse_event,沒有任何問題.
(60)改變對話框標題字體?

怎樣改變對話框標題文件的字體,改變資源中對話框屬性中的字體,將改變所有的控件的字體, 卻沒有改變標題,但我只想改變標題字體,不改基餘控件的屬性.是不是我錯過一些明顯的選項. 通過查找一些MFC代碼,我發現有一個CDialog模塊,裏面調用了一引起字體方法,但該對話框不是公用的,我相信它不會給我任何幫助.
1).就我所知,對話框的標題字體和其它的窗口標題一樣,它可以通過系統--顯示器--屬性--外觀來設置,如果自己想這樣做,我想你應該取得WM_NCPAINT句柄自己來畫出非用戶區域(包括標題在內),我從未做這樣做過,可能是個錯誤的方向.
2).如果你是在CView繼承的,那你可以在構造函數中看見如下代碼:

if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) )
        return false;
GetEditCtrl().SetFont( &my_CFont ,true )
接下來如果你想改變在對話框中的一個CEdit控件字體時,可以使用以下代碼:
if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) )
        return false;
( GetDlgItem (ID_ANY_CEDIT) ) ->SetFont( &my_CFont );
(61)怎樣知道CWinThread對象的狀態?

怎樣才能知道一個線程是在運行還是已經終止?
可以利用線程句柄所指的::GetExitCodeThread()函數,如果線程已經結束, 它將返回一個退出代碼,如果還在運行,則返回一個STILL_ACTIVE.不過在之此前,先將 CWinThread成員對象m_bAutoDelete設置爲FALSE.另外對象在線程結束時會自動檢測到.
(62)如何調整控件對話框條的大小?

我想讓用戶能夠在控制條出現時控制它的大小,在所有的例子中,在控件浮動時,改變尺寸還可以,但在工具條停靠在框架上時就無法調整其大小,該怎樣實現?
1)也許你錯過了一些注意點,我用的是codeguru站點上下載的CCoolDialogBar類, 在工具條停靠時也可以重新改變其大小.
2)我開發了一個應用程序,它的界面跟你所說的差不多,讓我試着解釋一下我是怎樣做的.
1.從CDialogBar類中繼承一個類,名爲CMyBar;
2.在CMyBar中增加一個成員變量,int m_iWidth;
3.在CMyBar中的OnPaint和OnNcPaint中畫出工具條(grab bar);
4.拖動工具條時在鼠標事件時繪出軌跡;
5.釋放鼠標時,計算CMyBar新寬度.可以通過取得當前軌跡位置,使m_iWidth等於新的寬度;
6.(重要)GetDockingFrame()->RecalcLayout();
7.在CMybar中增加一個成員方法CalcDynamicLayout;
8.在CalcDynamicLayout中,當工具條停靠時,通過計算m_iWidth返回值.
當然,這只是一個很簡單的方法,你可以做得比這更好.
3)可以試一下VC6.0中的CReBar類
(63)如何頂端顯示CStatic類文字?

我正寫一個小的應用程序,我想顯示一串文本(CStatic)並且無論別的應用程序運行時是否覆蓋,這些文字總會在最上面顯示.
1)用CreateEx來建立一個WS_POPUP窗口,使這個窗口總在最上面(always on top) 然後在該窗口中實現文字顯示.
2)建立窗口時用SetWindowPos()函數,用&wndTopMost作爲第一個參數,這樣就可以完成你想做的了.
(64)消息句柄出了什麼事?

我在CParentView中爲WM_LBUTTONDOWN定義一個句柄,但我建個新的CChildView, 句柄得不到處理.
1)仔細看一下你ChildView文件中的MESSAGE_MAP,可能在第兩個參數匹配 BEGIN_MESSAGE_MAP(Child,Parent)中有着錯誤的基類.如果你是用嚮導生成器, 那麼你很容易就會發生這種事情.
2)檢查一下消息映象宏中的類名和父類名是否正確,比如BEGIN_MESSAGE_MAP (CChildView,CParentView).
如果你用自己的消息句柄手工代替了嚮導所做的,確信你的改動是正確的, 一個錯誤的參數或者加了一個"const"將會改變消息映象而不會被正確調用.
3)我猜想你一定是用類嚮導生成器來建立你的CChildView,而且在基類的選擇中一定是選了CView,自己動手在消息映象中把它修改過來.
(65)樹形控件爲何閃爍?

我從CTreeCtrl中繼承了一個類,以縮進的格式顯示節點,現在我碰上些問題,當樹被重畫兩次之後(一次爲缺省,另一次爲對齊文本時)點選節點樹就會閃爍.
1)試一下LockWindowUpdate()API函數。
2)試一下加入TVS——HASBUTTONS標誌,
ModifyStyleEx(TVS_HASBUTTONS, 0);
....//drawing
ModifyStyleEx(0, TVS_HASBUTTONS);
如果它不再閃爍,那麼在將其定義爲自畫屬性,用PreCreateWindow()中加入CS——OWNDC。
(66)怎樣才能關閉樹形控件中的滾動條?

我想關閉樹形控件的滾動條,但它依然顯示出來,怎樣才能隱藏它?
1)在建立時加入TVS_NOSCROLL,注意此時你就不可以用鍵盤來實現翻頁,這種類型需要comct32.dll4.71版本以上纔可以,並且要在commctrl.h中定義如#define TVS_NOSCROLL 0x2000.
2)值得這樣試一下 ModifyStyle(WS_VSCROLL,0),將這段代碼放在建立之後,顯示之前。
(67)如何建立一個帶滾動條的窗口?

我想建立一個帶滾動的子窗口,但我沒有用嚮導生成器。
如果你讓你的窗口有一個滾動條,你必須首先初始化。如下
   SCROLLINFO si;
   si.cbSize = sizeof( SCROLLINFO );
   si.fMask = SIF_PAGE | SIF_RANGE;
   si.nMin = 0;
   si.nMax = 100;
   si.nPage = 10;
   SetScrollInfo( SB_HORZ, &si );
   si.nMin = 0;
   si.nMax = 50;
   si.nPage = 5;
   SetScrollInfo( SB_VERT, &si );
如果程序運行時你的窗口內容已經改變或者窗口被改變大小而重畫時,你必須重新設置滾動條。在MFC中包含類CScrollView,它已內建滾動條。
(68)如何實現對話框的拖放?

我有一個對話框程序,想讓它實現拖放。但無論用OnDrag或OnDrop等等,所有的的消息都發送給CView類而不是CDialog類,爲什麼?
你應該使用COleDropTarget類,試一下這些:
class CMyOleDropTarget: public COleDropTarget
{
protected:
    virtual DROPEFFECT OnDragEnter( CWnd* pWnd, COleDataObject*
pDataObject, DWORD dwKeyState, CPoint point )
    {
        TRACE( "DRAG Enter\n" );
        return DROPEFFECT_MOVE;
    };
    virtual DROPEFFECT OnDragOver( CWnd* pWnd, COleDataObject*
pDataObject, DWORD dwKeyState, CPoint point )
    {
        TRACE( "DRAG Over\n" );
        return DROPEFFECT_MOVE;
    };
};
CMyOleDropTarget DropTarget;
BOOL CDlgDlg::OnInitDialog()
{
    CDialog::OnInitDialog();
    DropTarget.Register( this );
不要忘記調用AfxOleInit()
BOOL CDlgApp::InitInstance()
{
    AfxEnableControlContainer();
    AfxOleInit();
}
(69)TrackMouseEvent()怎麼了

我使用TrackMouseEvent()函數來跟蹤鼠標是否已經離開我的窗口,但在MFC中,如果我使用 ::TrackMouseEvent()系統告訴我沒有定義,爲什麼?
1).請使用_TrackMouseEvent
2).在commctrl.h顯示爲_TrackMouseEvent(),請注意下劃線.
3).可能TrackMouseEvent()不支持Win98(在NT中工作得非常好),建議你結合WM_MOUSEMOVE消息和 SetCapture()函數,當鼠標移出窗口時你依然可以控制.

(70)奇怪的組合框控件

我有一個對話框程序,裏面只有幾個下拉式給合框.但當鼠標箭頭移動到組合框的上下按鈕時,會變成"6"或"9",一會兒又恢復到原狀,這是爲什麼?
1)也許是你的操作系統有問題,不防重新起動一次也許就行了(概率非常小8%-())你也可以試一下系統清除工具,如果這事情經常發生,可能你真的需要重裝一下95或NT,這也是個好的建議,每隔半年左右可以重裝一下系統.
2).我猜想可能是comctl32.dll文件被破壞了.
3).這個問題的原因很有可能是系統的資源不夠,你可以試着關閉一些程序、減少屏幕的分辨率來增加一些系統資源。
(71)關於使用MS SANS SERIF字體

我看過好多關於創建對話框、組合框等等使用MS SANS SERIF的例子,自己也做過多次。如: m_font.CreatePointFont (80, _T("MS Sans Serif")); 或 m_font.Create (-8, ....., _T("MS Sans Serif")); 那麼想問一下:1)該字體是否在所有的版本中都能實現(包括國際版本) 2)在控制面板上有沒有更好的字體代替“SYSTEM”字體?如果有人這樣做了,那又是怎樣設置字體大小等相關設置的?我希望有一個徹底的方法來選擇組合框等的字體。
1)有件事情我做過,在我所有的程序界面中都改變了字體.消息框來顯示用戶選擇的字體. 菜單,工具條以及其他控件的字體都隨用戶意願改變.但在對話框中最好還是用對話框編輯器, 其基本字體都是MS SANS SERIF,所以我也以這種字體來作爲所有的用戶界面. 以下爲我所做的代碼:

// here's the font I use:
SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
m_fntUI.CreateFontIndirect(&ncm.lfMessageFont);
// here's the code to change the font for a wnd and all it's children, and
resize the controls appropriately
void ChangeDialogFont(CWnd* pWnd, CFont* pFont, int nFlag)
{
CRect windowRect;
// grab old and new text metrics
TEXTMETRIC tmOld, tmNew;
CDC * pDC = pWnd->GetDC();
CFont * pSavedFont = pDC->SelectObject(pWnd->GetFont());
pDC->GetTextMetrics(&tmOld);
pDC->SelectObject(pFont);
pDC->GetTextMetrics(&tmNew);
pDC->SelectObject(pSavedFont);
pWnd->ReleaseDC(pDC);
long oldHeight = tmOld.tmHeight+tmOld.tmExternalLeading;
long newHeight = tmNew.tmHeight+tmNew.tmExternalLeading;
if (nFlag != CDF_NONE)
{
  // calculate new dialog window rectangle
  CRect clientRect, newClientRect, newWindowRect;
  pWnd->GetWindowRect(windowRect);
  pWnd->GetClientRect(clientRect);
  long xDiff = windowRect.Width() - clientRect.Width();
  long yDiff = windowRect.Height() - clientRect.Height();
  newClientRect.left = newClientRect.top = 0;
  newClientRect.right = clientRect.right * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
  newClientRect.bottom = clientRect.bottom * newHeight / oldHeight;
  if (nFlag == CDF_TOPLEFT) // resize with origin at top/left of window
  {
   newWindowRect.left = windowRect.left;
   newWindowRect.top = windowRect.top;
   newWindowRect.right = windowRect.left + newClientRect.right + xDiff;
   newWindowRect.bottom = windowRect.top + newClientRect.bottom + yDiff;
  }
  else if (nFlag == CDF_CENTER) // resize with origin at center of window
  {
   newWindowRect.left = windowRect.left -
       (newClientRect.right - clientRect.right)/2;
   newWindowRect.top = windowRect.top -
       (newClientRect.bottom - clientRect.bottom)/2;
   newWindowRect.right = newWindowRect.left + newClientRect.right + xDiff;
   newWindowRect.bottom = newWindowRect.top + newClientRect.bottom + yDiff;
  }
  pWnd->MoveWindow(newWindowRect);
}
pWnd->SetFont(pFont);
// iterate through and move all child windows and change their font.
CWnd* pChildWnd = pWnd->GetWindow(GW_CHILD);
while (pChildWnd)
{
  pChildWnd->SetFont(pFont);
  pChildWnd->GetWindowRect(windowRect);
  CString strClass;
  ::GetClassName(pChildWnd->m_hWnd, strClass.GetBufferSetLength(32), 31);
  strClass.MakeUpper();
  if(strClass==_T("COMBOBOX"))
  {
   CRect rect;
   pChildWnd->SendMessage(CB_GETDROPPEDCONTROLRECT,0,(LPARAM) &rect);
   windowRect.right = rect.right;
   windowRect.bottom = rect.bottom;
  }
  pWnd->ScreenToClient(windowRect);
  windowRect.left = windowRect.left * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
  windowRect.right = windowRect.right * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
  windowRect.top = windowRect.top * newHeight / oldHeight;
  windowRect.bottom = windowRect.bottom * newHeight / oldHeight;
  pChildWnd->MoveWindow(windowRect);
  pChildWnd = pChildWnd->GetWindow(GW_HWNDNEXT);
}
}

(72)爲什麼DLL在字符串表中找不到字符串

我用嚮導生成器中的"Use MFC in a Shared DLL"選項建立一個DLL,在字符串表資源中加一個字符串,當我使用csMyString.LoadString( IDS_MY_STRING ) csMyString 是空的,爲什麼會這樣?
1)MFC是由AfxGetResourceHandle調用資源的.所以,如果你想在你的DLL中讀出資源應該使用 AfxSetResourceHandle.你也可以在LoadLibrary的返回值中得到它,如果不想調用該DLL時也可以使用DLLMain函數的hInstance參數.
2)試一下在你函數打頭處使用AFX_MANAGE_STATE(AfxGetStaticModuleState()) (事實上每個被外部DLL調用的每一個函數都會使用它)
3)我記得先前的列表講過這個問題,試一下以下兩種方法: 如果你是用LoadLibrary()來調用DLL的,它會返回一個句柄,你可以在 AfxSetResourceHandle()中使用它.如:

   hinstnew = Loadbrary(...);
   ...
   hinstOld = AfxGetResourceHandle();
   AfxSetResourceHandle(hinstnew);
   LoadString(IDS_MY_STRING);
   AfxSetResourceHandle(hinstOld); // remember to set this back,
                           // or your night won't be nice.
如果你不是用LoadLibrary來調用DLL又該怎樣辦呢?你可以使用 GetModule("You DLL Name")來取得用戶句柄,剩下的就好辦了.

(73)關於複選框的文本顏色

有誰知道怎樣才能改變複選框中的文本選項的顏色?
1)你有沒有試過在控件中使用OnCtlColor,它將在重畫任何控件之前被調用,所以你可以有機會來改變文本選項的顏色。
2)爲什麼你一定要用PreDrawItem()?你是想在裏面做一些特定的代碼?我認爲DrawItem() 也能處理。在調用重畫函數之前取得索引號並改變顏色。

(74)系列化與版本的問題

我需要使用系列化來讀取我的文件,爲了保證文件能在各個版本中都能實現,我作了儘可能的努力,爲什麼會不成功.
答:下面的代碼是我過去使用過的,希望能對你有所幫助
// Use this macro to fix the versioning problem in the MFC
// Place it at the beginning of your CMyObject::Serialize implementation -
// it will guarantee that the correct version of the class is written to
// and read from the archive
//
// Usage: SERIALIZE_VERSION(CMyObject)
#define SERIALIZE_VERSION(this_class)     ar.SerializeClass(this_class::GetRuntimeClass());
// For classes which cannot use IMPLEMENT_SERIAL (such as abstract
// base classes). This guarantees the object can have
[Read/Write][Class/Object]
// called on it by placing a schema number in it. It also puts it in the
// list of known class names (AFX_CLASSINIT).
// Note: this is almost the same as IMPLEMENT_SERIAL_ABC
// in "MFC Internals", but this version uses AFX_CLASSINIT,
// with the result that it works!
#define DECLARE_DYNAMIC_SERIAL(class_name)     DECLARE_SERIAL(class_name)
#define IMPLEMENT_DYNAMIC_SERIAL(class_name, base_class_name, wSchema)     _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, NULL)    static const AFX_CLASSINIT
_init_##class_name(RUNTIME_CLASS(class_name));     CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb)         { pOb = (class_name*)
ar.ReadObject(RUNTIME_CLASS(class_name));             return ar; }
或者也可以這樣實現:
CMySerialRootDerivedClass::Serialize(CArchive& ar)
{
    //CMySerialRoot::Serialize(ar); // <- do not call this here
    if (ar.IsStoring())
    {
        ... store derived stuff here
    }
    else
    {
        int nVersion = ar.GetObjectSchema();
        switch(nVersion)
        {
        case 1:
            ... load derived version 1 stuff here
            break;
        case 2:
            ... load derived version 2 stuff here
            break;
        default:
            // report unknown version of
            // this object
            break;
        }
    }
    // serialize the base class version information
    // -> then serialize the base class
    ar.SerializeClass(RUNTIME_CLASS(CMySerialRoot));
    CMySerialRoot::Serialize( ar );
}
(75)在一個控件內檢測並使用ON_COMMAND消息

有一個控件(繼承CWnd)在CRormView.可不可以將它的ID在ON_COMMAND消息中發出,如果用pCtrl->OnCommand(ID_VIEW_ZOOMIN,..), 編譯器會報告參數不匹配,該怎麼辦?
1)爲什麼不用pCtrl->Post/SendMessage (WM_COMMAND, ID_VIEW_ZOOMIN)
2)通過重載CYourFormView::OnCmdMsg就可以.如:

BOOL CYourFormView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
return pCtrl->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)
  || CFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
3)使用WM_COMMAND消息,看一下關於WM_COMMAND和CWnd::PostMessage()的幫助.
DWORD wParam;
HIWORD(wParam) = wNotifyCode; // notification code
LOWORD(wParam) = ID_VIEW_ZOOMIN;
pCtrl->PostMessage(WM_COMMAND,(WPARAM)wParam, pCtrl->m_hWnd);
4)能夠這樣做,但不是象你們做法,你們必須得到控件的句柄或CWnd指針然後在句柄中使用::SendMessage() or ::PostMessage();在CWnd中使用 CWnd::SendMessage() or CWnd::PostMessage() 試一下這個.
CMyCtrl *pCtrl;
/* call GetDlgItem() from an instance of your form view */
pCtrl = ( CMyCtrl * )GetDlgItem( IDC_MYCONTROL );
if( pCtrl != NULL && ::IsWindow( pCtrl->GetSafeHwnd( ) )
pCtrl->SendMessage( WM_COMMAND, /*wParam*/, /*lParam*/ );
// see WM_COMMAND description on help/MSDN for a detailed explanation of
// {W|L}PARAM

(76)爲何MDI程序中有子窗口打開時主應用程序不能關.

我在MDI程序中增加了一個CRichEditView文檔模板,在子窗口視中我增加了下面一些代碼.
StartReport (void)
{
CReportFrame *rpt;
CReportDoc *rptDoc;
   // First get the right document template
POSITION pPos = theApp.GetFirstDocTemplatePosition();
theApp.GetNextDocTemplate ( pPos );
theApp.GetNextDocTemplate ( pPos );
CDocTemplate *pTemplate = theApp.GetNextDocTemplate ( pPos );
   // Verify validity
ASSERT(pTemplate != NULL);
ASSERT_KINDOF(CDocTemplate, pTemplate);
   // Create the frame
rptDoc = new CReportDoc;
rpt = (CReportFrame*)pTemplate->CreateNewFrame ( rptDoc, NULL );
pTemplate->InitialUpdateFrame (rpt, rptDoc);
   // Get access to the display area
CReportView *rptView = static_cast(rpt->GetActiveView());
CRichEditCtrl &rptCtrl = rptView->GetRichEditCtrl();
}
CReportFrame繼承於CMDIChildWnd
CReportDoc繼承於CRichEditDoc
CReportView繼承於from CRichEditView
  如果我關閉程序前不關閉新建的視,調試器將認爲程序依然在運行(程序管理器中依然存在) 我需要用調試菜單中的stop debugging來關閉程序;如果我手工關閉該視,程序將會正常關閉.如果有什麼不同的話,在手工關閉新的視之前程序會詢問是否保存. 那麼怎樣我才能關閉程序呢?
1)我也碰上過對話框,窗口不能自動關閉的情況,這主要是因爲繼承的對象不正確所造成的。通常應該在主程序中設置AfxGetMainWnd().
  你的程序讓我搞糊塗了,一連使用了多個GetNextDocTemplate(pPos),在這些文檔指針是NULL時通常會引起一些循環.在你的文檔模板中是否已經精心算好了數目?這樣可能會產生些bugs 我建議找出當前的文檔模板用CDocTemplate::CreateNewDocument()來代替你的"new CReportDoc"
2) 記住一個公共規則,關閉程序前要關閉所有的視.

(77)滾動視中LPtoDP失敗

在WINDOWS98/95中,當你給光標指針位置大於32767或者小於-21768函數CDC::LPtoDP 將失敗,程序工作在NT上但在95/98中用滾動視工作時卻出現了問題. LPtoDP是在下面函數中被調用的:
      SetScrollSizes(MM_HIMETRIC, sizeTotal);
函數是在CScrollView中調用的.我使用的是HIMETRIC映射方式,在我想將A4擴大150%時這個問題就會出現。怎樣才能解決這個問題?
1)在95中確實存在這樣的問題,95中的GDI不是32位的.當我們開發一個程序有編輯矢量圖象時手動而不是由LPtoDP()函數來完成轉換.(在NT中也存在同樣的問題)
2)簡言之,CScrollView或CWnd之所以32位參數會失敗是因爲95/98並不是真正的32 位操作系統,裏面仍然包含16位代碼.比如Scrollbars還是隻接受16位的值來調整範圍. NT是一個真正的32位操作系統,就沒有這些困惑.
  在95中不得不面對類似的滾動大文檔的問題時,我們只能另外寫些代碼來實現滾動的實際位置,當它超出-32K或+32K時,你也必須在你的應用中做些映射.
  作爲一個有關的注意點(可能你已經碰上過這個問題)如果在MFC處理滾動消息時,如: void CSomeWnd::OnVScroll (UINT nSBCode,UINT nPos, CScrollBar *pscrollBar) 中的 nPos參數只有16位長.克服這個限制可以使用SCROOLINFO結構運行::GetScrollInfo.SCROLLINFO 結構中的nTrackPos是一個真正的32位。
(78)ODBC許可問題

我有個程序想通過ODBC來使用一個MS Access數據庫,但是卻碰上了錯誤,系統顯示 "Records can't be read; no read permission on table SESSION".(記錄不能讀, 表單不允許讀)
)首先我假設access數據庫有一個缺省的用戶爲"admin",可以這樣完成"ODBC;UID=admin". 然後,當你繼承CRecordset類時你就不必帶參數打開,但下面的方法可能更好些:
Open(CRecordset::dynaset, NULL,CRecordset::useBookmarks | CRecordset::skipDeletedRecords)
  當然你必須提供DSN表示連接名字的數據庫在ODBC之下.
(79)怪異的字體

我們有一個MFC應用程序,主窗口是在客戶區域內畫些文本和圖形. 我們希望能在客戶區域內顯示文本,在不需要時則擦除.所以我們先得到一個DC(CClientDC), 然後設置字體和文本顏色就開始寫文本,在擦除時,我們用同樣的字體,同樣的地方用背景色重寫文本.
  這種方法絕大部分情況下都工作得很好,但偶爾文本並不能完全擦除,有些像素點依然可見. 好象在寫文本時比通常略微胖了些,就象用粗體一樣.字體是在寫文本時使用的,以後也沒有進行過任何的調整. 下面是我們使用的寫與擦除的函數.
void CSign::DrawSignName(CDC* pDC)
{
int OldBkMode;
// select the appropriate font
CFont* pOldFont = (CFont*) pDC->SelectObject(pSignNameFont);
OldBkMode = pDC->SetBkMode(TRANSPARENT);
// determine the colour of the text
if (IsSignNameVisible())
  pDC->SetTextColor(aColours[SIGN_NAME_COLOUR]);
else
  pDC->SetTextColor(aColours[DEVICE_INVISIBLE_COLOUR]);
// draw the text
pDC->TextOut(m_pointNameCoords.x, m_pointNameCoords.y, m_strName);
// restore the previously used font and background mode
pDC->SelectObject(pOldFont);
pDC->SetBkMode(OldBkMode);
} // DrawSignName
  函數是在消息句柄中調用的,而參數中的DC是這樣建立的:
CClientDC dc(AfxGetMainWnd()).
  字體是在程序初始化時建立的:
pSignNameFont = new CFont;
pSignNameFont->CreateFont(10,5,0,0,150,
       FALSE,FALSE,0,
       ANSI_CHARSET, OUT_DEFAULT_PRECIS,
       CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
       DEFAULT_PITCH | FF_SWISS, "Helvetica");
  是不是一次使用兩個指向同一個客戶窗口的DC有問題?程序中的DrawSignName()被多個消息句柄調用。
1)加入以下代碼:
{
m_strName.Empty();
Invalidate();
UpdateWindow();
more stuff;;;
}
  上面代碼會產生一個WM_ERASEBKGND消息,將會用背景色填滿窗口,然後再調用OnDraw(),這時只要將字符串置空即可。
2)我不清楚爲什麼程序不能正常工作,但我有個主意(它會更快些)可以在顯示文本的地方用一個背景色的矩形畫一下即可。我也不清楚爲什麼你們爲什麼要用透明文本,它將會給圖形系統帶來大量的工作。字體之所以有這種情況,是否你們安裝了文本輸出的圖形保真軟件?它會給你們帶來困惑的。
3)你只想簡單的用一個指針來保存一個指向DC的GDI對象,並試圖再次調用它時期望它能指向正確的對象。恕我直言,這不是正確的方法(我不知道是否這是顯示不正常的唯一原因)將它轉化爲一個Windows句柄纔是正確的:
//
// Creating:
//
pSignNameFont = new CFont;
pSignNameFont->CreateFont(10,5,0,0,150,
        FALSE,FALSE,0,
        ANSI_CHARSET, OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
        DEFAULT_PITCH | FF_SWISS, "Helvetica");
// Now converting into a windows handle
m_hSNFont = (HFONT) pSignNameFont->GetSafeHandle();
  直接保存一個對象是不安全的。
(80)自畫列表框樣例

很久以前,有人散發關於自畫列表框控件代碼,而自畫列表框外觀就象一個標準列表框,在那時我就有個想法想把程序員開發的所有自畫控件的代碼懼收集起來,這樣程序員們就可以使用現存的代碼了。
我想問一下在1996年關於MFC站點那兒有才能關於列表框或其它控件的代碼?
1)自畫列表框代碼如下,看看是不是你所想要的。
Header file
class CCustomListBox : public CListBox
{
public:
// Operations
    DECLARE_DYNCREATE(CCustomListBox)
    int AddLBItem(LPSTR);
    void HandleSelectionState(LPDRAWITEMSTRUCT lpdis);
    void HandleFocusState(LPDRAWITEMSTRUCT lpdis);
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
};
cpp file
IMPLEMENT_DYNCREATE(CCustomListBox, CListBox)
int CCustomListBox::AddLBItem(LPSTR itemStr)
{
    AddString((LPCSTR)itemStr);
    return 0;
}
void CCustomListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
    CDC* pDC = CDC::FromHandle(lpDIS->hDC);
    if ((lpDIS->itemState & ODS_SELECTED) &&
        (lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
    {
        pDC->InvertRect(&lpDIS->rcItem);
        pDC->DrawFocusRect(&lpDIS->rcItem);
    }
    if (!(lpDIS->itemState & ODS_SELECTED) &&
        (lpDIS->itemAction & ODA_SELECT))
    {
        pDC->InvertRect(&lpDIS->rcItem);
        pDC->DrawFocusRect(&lpDIS->rcItem);
    }
}
void CCustomListBox::HandleSelectionState(LPDRAWITEMSTRUCT lpdis)
{
// Ordinarily could check for "if (lpdis->itemState & ODS_SELECTED)"
// and do drawing for selected state, "else" draw non-selected state.
// But second call to InvertRect restores rectangle to original
// state, so will just call function whether selected or unselected.
    ::InvertRect (lpdis->hDC, (LPRECT)&lpdis->rcItem);
}
void CCustomListBox::HandleFocusState(LPDRAWITEMSTRUCT lpdis)
{
// Ordinarily would check for "if (lpdis->itemState & ODS_FOCUS)"
// and do drawing for focus state, "else" draw non-focus state.
// But second call to DrawFocusRect restores rectangle to original
// state, so will just call function whether focus or non-focus.
// New to Windows 3.0, this function draws a black dashed-rect
// border on the border of the specified rectangle
    ::DrawFocusRect( lpdis->hDC, (LPRECT) &lpdis->rcItem );
}
2)
[url]http://toronto.planeteer.com/~zalmoxe/[/url]
(81)CWnd::GetMenu()的問題

我有個程序用下面代碼:
    CWnd *pWnd = CWnd::GeForegroundWindow();
    if (pWnd == NULL) return FALSE;
    CMenu *pMenu = pWnd->GetMenu();
    if (pMenu == NULL) return FALSE;
    for (int i = 0; i < pMenu->GetMenuItemCount; i++) {
      pMenu->GetMenuItemID(...);
      pMenu->GetMenuString(...);
    }
  上述代碼工作除了在IE窗口外,別的窗口工作都很正常,請問怎樣才能在IE窗口中正常使用,如果不是用這種方法,那又該用什麼方法?
IE有一個定義菜單,是用自定義系列控件中的彈出菜單。所以你就不能再使用枚舉這種方法了,試一下處理WM_INITMENUPOPUP或WM_INITMENU。在VC的CD中有類似的例子(關於剪切與複製)你得到消息句柄時就可以列出所有的菜單項。上面的代碼之所不工作可能是因爲微軟的自畫菜單項的保存菜單項用了不同的格式,想要明白菜單和畫標是否是自畫的,你可以用這種方法測試lpmii->fType & MFT_OWNERDRAW.Ipmii是一個菜單結構,返回得到的菜單項信息。lpmii->dwTypeData 返回(菜單)項目的類型,如果dwTypeData返回的值沒有什麼用的話還有一個機會,lpmii->dwItemData將指向一個(程序)開始時的菜單項中的字符串結構。以上方法比較好,因爲現在好多程序都使用自定義菜單。
(82)用MFC製作彈出窗口

我正在試着用MFC來製作彈出窗口,我看過一些關於建立彈出窗口的文章,它們是使用 CWnd對象的。但在文檔,視窗結構中是怎樣實現的?
你可以建立一個非模態對話框(使用Create函數),你可以在任何建立窗口,子窗口等。如果你一定要在文檔、視窗結構中實現,你也可以用CCreateContest類。下面是建立MDI窗口的例子:

{
    LPCTSTR lpszClassName = NULL;
    CCreateContext cContext;
    cContext.m_pNewViewClass = RUNTIME_CLASS ( CMyView )
    DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW &
~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
    // TOD Add your specialized code here and/or call the base class
    if ( CMDIChildWnd::Create(lpszClassName, lpszWindowName, dwStyle,
pParentWnd->rectDefault, pParentWnd, &cContext) )
    {
        InitialUpdateFrame ( NULL, TRUE );
        CScrollView *pView = ( CScrollView* ) GetActiveView();
        if ( pView )
            pView->ResizeParentToFit ( FALSE );
        return TRUE;
    }
    else
        return FALSE;
}
  CCreateContext有一個成員爲m_pCurrentDoc,你可以用它來將一個文檔分配到相應的窗口上.
(83)怎樣取消一個彈出式菜單

我有一個應用程序不顯示窗口(建立窗口時使用了SW_HIDE參數),它只在任務條顯示一個圖標,我是這樣做的:
        NOTIFYICONDATA tnid;
        tnid.cbSize = sizeof(NOTIFYICONDATA);
        tnid.hWnd = m_hWnd;
        tnid.uID = 1;
        tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
        tnid.uCallbackMessage = MYWM_NOTIFYICON;
        tnid.hIcon = AfxGetApp()->LoadIcon( IDI_ICON1 );
        lstrcpyn(tnid.szTip, "Giroimag Image Mail Exchange", strlen("Giroimag Image Mail Exchange")+1);
        Shell_NotifyIcon(NIM_ADD, &tnid);
  當我點擊任務條時,程序會顯示一個彈出菜單:
        CMenu m_Menu;
        m_Menu.CreatePopupMenu();
        m_Menu.AppendMenu( MF_STRING, IDM_ABOUT, "Op&1" );
        m_Menu.AppendMenu( MF_SEPARATOR, 0 );
        m_Menu.AppendMenu( MF_STRING, IDM_CONFIG, "Op&2" );
        m_Menu.AppendMenu( MF_STRING, IDM_STATUS, ""Op&3" );
        m_Menu.AppendMenu( MF_SEPARATOR, 0 );
        m_Menu.AppendMenu( MF_STRING, IDM_SEND, "Op&4" );
        m_Menu.AppendMenu( MF_STRING, IDM_RECEIVE, "Op&5" );
        m_Menu.AppendMenu( MF_SEPARATOR, 0 );
        m_Menu.AppendMenu( MF_STRING, IDM_CLOSE, "Op&6" );
        POINT p;
        GetCursorPos( & p );
        m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this );
  到這爲止,程序運行很正常,問題在於如果我不選擇任何菜單該怎樣取消它?我以爲按ESC或者在菜單外面點擊就可以取消,但事實並不是這樣。我也試過用WIN32API中的TrackPopupMenuEx函數但沒有用,到底我該怎麼做?
1)最簡單的方法在消息映象中加"Cancel Menu"命令即可。
2)儘管你的主窗口不可見,但在你可以在調用m_Menu.TrackPopupMenu();時將其置爲最前。
3)在你彈出菜單之前,設置你的窗口爲最前窗口,調用下面的代碼,問題就會迎刃而解。
POINT p;
GetCursorPos( & p );
// Increase the thread priority by invoking SetForegroundWindow.
SetForegroundWindow();
m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this );
  4)調用TrackPopupMenu()之前,你必須先調用SetForegroundWindow( m_hWnd ),然後調用PostMessage( m_hWnd, WM_NULL, 0, 0 ):
         POINT point;
         GetCursorPos( &point );
         SetForegroundWindow( m_hWnd );
         TrackPopupMenu( hPopup,
            TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
            point.x,
            point.y,
            0,
            m_hWnd, 0 );
         PostMessage( m_hWnd, WM_NULL, 0, 0 );
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章