Как манипулировать DLGTEMPLATE программно? - PullRequest
3 голосов
/ 15 октября 2008

Что?

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

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

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

Как?

Я делаю это, подключая API создания Dialog / Property Sheet. Который дает мне доступ к DLGTEMPLATE до того, как будет создан фактический диалог и до того, как у него будет HWND.

Почему?

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

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

Ответы [ 5 ]

5 голосов
/ 15 октября 2008

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

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

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

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

4 голосов
/ 16 октября 2008

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

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

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

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

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

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

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

<p>What I did was build a series of functions like this one following  that allowed me to assemble DIALOGS in memory.  (My need was so I could have some common code that didn't need an associated RC file for some very basic messages).</p>

<p>Here is an example...</p>

<pre>
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 * Заголовок, WORD Id, char * WinClass, Стиль DWORD, короткий х, коротко, короткая сх, коротко, DWORD ExStyle, int HelpID); int DestroyDlgTemplateEx (MTDialogTemplateType * dlgtmp); MTDialogTemplateType * CreateDlgTemplateEx (char * Name, // Мы используем имя только для справки, поэтому оно может быть NULL короткий х, коротко, короткая сх, коротко, DWORD ExtendedStyle, Стиль DWORD, char * Меню, char * WinClass, char * Caption, char * FontTypeFace, int FontSize, int FontWeigth, int FontItalic, Int Charset, int HelpID, int NumberOfControls);

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

1 голос
/ 16 октября 2008

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

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

1 голос
/ 15 октября 2008

См. Функцию 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 );
        }
    }
}
0 голосов
/ 16 октября 2008

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

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

...