(转)MFC技巧学习<六>
66. 解决外部符号错误:_main,_WinMain@16,__beginthreadex
在创建MFC项目时, 不使用MFC AppWizard向导, 如果没有设置好项目参数, 就会在编译时产生很多连接错误, 如error LNK2001错误, 典型的错误提示有:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
下面介绍解决的方法:
1). Windows子系统设置错误, 提示:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Windows项目要使用Windows子系统, 而不是Console, 可以这样设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:console改成/subsystem:windows
2). Console子系统设置错误, 提示:
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
控制台项目要使用Console子系统, 而不是Windows, 设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:windows改成/subsystem:console
3). 程序入口设置错误, 提示:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
通常, MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可
4). 线程运行时库设置错误, 提示:
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
这是因为MFC要使用多线程时库, 需要更改设置:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded
其中,
Single-Threaded单线程静态链接库(release版本)
Multithreaded多线程静态链接库(release版本)
multithreaded DLL多线程动态链接库(release版本)
Debug Single-Threaded单线程静态链接库(debug版本)
Debug Multithreaded多线程静态链接库(debug版本)
Debug Multithreaded DLL多线程动态链接库(debug版本)
单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用
67. 创建包含多个子目录的目录
void CreateAllDirectories(CString strDir) { //remove ending / if exists if(strDir.Right(1)=="\\") strDir=strDir.Left(strDir.GetLength()-1); // base case . . .if directory exists if(GetFileAttributes(strDir)!=-1) return; // recursive call, one less directory int nFound = strDir.ReverseFind('\\'); CreateAllDirectories(strDir.Left(nFound)); // actual work CreateDirectory(strDir,NULL); }
68. ReverseFind()
#include <STDIO.H> #include <AFX.H> int main() { CString s; s.Format("abcdefghijk"); int nPos = s.ReverseFind('a'); printf("nPos is %d\n",nPos); return 0; }
其中,'a'对应的nPos是0,'h'对应的nPos是7,以此类推。但是:s.ReverseFind('a')和s.Find('a')的结果是一样的。
问题是:ReverseFind() 和 Find() 有什么区别呢:
对于ReverseFind(),查找顺序是从后往前,找到后的nPos是按前后顺序排列的。
而Find()是从前往后查的,找到后的nPos也是按前后顺序排列的。
69. MDI中如何只屏蔽掉子框架的右上角的关闭按钮
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1) return -1; 。。。 CMenu* pSysMenu = GetSystemMenu(FALSE); pSysMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND |MF_DISABLED|MF_GRAYED); return 0; }
70. 程序如何删除自己
///////////////////////////////////////////////// int WINAPI WinMain(HINSTANCE h, HINSTANCE b, LPSTR psz, int n) { // Is this the Original EXE or the clone EXE? // If the command-line 1 argument, this is the Original EXE // If the command-line >1 argument, this is the clone EXE if (__argc == 1) { // Original EXE: Spawn clone EXE to delete this EXE // Copy this EXEcutable image into the user''s temp directory TCHAR szPathOrig[_MAX_PATH], szPathClone[_MAX_PATH]; GetModuleFileName(NULL, szPathOrig, _MAX_PATH); GetTempPath(_MAX_PATH, szPathClone); GetTempFileName(szPathClone, __TEXT("Del"), 0, szPathClone); CopyFile(szPathOrig, szPathClone, FALSE); //***注意了***: // Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL, OPEN_EXISTI NG, FILE_FLAG_DELETE_ON_CLOSE, NULL); // Spawn the clone EXE passing it our EXE''s process handle // and the full path name to the Original EXE file. TCHAR szCmdLine[512]; HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE, GetCurrentProcessId()); wsprintf(szCmdLine, __TEXT("%s %d \"%s\""), szPathClone, hProcessOrig, szPat hOrig); STARTUPINFO si; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); PROCESS_INFORMATION pi; CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi); CloseHandle(hProcessOrig); CloseHandle(hfile); // This original process can now terminate. } else { // Clone EXE: When original EXE terminates, delete it HANDLE hProcessOrig = (HANDLE) _ttoi(__targv[1]); WaitForSingleObject(hProcessOrig, INFINITE); CloseHandle(hProcessOrig); DeleteFile(__targv[2]); // Insert code here to remove the subdirectory too (if desired). // The system will delete the clone EXE automatically // because it was opened with FILE_FLAG_DELETE_ON_CLOSE } return(0); }
这一段程序思路很简单:不是不能在运行时直接删除本身吗?好,那么程序先复制(CLONE)一个自己,用复制品起动另一个进程,然后自己结束运行,则原来的EXE文件不被系统保护.这时由新进程作为杀手删除原来的EXE文件,并且继续完成程序其他的功能。
新进程在运行结束后,复制品被自动删除。这又是值得介绍的一个把戏了,注意:
// Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
这里面的FILE_FLAG_DELETE_ON_CLOSE标志,这个标志是告诉操作系统,当和这个文件相关的所有句柄都被关闭之后(包括上面这个CREATEFILE创建的句炳),就把这个文件删除。几乎所有的临时文件在创建时,都指明了这个标志。另外要注意的是:在复制品进程对原始程序操刀之前,应该等待原进程退出.在这里用的是进程同步技术.用
HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE,GetCurrentProcessId());
得到原进程句柄.SYNCHRONICE标志在NT下有效,作用是使OpenProcess得到的句柄可以做为同步对象.复制品进程用WaitForSingleObject函数进行同步,然后一个DeleteFile,以及进行其它销毁证据(比如删目录)的工作,一切就完事了。
程序是基于CONSOLE的,通过传入的参数确定是原始的进程还是复制品新进程,并且得到需要操作的目标文件的信息(主要是路径),复制品放在系统的TEMP目录(GetTempPath得到),你也可以随便找个你认为安全的地方(比如:WINDOWS\SYSTEM32等等)。这里面没有甚么深的技术.再看其他的一些实现删除自己的例子,比如说在进程退出前,用fwrite等方法输出一个.BAT文件,在里面写几句DEL,然后WINEXEC一下这个BAT文件即可.玩儿过DOS的虫虫大多都会。
71. 隐藏标题栏和菜单栏
隐藏标题栏 ModifyStyle(WS_CAPTION,0)
隐藏菜单栏 SetMenu(NULL)
72. InflateRect
InflateRect这个函数用于增大或减小一个矩形的大小.如
m_graphRect.InflateRect(-70, -30, -30, -50);
将矩形左边坐标加70,上面加30,右边减30,下面减50。
73. 怎么让无模式对话框显示在主窗口后面
要解决这个问题的关键在于CDialog的Create并不能建立一个无属主的窗口.必须用另外方式建窗口.
比如你的对话框类叫CDlgNoOwner,在CMainFrame中加一个CDlgNoOwner类的成员变量,
弹出这个对话框的消息处理函数为
void CMainFrame::OnNoowner() { CDlgNoOwner *m_dlgTest=new CDlgNoOwner(this); HWND hwndDlg=::CreateDialog(AfxGetInstanceHandle(),MAKEINTRESOURCE(CDlgNoOwner::IDD),NULL/*owner*/,NULL/*dlgproc*/); //注意此处DLGPROC为NULL,并不要紧,因为接下要subclass啦 m_dlgTest->SubclassWindow (hwndDlg);//挂接到成员变量! m_dlgTest->ShowWindow (SW_SHOW); //这时可以看到一个"自由"的对话框弹出,和你的主窗口是平起平坐的. }
当然不要忘了在对话框关闭时DestroyWindow()..那都是在对话框类中的标准处理了.
74. 隐藏窗口(子窗口没有焦点时)
在程序启动时 InitDialog 中使用 SetWindowPos 将窗体设置到屏幕以外
然后再隐藏
1.在OnInitDialog()函数里设置定时器:(WINDOWS API里面响应消息WM_INITDIALOG)
SetTimer(1, 1, NULL);
2.添加处理WM_TIMER的消息处理函数OnTimer,添加代码:
if(nIDEvent == 1)
{
DeleteTimer(1);
ShowWindow(SW_HIDE);
}
75.修改视图背景
How do I change the background color of a view?
To change the background color for a CView, CFrameWnd, or CWnd object, process the WM_ERASEBKGND message. The following code shows how:
BOOL CSampleView::OnEraseBkgnd(CDC* pDC) { // Set brush to desired background color. CBrush backBrush(RGB(255, 128, 128)); // Save old brush. CBrush* pOldBrush = pDC->SelectObject(&backBrush); CRect rect; pDC->GetClipBox(&rect); // Erase the area needed. pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATCOPY); pDC->SelectObject(pOldBrush); return TRUE; }
I solved the problem like this:
HBRUSH dlgtest::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { switch (nCtlColor) { case CTLCOLOR_BTN: case CTLCOLOR_STATIC: { pDC->SetBkMode(TRANSPARENT); } case CTLCOLOR_DLG: { CBrush* back_brush; COLORREF color; color = (COLORREF) GetSysColor(COLOR_BTNFACE); back_brush = new CBrush(color); return (HBRUSH) (back_brush->m_hObject); } } return(CFormView::OnCtlColor(pDC, pWnd, nCtlColor)); }
76. 如何实现点击对话框外的地方使对话框到主窗口的后面
只能将桌面做为父窗口
pMDlg = new CMDlg; pMDlg->Create(IDD_M_DIALOG,CWnd::GetDesktopWindow()/* 设置父窗口 */); pMDlg->ShowWindow(SW_SHOW);然后在任务栏里隐藏对话框程序
如何让对话框应用程序在在任务栏上不出现,并且不隐藏窗口。
[解决方法]
把对话框的扩展属性修改成为WS_EX_TOOLWINDOW。
[程序实现]
把对话框的属性设置成为toolwindow,然后在需要的地方执行本代码。
{
DWORD Style = ::GetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE); Style = WS_EX_TOOLWINDOW ; AfxGetMainWnd()->ShowWindow(FALSE); ::SetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE,Style); AfxGetMainWnd()->ShowWindow(TRUE);
}
77. 想在程序一启动时就自动关闭窗口,不在任务栏里显示
用CTRL+W打开ClassWizard;
点击Class Info页,类名是工程名Dlg,
再在左下方的"Filter"中选择"Windows";
回到Message Maps页,就可以看到消息中有WM_WINDOWPOSCHANGING,
加入代码,如上所示.
这样运行*.EXE,不但看不到主界面,任务栏也没有,就是任务管理器中的"应用程序"中也不列出,那该如何关闭它?
在任务管理器的"进程"中可以找到它,这是黑客程序常用的方法.
如果需要的话,连"进程"中也看不到.这样要终止它就是问题了.
78.修改打印预览的ToolBar
为AFX_IDD_PREVIEW_TOOLBAR这个ID创建一个DialogBar。则系统就会用新创建的DialogBar代替系统默认的那个
79. 如何实现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.否则会出错。
80. 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);
}
81. 如何用键盘滚动分割的视口?
我的问题是当我用鼠标滚动分割窗口时,视口滚动都很正常,但用键盘时,却什么也没有发生.
在你的视图继承类中加入如下两个函数,假定该类为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; }
82. 如何改变默认的光标形状?
我试着将光标改变为其它的形状和颜色,但却没有变化.
在对话框/窗口/你需要的地方加上对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.
83. 如何选择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; }
84. 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); } }
85. 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; }
86. 如何限制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; }
87. 怎样实现3D效果?
在对话框中怎样实现Edit和Listboxes控件的3D效果?(环境95/NT VC5.0)
1). 使用带WS_EX_CLIENTEDGE标志的::CreateWindowEx来替换::CreateWindow 或者用CWnd::CreateEx替换CWnd::Create.
2).在建立控件之后,调用ModifyStyleEx(0, WS_EX_CLIENTEDGE).
88. How do I update the text of a pane in a status bar?
By default, a CStatusBar pane is not enabled when the pane is created. To activate a pane, you must call the ON_UPDATE_COMMAND_UI() macro for each pane on the status bar and update the panes. Because panes do not send WM_COMMAND messages, you cannot use ClassWizard to activate panes; you must type the code manually. For example, suppose one pane has ID_INDICATOR_PAGE as its identifier and that it contains the current page number in a document. To make the ID_INDICATOR_PAGE pane display text, add the following to a header file (probably the MAINFRM.H file):
afx_msg void OnUpdatePage(CCmdUI *pCmdUI);
Add the following to the application message map:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_PAGE, OnUpdatePage)
Add the following to a source code file (probably MAINFRM.CPP):
void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI)
{
pCmdUI->Enable();
}
To display text in the panes, either call SetPaneText() or call CCmdUI::SetText() in the OnUpdate() function. For example, you might want to set up an integer variable m_nPage that contains the current page number. Then, the OnUpdatePage() function might read as follows:
void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI) { pCmdUI->Enable(); char szPage[16]; wsprintf((LPSTR)szPage, "Page %d", m_nPage); pCmdUI->SetText((LPSTR)szPage); }
This technique causes the page number to appear in the pane during idle processing in the same manner that the application updates other indicators.
89. 动态修改对话框的大小
[问题提出]
关于如何动态改变对话框的大小,我做了个Demo,大家看看.
[程序实现]
//本函数使用方法:
//第一个参数:如果是TRUE表示显示扩展的对话框,如果是FALSE,表示缩小对话框。
//第二个参数:表示本对话框的HWND,
//第三个参数:表示缩小后大小的控件的ID
void COptionDlg::ExpandBox(BOOL fExpand, HWND hwnd, int nIDDefaultBox) { CWnd *pWndBox=GetDlgItem(nIDDefaultBox); RECT rcDefaultBox,rcChild,rcIntersection,rcWnd; pWndBox->GetWindowRect(&rcDefaultBox); HWND hwndChild = ::GetTopWindow(hwnd); for (; hwndChild != NULL; hwndChild = ::GetNextWindow(hwndChild,GW_HWNDNEXT)) { ::GetWindowRect(hwndChild, &rcChild); if (!IntersectRect(&rcIntersection, &rcChild, &rcDefaultBox)) ::EnableWindow(hwndChild, fExpand); } ::GetWindowRect(hwnd, &rcWnd); if (GetWindowLong(hwnd, GWL_USERDATA) == 0) { SetWindowLong(hwnd, GWL_USERDATA, MAKELONG(rcWnd.right - rcWnd.left, rcWnd.bottom - rcWnd.top)); ::ShowWindow(pWndBox->m_hWnd, SW_HIDE); } ::SetWindowPos(hwnd, NULL, 0, 0, rcDefaultBox.right - rcWnd.left, rcDefaultBox.bottom - rcWnd.top, SWP_NOZORDER | SWP_NOMOVE); if(fExpand) { DWORD dwDims = GetWindowLong(hwnd, GWL_USERDATA); ::SetWindowPos(hwnd, NULL, 0, 0, LOWORD(dwDims), HIWORD(dwDims), SWP_NOZORDER | SWP_NOMOVE); ::SendMessage(hwnd, DM_REPOSITION, 0, 0); } }
90. 用DoModal()调用模态对话框,总是显示在正中,我重载了它,并添加了MoveWindow(),可是其m_hWnd是一串零,调用失败。
请问有何方法可使调用的模态对话框显示于自定义位置?
我不清楚你把MoveWindow()加在什么地方了,正确的方法是在OnInitDialog中添加MoveWindow,如:
MoveWindow(0, 1, 300, 200);
需要注意的是前两个参数不能都为0。如果你确实希望把窗口放在(0, 0)处,可以在对话框设计窗口的属性中选中Absolute Align,然后再加入
MoveWindow(0, 0, 300, 200);
为什么会是这样?你看了MFC的源程序就会明白。原来MFC在调用你的OnInitDialog之后,会调用CDialog::CheckAutoCenter()(在dlgcore.cpp中)检查是否需要将窗口居中,你看了这个函数后就明白为什么需要上面那么做了。