Как программно манипулировать DLGTEMPLATE?

Что?

У меня есть DLGTEMPLATE, загруженный из библиотеки ресурсов, как я могу программно изменить строки, назначенные элементам управления во время выполнения?

Я хочу иметь возможность сделать это до создания диалогового окна, чтобы я мог сказать, что отображаемые строки были получены из библиотеки ресурсов, а не из вызовов SetWindowText при инициализации диалогового окна.

Google нашел примеры создания DLGTEMPLATE в коде или изменения простых битов стиля, но ничего не нашел при редактировании строк в памяти.

Как?

Я делаю это, подключая API создания диалогового окна/листа свойств. Что дает мне доступ к DLGTEMPLATE до того, как будет создан фактический диалог и до того, как у него будет HWND.

Почему?

Я хочу иметь возможность выполнять локализацию во время выполнения и тестирование локализации. Я уже реализовал это для загрузки строки (включая оболочку MFC 7.0), меню и таблиц ускорителей, но я изо всех сил пытаюсь справиться с созданием диалогового окна/листа свойств.

Примеры кода были бы идеальным ответом, в идеале класс для обертывания DLGTEMPLATE, если я разработаю свое собственное решение, я опубликую его.


person titanae    schedule 15.10.2008    source источник


Ответы (5)


Вы не можете редактировать строки в памяти. Структура DLGTEMPLATE представляет собой прямое файловое сопоставление соответствующих байтов библиотеки ресурсов. Это только для чтения.

Вам нужно будет обработать всю структуру DLGTEMPLATE и записать новую со строками измененной длины.

Откровенно говоря, будет проще просто подключить WM_INITDIALOG и изменить строки, взаимодействуя с элементами управления, чем создавать модуль записи DLGTEMPLATE. Потому что их не так много вокруг. Если у вас нет дополнительных требований для фактического сохранения измененных ресурсов диалога на диск в виде необработанных файлов .res (или попытки изменить .dll на месте), я действительно рекомендую вам избегать этого подхода.

Вы говорите, что уже делаете это для таблиц ускорителей и строк меню — если вы можете гарантировать, что исправленные строки будут короче, тогда просто сделайте двоичную копию структуры DLGTEMPLATE и напишите нетривиальный код сканирования, необходимый для поиска. каждую строку, чтобы вы могли исправить копию на месте.

person Chris Becke    schedule 15.10.2008

Где-то есть файл (который, я думаю, создан Microsoft, но я не совсем уверен) под названием RESFMT.ZIP, который объясняет это с помощью некоторых примеров кода. Рэймонд Чен также дает несколько отличных пояснений по этому поводу в своем блоге. Обратите внимание, что формат элементов управления DIALOGEX и DIALOG отличается.

Как отмечалось в некоторых других ответах, вам нужно будет снова создать структуру с самого начала. Это не так уж и плохо, так как у вас уже есть основная информация. Добавление элементов управления становится трудным.

По сути, выделите большой блок памяти в WORD *lpIn. Затем добавьте структуру поверх этого. добавление базовой информации для ДИАЛОГА (см. DLGTEMPLATE) и элементов управления довольно очевидно, так как информация есть в MSDN.

Две самые большие проблемы, с которыми вы столкнетесь: убедиться, что разные части начинаются на границе выравнивания, и интерпретировать значения элементов управления DIALOG, особенно когда нужно добавить только строку или строку или порядковый номер. Каждый элемент управления должен начинаться на ровной границе.

Для первого (позаимствовано откуда-то, кажется, RESFMT.ZIP):

WORD *AlignDwordPtr (WORD *lpIn)
    {
    ULONG ul;

    ul = (ULONG) lpIn;
    ul +=3;
    ul >>=2;
    ul 

Что я сделал, так это создал серию функций, подобных этой, которая позволила мне собрать ДИАЛОГ в памяти. (Мне нужно было иметь какой-то общий код, который не нуждался в связанном файле RC для некоторых очень простых сообщений).

Вот пример...

WORD *AddStringOrOrdinalToWordMem( WORD *lpw, char    *sz_Or_Ord )
    {
    LPWSTR  lpwsz;
    int     BufferSize;

    if (sz_Or_Ord == NULL)
        {
        *lpw++ = 0;
        }
    else
        {
        if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE macro 
            {
            *lpw++ = 0xFFFF;
            *lpw++ = LOWORD(sz_Or_Ord);
            }
        else
            {
            if (strlen(sz_Or_Ord))
                {
                lpwsz = ( LPWSTR ) lpw;
                BufferSize = MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, 0 );
                MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, BufferSize );
                lpw = lpw +  BufferSize;
                }
            else
                {
                *lpw++ = 0;
                }
            }
        }
    return( lpw );
    }

