句 柄

句      柄


出處: 方塘2003.9.22 整理,之所以前面引用了 賈旭濱 先生的這段描述,因爲他寫的是在精彩!
=== 重慶大學光電工程學院 賈旭濱 對句柄有下面的描述:

  句柄概念在WINDOWS編程中是一個很重要的概念,在許多地方都扮演着重要的角色。但由此而產生的句柄概念也大同小異,比如:<<Microsoft Windows 3 Developer's Workshop>>(Microsoft Press,by Richard Wilton)一書中句柄的概念是:在Windows環境中,句柄是用來標識項目的,這些項目包括:
*.模塊(module)
*.任務(task)
*.實例(instance)
*.文件(file)
*.內存塊(block of memory)
*.菜單(menu)
*.控制(control)
*.字體(font)
*.資源(resource),包括圖標(icon),光標(cursor),字符串(string)等
*.GDI對象(GDI object),包括位圖(bitmap),畫刷(brush),元文件(metafile),調色板(palette),畫筆(pen),區域(region),以及設備描述表(device context)。

  WINDOWS程序中並不是用物理地址來標識一個內存塊,文件,任務或動態裝入模塊的,相反的,WINDOWS API給這些項目分配確定的句柄,並將句柄返回給應用程序,然後通過句柄來進行操作。

  在<<WINDOWS編程短平快>>(南京大學出版社)一書中是這麼說的:句柄是WONDOWS用來標識被應用程序所建立或使用的對象的唯一整數,WINDOWS使用各種各樣的句柄標識諸如應用程序實例,窗口,控制,位圖,GDI對象等等。WINDOWS句柄有點象C語言中的文件句柄。

  從上面的2個定義中的我們可以看到,句柄是一個標識符,是拿來標識對象或者項目的,它就象我們的姓名一樣,每個人都會有一個,不同的人的姓名不一樣,但是,也可能有一個名字和你一樣的人。從數據類型上來看它只是一個16位的無符號整數。應用程序幾乎總是通過調用一個WINDOWS函數來獲得一個句柄,之後其他的WINDOWS函數就可以使用該句柄,以引用相應的對象。在WINDOWS編程中會用到大量的句柄,比如:HINSTANCE(實例句柄),HBITMAP(位圖句柄),HDC(設備描述表句柄),HICON(圖標句柄)等等,這當中還有一個通用的句柄,就是HANDLE,比如下面的語句:

HINSTANCE hInstance;

可以改成:

HANDLE hInstance;

上面的2句語句都是對的。

一個WINDOWS應用程序可以用不同的方法獲得一個特定項的句柄。許多API函數,諸如CreateWindow,GlobalAlloc,OpenFile的返回值都是一個句柄值。另外,WINDOWS也能通過應用程序的引出函數將一個句柄作爲參數傳送給應用程序,應用程序一旦獲得了一個確定項的句柄,便可在WINDOWS環境下的任何地方對這個句柄進行操作。其實句柄的大量使用已經影響到了每一個WINDOWS的程序設計。

句柄只有當唯一的確定了一個項目的時候,它纔開始有意義。句柄對應着項目表中的一項,而只有WINDOWS本身才能直接存取這個表,應用程序只能通過API函數來處理不同的句柄,舉個例子來說吧!比如:我們可以爲我們的應用程序申請一塊內存塊,通過調用API函數GlobalAlloc,來返回一個句柄值:

hMem=GlobalAlloc(......);

其實現在hMem的值只是一個索引值,不是物理地址,應用程序還不能直接存取這塊內存。這兒還有一個話外題,就是,一般情況下我們在編程的時候,給應用程序分配的內存都是可以移動的或者是可以丟棄的,這樣能使有限的內存資源充分利用,所以,在某一個時候我們分配的那塊內存的地址是不確定的,因爲他是可以移動的,所以得先鎖定那塊內存塊,這兒應用程序需要調用API函數GlobalLock函數來鎖定句柄。如下:

lpMem=GlobalLock(hMem);

這樣應用程序才能存取這塊內存。

我想現在大家已經能對句柄概念有所瞭解了,我希望我的文章能對大家有所幫助。其實如果你學過SDK編程,那對句柄的概念理解會更好,更深。如果你是直接學VC6的MFC編程的,建議你看一下SDK編程,這會對你大有好處。

