Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Показаны сообщения с ярлыком ATL. Показать все сообщения
Показаны сообщения с ярлыком ATL. Показать все сообщения

DLGTEMPLATE

Бывает нужно прочитать шаблон диалога из ресурса. Для этого в библиотеке ATL есть набор полезных рюшечек: файл atlwin.h, класс _DialogSplitHelper. Например там есть объявление структур DLGTEMPLATEEX, DLGITEMTEMPLATEEX, реализации функций IsDialogEx, DlgTemplateItemCount, FindFirstDlgItem и FindNextDlgItem. Пользуйтесь на здоровье.

How to subclass a window multiple times

Иногда бывает необходимо привязаться к окну и добавить логики по обработке оконных сообщений. Механизм имеет всем известное название сабклассинг. MFC, как и многие библиотеки, поддерживает сабклассинг окон. Проблема лишь в том, что сделать это можно лишь один раз. А если надо много раз? А если к нам попадает хэндл окна и мы понятия не имеем кем оно было создано? Проблемка, однако. Но на помощь как всегда приходит могучий ATL.

#pragma once
 
#include <atlwin.h>
 
class CWindowSubclass
{
 
public:
   CWindowSubclass(CMessageMap *pObject, DWORD msgMapID = 0);
   CWindowSubclass(HWND hWnd, CMessageMap *pObject, DWORD msgMapID = 0);
   ~CWindowSubclass();
 
public:   
   void SwitchMessageMap(DWORD msgMapID);
   BOOL SubclassWindow(HWND hWnd);
   HWND UnsubclassWindow(bool force = false);
   HWND GetHWnd() const;
   operator bool() const;
 
private:   
   static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
   LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);   
 
private:
   HWND m_hWnd;
   CWndProcThunk m_thunk;
   WNDPROC m_pfnSuperWindowProc;
   CMessageMap *m_pObject;
   DWORD m_msgMapID;
};
#include "stdafx.h"
#include "WindowSubclass.h"
 
CWindowSubclass::CWindowSubclass(CMessageMap *pObject,  DWORD msgMapID/* = 0*/)
   :  m_hWnd(NULL),         
      m_pObject(pObject),
      m_msgMapID(msgMapID),
      m_pfnSuperWindowProc(NULL)
{
}
 
CWindowSubclass::CWindowSubclass(HWND hWnd,  CMessageMap *pObject, DWORD msgMapID/* = 0*/)
   :  m_hWnd(NULL),
      m_pObject(pObject),
      m_msgMapID(msgMapID),
      m_pfnSuperWindowProc(NULL)
{
   SubclassWindow(hWnd);
}
 
CWindowSubclass::~CWindowSubclass()
{
   if (m_hWnd) {
      UnsubclassWindow(TRUE);
   }
}
 
HWND CWindowSubclass::GetHWnd() const
{
   return(m_hWnd);
}
 
CWindowSubclass::operator bool() const
{
   return m_hWnd != NULL;
}
 
void CWindowSubclass::SwitchMessageMap(DWORD msgMapID)
{
   m_msgMapID = msgMapID;
}
 
LRESULT CWindowSubclass::DefWindowProc(UINT uMsg,  WPARAM wParam,  LPARAM lParam)
{
#ifdef STRICT
   return ::CallWindowProc(m_pfnSuperWindowProc, m_hWnd, uMsg, wParam, lParam);
#else
   return ::CallWindowProc(reinterpret_cast<FARPROC>(m_pfnSuperWindowProc), m_hWnd, uMsg, wParam, lParam);
#endif
}
 
LRESULT CWindowSubclass::WindowProc(HWND hWnd,  UINT uMsg,  WPARAM wParam,  LPARAM lParam)
{
   CWindowSubclass * const pThis = reinterpret_cast<CWindowSubclass *>(hWnd);
 
   ATLASSERT(pThis);
   if (!pThis) {
      return 0;
   }
 
   ATLASSERT(pThis->m_hWnd);
   ATLASSERT(pThis->m_pObject);
   if (!pThis->m_hWnd || !pThis->m_pObject) {
      return 0;
   }
 
   LRESULT lRes = 0;
   BOOL bRet = pThis->m_pObject->ProcessWindowMessage(pThis->m_hWnd, uMsg, wParam, lParam, lRes, pThis->m_msgMapID);
   if (!bRet) {
      if (uMsg != WM_NCDESTROY)
         lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
      else
      {
         const LONG_PTR pfnWndProc = ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC);
         lRes = pThis->DefWindowProc(uMsg, wParam, lParam);
         if (pThis->m_pfnSuperWindowProc && ::GetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC) == pfnWndProc) {
            ::SetWindowLongPtr(pThis->m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pThis->m_pfnSuperWindowProc));
         }
         pThis->m_hWnd = NULL;
      }
   }
   return lRes;
}
 
 
BOOL CWindowSubclass::SubclassWindow(HWND hWnd)
{      
   ATLASSUME(m_hWnd == NULL);
   ATLASSERT(::IsWindow(hWnd));
 
   const BOOL result = m_thunk.Init(WindowProc, this);
   if (result == FALSE)
      return result;
 
   const WNDPROC pProc = m_thunk.GetWNDPROC();
   const WNDPROC pfnWndProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(pProc)));
   if (pfnWndProc == NULL)
      return FALSE;
   m_pfnSuperWindowProc = pfnWndProc;
   m_hWnd = hWnd;
   return TRUE;
}
 
