Сообщений 0    Оценка 1        Оценить  
Система Orphus

CNotifyIcon и MFC

Автор: Игорь Вартанов
Опубликовано: 16.10.2001
Исправлено: 13.03.2005
Версия текста: 1.0

Как это делается в Win32 API
Переходим к MFC
Вместо заключения

Собственно говоря, класс CNotifyIcon изначально был написан для использования в приложениях, использующих "голый" Win32 API, но ничто не препятствует использовать его и в MFC-приложениях. Рассмотрим, каким образом это реализуется.

Как это делается в Win32 API

Для начала напомним, каким образом используется CNotifyIcon в WinAPI-приложениях. В нужном месте (согласно логики программы) создается экземпляр объекта класса, которому через параметры конструктора передается хэндл окна, создающего иконку в System Area (ему впоследствии система будет слать сообщения о действиях пользователя) и обобщенный идентификатор ресурсов (если ресурсы модуля, хэндл которого передается в одном из следующих параметров, содержат иконку, меню и текст подсказки с указанным идентификатором, то они будут загружены объектом класса). Два следующих параметра дают возможность явно задать хэндл модуля, содержащего ресурсы, и строку подсказки. При их отсутствии ресурсы будут загружаться из модуля приложения.

Класс имеет конструктор с одним параметром, который строит "пустой" объект, не выводящий в System Area ничего. Чтобы вывести при помощи такого объекта иконку, нужно для него вызвать метод Modify(). Кроме того, класс содержит еще два конструктра, в которых имеется возможность раздельно задать идентификаторы ресурсов меню, иконки и строки подсказки.

Отдельно необходимо сказать об идентификаторе нотификационного сообщения m_uRegisteredMessage. Для оповещения о действиях пользователя, система использует посылку окну-создателю сообщений с идентификатором, переданным системе при создании иконки. Вы можете самостоятельно выбрать его значение, а можете передать значение, равное нулю (нуль передается по-молчанию и в случае, когда этот параметр не указан). В последнем случае код класса самостоятельно назначит идентификатор.

Для того, чтобы код класса имел возможность обработать сообщения с указанным идентификатором (класс самостоятельно реализует вызов контекстного меню), необходимо обеспечить маршрутизацию оконных сообщений окна коду класса. Делается это посредством вызова метода Dispatch() в оконной процедуре (либо, в зависимости от типа приложения, в процедуре диалога).

Для наглядности приведем пример фрагмента клиентского кода, реализующего все вышеперeчисленные манипуляции.

BOOL CALLBACK DlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
    static CNotifyIcon* pNI = NULL;

    // все сообщения прокачиваются через Dispatch()
    if( pNI )
        pNI->Dispatch( hDlg, msg, wParam, lParam );

    switch( msg )
    {
    case WM_INITDIALOG:
        // создадим экземпляр класса на основе ресурсов с единым 
        // идентификатором, выбор идентификатора нотификационного 
        // сообщения предоставим классу
        pNI = new CNotifyIcon( hDlg, ID_NOTIFYICON_RES );
        . . .
        break;
    case WM_COMMAND:
        switch( LOWORD( wParam ) )
        {
        case IDCANCEL:
            if( BN_CLICKED == HIWORD( wParam) )
            {
                delete pNI;
                pNI = NULL; // чтобы предотвратить вызов Dispatch() для 
                            // недействительного уже указателя pNI
                EndDialog( hDlg, 0 );
            }
        }
        break;
    }
    return 0;
}

Переходим к MFC

Вот и настало время вспомнить, собственно, о теме разговора. Статический указатель на экземпляр класса CNotifyIcon станет открытым членом класса, скорее всего класса главного окна - наследника CDialog или CFrameWnd, создание экземпляра класса переместится в один из инициализационных методов. А вот вызов Dispatch() не имеет альтернативы - его придется поместить в перекрытый метод функции окна, которое зарегистрируется получателем нотификационных сообщений - опять таки, скорее всего это будет главное окно приложения. Хотя нет, альтернатива все же есть - вместо перекрытой WindowProc() для наших целей в большинстве случаев подойдет и DefWindowProc(). О редких исключениях мы можем поговорить отдельно (например посредством mail :-)), тем более что основной способ всегда в вашем распоряжении.

Таким образом приведенный выше фрагмент кода в интерпретации MFC будет выглядеть следующим образом:

class CMyDialog : public CDialog
{
public:
    . . .
    CNotifyIcon* m_pNI;
    . . .
};

CMyDialog::CMyDialog(. . .)
{
    m_pNI = NULL;
    // это нужно сделать обязательно, чтобы предотвратить вызовы
    // Dispatch() до корректной инициализации m_pNI
}

BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();
    . . .
    m_pNI = new CNotifyIcon( m_hWnd, ID_NOTIFYICON_RES );
    . . .
    return TRUE;
}

void CMyDialog::OnCancel()
{
    delete m_pNI;
    m_pNI = NULL; // чтобы предотвратить вызовы Dispatch()
                  // для недействительного указателя m_pNI
    CDialog::OnCancel();
}

LRESULT CMyDialog::WindowProc( UINT msg, WPARAM wParam, LPARAM lParam )
{
    if( m_pNI )
        m_pNI->Dispatch( m_hWnd, msg, wParam, lParam );
    return CDialog::WindowProc( msg, wParam, lParam );
}

Адаптировать приведенный код к другому типу MFC-проектов, на мой взгляд, не составит труда. Совершенно необязательно делать указатель на экземпляр класса CNotifyIcon членом класса главного окна, главное - обеспечить его видимость в тех участках кода, где вы будете вызывать методы класса.

Вместо заключения

На этом можно было бы и закончить. Но может быть у кого-то из читающих эти строки закралась мысль: "А так ли необходимо прокачивать всю груду оконных сообщений через Dispatch()?"

Достаточно ознакомиться с исходными текстами класса, чтобы убедиться, что накладные расходы вызова метода Dispatch() минимальны. Метод выполняет несколько проверок и возвращает управление программе в случае, если только это не сообщение от иконки о том, что пользователь отпустил правую кнопку мыши, либо если это не сообщение системы о пересоздании панели задач. В последнем случае выполнится восстановление иконки в System Area в том виде, как она выглядела в момент закрытия панели задач.

И именно для тех, кому подобная реализация покажется неоптимальной, метод Dispatch() сделан виртуальным - наследуйтесь от класса и определяйте собственную реализацию Dispatch() (хотя при доступных исходниках ничто не помешает вам внести изменения прямо в них, однако мне все же этого не хотелось бы :-)). Именно этим способом автор действовал при написании класса CNotifyIconEx, расширяющего возможности по добавлению пользовательских обработчиков сообщений мыши.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 0    Оценка 1        Оценить