Вопрос

Что?

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

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

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

Как?

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

Почему?

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

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

Это было полезно?

Решение

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

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

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

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

Другие советы

Где-то есть файл (думаю, созданный 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);

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

См. функцию API::EnumChildWindows( HWND, WNDENUMPROC, LPARAM)

Вы можете вызвать это в 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 );
        }
    }
}

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top