=== 有關 句柄或指針的 常用函數

1. 如何獲取應用程序的 實例句柄? AfxGetInstanceHandle()
    應用程序的 實例句柄保存在CWinAppIm_hInstance 中,可以這麼調用 AfxGetInstancdHandle獲得句柄.
      Example: HANDLE hInstance=AfxGetInstanceHandle();

2. 如何通過代碼獲得應用程序主窗口的 指針? AfxGetMainWnd  GetSafeHwnd() AfxGetAppName()  AfxGetThread
   主窗口的 指針保存在CWinThread::m_pMainWnd中,調用 AfxGetMainWnd實現。

  【例】   AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED); //使程序最大化.

  【例】此例的主窗口是對話框,下面的代碼是在另外一個CFileTreeCtrl 類(子窗)中相關函數實現在主對話框(主窗)中的幾個 靜態文本框(子窗)中顯示路徑:
            CWnd* m_pCWnd= AfxGetMainWnd(); //得到主窗口指針,通過主窗指針訪問其他子窗資源
            //方法一
            m_pCWnd->SetDlgItemText(IDC_STATIC_path,"CWnd* "+m_sCurPath); //在主窗中的子窗(ID:IDC_STATIC_path)中顯示字符串
            m_pCWnd->SetDlgItemText(IDC_STATIC_who,"路徑顯示由FileTreeCtrl類完成:");
            //方法二
            m_pCWnd->SendMessage(STN_CLICKED); //向主窗口發送一個消息,顯示任務由主窗完成。
                                               //在主窗的.cpp中有:ON_MESSAGE(STN_CLICKED, OnSTATICpath3)相關描述

           //有的函數必須通過窗口的 句柄 來訪問,我們可以使用下面的方法三
           //CWnd::GetSafeHwnd 
           //Returns the window handle for a window
           //HWND GetSafeHwnd( ) const;
           HWND m_hWnd_tree =GetSafeHwnd();//【注】此處得到的只是當前窗口(FileTree類)的句柄
           HWND m_hWnd = m_pCWnd->GetSafeHwnd();// 這裏纔是主窗口的句柄(由主窗指針得到主窗的句柄)

           //BOOL SetWindowText( HWND hWnd, LPCTSTR lpString )
           ::SetWindowText(m_hWnd,"ok2222");//修改主窗口標題
           ::SetDlgItemText(m_hWnd,IDC_STATIC_path2,"HWND: "+m_sCurPath);

  【另】AfxGetThread
      CWinThread* AfxGetThread( );
      Return Value:Pointer to the currently executing thread.

3. 如何在程序中獲得其他程序的 圖標? AfxGetInstanceHandle()

  HINSTANCE AfxGetInstanceHandle( );
   Return Value
     
An HINSTANCE to the current instance of the application. If called from within a DLL linked with the USRDLL version of MFC, an HINSTANCE to the DLL is returned.
   Remarks
     
This function allows you to retrieve the instance handle of the current application. AfxGetInstanceHandle always returns the HINSTANCE of your executable file (.EXE) unless it is called from within a DLL linked with the USRDLL version of MFC. In this case, it returns an HINSTANCE to the DLL.

     兩種方法:
        (1) SDK函數 SHGetFileInfo 或使用 ExtractIcon獲得圖標資源的 handle(句柄),
        (2) SDK函數 SHGetFileInfo獲得有關文件的 很多信息,如大小圖標,屬性,類型等.

          Example(1): 在程序窗口左上角顯示 NotePad圖標.
           void CSampleView: OnDraw(CDC * pDC)
             {
                if( :: SHGetFileInfo(_T("c:\\pwin95\\notepad.exe"),0,
                     &stFileInfo,sizeof(stFileInfo),SHGFI_ICON))
                   {
                     pDC ->DrawIcon(10,10,stFileInfo.hIcon);
                   }
              }

                                                                           
         Example(2):同樣功能,Use ExtractIcon Function
          void CSampleView:: OnDraw(CDC *pDC)
              {
                HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T
                 ("NotePad.exe"),0);

                if (hIcon &&hIcon!=(HICON)-1)
                   pDC->DrawIcon(10,10,hIcon);
              }
   【說明】關於如何得到系統文件的正確路徑,象win.ini system32.ini等的路徑,各種系統中具體的路徑是不一樣的。如:
           獲得notepad.exe的路徑正規上來說用GetWindowsDirectory 函數得到;
           如果是調用 win95下的畫筆,應該用訪問註冊表的方法獲得其路徑;
           要作成一個比較考究的程序,考慮應該全面點.

   【另】
                HINSTANCE AfxGetResourceHandle( );
                Return Value:
