15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Propery sheet shell extensions using MFC

Marat Spivak -- marat@CacheLink.com
Tuesday, January 14, 1997

Environment: VC++ 4.2b, Win95, WinNT 4.0

I have recently picked up a project that deals with creating a property
sheet shell extension.
The original project mostly uses Win32 SDK, however it uses MFC to
derive the necessary COM interfaces from CCmdTarget.  I now find myself
needing to do some UI-intensive work in the property pages, and I would
like convert the existing property sheet extension DLL to use MFC
(derive the pages from CPropertyPage).

I have struggled with this for some time, but cannot get it to work.
The shell calls my extension with a pointer to a function that accepts a
HPROPSHEETPAGE that is returned from a CreatePropertySheetPage call, and
nothing I've tried works.

I have searched on MSDN for samples of Property Sheet shell extensions
that use MFC, but have come up dry.

Does anyone have such a sample?

Regards,
Marat




Michael S. Scherotter -- mss@tartus.com
Wednesday, January 15, 1997

COlePropertyPage does this.

----------
> From: Marat Spivak 
> To: 'mfc-l@netcom.com'
> Subject: Propery sheet shell extensions using MFC
> Date: Tuesday, January 14, 1997 3:30 PM
> 
> Environment: VC++ 4.2b, Win95, WinNT 4.0
> 
> I have recently picked up a project that deals with creating a property
> sheet shell extension.
> The original project mostly uses Win32 SDK, however it uses MFC to
> derive the necessary COM interfaces from CCmdTarget.  I now find myself
> needing to do some UI-intensive work in the property pages, and I would
> like convert the existing property sheet extension DLL to use MFC
> (derive the pages from CPropertyPage).
> 
> I have struggled with this for some time, but cannot get it to work.
> The shell calls my extension with a pointer to a function that accepts a
> HPROPSHEETPAGE that is returned from a CreatePropertySheetPage call, and
> nothing I've tried works.
> 
> I have searched on MSDN for samples of Property Sheet shell extensions
> that use MFC, but have come up dry.
> 
> Does anyone have such a sample?
> 
> Regards,
> Marat



Kostya Sebov -- sebov@is.kiev.ua
Friday, January 17, 1997

>   Environment: VC++ 4.2b, Win95, WinNT 4.0
>
>   I have recently picked up a project that deals with creating a property
>   sheet shell extension.
>   The original project mostly uses Win32 SDK, however it uses MFC to
>   derive the necessary COM interfaces from CCmdTarget.  I now find myself
>   needing to do some UI-intensive work in the property pages, and I would
>   like convert the existing property sheet extension DLL to use MFC
>   (derive the pages from CPropertyPage).
>
>   I have struggled with this for some time, but cannot get it to work.
>   The shell calls my extension with a pointer to a function that accepts a
>   HPROPSHEETPAGE that is returned from a CreatePropertySheetPage call, and
>   nothing I've tried works.
>
>   I have searched on MSDN for samples of Property Sheet shell extensions
>   that use MFC, but have come up dry.
>
>   Does anyone have such a sample?
>
>   Regards,
>   Marat
>
>
In fact MFC has come very close to make property page export possible, but
nevertheless you have to full some gap on your own.

I'm including a class from our company internal library (implemented as MFC
extension DLL), which you may use freely (my bosses kingly allowed me to do
so). Although this class has not been used in the shell extension, its a part
of an our internal technique of sharing the property pages between various
modules of uor product (which are mostly MFC regular DLLs). This has been
tested under Win95, NT 3.51 and NT 4.0 and proved to be very reliable. It is
written to support multiple UI threads, however it was never tested in such
case.

Note that I perform some "surgery" so the code may not compile at the very
first time.

*** KfxProps.h ***

//--------------------------------------------------------|
//                                                        |
//         Common Controls and Services Library           |
//   Improved CPropertyPage & CPropertySheet derivatives  |
//                                                        |
//                 by Kostyantin Sebov                    |
//                                                        |
//       (C)Copyright 1994-1996, Intelligent Systems      |
//                    Kiev - Boston                       |
//                                                        |
//--------------------------------------------------------|

#ifndef __KFXPROPS_H
#define __KFXPROPS_H

/////////////////////////////////////////////////////////////////////////////
// CGenericPage dialog

