从GetSafeHwnd()和GetSafeHandle()分析句柄和指针

本文详细阐述了GetSafeHwnd()与GetSafeHandle()的功能差异,解释了句柄与指针的不同之处,并介绍了如何在Windows编程中正确使用这两种机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GetSafeHwnd()GetSafeHandle()的主要区别:

1.使用者不同:

1)窗体使用:

GetSafeHwnd()用于获取窗体的安全句柄(即HWND),有了HWND我们就可以方便的对HWND指向的窗体进行所需的操作了;

2GDI对象使用:

GetSafeHandle(),用于获取GDI对象的句柄。


注意:在使用指针时强烈建议这么做:

// pSomeWnd 为一个窗体的指针

if ( NULL != pSomeWnd && NULL != pSomeWnd->GetSafeHwnd())

{

  // do something.

   }

//////////////////////////////////////////////////////////////////////

补充知识:

内存句柄与指针的区别:

1.句柄其实就是指针,但是他和指针最大的不同是:给你一个指针,你可以通过这个指针做任何事情,也许是好事,也许是通过这个指针破坏内存,干一些捣乱的事情。这个我想大家都会碰到过,因为乱用指针可能会导致程序崩溃。
句柄就没有这个缺点,通过句柄,你只能干一些windows允许你干的事情(例如调用一些api函数等等),没有了指针的随意。

2.句柄是一些表的索引也就是指向指针的指针。 句柄和指针都是地址,句柄是windows编程的一个关键性的概念,编写windows应用程序总是要和各种句柄打交道。
所谓句柄,就是一个唯一的数,用以标识许多不同的对象类型,如窗口、菜单、内存、画笔、画刷等。在win32里,句柄是指向一个无类型对象”(void)的指针,也就是一个4字节长的数据。
无论它的本质是什么,句柄并不是一个真正意义上的指针
从构造上看,句柄是一个指针,尽管它没有指向用于存储某个对象的内存位置。事实上,句柄指向一个包含了对该对象进行引用的位置。
句柄的声明是这样的:
typedef void handle

由于windows是一个多任务操作系统,它可以同时运行多个程序或一个程序的多个副本。这些运行的程序称为一个实例。为了对同一程序的多个副本进行管理,windows引入了实例句柄。windows为每个应用程序建立一张表,实例句柄就好象是这张表的一个索引。

 
不同之处还在于:
1
、句柄所指的可以是一个很复杂的结构,并且很有可以是与系统有关的,比如说上面所说的线程的句柄,它指向的就是一个类或者结构,他和系统有很密切的关系,当一个线程由于不可预料的原因而终止时,系统就可以回收它所占用的资源,如cpu,内存等等。反过来想可以知道,这个句柄中的某一些项,是与系统进行交互的。由于windows系统是一个多任务的系统,它随时都可能要分配内存,回收内存,重组内存等。 
2
、指针它也可以指向一个复杂的结构,但是通常是用户定义的,所有的必需的工作都需用户完成,特别是在删除的时候。 但在vc++6.0中也有一些指针,它们都是在处理一些小问题才用的,如最常见的字符的指针 它也是需要用户处理的,譬如你动态分配了内存;但是cstring就不要用户处理了,它其实是vc++中的一个类,所有的操作都由成员函数完成,产生(分配)由构造函数,删除(回收)由析构函数完成。


附注:获得窗口句柄三种方法

1.HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName);

 

  HWND FindWindowEx( HWND hwndParent, HWND hwndChildAfter,                         LPCTSTR lpszClass, LPCTSTR lpszWindow );

2.HWND WindowFromPoint( 
          POINT Point);//
获得当前鼠标光标位置的窗口hwnd

3.BOOL CALLBACK EnumChildProc( HWND hwnd, LPARAM lParam);

 

BOOL EnumChildWindows( HWND hWndParent,
                       WNDENUMPROC lpEnumFunc,
                       LPARAM lParam );
BOOL EnumWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam ); 
BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam );

补充知识:指针和句柄之间的转换

a.由指针获得句柄 
      CWND* pwnd ;
      HWND  hwnd ;
      hwnd = pwnd-> GetSafeHwnd();

b.由句柄得到指针:
      CWND* pwnd = FromeHandle(hmyhandle);
      pwnd-> SetWindowText(" hello world!" ) ;

