3

何?

リソース DLL から DLGTEMPLATE をロードしましたが、実行時にコントロールに割り当てられた文字列をプログラムで変更するにはどうすればよいですか?

ダイアログが作成される前にこれを実行できるようにしたいので、表示されている文字列がリソース DLL からのものであり、ダイアログが初期化されたときの SetWindowText の呼び出しからのものではないことがわかります。

Google は、コードで DLGTEMPLATE を作成したり、単純なスタイル ビットをいじったりする例を見つけましたが、メモリ内の文字列を編集する例はありません。

どのように?

ダイアログ/プロパティ シート作成 API をフックすることでこれを行っています。これにより、実際のダイアログが作成され、HWND が作成される前に、DLGTEMPLATE にアクセスできるようになります。

なんで?

ランタイムのローカリゼーションとローカリゼーションのテストができるようになりたいです。文字列 (MFC 7.0 ラッパーを含む)、メニュー、およびアクセラレータ テーブルを読み込むために既にこれを実装していますが、ダイアログ/プロパティ シートの作成を処理するのに苦労しています。

コード例は完璧な答えです。理想的には、DLGTEMPLATE をラップするクラスです。独自のソリューションを作成した場合は投稿します。

4

5 に答える 5

6

メモリ内の文字列を編集することはできません。DLGTEMPLATE 構造体は、リソース dll の関連バイトの直接ファイル マッピングです。それは読み取り専用です。

DLGTEMPLATE 構造全体を処理し、変更された長さの文字列を使用して新しいものを書き出す必要があります。

DLGTEMPLATE ライタを構築するよりも、WM_INITDIALOG をフックし、コントロールと対話して文字列を変更する方が率直に言って簡単です。周りに人が少ないから。変更されたダイアログ リソースを生の .res ファイルとして実際にディスクに保存する (または .dll をインプレースで変更しようとする) という追加の要件がない限り、このアプローチは避けることを強くお勧めします。

アクセラレータ テーブルとメニュー文字列に対して既にこれを行っていると言っています - パッチを適用した文字列が短くなることが保証できる場合は、DLGTEMPLATE 構造体のバイナリ コピーを作成し、検索に必要な重要なスキャン コードを記述します。各文字列を配置して、コピーをその場でパッチできます。

于 2008-10-15T13:30:50.730 に答える
4

RESFMT.ZIP という名前のファイルがどこかにあります (これは Microsoft に由来すると思いますが、完全にはわかりません)。これは、いくつかのコード例でこれを説明しています。Raymond Chen も、彼のブログでこれについて優れた説明を行っています。DIALOGEX コントロールと DIALOG コントロールの形式が異なることに注意してください。

他のいくつかの回答で述べたように、最初から構造を再度作成する必要があります。基本的な情報はすでにあるので、これはすべて悪いことではありません。コントロールを追加するのは難しいところです。

基本的に、大きなメモリ ブロックを WORD *lpIn に割り当てます。次に、その上に構造を追加します。DIALOG の基本情報 (DLGTEMPLATE を参照) とコントロールの追加は、MSDN に情報があるため、非常に明白です。

発生する 2 つの最大の問題は、さまざまな部分が整列境界で始まることを確認することと、DIALOG コントロールの値を解釈することです。特に、文字列のみ、または文字列または序数を追加する場合は特にそうです。各コントロールは、偶数境界で開始する必要があります。

最初の場合(RESFMT.ZIPと思われる場所から借用):

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

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

私がしたことは、次のような一連の関数を構築することで、メモリ内で DIALOGS を組み立てることができました。(私の必要性は、いくつかの非常に基本的なメッセージに関連付けられた RC ファイルを必要としない一般的なコードを持つことができるようにするためでした)。

ここに例があります...

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

    もし (sz_Or_Ord == NULL)
        {
        *lpw++ = 0;
        }
    そうしないと
        {
        if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE マクロ
            {
            *lpw++ = 0xFFFF;
            *lpw++ = LOWORD(sz_Or_Ord);
            }
        そうしないと
            {
            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;
                }
            そうしないと
                {
                *lpw++ = 0;
                }
            }
        }
    return( lpw );
    }

完全なモジュールへのヘッダー ファイルには、次の関数が含まれていました。

WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp, char *タイトル、 ワードID、 char *WinClass, DWORD スタイル、 短い×、 短いy、 短いcx、 ショートサイ、 DWORD ExStyle、 int HelpID); int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp); MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // 参照用に名前を使用するため、NULL にすることができます 短い×、 短いy、 短いcx、 ショートサイ、 DWORD 拡張スタイル、 DWORD スタイル、 char *メニュー、 char *WinClass, char *キャプション, char *FontTypeFace, int フォントサイズ、 int FontWeigth、 int FontItalic、 int 文字セット、 int HelpID、 int NumberOfControls);

これにより、コードからダイアログ全体を簡単に組み立てることができました。

于 2008-10-16T00:00:12.723 に答える
1

API関数を参照してください:: EnumChildWindows(HWND、WNDENUMPROC、LPARAM)

これをCFormView::CreateまたはCDialog::OnInitDialogで呼び出して、コントロールのキャプションを置き換える機会を自分に与えることができます。心配しないでください。古い文字列は、交換する前にちらつくことはありません。

ダイアログリソースで、コントロールのキャプションをある種の辞書のキーに設定します。/ clrをコンパイルする場合は、管理対象文字列テーブルリソースを使用できます。コールバックで、辞書で翻訳された文字列を検索し、コントロールのキャプションを翻訳に設定します。/ clrと管理対象文字列テーブルのもう1つの利点は、System :: Threading :: Thread :: CurrentThread-> CurrentUICultureを既に設定しているWindows(またはユーザー)が適切な言語を自動的に検索できることです。

このようなもの

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 );
        }
    }
}
于 2008-10-15T18:51:11.163 に答える
1

テンプレートを表すmemバッファで、変更する文字列を見つける必要があります。これを行う唯一の方法は、テンプレート全体をトラバースすることです。これは簡単ではありません。それが終わったら、新しい文字列が元の文字列よりも長い場合は、バッファにバイトを挿入します。または、新しい文字列が短い場合は、バッファを縮小します。

クリスが書いたように、WM_INITDIALOGのテキストを変更して、SetWindowText()を呼び出さないという要件を言い換える方がはるかに簡単です。

于 2008-10-15T20:43:46.240 に答える
0

おかげさまで、私は実際にこの問題に 24 時間かかってから、グローバル Windows フック フィルタリング WM_INITDIALOG を使用しました。これははるかに単純な方法であり、うまく機能し、API フックは不要で、2 ページのコードがわずか数行になりました。

すべての答えをありがとう。

于 2008-10-16T09:52:19.797 に答える