HWND CWindowSubclass::UnsubclassWindow(bool force/* = false*/)
{
   ATLASSUME(m_hWnd != NULL);
 
   const WNDPROC pOurProc = m_thunk.GetWNDPROC();
   const WNDPROC pActiveProc = reinterpret_cast<WNDPROC>(::GetWindowLongPtr(m_hWnd, GWLP_WNDPROC));
 
   HWND hWnd = NULL;
   if (force || pOurProc == pActiveProc) {
      if (!::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_pfnSuperWindowProc)))
         return NULL;
 
      m_pfnSuperWindowProc = NULL;
      hWnd = m_hWnd;
      m_hWnd = NULL;
   }
   return hWnd;
}

Использовать этот класс проще простого, достаточно передать наследника ATL::CMessageMap в качестве параметра конструктора вместе с дескриптором окна. Плюс привычная для ATL конструкция:

BEGIN_MSG_MAP(MyCMessageMapDerivedClass)
   MESSAGE_HANDLER(WM_MESSAGE, OnMessage)
END_MSG_MAP()

Класс можно использовать множество раз для одного окна. За это спасибо механизму переходников ATL.

Use ATL string convertion properly

В ATL 7 появилось новое семейство СX2Y макросов для конвертации строк из текущей кодовой страницы в UNICODE и обратно. И я заметил, что их часто используют абсолютно неправильно! Например:

const std::wstring some = L"Some";
AfxMessageBox(CW2T(some.c_str()));

Код выглядит, как не содержащий ошибок. Но на самом деле это не так. Взгляните на макрос CW2T когда определен _UNICODE (atlconv.h):

#ifdef _UNICODE

    #define CW2T CW2W

А теперь самое интересное:

template< int t_nBufferLength = 128 >
class CW2WEX
{
public:
    CW2WEX( _In_opt_ LPCWSTR psz ) throw(...) :
        m_psz( m_szBuffer )
    {
        Init( psz );
    }
    CW2WEX( _In_opt_ LPCWSTR psz, UINT nCodePage ) throw(...) :
        m_psz( m_szBuffer )
    {
        (void)nCodePage;  // Code page doesn't matter

        Init( psz );
    }
    ~CW2WEX() throw()
    {
        AtlConvFreeMemory(m_psz,m_szBuffer,t_nBufferLength);
    }

    operator LPWSTR() const throw()
    {
        return( m_psz );
    }

private:
    void Init( _In_opt_ LPCWSTR psz ) throw(...)
    {
        if (psz == NULL)
        {
            m_psz = NULL;
            return;
        }
        int nLength = lstrlenW( psz )+1;
        AtlConvAllocMemory(&m_psz,nLength,m_szBuffer,t_nBufferLength);
        ATLASSUME(m_psz != NULL);
        Checked::memcpy_s( m_psz, nLength*sizeof( wchar_t ), psz, nLength*sizeof( wchar_t ));
    }

public:
    LPWSTR m_psz;
    wchar_t m_szBuffer[t_nBufferLength];

private:
    CW2WEX( const CW2WEX& ) throw();
    CW2WEX& operator=( const CW2WEX& ) throw();
};
typedef CW2WEX<> CW2W;

CW2WEX выполняет копирование строки даже в случае, когда этого можно избежать, как в примере выше!

Используйте макросы CX2CY чтобы избежать лишнего копирования строк:

#ifdef _UNICODE

    #define CW2T CW2W
    #define CW2TEX CW2WEX
    #define CW2CT CW2CW
    #define CW2CTEX CW2CWEX
    #define CT2W CW2W
    #define CT2WEX CW2WEX
    #define CT2CW CW2CW
    #define CT2CWEX CW2CWEX

template< int t_nBufferLength = 128 >
class CW2CWEX
{
public:
    CW2CWEX( _In_ LPCWSTR psz ) throw(...) :
        m_psz( psz )
    {
    }
    CW2CWEX( _In_ LPCWSTR psz, UINT nCodePage ) throw(...) :
        m_psz( psz )
    {
        (void)nCodePage;
    }
    ~CW2CWEX() throw()
    {
    }

    operator LPCWSTR() const throw()
    {
        return( m_psz );
    }

public:
    LPCWSTR m_psz;

private:
    CW2CWEX( const CW2CWEX& ) throw();
    CW2CWEX& operator=( const CW2CWEX& ) throw();
};
typedef CW2CWEX<> CW2CW;

ATL Server attributes are not supported by Orcas

Только что выяснил, что Visual Studio 2008 больше не поддерживает attributed ATL. Плохо.
Copyright 2007-2011 Chabster