mfc类中有的还提供了标准方法,比如window 句柄 : 
static CWND pascal FromHandle( HWND hwnd )
HWND GetSafeHwnd( ) const

对于位图: 
static cbitmap pascal fromhandle( hbitmap hbitmap )
static cgdiobject pascal fromhandle( hgdiobj hobject )
hgdiobj getsafehandle( ) const


有人说句柄就是一个标示,一个id号,是错误的。一个id号可以包括多个资源,比如说单文档中的idr_mainframe,一般是指在硬盘上的资源。但是当把硬盘上的资源调入内存以后,将有一个句柄指向它,但是句柄只能指向一个资源。而且句柄知道所指的内存有多大。而指针指向地址,它不知道分配的内存有多大。
但是如果你定义一个句柄,然后在vc里面右击鼠标,选择" go to definition of handle,你会发现它的本质就是一个指针,但是它的作用不同于指针。

句柄是个指针,指向一块内存,但至于这块内存跟句柄所标识的对象是怎么联系起来的,调用者不需要清楚,调用者只需要知道,这个句柄联系着一个win32对象。
    句柄是物理地址,可以跨进程传递,例如,handle ha进程a的一个窗口,你可以在进程b中利用一个跟ha相等的值(相等就是说它们强制转成int32的值相等)初始化一个句柄,

