Pregunta

¿Qué ?

Tengo un DLGTEMPLATE cargado desde una DLL de recursos, ¿cómo puedo cambiar las cadenas asignadas a los controles en tiempo de ejecución mediante programación?

Quiero poder hacer esto antes de crear el cuadro de diálogo, de modo que puedo decir que las cadenas en pantalla provienen de la DLL del recurso, y no de las llamadas a SetWindowText cuando se inicializa el cuadro de diálogo.

Google ha encontrado ejemplos de cómo crear DLGTEMPLATE en código, o de mezclar bits de estilo simple pero nada al editar las cadenas en la memoria.

How?

Estoy haciendo esto al enganchar las API de creación de la hoja de diálogo / propiedad. Lo que me da acceso al DLGTEMPLATE antes de que se cree el diálogo real y antes de que tenga un HWND.

¿Por qué ?

Quiero poder realizar la localización en tiempo de ejecución y las pruebas de localización. Ya tengo esto implementado para cargar cadenas (incluyendo el contenedor MFC 7.0), menús y tablas de aceleradores, pero me cuesta manejar la creación de la hoja de diálogo / propiedad.

Los ejemplos de código serían la respuesta perfecta, idealmente una clase para envolver el DLGTEMPLATE. Si trabajo mi propia solución, la publicaré.

¿Fue útil?

Solución

No puedes editar las cadenas en la memoria. La estructura DLGTEMPLATE es una asignación de archivos directa de los bytes relevantes de la dll de recursos. Eso es solo lectura.

Necesitará procesar toda la estructura DLGTEMPLATE y escribir una nueva con las cadenas de longitud modificadas.

Francamente, será más fácil simplemente enganchar el WM_INITDIALOG y alterar las cadenas interactuando con los controles que creando un escritor DLGTEMPLATE. Porque no hay muchos de esos alrededor. A menos que tenga un requisito adicional para guardar realmente los recursos de diálogo alterados en el disco como archivos .res en bruto (o intentar modificar la .dll en el lugar), la ID realmente recomienda evitar este enfoque.

Dice que ya está haciendo esto para las tablas del acelerador y las cadenas de menú: si puede garantizar que las parches en las cadenas serán más cortos, simplemente haga una copia binaria de la estructura DLGTEMPLATE y escriba el código de escaneo no trivial es necesario encontrar cada cadena para poder parchear la copia en su lugar.

Otros consejos

Hay un archivo en alguna parte (que creo que se originó en Microsoft pero no estoy completamente seguro) llamado RESFMT.ZIP que explica esto con algunos ejemplos de código. Raymond Chen también hace excelentes explicaciones de esto en su blog. Tenga en cuenta que el formato de los controles DIALOGEX y DIALOG son diferentes.

Como se señaló en algunas otras respuestas, necesitaría volver a crear la estructura desde el principio. Esto no es del todo malo ya que ya tienes la información básica. Agregar los controles es donde se pone difícil.

Básicamente, asigne un bloque grande de memoria en un WORD * lpIn. Luego agrega la estructura encima de eso. agregar la información básica para el DIALOG (ver DLGTEMPLATE) y los controles es bastante obvio ya que la información está en MSDN.

Los dos problemas más grandes con los que se encontrará son: asegurarse de que las distintas partes comiencen en un límite de alineación e interpretar los valores de los controles DIALOG, especialmente cuando se agrega solo una cadena o una cadena u ordinal. Cada control debe comenzar en un límite uniforme.

Para el primero (tomado de algún lugar, creo que RESFMT.ZIP):

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

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

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).

Here is an example...

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 );
    }

El archivo de encabezado al módulo completo incluía estas funciones:

WORD * AddControlToDialogTemplateEx (MTDialogTemplateType * dlgtmp,                                    Título del título                                    ID de la palabra,                                    char * WinClass,                                    Estilo DWORD,                                    x corto,                                    corto y                                    breve cx                                    corto cy                                    DWORD ExStyle,                                    int HelpID); int DestroyDlgTemplateEx (MTDialogTemplateType * dlgtmp); MTDialogTemplateType * CreateDlgTemplateEx (char * Name, // Usamos el nombre solo como referencia, por lo que puede ser NULL                                             x corto,                                             corto y                                             breve cx                                             corto cy                                             DWORD ExtendedStyle,                                             Estilo DWORD,                                             menú del * char,                                             char * WinClass,                                             leyenda                                             char * FontTypeFace,                                             int FontSize,                                             int FontWeigth,                                             int FontItalic,                                             int Charset,                                             int helpID,                                             int NumberOfControls);

Lo que me permitió ensamblar diálogos enteros fácilmente desde el código.

Consulte la función de la API: EnumChildWindows (HWND, WNDENUMPROC, LPARAM)

Puede llamar a esto en CFormView :: Create o CDialog :: OnInitDialog para tener la oportunidad de reemplazar los títulos de control. No se preocupe, las cuerdas antiguas no parpadean antes de que las reemplace.

En su recurso de diálogo, establezca las leyendas de control en una clave en algún tipo de diccionario. Si está compilando / clr puede usar un recurso de tabla de cadenas gestionado. En su devolución de llamada, busque la cadena traducida en su diccionario y establezca el título del control a la traducción. Otro beneficio de / clr y la tabla de cadenas administradas es que puede buscar automáticamente el idioma correcto en Windows (o usted) que ya haya configurado System :: Threading :: Thread :: CurrentThread- > CurrentUICulture.

Algo como esto

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 );
        }
    }
}

Tendrá que ubicar la cadena que desea modificar en el búfer mem que representa la plantilla. La única forma de hacerlo es atravesar toda la plantilla. Lo que no es fácil. Una vez que hayas hecho eso, inserta bytes en el búfer si tu nueva cadena es más larga que la original. O reduce el tamaño del búfer si la nueva cadena es más corta.

Como escribió Chris, sería mucho más fácil modificar el texto en WM_INITDIALOG e intentar reformular su requisito que dice que no puede llamar a SetWindowText ().

Gracias a todos, realmente tuve 24 horas de descanso en el problema, luego fui con un filtro global de ventanas WM_INITDIALOG, que era un método mucho más simple, funcionó bien, no se requiere conexión API, 2 páginas de código hasta solo un pocas lineas.

Gracias por todas las respuestas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top