天天看點

VC 小技巧(一)

1. 修改主視窗風格

  AppWizard 生成的應用程式架構的主視窗具有預設的視窗風格,比如在視窗标題條中自動添加文檔名、視窗是疊加型的、可改變視窗大小等。要修改視窗的預設風格,需要重載CWnd::PreCreateWindow (CREATESTRUCT& cs )函數,并在其中修改CREATESTRUCT 型參數cs 。

  CWnd::PreCreateWindow 函數先于視窗建立函數執行。如果該函數被重載,則視窗建立函數将使用CWnd::PreCreateWindow 函數傳回的CREATESTRUCT cs 參數所定義的視窗風格來建立視窗;否則使用預定義的視窗風格。

  CREATESTRUCT 結構定義了建立函數建立視窗所用的初始參數,其定義如下:

  typedef struct tagCREATESTRUCT {

  LPVOID lpCreateParams; // 建立視窗的基本參數

  HANDLE hInstance; // 擁有将建立的視窗的子產品執行個體句柄

  HMENU hMenu; // 新視窗的菜單句柄

  HWND hwndParent; // 新視窗的父視窗句柄

  int cy; // 新視窗的高度

  int cx; // 新視窗的寬度

  int y; // 新視窗的左上角Y 坐标

  int x; // 新視窗的左上角X 坐标

  LONG style; // 新視窗的風格

  LPCSTR lpszName; // 新視窗的名稱

  LPCSTR lpszClass; // 新視窗的視窗類名

  DWORD dwExStyle; // 新視窗的擴充參數

  } CREATESTRUCT;

  CREATESTRUCT 結構的style 域定義了視窗的風格。比如,預設的MDI 主視窗的風格中就包括FWS_ADDTOTITLE (在标題條中顯示目前的工作文檔名)、FWS_PREFIXTITLE (把文檔名放在程式标題的前面)、WS_THICKFRAME (視窗具有可縮放的邊框)等風格。由于多種風格參數由邏輯或( “ | ” )組合在一起的,是以添加某種風格,就隻需用 “ | ” 把對應的參數加到CREATESTRUCT 結構的style 域中;删除已有的風格,則需用 “ & ” 連接配接CREATESTRUCT 結構的style 域與該風格的邏輯非值。

  CREATESTRUCT 結構的x 、y 、cx 、cy 域分别定義了視窗的初始位置和大小,是以,在CWnd::PreCreateWindow 函數中給它們指派,将能定義視窗的初始顯示位置和大小。

  下例中的代碼将主框視窗的大小将固定為1/4 螢幕,标題條中僅顯示視窗名,不顯示文檔名。

  BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

  {

  // TODO: Modify the Window class or styles here by modifying

  // the CREATESTRUCT cs

  // 修改主窗風格

  cs.style &= ~FWS_ADDTOTITLE; // 去除标題條中的文檔名

  cs.style &= ~WS_THICKFRAME; // 去除可改變大小的邊框

  cs.style |= WS_DLGFRAME; // 增加不能改變大小的邊框

  // 确定主窗的大小和初始位置

  int cxScreen = ::GetSystemMetrics(SM_CXSCREEN);// 獲得螢幕寬

  int cyScreen = ::GetSystemMetrics(SM_CYSCREEN); // 獲得螢幕高

  cs.x = 0; // 主窗位于左上角

  cs.y = 0;

  cs.cx = cxScreen/2; // 主窗寬為1/2 螢幕寬

  cs.cy = cxScreen/2; // 主窗高為1/2 螢幕高

  return CMDIFrameWnd::PreCreateWindow(cs);

  }

  2. 視窗的分割與停靠

  一、建立一個類CMySplitter, 基類為CSplitterWnd

  二、重載該類的OnMouseMove 函數:

  void CMySplitter::

  OnMouseMove(UINT nFlags, CPoint point)

  {

  // 限制切分條的運動範圍。

  if(point.x<228||point.x>600)

  {

  CWnd:: OnMouseMove(nFlags, point);

  }

  else

  {

  CSplitterWnd:: OnMouseMove(nFlags, point);

  }

  }

  三、 然後就可以跟一般的視窗分割那樣去做了,if(point.x<228||point.x>600) 這裡的範圍可以随你去設定了 ^_^ ,夠簡單吧。

  四、切分視窗

  在MaiFram.h 建立切分條對象:

  protected:

  CMySplitter m_wndSplitter; // 切分視窗對象

  // 在MaiFram.cpp 中實作視窗切分:

  BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT ,CCreateContext* pContext)

  {

  // 建立拆分器視窗

  if (!m_wndSplitter.CreateStatic(this, 1, 2))

  return FALSE;

  if

  (!m_wndSplitter.CreateView(0, 0,

  RUNTIME_CLASS(CLeftView),CSize(228,100), pContext)

  ||!m_wndSplitter.CreateView(0,1, RUNTIME_CLASS(CDataEditView),  CSize(100, 100), pContext))

  {

  m_wndSplitter.DestroyWindow();

  return FALSE;

  }

  return TRUE;

  }

  3. 如何使我的程式在啟動時不建立一個新文檔

  [ 問題]

  如何使我的程式在啟動時不建立一個新文檔?

  [ 解答]

  在程式的InitInstance 中的ProcessShellCommand 函數之前加入: cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing

  4. 初始化應用程式的大小

  如果想使應用程式界面(文檔)在開始運作是按你的尺寸展現在螢幕上,

  添加代碼如下:

  BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

  {

  int xsize=::GetSystemMetrics(SM_CXSCREEN);

  int ysize=::GetSystemMetrics(SM_CYSCREEN);

  cs.cx=xsize*5/10;

  cs.cy=ysize*5/10;

  cs.x=(xsize-cs.cx)/2;

  cs.y=(ysize-cs.cy)/2;

  }

  其中的5/10 是你的初始界面占螢幕的百分比,可以自己修改。如果想使應用程式大小固定添加cs.style&=~WS_THICKFRAME;

  5. 如何全屏顯示( 沒有标題, 沒有菜單, 沒有工具條)

  [ 解決方法]

  重載CMainFrame 的ActivateFrame 函數:

  void CMainFrame::ActivateFrame(int nCmdShow)

  {

  CRect cRectdesktop;

  WINDOWPLACEMENT windowplacement;

  ::GetWindowRect(::GetDesktopWindow(),&cRectdesktop);

  ::AdjustWindowRectEx(&cRectdesktop,GetStyle(),TRUE,GetExStyle());

  windowplacement.rcNormalPosition=cRectdesktop;

  windowplacement.showCmd=SW_SHOWNORMAL;

  SetWindowPlacement(&windowplacement);

  CFrameWnd::ActivateFrame(nCmdShow);

  }

  6. 如何限制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;

  }

  7. 程式如何删除自己

  /

  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 的蟲蟲大多都會。

  8. 如何實作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. 否則會出錯。

  9. 想在程式一啟動時就自動關閉視窗, 不在工作列裡顯示

  用CTRL+W 打開ClassWizard;

  點選Class Info 頁, 類名是工程名Dlg,

  再在左下方的"Filter" 中選擇"Windows";

  回到Message Maps 頁, 就可以看到消息中有WM_WINDOWPOSCHANGING,

  加入代碼, 如上所示.

  這樣運作*.EXE, 不但看不到主界面, 工作列也沒有, 就是任務管理器中的" 應用程式" 中也不列出, 那該如何關閉它?

  在任務管理器的" 程序" 中可以找到它, 這是黑客程式常用的方法.

  如果需要的話, 連" 程序" 中也看不到. 這樣要終止它就是問題了

  10. 如何修改frame 視窗的背景顔色

  MDI 視窗的客戶區是由frame 視窗擁有的另一個視窗覆寫的。為了改變frame 視窗背景的顔色,隻需要這個客戶區的背景顔色就可以了。你必須自己處理WM_ERASEBKND 消息。下面是工作步驟:

  建立一個從CWnd 類繼承的類,就叫它CMDIClient 吧;

  在CMDIFrameWnd 中加入CMDIClient 變量;(具體情況看下面的代碼)

  #include "MDIClient.h"

  class CMainFrame : public CMDIFrameWnd

  {

  ...

  protected:

  CMDIClient m_wndMDIClient;

  }

  重載CMDIFrameWnd::OnCreateClient ,下面是這段代碼,請注意其中的SubclassWindow() ;

  BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)

  {

  if ( CMDIFrameWnd::OnCreateClient(lpcs, pContext) )

  {

  m_wndMDIClient.SubclassWindow(m_hWndMDIClient);

  return TRUE;

  }

  else

  return FALSE;

  }

  最後要在CMDIClient 中加入處理WM_ERASEBKGND 的函數。

  11. 在MFC 下實作圖像放大鏡

  一、 引言

  當我們想仔細觀察某個細微的東西時,一般都會使用放大鏡。而要看清顯示在計算機螢幕上的圖檔或文字時通常也

  可以借助于Windows 作業系統附帶的放大程式來實作。但該程式隻能以固定的放大倍數

  去進行觀看,有時并不能滿足我們的需要。本文就通過MFC 基本類庫提供的StretchBlt 函數來實作對螢幕圖象的局部放大,并且可以随意放大、縮小,

  選取到合适的放大倍數來對圖像的細節進行觀察。

  二、 設計與實作

  本程式主要用來對圖像的局部進行可調倍數的放大,應當具有以下主要功能:

  1. 移動MOUSE 放大顯示圖像的不同部位

  2. 左擊增加放大倍率、右擊減少放大倍率。

  從光學角度來看,對物體的放大成像是通過把較小的真實物體顯示成尺寸較大的虛像來實作的。是以我們可以用類

  似的原理,把圖像中待放大的區間從較小的顯示範圍拉伸到一個比較大的顯示範圍即可達到圖

  像放大的效果,兩個區間的比值也就是圖像的放大倍率。可以通過縮小源區間的範圍或擴大放大區間的範圍來實作放大倍率的調整。在MFC 基本類庫中提供有

  CDC 類的StretchBlt 函數可以将一幅位圖從一個源矩形以一定的光栅操作拷貝到另外一個不同大小的目标矩形中去,是以可以用此函數來實作圖象放大

  的功能,其函數原形聲明如下:

  BOOL StretchBlt( int x, int y, // 目标矩形的坐标原點

  int nWidth, int nHeight, // 目标矩形的長度和寬度

  CDC* pSrcDC, // 源裝置環境句柄

  int xSrc, int ySrc, // 源矩形的坐标原點

  int nSrcWidth, int nSrcHeight, // 源矩形的長度和寬度

  DWORD dwRop ); // 光栅操作标志

  當指定的源和目标矩形的寬度或高度不一樣時,StretchBlt 函數将建立一個位圖的鏡像。如果是寬度有變化,就沿x 軸建立鏡像;如果是高度上有變化就沿y 軸建立鏡像。而且該函數可以在記憶體中對源圖象做拉伸或壓縮處理後再拷貝到目标矩形中去。

  要放大圖像首先要把圖像顯示出來,一般可以從檔案動态裝載或者直接從資源中用LoadBitMap 讀取位圖資源。下面的代碼放在視類的OnDraw 函數中,用以在第一次調用時将位圖裝載并顯示出來,以後再被調用隻是負責重畫:

  ……

  static bool load;

  if (!load)

  {

  BITMAP bm;

  load = !load;

  // 裝載位圖到 m_pBitmap

  m_pBitmap->LoadBitmap(IDB_BITMAP1);

  // 建立相關的裝置環境

  m_pdcMem->CreateCompatibleDC(pDC);

  // 将位圖從m_ pBitmap 中裝載到m_pdcMem 中

  m_pdcMem->SelectObject(m_pBitmap);

  m_pBitmap->GetObject(sizeof(bm),&bm);

  m_sizeSource.cx = bm.bmWidth;

  m_sizeSource.cy = bm.bmHeight;

  m_sizeDest = m_sizeSource;

  // 把位圖從m_pdcMem 中裝載到目前正在使用的裝置環境中

  pDC->StretchBlt(0,0,m_sizeSource.cx,m_sizeSource.cy,m_pdcMem,0,0,m_sizeSource.cx,m_sizeSource.cy,mana);

  }

  else

  {

  // 重畫圖像

  pDC->StretchBlt(0,0,m_sizeSource.cx,m_sizeSource.cy,m_pdcMem,0,0,m_sizeSource.cx,m_sizeSource.cy,mana);

  SetCursor(NULL);// 隐藏滑鼠

  }

  要實作前面提到的第一個功能:移動MOUSE 放大顯示圖像的不同部位,顯然首先要在

  WM_MOUSEMOVE 消息的響應函數裡編寫代碼。以整形變量s 和d 來分别表示所選取的源和目标區域的大小,再通過消息響應函數OnMouseMove

  的入口參數point 來确定目前的滑鼠位置就可以計算出我們要選取的源和目标區域在圖像的位置。放大的工作隻需通過StretchBlt 函數将源區域中所

  在的圖像拉伸到目标矩形那麼大,并拷貝給目标區域即可實作所選區域的放大效果,下面是部分主要代碼:

  ……

  // 确定目标區域、源區域的坐标位置

  CRect srect,drect,mrect;

  srect.left = point.x - s;

  srect.top = point.y - s;

  srect.right = point.x + s;

  srect.bottom = point.y + s;

  drect.left = point.x - d;

  drect.top = point.y - d;

  drect.right = point.x + d;

  drect.bottom = point.y + d;

  mrect.left = oldx - d;

  mrect.top = oldy - d;

  mrect.right = oldx + d;

  mrect.bottom = oldy + d;

  dd = 2*d;

  // 擷取可用裝置環境句柄

  CDC * pDC = GetDC();

  OnPrepareDC(pDC);

  if (recover)

  {

  pDC->BitBlt(mrect.left,mrect.top,dd,dd,m_pdcMem,mrect.left,mrect.top,mana);

  }

  // 隐藏滑鼠

  SetCursor(NULL);

  // 拉伸放大

  pDC->StretchBlt(drect.left,drect.top,drect.Width(),drect.Height(),m_pdcMem,srect.left,srect.top,srect.Width(),srect.Height(),SRCCOPY);

  // 儲存目前滑鼠位置備用

  oldx = point.x; oldy = point.y;

  // 釋放裝置環境句柄

  ReleaseDC(pDC);

  recover = true;

  ……

  為了實作第二個功能:左擊增加放大倍率、右擊減少放大倍率,可以分别在消息

  WM_LBUTTONDOWN 和消息WM_RBUTTONDOWN 中添加改變選取區域大小的代碼來實作。如果選取源矩形不變而改變目标矩形的大小會随着放

  大倍數的增大,顯示區域也不斷增大,當放大到一定程度的時候會另人無法忍受,是以選取通過縮放源矩形大小來控制放大倍數的方案:

  void CZoomInView::OnRButtonDown(UINT nFlags, CPoint point)

  {

  if (s < 60)

  {

  SetCursor(NULL);

  s+=3;

  OnMouseMove(nFlags, point);

  }

  CView::OnRButtonDown(nFlags, point);

  }

  ……

  void CZoomInView::OnLButtonDown(UINT nFlags, CPoint point)

  {

  if(s>5)

  {

  s-=3;

  SetCursor(NULL);

  OnMouseMove(nFlags, point);

  }

  CView::OnLButtonDown(nFlags, point);

  }

  (完)