An HINSTANCE handle where the default resources of the application are loaded.

4.   有關取得桌面句柄 GetDesktopWindow()

MSDN中的例子:

// 靜態函數CWnd:: GetDesktopWindow 返回桌面窗口的指針。下例說明了MFC
void CFrameWnd::BeginModalState ()
{
  //first count all windows that need to be disabled
  UINT nCount=0;
  HWND hWnd=:: GetWindow (:: GetDesktopWindow (), GW_CHILD);
  while (hWnd!=NULL)
  {
    if (:: IsWindowEnabled (hwnd) &&
        CWnd::FromHandlePermanent (hWnd)!=NULL &&
        AfxIsDescendant (pParent->m_hWnd, hWnd) &&
        :: SendMessage (hWnd, WM_DISABLEMODAL, 0, 0) == 0)
    {
       ++nCount;
    }
    hWnd=:: GetWindow (hWnd, GW_HWNDNEXT);
  }
}

//用戶的問題:下面程序取的不是同一程序的句柄,但是GetModuleFileName返回的結果一樣請問爲什莫 

HWND ChWnd;//子窗口句柄 
HWND hwDesktop=::GetDesktopWindow();//取得桌面句柄 
ChWnd=::GetWindow(hwDesktop,GW_CHILD);//取得桌面子句柄 
CString csTitle,csClass,csTm,mLookstring; 
char szBuffer[255]; 
while(ChWnd!=NULL)//循環取子句柄的同級句柄 

    if(::IsWindowVisible(ChWnd))//判斷是否爲可顯示窗口 
    { 
         ::GetWindowText(ChWnd,csTitle.GetBuffer(255),254); 
         ::GetClassName(ChWnd,csClass.GetBuffer(255),254); 
         csTitle.ReleaseBuffer();//標題 
         csClass.ReleaseBuffer();//類名 
         csTm.Format("%08X:",ChWnd); 
         if(csTitle=="") 
         { 
               mLookstring=csTm+csClass; 
         }else 
         { 
               mLookstring=csTm+csTitle; 
         } 
         //這裏的窗口句柄不是同一個程序吧?(問題所在!)可是爲什莫結果一樣 
         HINSTANCE hInstance = (HINSTANCE)::GetWindowLong(ChWnd,DWL_USER); 
         ::GetModuleFileName(hInstance, szBuffer, sizeof(szBuffer)); 
         MessageBox(szBuffer,mLookstring); 
    } 
    ChWnd=::GetWindow(ChWnd,GW_HWNDNEXT); 


回答: 

問題在於Win32下GetWindowLong(ChWnd,DWL_USER)總是返回當前程序運行的hInstance,所以你得到的文件名總是一個。所以你要用枚舉所有"進程的程序名"來獲得程序名。 
 

=== 再談句柄於指針的區別

 許多開始學習VC的朋友,最多聽說的兩個詞莫過於指針和句柄了。 
 但是,確經常搞不清他們之間的區別。 
 首先,句柄是一個窗口的標誌,也就是所有從CWND類繼承下來的,多有句柄這個成員。 
 他能做的,也就是唯一代表一個桌面上的窗口罷了。而指針是一個地址,如果它指向了一個內存中的對象,那麼就可以對它進行任意操作了,當然,並不侷限於自己的應用程序,你如果能夠獲得別的應用程序的某個對象的指針,也可以進行操作。然而,如果要獲得指針,首先,必須找到那個窗口的句柄,然後用函數FromHandle就可以得到他的指針了。

===  問題1:

如何在自定義的消息中發送一個字符串?例如: 
SendMessage(MyWnd,WM_USERDEFINED, 0,0) 
如何將字符串Buffer寫入wParam或lParam?

 你可以把字符串的地址傳遞過去,因爲地址正好是32位。如: 