class KFXCTL_EXT_CLASS CGenericPage : public CPropertyPage
{
typedef CPropertyPage inherited;
DECLARE_DYNAMIC(CGenericPage)

// Construction
public:
    CGenericPage( UINT idTemplate );
    ~CGenericPage();

    virtual HPROPSHEETPAGE CreatePage();

// Dialog Data
    //{{AFX_DATA(CGenericPage)
    //}}AFX_DATA

//Attributes
public:
    BOOL m_bAutoDestroy;

    void SetModified( BOOL bChanged = TRUE )
        { m_bChanged = bChanged; inherited::SetModified( bChanged );}

    BOOL IsModified() const { return m_bChanged; }

protected:
    virtual BOOL OnCreatePage();
    virtual void OnReleasePage();

// Overrides
    // ClassWizard generate virtual function overrides
    //{{AFX_VIRTUAL(CGenericPage)
    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
    //}}AFX_VIRTUAL

// Implementation
protected:
    BOOL m_bChanged;

    // Generated message map functions
    //{{AFX_MSG(CGenericPage)
        // NOTE: the ClassWizard will add member functions here
    //}}AFX_MSG

    afx_msg void OnModified(){ SetModified(); UpdateDialogControls( this, FALSE ); }
    afx_msg void OnModified( UINT ){ OnModified(); } // To be used in ON_CONTROL_EX

    DECLARE_MESSAGE_MAP()

private:
    static UINT CALLBACK PropPageCallback( HWND hWnd, UINT message, LPPROPSHEETPAGE pPropPage );
    static LRESULT CALLBACK PreCreateHook( int code, WPARAM wParam, LPARAM lParam );

    friend class CGenericSheet;
};

//////////////////////////////////////////////////////////////////////////////////
#endif//ndef __KFXPROPS_H

*** end of KfxProps.h ***

*** GenericPage.cpp ***

#include "StdAfx.h"
//#include 

#include 

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CGenericPage property page

struct CCreatePageHook : public CNoTrackObject
{
    HHOOK   m_hOldCbtHook;
    CGenericPage* m_pPageInit;

    CCreatePageHook()
    :   m_hOldCbtHook( NULL ),
        m_pPageInit( NULL )
    {}

    ~CCreatePageHook()
    {
        if( m_hOldCbtHook )
        {
            ::UnhookWindowsHookEx( m_hOldCbtHook );
            m_hOldCbtHook  = NULL;
        }
    }
};

CThreadLocal hookCreatePage; // one could implement it using __declspec(thread)

LRESULT CALLBACK CGenericPage::PreCreateHook( int code, WPARAM wParam, LPARAM lParam )
{
    ASSERT( hookCreatePage->m_hOldCbtHook );
    ASSERT( hookCreatePage->m_pPageInit );

    if( code == HCBT_CREATEWND )
    {
        _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
        CGenericPage* pPage = hookCreatePage->m_pPageInit;

        if( pThreadState->m_pWndInit == pPage )
        {
            ASSERT( AfxGetModuleState() != pPage->m_pModuleState );
            AFX_MANAGE_STATE( pPage->m_pModuleState );

            // The following call is performed in the context of the RIGHT state.
            LRESULT lRes = CallNextHookEx( hookCreatePage->m_hOldCbtHook, code, wParam, lParam );

            ASSERT( !pThreadState->m_pWndInit ); // Already removed
            ASSERT( pPage == CWnd::FromHandlePermanent( pPage->m_hWnd )); // Attached

            ::UnhookWindowsHookEx( hookCreatePage->m_hOldCbtHook );
            hookCreatePage->m_hOldCbtHook = NULL;

            hookCreatePage->m_pPageInit = NULL;

            return lRes;
        }
    }

    return CallNextHookEx( hookCreatePage->m_hOldCbtHook, code, wParam, lParam );
}

UINT CALLBACK AfxPropPageCallback(HWND, UINT message, LPPROPSHEETPAGE pPropPage); // Undocumented private MFC function defined in somewhere like DlgProp.cpp

