WEB开发网
开发学院软件开发VC 获得 Win32 窗口句柄的更好的方法 阅读

获得 Win32 窗口句柄的更好的方法

 2006-07-19 11:29:08 来源:WEB开发网   
核心提示: 获得 Win32 窗口句柄的更好的方法为了解决这个问题,本文设计了一个更加完善的类:CFindWnd,获得 Win32 窗口句柄的更好的方法(2),用更好的算法专门来获取 IE 窗口,CFindWnd 查找某个窗口(给定窗口名字)的第一个子窗口,注意 WM_CONTEXTMENU 消息的发

获得 Win32 窗口句柄的更好的方法

为了解决这个问题,本文设计了一个更加完善的类:CFindWnd,用更好的算法专门来获取 IE 窗口。CFindWnd 查找某个窗口(给定窗口名字)的第一个子窗口。 例如,它的使用方法如下: CFindWnd ies(m_hWnd, "Internet Explorer_Server");
myHwndIE = ies.m_hWnd;
这个类的构造函数调用函数:FindChildClassHwnd(hwndParent, (LPARAM)this)函数,该函数又调用:EnumChildWindows 和 FindWindowEx  搜索所有后裔窗口直到找到类名匹配窗口为止。FindWindow 用来查找最顶层窗口,而搜索子窗口还得用 FindWindowEx,它是 Win32 API 函数。CFindWnd 返回第一个匹配的窗口,所以它只被用于查找你期望只有一个实例的窗口。通常在搜索特定窗口时,一般最保险的做法都是检查窗口类名。

百家争鸣

有一个读者来信指出:根本没有必要使用子类IE窗口的方法来禁用上下文菜单。完全可以在 CHtmlCtrl 内部实现,象下面这样: BOOL CHtmlCtrl::PreTranslateMessage(MSG* pMsg)
{
 if (pMsg->message == WM_CONTEXTMENU)
 return TRUE; // eat it
 return CHtmlView::PreTranslateMessage(pMsg);
}   
   
这样做是可行的,因为MFC实现了非常有独创性的、强大的特性─在 CWinThread 的主消息泵中,MFC 调用 CWnd::WalkPreTranslateTree 函数。这个函数循环消息目的地窗口的所有父窗口,调用每一个父窗口的 PreTranslateMessage ,一旦截获消息发送到后裔窗口则停止循环。非常聪明!