void CInfoBar::OnTimer(UINT_PTR nIDEvent) { switch (nIDEvent) { case 1: SYSTEMTIME t; GetLocalTime(&t); wchar_t tmpWideStr[50]; memset(tmpWideStr, 0, 50 * sizeof(wchar_t)); swprintf_s(tmpWideStr, 50, _T("%.2d:%.2d:%.2d"), t.wHour, t.wMinute, t.wSecond); m_strTime = tmpWideStr; DrawInfo(); InitInfo(); break; default: break; } }void CInfoBar::DrawInfo() { try { CWnd* pInfoWnd = GetDlgItem(IDC_STATIC_INFO); if (!pInfoWnd) return; CClientDC dcClient(pInfoWnd); // 使用CClientDC自动管理DC CDC* pDC = &dcClient; CDC memDC; if (!memDC.CreateCompatibleDC(pDC)) { return; // memDC析构时会自动处理 } CRect rect; pInfoWnd->GetClientRect(&rect); // 获取控件的客户区 CBitmap bitmap; if (!bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height())) { return; // bitmapmemDC由析构函数处理 } CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); // 绘制背景 DrawBkg(&memDC, rect, VERTICAL, RGB(200, 226, 204), RGB(200, 226, 204), 1); // 设置时间字体并绘制 CFont* pOldFont = memDC.SelectObject(&m_timeFont); memDC.DrawText(m_strTime, m_strTime.GetLength(), m_clockRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); memDC.SelectObject(pOldFont); // 设置信息字体并绘制 pOldFont = memDC.SelectObject(&m_infoFont); if (m_bFlicker) { if (m_bFlickerType) { memDC.FillSolidRect(m_infoRect, RGB(255, 255, 0)); memDC.SetTextColor(RGB(255, 0, 0)); memDC.SetBkColor(RGB(255, 255, 0)); memDC.SetBkMode(OPAQUE); } else { memDC.SetTextColor(RGB(200, 0, 0)); memDC.SetBkMode(TRANSPARENT); } } else { memDC.SetTextColor(RGB(0, 100, 0)); memDC.SetBkMode(TRANSPARENT); } memDC.DrawText(m_strInfo, m_strInfo.GetLength(), m_infoRect, DT_CENTER | DT_SINGLELINE | DT_VCENTER); memDC.SelectObject(pOldFont); // 输出到屏幕 pDC->BitBlt(0, 0, rect.Width(), rect.Height(), &memDC, 0, 0, SRCCOPY); // 清理:自动处理 memDC.SelectObject(pOldBitmap); // bitmapmemDC由析构函数释放 } catch (const std::exception& e) { const std::string typeName = typeid(e).name(); char info[TMSZ]; sprintf_s(info, _countof(info), "[%s] %s", typeName.c_str(), e.what()); theApp.m_logger->Err(info); } }优化DrawInfo函数,定时器每秒钟调用一次
03-23
### 指针的分类及其具体用法 #### 一、传统指针 在C/C++中,传统的指针是一种基本的数据类型,它存储的是变量或对象的内存地址。 - **定义与初始化** 定义一个指针时需要指定其数据类型。例如,在C++中可以通过以下方式声明并初始化指针[^1]: ```cpp int a = 10; int* p = &a; // 声明一个指向int类型的指针,并将其初始化为变量a的地址 ``` - **操作** 使用解引用运算符`*`访问指针所指向的内容。还可以通过算术运算调整指针的位置。 ```cpp *p = 20; // 修改指针p指向的值 ++p; // 将指针移动到下一个int位置 ``` - **比较运算** 可以将指针与整数或其他指针进行比较,这常用于判断指针的有效性或相对位置[^1]。 --- #### 二、裸指针 (Raw Pointer) Rust语言中有类似的原始指针概念,称为裸指针(raw pointer),分为可变(`*mut T`)不可变(`*const T`)两种形式。 - **特点** 裸指针并不遵循Rust的安全规则,因此可能引发未定义行为。创建裸指针本身无需放在`unsafe`块中,但如果对其进行解引用,则必须置于`unsafe`上下文中[^2]。 - **示例** 下面展示了如何安全地处理裸指针: ```rust let mut num = 42; let r1 = &num as *const i32; // 创建不可变裸指针 let r2 = &mut num as *mut i32; // 创建可变裸指针 unsafe { println!("r1 is: {}", *r1); // 解引用裸指针需使用unsafe *r2 += 1; println!("r2 incremented is: {}", *r2); } ``` --- #### 三、智能指针(Smart Pointers) 为了更好地管理资源生命周期,现代C++引入了智能指针的概念,主要包括`std::shared_ptr`, `std::unique_ptr`, `std::weak_ptr`。 - **共享所有权 (`std::shared_ptr`)** 当多个对象共同拥有某一资源时适用此类型。每当有一个新的`shared_ptr`复制该指针时,内部计数器会增加;当最后一个`shared_ptr`销毁时释放资源。 ```cpp std::shared_ptr<int> sp(new int(10)); // 动态分配并封装于shared_ptr auto sp_copy = sp; // 复制不会拷贝实际数据,仅增加引用计数 ``` - **独占所有权 (`std::unique_ptr`)** 表达单一所有权语义,即同一时间只有一个`unique_ptr`能够控制某一块动态分配的空间。转移所有权可通过赋值实现。 ```cpp std::unique_ptr<double> uptr(new double(3.14)); // 自动析构uptr时删除关联的对象 ``` - **弱引用 (`std::weak_ptr`)** 防止循环引用问题的一种机制。不参与引用计数维护,而是依赖于对应的`shared_ptr`存在与否来决定有效性[^3]。 ```cpp class Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; public: void setNext(std::shared_ptr<Node>& n) { this->next = n; } void clearPrev() { this->prev.reset(); } ~Node() {} }; ``` --- #### 四、框架特定指针(MFC中的指针获取方法) 对于Microsoft Foundation Classes (MFC) 应用程序开发环境而言,某些情况下需要从窗口类实例获得设备上下文(DC)或者其他子组件的相关信息。 - **取得控件指针** 利用成员函数`GetDlgItem()`返回对应ID号下的控件指针[CWnd*][^4]。 ```cpp CWnd* pButton = GetDlgItem(IDC_BUTTON1); if(pButton != NULL){ pButton->EnableWindow(TRUE); } ``` - **转换至句柄** 如果进一步需求涉及操作系统层面的操作,则可以调用`GetSafeHwnd()`得到HWND型别的句柄[^4]。 ```cpp HWND hBtnHandle = pButton->GetSafeHwnd(); ::SendMessage(hBtnHandle, BM_SETSTATE, TRUE, 0L); ``` - **绘图支持** 若要执行图形绘制任务,可以从任何继承自CWnd派生出来的类那里请求CDC*(Device Context)*指针[^4]。 ```cpp CDC* pDc = pButton->GetDC(); if(pDc != nullptr){ CPen penBlack(PS_SOLID, 1, RGB(0,0,0)); pDc->SelectObject(&penBlack); pDc->Rectangle(...); // 绘制矩形框等动作 pButton->ReleaseDC(pDc); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值