Bootstrap

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加入了网络浏览器,我该如何对事件进行处理?

www.vcdj.com(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,绘画时会带来一些问题, 你可以使用一些定制代码,在www.codeguru.com站点上有下载,如果你使用的是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).抱歉不能给你一个满意的回答,你可以在网站
http://www.microsoft.com/enable/dev/tooldev.htm 中找到一篇关于模拟输入的文章.
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)
http://toronto.planeteer.com/~zalmoxe/

(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 );

;