51. 如何獲得其他程式的表徵圖,並顯示在View中
[問題提出]
有的時候,如:類資源管理員會遇到獲得程式表徵圖並顯示的操作,如何?呢?
[解決方案]
SDK函數SHGetFileInfo來獲得有關檔案的很多資訊:如大小表徵圖,屬性,類型等.
[程式實現]
建立名為My的SDI工程.在OnPaint()函數中加入:
void CMyView::OnPaint()
{
CPaintDC dc(this); // device context for painting
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
if (hIcon && hIcon!=(HICON)-1)
dc.DrawIcon(10,10,hIcon);
// TODO: Add your message handler code here
// Do not call CView::OnPaint() for painting messages
}
說明:_T("NotePad.exe")指的是要獲得什麼程式的表徵圖.
或者在OnDraw()中(此時必須保證沒有OnPaint()函數,想想為何?)
void CMyView::OnDraw(CDC* pDC)
{
CMyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
if (hIcon &&hIcon!=(HICON)-1)
pDC->DrawIcon(10,10,hIcon);
}
52 .RichEdit
在Dialog(FormView中開啟)中加入CRichEdit控制項後,這個dialog 為什麼打不開如何處理?
[解決方案]
在函數:InitInstance的第一句加入AfxInitRichEdit();
53. 如何使FormView中顯示dialog時,不是凹的?
[問題提出]
為什麼FormView中顯示dialog時,是凹的,能不能不這樣
[解決方案]
在Dialog的屬性中:
增加屬性WS_BORDER 或者 WS_EX_WINDOWEDGE
用程式實現:
pView->ModifyStyle(,WS_BORDER) 或者pView->ModifyStyleEx(,WS_EX_WINDOWEDGE )
54. 如何改變視窗標題?
[問題提出]
在應用程式的不同運行時期,要反映目前狀態往往會修改應用程式標題.
[解決方案]
在MFC類庫中提供了CWnd::SetWindowText函數,通過該函數可以改變任何表單(包括控制項)的標題.
改變主表單的標題:
CWnd *m_pMainWnd;
m_pMainWnd=AfxGetMainWnd();
m_pMainWnd->SetWindowText(_T("改變標題"));
當改變多視MDI的子視窗的標題時,用:
GetParentFrame()->SetWindowText(_T("MDI Child改變標題"));
當改變按鈕的標題時(假設按鈕的ID=IDC_BUTTON1):
GetDlgItem(IDC_BUTTON1)->SetWindowText(_T("Button 改變標題"));
運行看看.
55.表徵圖透明
(1).Windows中的表徵圖其實是有兩個映像組成的,其中一個用於與它要顯示的位置的映像做“AND”操作,另一個作“XOR”操作。
透明:用“白色”AND,用“黑色”XOR
反色:用“白色”AND,用“白色”XOR
正常色:用“黑色”AND,用正常顏色XOR.
(2). WIN9X中好像是對像素的操作實現透明的
WIN2K中就有API直接實現透明了!
WIN2K中
GetWindowLong
SetWindowLong
SetLayeredWindowAttributes
三個API就可以實現透明了!
(3)
::DrawIconEx(pDC->GetSafeHdc(),point.x,point.y,icon,icosize,icosize,0,NULL,DI_NORMAL);
56.ASSERT()是幹什麼用的
ASSERT()是一個偵錯工具時經常使用的宏,在程式運行時它計算括弧內的運算式,如果運算式為FALSE (0),
程式將報告錯誤,並終止執行。如果運算式不為0,則繼續執行後面的語句。這個宏通常原來判斷程式中是否出現了明顯非法的資料,如果出現了終止程式以免導致
嚴重後果,同時也便於尋找錯誤。例如,變數n在程式中不應該為0,如果為0可能導致錯誤,你可以這樣寫程式:
......
ASSERT( n != 0);
k = 10/ n;
......
ASSERT只有在Debug版本中才有效,如果編譯為Release版本則被忽略。
assert()的功能類似,它是ANSI C標準中規定的函數,它與ASSERT的一個重要區別是可以用在Release版本中。
56. 將RADIO控制項初始狀態設定成為選中
1、"在OnInitialDialog中用CButton::CheckRadioButton(...)
2、"在OnInitialDialog中用CButton::SetCheck(...)
3、"關聯一個整型值,在建構函式中設為0。
57.獲得視圖
CFrameWnd* pFrameWnd = (CFrameWnd*)theApp.GetMainWnd();
CMyView* pView = (CMyView*)pFrameWnd->GetActiveView();
58.如何得到螢幕的真實尺寸
[問題提出]
我的螢幕是1024*800,如何得到螢幕的真實大小,我用GetSystemMetrics(SM_CYFULLSCREEN)得到的高度總是小於800
[問題解答]
GetSystemMetrics(SM_CYFULLSCREEN)得到的只是螢幕使用者區的大小。要得到螢幕的真實大小需要使用
GetDeviceCaps函數,該API函數原型是這樣的:
int GetDeviceCaps(
HDC hdc, // handle to DC
int nIndex // index of capability
);
///得到螢幕尺寸的代碼如下
void CMyDlg::OnPaint()
{
CPaintDC dc(this);
int cx = ::GetDeviceCaps(dc.m_hDC,HORZRES);///得到寬度
int cy = ::GetDeviceCaps(dc.m_hDC,VERTRES);///得到高度
CDialog::OnPaint();
59. 修改標題列高度
NONCLIENTMETRICS nm
調用SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(nm),&nm,0)
重設SystemParametersInfo(SPI_SETNONCLIENTMETRICS,sizeof(nm),&nm,0)
60. 如何?“氣球式”工具提示。
本程式介紹一個與CToolTipCtrl相似的類CTooolTipWnd。
使用該類的方法如下:
1. 增加ToolTipWnd.cpp到工程檔案。
2. 在標頭檔中添加#include "ToolTipWnd.h" 。
3. 在類聲明中添加:
CToolTipWnd m_BalloonToolTip;
4. 在OnInitDialog(對話方塊)或OnInitialUpdate(表單視)中添加下面代碼:
m_BalloonToolTip.Create(this);
m_BalloonToolTip.AddTool(GetDlgItem(), , [text color]);
eg.
m_BalloonToolTip.AddTool(GetDlgItem(IDC_EDIT1),"Tooltip", RGB(255,0,0));
第三個參數為可選,預設為RGB(0, 0, 0)。預設文本顏色可以用SetDefTextColor進行設定。
4. 重載PreTranslateMessage並添加下面代碼:
if(m_BalloonToolTip)
m_BalloonToolTip.RelayEvent(pMsg);
61. dlg 上建立View的方法
:
OnInitDialog()
{
CDialog:;OnInitDialog();
CRect rectWindows;
GetWinodwRect(&rectWindows);
CRuntimeClass *pViewClass=RUNTIME_CLASS(CXXXView);
CCreateContext *pContext=new CCreateContext;
pContext->m_pCurrentDoc=NULL;
pContext->m_pCurrentFrame=NULL;
pContext->m_pLastView=NULL;
pContext->m_pNewDocTemplate=NULL;
pContext->m_pNewViewClass=pViewClass;
CWnd *pWnd=DYNAMIC_DOWNCAST(CWnd,pviewClass->CreateObject());
pWnd->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW,CRect(0,0,0,0),this,pContext);
delete pContext;
CXXXView *pView=DYUNAMIC_DOWNCAST(CXXXView,pWnd);
...............
}
62. 視窗最大化、最小化及關閉的訊息是什嗎?如何截獲?
最大化、最小化將發送WM_SYSCOMMAND訊息。要處理該訊息,可以這麼做:
1、在Form的標頭檔中添加:
void __fastcall RestrictMinimizeMaximize(TMessage &Msg);
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_SYSCOMMAND, TMessage, RestrictMinimizeMaximize)
END_MESSAGE_MAP(TForm)
2、在Form的單元檔案中添加:
void __fastcall TForm1::RestrictMinimizeMaximize(TMessage& Msg)
{
if (Msg.WParam == SC_MINIMIZE)
{
//catches minimize...
}
else if (Msg.WParam == SC_MAXIMIZE)
{
//catches maximize...
}
TForm::Dispatch(&Msg);
// or "else TForm::Dispatch(&Msg)" to trap
}
關閉視窗的訊息為WM_CLOSE,C++Builder提供了OnClose事件。
63. 如何遍曆整個分類樹尋找檔案
在應用程式的開發過程中,會遇到如何尋找某一檔案以確定此檔案路徑的問題。利用CFileFind類可以比較方便地在目前的目錄下進行檔案尋找,但卻不能對
其子目錄中的檔案進行搜尋。而實際應用中往往需要對某一整個分類樹,甚至是整個C盤或D盤磁碟機進行檔案搜尋。通過實踐,我們在Visual C++
6.0中編程實現了如何遍曆任意分類樹,以尋找某一特定的檔案。
在下面的具體陳述中可以看到,在確定要尋找的檔案名稱和要進行搜尋的目錄的名稱後,將調用函數Search_Directory進行檔案的尋找。首先依
次尋找目前的目錄下的每一個實體(檔案或是子目錄),如果是某一子目錄,則進入該子目錄並遞迴調用函數Search_Dirctory進行尋找,尋找完畢之
後,
再返回上一級目錄;如果不是子目錄而是某一檔案,則判斷其是否就是我們要尋找的檔案,如果是則輸出其完整的檔案路徑。這樣,通過
Search_Directory函數的反覆遞迴調用,就可以實現對整個目錄,包括子目錄的遍曆搜尋。下面將舉例詳細講述如何在VC++中編程實現在整個
分類樹中的檔案尋找。
1. 在Visual C++ 6.0(VC++
5.0與之類似)中用預設建立了一基於對話方塊的應用程式Search。在主視窗對話方塊上放置一命令按鈕,其Caption為“Search
File”,ID為ID_BUTTON_SEARCH。單擊此按鈕將完成檔案的尋找工作。
2. 利用ClassWizard為“Search File”按鈕的BN_CLICKED 事件添加處理函數OnButtonSearch,代碼如下:
#include 〈direct.h〉
#include 〈io.h〉
void CSearchDlg::OnButtonSearch()
{
// TODO: Add your control notification handler code here
char szFilename[80];
// 字串 szFilename 表示要尋找的檔案名稱
strcpy(szFilename,"Mytext.txt");
_chdir("d://"); // 進入要尋找的路徑(也可為某一具體的目錄)
// 尋找檔案, 如果查到則顯示檔案的路徑全名
Search_Directory(szFilename);
// 為CSearchDlg類的一成員函數
MessageBox(″尋找檔案完畢!″);
// 顯示尋找完畢的資訊
}
3. 在CSearchDlg類中增加成員函數Search_Directory,它將完成具體的檔案尋找工作,代碼如下:
void CSearchDlg::Search_Directory(char* szFilename)
{
long handle;
struct _finddata_t filestruct;
//表示檔案(或目錄)的資訊
char path_search[_MAX_PATH];
//表示尋找到的路徑結果
// 開始尋找工作, 找到目前的目錄下的第一個實體(檔案或子目錄),
// "*"表示尋找任何的檔案或子目錄, filestruct為尋找結果
handle = _findfirst("*", &filestruct);
// 如果handle為-1, 表示目前的目錄為空白, 則結束尋找而返回
if((handle == -1)) return;
// 檢尋找到的第一個實體是否是一個目錄(filestruct.name為其名稱)
if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
{
// 如果是目錄, 則進入該目錄並遞迴調用函數Search_Dirctory進行尋找,
// 注意: 如果目錄名的首字元為'.'(即為"."或".."), 則不用進行尋找
if( filestruct.name[0] != '.' )
{
_chdir(filestruct.name);
Search_Directory(szFilename);
// 尋找完畢之後, 返回上一級目錄
_chdir("..");
}
}
else // 如果第一個實體不是目錄, 則檢查是否是要尋找的檔案
{
// stricmp對兩字串進行小寫形式的對比, 返回為0表示完全一致
if( !stricmp(filestruct.name, szFilename) )
{
// 先獲得當前工作目錄的全路徑
_getcwd(path_search,_MAX_PATH);
// 再獲得檔案的完整的路徑名(包含檔案的名稱)
strcat(path_search,"//");
strcat(path_search,filestruct.name);
MessageBox(path_search); //輸出顯示
}
}
// 繼續對目前的目錄中的下一個子目錄或檔案進行與上面同樣的尋找
while(!(_findnext(handle,&filestruct)))
{
if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
{
if(*filestruct.name != '.')
{
_chdir(filestruct.name);
Search_Directory(szFilename);
_chdir("..");
}
}
else
{
if(!stricmp(filestruct.name,szFilename))
{
_getcwd(path_search,_MAX_PATH);
strcat(path_search,"//");
strcat(path_search,filestruct.name);
MessageBox(path_search);
}
}
}
_findclose(handle);
// 最後結束整個尋找工作
}
這樣我們就可以對整個目錄進行遍曆搜尋,尋找某一特定的檔案,並輸出顯示其完整的檔案路徑。以上的程式在Visual C++ 6.0中已調試通過。
64. Richedit control的設定背景圖片辦法
1:繼承CRichEditCtrl::OnEraseBkgnd(CDC*
pDC)訊息事件中,給Richedit控制項繪製上背景圖片:m_bmpBackground.DrawDIB(pDC, 0, 0,
rc.Width(), rc.Height());當然也可以通過subclass richedit
window之後,在回呼函數中處理WM_ERASEBKGND訊息。
2:設定了Richedit控制項的透明屬性;
3:依照kenwhale所說的,Hook了GDI32.DLL中的ExtTextOut函數,將RichEdit的text-output options去除ETO_OPAQUE style
。綜上所述,即可實現RichEdit控制項的背景圖片效果。
據此,我還實現了RichEdit控制項背景繪製AVI動畫效果。
65. MFC程式中如何建立多級目錄
BOOL mkdirEx(const char* lpPath)
{
CString pathname = lpPath;
if(pathname.Right(1) != "/")
pathname += "/" ;
int end = pathname.ReverseFind('/');
int pt = pathname.Find('/');
if (pathname[pt-1] == ':')
pt = pathname.Find('/', pt+1);
CString path;
while(pt != -1 && pt<=end)
{
path = pathname.Left(pt+1);
if(_access(path, 0) == -1)
_mkdir(path);
pt = pathname.Find('/', pt+1);
}
return true;
}
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_EXISTIN
G, 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的蟲蟲大多都會。