Заголовочный файл к полному модулю включал следующие функции:

WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp, char *Title, WORD Id, char *WinClass, DWORD Style, short x, short y, short cx, short cy, DWORD ExStyle, int HelpID); int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp); MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // We use name just for reference, so it can be NULL short x, short y, short cx, short cy, DWORD ExtendedStyle, DWORD Style, char *Menu, char *WinClass, char *Caption, char *FontTypeFace, int FontSize, int FontWeigth, int FontItalic, int Charset, int HelpID, int NumberOfControls);

Это позволило мне легко собирать целые диалоги из кода.

person David L Morris    schedule 16.10.2008

См. функцию API ::EnumChildWindows( HWND , ВНДЕНУМПРОК, ЛПАРАМ )

Вы можете вызвать это в CFormView::Create или CDialog::OnInitDialog, чтобы дать себе возможность заменить заголовки элементов управления. Не волнуйтесь, старые струны не мерцают, пока вы их не замените.

В своем диалоговом ресурсе установите для управляющих заголовков ключ в каком-либо словаре. Если вы компилируете /clr, вы можете использовать управляемый ресурс таблицы строк. В своем обратном вызове найдите переведенную строку в своем словаре и установите заголовок элемента управления для перевода. Еще одно преимущество /clr и таблицы управляемых строк заключается в том, что вы можете автоматически искать нужный язык Windows (или вами), уже установив System::Threading::Thread::CurrentThread->CurrentUICulture.

Что-то вроде этого

CMyDialog::OnInitDialog()
{
    ::EnumChildWindows(
        this->GetSafeHwnd(),
        CMyDialog::UpdateControlText,
        (LPARAM)this )
}

BOOL CALLBACK CMyDialog::UpdateControlText( HWND hWnd, LPARAM lParam )
{
    CMyDialog* pDialog = (CMyDialog*)lParam;
    CWnd* pChildWnd = CWnd::FromHandle( hWnd );

    int ctrlId = pChildWnd->GetDlgCtrlID();
    if (ctrlId)
    {
        CString curWindowText;
        pChildWnd->GetWindowText( curWindowText );
        if (!curWindowText.IsEmpty())
        {
            CString newWindowText = // some look up
            pChildWnd->SetWindowText( newWindowText );
        }
    }
}
person Aidan Ryan    schedule 15.10.2008

Вам нужно будет найти строку, которую вы хотите изменить, в буфере памяти, представляющем шаблон. Единственный способ сделать это — пройти весь шаблон. Что не легко. После того, как вы это сделаете, либо вставьте байты в буфер, если ваша новая строка длиннее исходной. Или сократите буфер, если новая строка короче.

Как писал Крис, было бы намного проще изменить текст в WM_INITDIALOG и попытаться перефразировать ваше требование, в котором говорится, что вы не можете вызывать SetWindowText().

person Serge Wautier    schedule 15.10.2008

Спасибо всем, на самом деле у меня было 24 часа на решение проблемы, а затем я применил глобальную фильтрацию хуков Windows WM_INITDIALOG, что было гораздо более простым методом, сработало просто отлично, не требуется перехват API, 2 страницы кода до нескольких строк.

Спасибо за ответы на все вопросы.

person titanae    schedule 16.10.2008
comment
Мне интересно, зачем нужен глобальный хук, а не просто замена в InitDialog. - person Aidan Ryan; 16.10.2008
comment
Исходные базы слишком велики, многие UI-библиотеки используются в нескольких приложениях, глобальный хук был намного проще. Код, совместно используемый другими, в значительной степени соответствует этому, глобальный хук просто перехватывает WM_INITDIALOG перед целевым DlgProc. - person titanae; 20.10.2008