UINT CALLBACK CGenericPage::PropPageCallback( HWND hWnd, UINT message, LPPROPSHEETPAGE pPropPage )
{
    UINT uRes = AfxPropPageCallback( hWnd, message, pPropPage );

    CGenericPage* pPage = STATIC_DOWNCAST( CGenericPage, (CObject*)pPropPage->lParam );

    switch( message )
    {
        case PSPCB_CREATE:
        {
            if( !uRes || !pPage->OnCreatePage())
                uRes = FALSE;
            else if( AfxGetModuleState() != pPage->m_pModuleState )
            {
                // AfxPropPageCallback sets Subclassing CBT hook on window creation.
                //  We need to overlay it with another hook so that we can set correct module state.

                _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
                ASSERT( pThreadState->m_pWndInit == pPage );
                ASSERT( pThreadState->m_hHookOldCbtFilter );

                if( !hookCreatePage->m_hOldCbtHook )
                {
                    hookCreatePage->m_hOldCbtHook = ::SetWindowsHookEx( WH_CBT, PreCreateHook, NULL, ::GetCurrentThreadId());
                    if( !hookCreatePage->m_hOldCbtHook )
                        AfxThrowMemoryException(); // Just like std AfxHookWindowCreate behaves
                }
                else
                    ASSERT( FALSE );

                ASSERT( !hookCreatePage->m_pPageInit );
                hookCreatePage->m_pPageInit = pPage;
            }
        }
        break;

        case PSPCB_RELEASE:
            pPage->OnReleasePage();
        break;
    }

    return uRes;
}

IMPLEMENT_DYNAMIC(CGenericPage, CPropertyPage)

CGenericPage::CGenericPage( UINT idTemplate )
:   CPropertyPage( idTemplate ),

    m_bAutoDestroy( FALSE ),

    m_bChanged( FALSE )
{
    //{{AFX_DATA_INIT(CGenericPage)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT

    m_psp.pfnCallback = PropPageCallback;
}

CGenericPage::~CGenericPage()
{
}

HPROPSHEETPAGE CGenericPage::CreatePage()
{
    PROPSHEETPAGE psp; memcpy( &psp, &m_psp, sizeof( psp ));
    PreProcessPageTemplate( psp, FALSE );

    HPROPSHEETPAGE hpgRes = ::CreatePropertySheetPage( &psp );

    if( !hpgRes )
        OnReleasePage();

    return hpgRes;
}

void CGenericPage::DoDataExchange(CDataExchange* pDX)
{
    inherited::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CGenericPage)
        // NOTE: the ClassWizard will add DDX and DDV calls here
    //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CGenericPage, CPropertyPage)
    //{{AFX_MSG_MAP(CGenericPage)
        // NOTE: the ClassWizard will add message map macros here
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGenericPage message handlers


BOOL CGenericPage::OnCreatePage()
{
    return TRUE;
}

void CGenericPage::OnReleasePage()
{
    if( m_bAutoDestroy )
        delete this; // must be created on heap
}

*** end of GenericPage.h ***

I'd suggest that you use the class in your extension like this
(not the actual code: names may be incorrect -- I never truied to compile it):

class CMyShellPage : public CGenericPage
{
//  your implementation stuff goes here (mostly generated by the Class Wizard)
public:
    CMyShellPage( CSomeData dataSomething ); // just for example
};

// ... other code

HRESULT CMyShellExtension::XShellExtension::AddPropertyPages( LPFNADDPROPERTYPAGE pfcAddPage, LPARAM lParam )
{
    METHOD_PROLOGUE(XShellExtension); // Correctly manages module context switch
                                      //   you don't need to include AFX_MANAGE_STATE(...)

    try
    {
        CMyShellPage* pPage = new CMyShellPage( pThis->m_pSomeData );
        pPage->m_bAutoDestroy = TRUE;

        if( !pfnAddPage( pPage->CreatePage())) // if CreatePage fails it returns NULL and _automatically delete's_ pPage
            return E_FAIL;
    }
    catch(CMemoryException* pX)
    {
        pX->Delete();

        return E_OUTOFMEMORY;
    }

    return NOERROR;
}


Hope this will welp!

To everyone in the list: I appreciate any thoughts and comments on the technique
used above.

To the MFC developpers monitoring the list: I'm especially interested in your
comments and hope the next verrsions of MFC will include the analogous
functionality.


--- 
Kostya Sebov. 
----------------------------------------------------------------------------
Tel: (38 044) 266-6387 | Fax: (38 044) 266-6195 | E-mail: sebov@is.kiev.ua




| Вернуться в корень Архива |