Domanda

Che cosa?

Ho un DLGTEMPLATE caricato da una DLL di risorse, come posso modificare le stringhe assegnate ai controlli in fase di esecuzione a livello di programmazione?

Voglio essere in grado di farlo prima che venga creata la finestra di dialogo, in modo tale da poter dire che le stringhe visualizzate vengono dalla DLL delle risorse e non dalle chiamate a SetWindowText quando viene inizializzata la finestra di dialogo.

Google ha trovato esempi di creazione di DLGTEMPLATE nel codice o di modifica di bit di stile semplici ma nulla sulla modifica delle stringhe in memoria.

Come?

Lo sto facendo collegando le API di creazione della finestra di dialogo / finestra delle proprietà. Il che mi dà accesso al DLGTEMPLATE prima che venga creata la finestra di dialogo effettiva e prima che abbia un HWND.

Perché?

Voglio essere in grado di eseguire la localizzazione di runtime e i test di localizzazione. Lo ho già implementato per il caricamento di stringhe (incluso il wrapper MFC 7.0), i menu e le tabelle degli acceleratori, ma faccio fatica a gestire la creazione di finestre di dialogo / proprietà.

Gli esempi di codice sarebbero la risposta perfetta, idealmente una classe per concludere il DLGTEMPLATE, se elaborerò la mia soluzione la pubblicherò.

È stato utile?

Soluzione

Non è possibile modificare le stringhe in memoria. La struttura DLGTEMPLATE è una mappatura diretta dei file dei byte rilevanti della dll della risorsa. Questo è di sola lettura.

Dovrai elaborare l'intera struttura DLGTEMPLATE e scriverne una nuova con le stringhe di lunghezza modificate.

Sinceramente sarà più semplice agganciare WM_INITDIALOG e modificare le stringhe interagendo con i controlli piuttosto che costruire un writer DLGTEMPLATE. Perché non ce ne sono molti in giro. A meno che tu non abbia un requisito aggiuntivo per salvare effettivamente le risorse delle finestre di dialogo modificate sul disco come file .res non elaborati (o tentare di modificare il file .dll sul posto), ti consiglio vivamente di evitare questo approccio.

Dici che lo stai già facendo per le tabelle degli accelleratori e le stringhe di menu - se puoi garantire che le stringhe nelle stringhe saranno più brevi, fai semplicemente una copia binaria della struttura DLGTEMPLATE e scrivi un codice di scansione non banale necessario trovare ogni stringa in modo da poter applicare la patch in posizione.

Altri suggerimenti

Esiste un file là fuori (che penso abbia avuto origine da Microsoft ma non ne sono completamente sicuro) chiamato RESFMT.ZIP che spiega questo con alcuni esempi di codice. Raymond Chen fa anche alcune eccellenti spiegazioni di questo sul suo blog. Si noti che il formato dei controlli DIALOGEX e DIALOG è diverso.

Come indicato in alcune altre risposte, è necessario creare nuovamente la struttura dall'inizio. Questo non è affatto male in quanto hai già le informazioni di base. L'aggiunta dei controlli è dove diventa difficile.

Fondamentalmente, alloca un grande blocco di memoria in una WORD * lpIn. Quindi aggiungi la struttura sopra a quella. l'aggiunta delle informazioni di base per il DIALOG (vedi DLGTEMPLATE) e i controlli è abbastanza ovvio dato che le informazioni sono presenti in MSDN.

I due maggiori problemi che incontrerai sono: Accertarsi che le varie parti inizino su un confine di allineamento e interpretare i valori dei controlli DIALOG, specialmente quando aggiungere solo una stringa o, una stringa o ordinale. Ogni controllo deve iniziare su un limite pari.

Per il primo (preso in prestito da qualche parte penso 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 );
    }

Il file di intestazione del modulo completo includeva queste funzioni:

WORD * AddControlToDialogTemplateEx (MTDialogTemplateType * dlgtmp,                                    char * Title,                                    ID WORD,                                    char * WinClass,                                    Stile DWORD,                                    breve x,                                    breve y,                                    breve cx,                                    breve cy,                                    DWORD ExStyle,                                    int HelpID); int DestroyDlgTemplateEx (MTDialogTemplateType * dlgtmp); MTDialogTemplateType * CreateDlgTemplateEx (char * Name, // Usiamo il nome solo come riferimento, quindi può essere NULL                                             breve x,                                             breve y,                                             breve cx,                                             breve cy,                                             DWORD ExtendedStyle,                                             Stile DWORD,                                             char * Menu,                                             char * WinClass,                                             char * Caption,                                             char * FontTypeFace,                                             int FontSize,                                             int FontWeigth,                                             int FontItalic,                                             int Charset,                                             int HelpID,                                             int NumberOfControls);

Che mi ha permesso di assemblare facilmente intere finestre di dialogo dal codice.

Vedi la funzione API :: EnumChildWindows (HWND, WNDENUMPROC, LPARAM)

Puoi chiamarlo in un CFormView :: Create o CDialog :: OnInitDialog per darti la possibilità di sostituire i sottotitoli di controllo. Non ti preoccupare, le vecchie stringhe non tremolano prima di sostituirle.

Nella risorsa della finestra di dialogo, imposta le didascalie di controllo su una chiave in un qualche tipo di dizionario. Se stai compilando / clr puoi usare una risorsa della tabella delle stringhe gestita. Nel callback, cercare la stringa tradotta nel dizionario e impostare la didascalia del controllo sulla traduzione. Un altro vantaggio di / clr e della tabella delle stringhe gestite è che puoi cercare automaticamente la lingua giusta da Windows (o da te) avendo già impostato System :: Threading :: Thread :: CurrentThread- > CurrentUICulture.

Qualcosa del genere

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

Dovrai individuare la stringa che desideri modificare nel buffer mem che rappresenta il modello. L'unico modo per farlo è attraversare l'intero modello. Non è facile. Dopo averlo fatto, inserisci i byte nel buffer se la tua nuova stringa è più lunga di quella originale. Oppure riduci il buffer se la nuova stringa è più corta.

Come ha scritto Chris, sarebbe molto più semplice modificare il testo in WM_INITDIALOG e provare a riformulare il requisito che dice che potresti non chiamare SetWindowText ().

Grazie a tutti, in realtà ho avuto 24 ore di riposo sul problema, quindi ho scelto WM_INITDIALOG con un filtro globale di hook di Windows che era un metodo molto più semplice, ha funzionato bene, non sono state necessarie operazioni di collegamento API, 2 pagine di codice fino a un solo poche righe.

Grazie per tutte le risposte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top