经验证明:要使前面的代码段按照期望的结果运行,你还必须截获 WM_RBUTTONDOWN 和 WM_RBUTTONDBLCLK 消息,同时还要做必要的检查以保证目标窗口的类名是 “Internet Explorer_Server”,这样就不会意外地捕获其它子窗口的上下文菜单(除非你确实要这么做)。下面是 CHtmlCtrl::PreTranslateMessage 的最终代码: 头文件 HtmlCtrl.h
////////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////////
// 该结构在命令映射中定义一个入口,这个映射将文本串映射到命令IDs,
// 如果命令映射中有一个映射到 ID_APP_ABOUT 的入口 “about”,并且
// HTML 有一个链接锚 <A HREF="app:about">,那么单击该链接时将执行
// ID_APP_ABOUT 命令。为了设置这个映射,调用 CHtmlCtrl::SetCmdMap.
//
//
struct HTMLCMDMAP {
  LPCTSTR name;   // command name used in "app:name" HREF in
           // <A UINT nID;
};
//////////////////
// 这个类将 CHtmlView 转换为普通的能在对话框和框架中使用的控制
//
class CHtmlCtrl : public CHtmlView {
protected:
  HTMLCMDMAP* m_cmdmap;  // command map
  BOOL m_bHideMenu;    // hide context menu
public:
  CHtmlCtrl() : m_bHideMenu(FALSE), m_cmdmap(NULL) { }
  ~CHtmlCtrl() { }
  // get/set HideContextMenu property
  BOOL GetHideContextMenu()     { return m_bHideMenu; }
  void SetHideContextMenu(BOOL val) { m_bHideMenu=val; }
  // Set doc contents from string
  HRESULT SetHTML(LPCTSTR strHTML);
  // set command map
  void SetCmdMap(HTMLCMDMAP* val)  { m_cmdmap = val; }
  // create control in same place as static control
  BOOL CreateFromStatic(UINT nID, CWnd* pParent);
  // create control from scratch
  BOOL Create(const RECT& rc, CWnd* pParent, UINT nID,
   DWORD dwStyle = WS_CHILD|WS_VISIBLE,
     CCreateContext* pContext = NULL)
  {
   return CHtmlView::Create(NULL, NULL, dwStyle, rc, pParent,
     nID, pContext);
  }
  // 重写该函数可以截获子窗口消息,从而禁用上下文菜单。
  virtual BOOL PreTranslateMessage(MSG* pMsg);
  // 通常,CHtmlView 自己是在 PostNcDestroy 销毁的,但对于一个界面控制来说
  // 我们不想那样做,因为控制一般都是作为另一个窗口对象的成员实现的。
  //
  virtual void PostNcDestroy() { }
  // 重写以便旁路掉对 MFC doc/view 框架的依赖,CHtmView 仅仅在这里依附于框架。
  afx_msg void OnDestroy();
  afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest,
   UINT msg);
  // 重写以便截获 "app:" 伪协议
  virtual void OnBeforeNavigate2( LPCTSTR lpszURL,
   DWORD nFlags,
   LPCTSTR lpszTargetFrameName,
   CByteArray& baPostedData,
   LPCTSTR lpszHeaders,
   BOOL* pbCancel );
  // 你可以重写处理 "app:" 命令的代码。注意只是在不使用命令映射机制时才需要重写
  virtual void OnAppCmd(LPCTSTR lpszCmd);
  DECLARE_MESSAGE_MAP();
  DECLARE_DYNAMIC(CHtmlCtrl)
};
实现文件 HtmlCtrl.cpp
////////////////////////////////////////////////////////////////
// 可用于对话框和其它窗口,不需要框架
//
// 特性:
// - SetCmdMap 可以设置 "app:command" 链接的命令映射.
// - SetHTML 可以将串内容设置成 HTML.
#include "StdAfx.h"
#include "HtmlCtrl.h"
// 声明 typedef ATL 智能指针;如:SPIHTMLDocument2
#define DECLARE_SMARTPTR(ifacename) typedef CComQIPtr<ifacename> SP##ifacename;
// IHTMLDocument2 的智能指针
DECLARE_SMARTPTR(IHTMLDocument2)
// 一个很有用的宏,用于检查 HRESULT
#define HRCHECK(x) hr = x; if (!SUCCEEDED(hr)) { \
  TRACE(_T("hr=%p\n"),hr);\
  return hr;\
}
... // same as earlier version
// Return TRUE if hwnd is Internet Explorer window.
inline BOOL IsIEWindow(HWND hwnd)
{
  static LPCSTR IEWNDCLASSNAME = "Internet Explorer_Server";
  char classname[32]; // always char, never TCHAR
  GetClassName(hwnd, classname, sizeof(classname));
  return strcmp(classname, IEWNDCLASSNAME)==0;
}
//////////////////
// 重写后捕获 "Internet Explorer_Server" 窗口上下文菜单消息.
//
BOOL CHtmlCtrl::PreTranslateMessage(MSG* pMsg)
{
  if (m_bHideMenu) {
   switch (pMsg->message) {
   case WM_CONTEXTMENU:
   case WM_RBUTTONUP:
   case WM_RBUTTONDOWN:
   case WM_RBUTTONDBLCLK:
     if (IsIEWindow(pMsg->hwnd)) {
      if (pMsg->message==WM_RBUTTONUP)
        // let parent handle context menu
        GetParent()->SendMessage(WM_CONTEXTMENU, pMsg->wParam,
         pMsg->lParam);
      return TRUE; // eat it
     }
   }
  }
  return CHtmlView::PreTranslateMessage(pMsg);
}
//////////////////
// 重写后将 "app:" 链接传递到虚函数,而不是浏览器.
//
void CHtmlCtrl::OnBeforeNavigate2( LPCTSTR lpszURL,
  DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData,
  LPCTSTR lpszHeaders, BOOL* pbCancel )
{
  const char APP_PROTOCOL[] = "app:";
  int len = _tcslen(APP_PROTOCOL);
  if (_tcsnicmp(lpszURL, APP_PROTOCOL, len)==0) {
   OnAppCmd(lpszURL + len);     // call virtual handler fn
   *pbCancel = TRUE;         // cancel navigation
  }
}
//////////////////
// 当浏览器试图导航到 "app:foo" 时调用该函数.
// 默认的处理例程查找"foo"命令的命令映射,并向找到的父窗口发送
// WM_COMMAND 消息。调用 SetCmdMap 设置命令映射。如果要实现更
// 复杂的处理,只要重写这个函数即可.
//
void CHtmlCtrl::OnAppCmd(LPCTSTR lpszCmd)
{
  if (m_cmdmap) {
   for (int i=0; m_cmdmap[i].name; i++) {
     if (_tcsicmp(lpszCmd, m_cmdmap[i].name) == 0)
      // Use PostMessage to avoid problems with exit command. (Let
      // browser finish navigation before issuing command.)
      GetParent()->PostMessage(WM_COMMAND, m_cmdmap[i].nID);
   }
  }
}
//////////////////
// 将串转换为 HTML 文档
//
HRESULT CHtmlCtrl::SetHTML(LPCTSTR strHTML)
{
  HRESULT hr;
  // Get document object
  SPIHTMLDocument2 doc = GetHtmlDocument();
  // Create string as one-element BSTR safe array for
  // IHTMLDocument2::write.
  CComSafeArray<VARIANT> sar;
  sar.Create(1,0);
  sar[0] = CComBSTR(strHTML);
  // open doc and write
  LPDISPATCH lpdRet;
  HRCHECK(doc->open(CComBSTR("text/html"),
   CComVariant(CComBSTR("_self")),
   CComVariant(CComBSTR("")),
   CComVariant((bool)1),
   &lpdRet));
 
  HRCHECK(doc->write(sar)); // write contents to doc
  HRCHECK(doc->close());   // close
  lpdRet->Release();     // release IDispatch returned
  return S_OK;
}  
  和以前相比,这个类功能更强,具备了 Get/SetHideContextMenu 属性处理机制,对 WM_CONTEXTMENU 消息的处理采取了发送到父窗口,而不是过滤掉它。这样就使得你能实现自己的上下文菜单。注意 WM_CONTEXTMENU 消息的发送是在鼠标右键向上释放的时候进行的,而不是按下时处理的。具体细节请参考源代码。

上一页  1 2 3 4  下一页

Tags:获得 Win 窗口

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接