char s[256]; 
SendMessage(MyWnd,WM_USERDEFINED, (WPARAM) s,0) 
接收方只需要將wParam賦給一個char*就可以了。但此方法只能使用於在一個進程內部傳遞數據。 
 

===  問題2: 
1.在VC應用程序框架中,如何加入自己做的類,如何定義這個類的對象,我想在鼠標點擊某個菜單項的時候才生成這個對象,做得到嗎?(這個類的構造函數是帶參數的)。 
2.消息發送函數: 
  PostMessage(HWND handle,WM_MYMESSAGE, 
              WPARAM wParam,LPARAM lParam) 
  中: 
第一個參數如何獲得?
如果我的消息是在自己的應用程序中生成的,想發給應用程序的窗口讓它顯示某些數據(用TextOut函數),能做到嗎?
(也可以說是這樣的問題:用Appwizard生成應用程序框架,在生成的類(如CView)中如何得到窗口的句柄,並放在PostMessage函數中。) 
3.wParam,lParam,在消息響應函數中如何用?vc是怎樣保證這兩個數傳到函數中的?問題比較多,謝謝! 
水平: 剛入門 

回答: 

1、這個問題是肯定的。你可以使用ClassWizard定義類,也可以手工輸入。如果類定義已經在某個文件中,只要使用Project|Add files將文件加入工程就可以了。要想定義類的對象,只要在你的菜單項的相應事件中就可以了。如: 

... 
MyClass myObject("Hello"); 
myObject.MyMethod(); 
... 

2、在你自己的程序中傳遞消息當然沒有任何問題,只要你知道要調用的窗口類是從CWnd繼承來的,你就可以使用GetSafeHwnd函數獲得窗口句柄。不過一般在自己的同一個程序中有時不願意使用自定義消息,因爲太麻煩。你完全可以給要調用的類添加一個成員函數,只要想顯示數據,直接調用這個成員函數不就可以了?何必使用PostMessage呢?一般只有在程序間調用,才願意使用自定義消息。這時,你通常可以使用FindWindow獲得窗口句柄(QA000251 "如何利用FindWindow()函數查找程序")。 
3、對於MFC已經定義了消息處理函數的消息,MFC會自動把wParam和lParam映射爲更容易使用的方式。如OnMouseMove( UINT nFlags, CPoint point )。對於MFC不能自動處理的函數,如你使用ON_MESSAGE定義消息函數,則MFC會把wParam和lParam原原本本的傳遞給你的函數,不做任何處理。

===  一個未公開的Win32 API函數:GetTaskmanWindow  ()

    下例中還用到: GetProcAddress    GetParent(hWnd)
                    HMODULE hUser32 = GetModuleHandle("user32");

download

// getaskmanwnd.cpp (Windows NT/2000)
//
// 利用一個未公開的Win32 API函數:GetTaskmanWindow,
// 對Windows的任務欄進行操作(顯示/隱藏)。這個函數返回擁有任務欄按鈕的窗口句柄。
//
// This example will show you how you can obtain a handle to the
// Windows Taskbar window.
//
// (c)1999 Ashot Oganesyan K, SmartLine, Inc
// mailto:[email protected], http://www.protect-me.com, http://www.codepile.com
#include <windows.h>
#include <stdio.h>
// User32!GetTaskmanWindow (NT specific!)
//
// This function returns a handle to the window that ownes the taskbar buttons
//
// HWND GetTaskmanWindow()
//
typedef HWND (WINAPI *PROCGETTASKMANWND)(void);
PROCGETTASKMANWND GetTaskmanWindow;
void main(int argc, char* argv[])
{
if (argc<2)
{
printf("Usage:\n\ngetaskmanwnd.exe S|H\n");
return;
}
HMODULE hUser32 = GetModuleHandle("user32");
if (!hUser32)
return;
GetTaskmanWindow = (PROCGETTASKMANWND)GetProcAddress(hUser32,"GetTaskmanWindow");
if (!GetTaskmanWindow)
return;
HWND hWnd = GetTaskmanWindow();
if (!hWnd)
return;
if (*argv[1]=='H' || *argv[1]=='h')
ShowWindow(GetParent(hWnd),SW_HIDE);
else
ShowWindow(GetParent(hWnd),SW_SHOW);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章