ダイアログのフォントの取得と変更

解決


sel  2010-11-29 10:25:54  No: 72092  IP: [192.*.*.*]

こんにちは。
VC++ 非MFC,非.NET Frameworkで組んでいます。
リソースエディタでダイアログ全体のフォントを指定できますが、
これをコードで取得したり変更したりする事はできないでしょうか?

試しにダイアログのフォントを変更してみても反映されず、
LOGFONTを取得してみるとSystemFontとなっていました。

変更に関しては、全コントロールを列挙して1つ1つにフォントを設定して
サイズを再計算する事で一応はできたのですが、
元のフォントがわからないのがちょっと不便です。
また、変更もちょっと無理矢理感が否めません。

「ダイアログのフォントを取得」「変更」などでぐぐってはみたのですが、
いまいちそれらしい物はヒットしませんでした。
ダイアログ自体に設定されたフォントを取得したり一括変更したりする事は可能でしょうか?

あと、上のLOGFONT取得時に、GetLogFont()とかが使えないので
SelectObject()で取得したのですが、これも他に方法はないでしょうか?

よろしくお願いします。

編集 削除
仲澤@失業者  2010-11-29 13:06:14  No: 72093  IP: [192.*.*.*]

>試しにダイアログのフォントを変更してみても反映されず、
対象DLGにWM_SETFONT をSendMessage()してもだめだったという
ことでしょうか。

>あと、上のLOGFONT取得時に、GetLogFont()とかが使えないので
意味がわかりません。
一般にGDIオブジェクトの詳細情報を取得するには、
  1.対象のGDIオブジェクトのハンドルを取得する( HBITMAP、HFONT等)
  2.GetObject()に上のハンドルと取得したい構造体のポインタを渡す。
と取得できるはずですが、エラーになるということでしょうか。

編集 削除
sel  2010-11-29 13:38:49  No: 72094  IP: [192.*.*.*]

レスありがとうございます。

>>試しにダイアログのフォントを変更してみても反映されず、
>対象DLGにWM_SETFONT をSendMessage()してもだめだったという
ことでしょうか。

その通りです。
うまくいかないので試しに取得してみると、リソースエディタではMS PゴシックなのにSystemFontだったので、ああ違うんだなと。

>>あと、上のLOGFONT取得時に、GetLogFont()とかが使えないので
>意味がわかりません。

すみません、わかりにくい書き方でした。
GetLogFont()はCFontクラスのメンバなので、非MFCでは使えないという意味です。
HFONTさえ取れればGetObject()でLOGFONTは取得できるのですが、
1の、肝心のHFONTの取得が、SelectObject()で(したくないのに)変更する以外に
いい方法が見つからなかったのです。

編集 削除
仲澤@失業者  2010-11-29 17:02:24  No: 72095  IP: [192.*.*.*]

>1の、肝心のHFONTの取得が、SelectObject()で(したくないのに)変更する>以外に
>いい方法が見つからなかったのです。

DLG に WM_GETFONT を SendMessage()しても、有効な
HFONTを取得できなかった、ということでしょうか。
それは、WM_INITDIALOG受領後のことでしょうか。

編集 削除
sel  2010-11-29 17:22:51  No: 72096  IP: [192.*.*.*]

WM_SETFONTを試しておきながらWM_GETFONTの事をすっかり忘れていました。。
WM_INITDIALOGの応答でWM_GETFONTをしてみたら、
なぜか高さが-16と負値になっていますが、ちゃんとフォントが取得できました。
(GetDC()してSelectObject()で旧ハンドル取得とかやってました。お恥ずかしい限り)

しかし変更に関してはやはりWM_SETFONTではだめなようです。
SendMessage(hwndDlg, WM_SETFONT, (WPARAM)hFont, MAKELPARAM(1,0));
こんな感じで呼んでいるのですが・・。
やはりコードで一括変更みたいなのは無理なのでしょうか?

編集 削除
m  2010-11-29 18:04:33  No: 72097  IP: [192.*.*.*]

画面解像度に応じてダイアログの拡大(縮小)表示
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200406/04060089.txt

ダイアログ: メモリ上にテンプレートを作成
http://hp.vector.co.jp/authors/VA047249/program/tips/dlgmem.htm

のようにすれば、フォントを変更してダイアログを作成できると思います。
ちょっと面倒ですが。

編集 削除
仲澤@失業者  2010-11-29 19:15:49  No: 72098  IP: [192.*.*.*]

>なぜか高さが-16と負値になっていますが、
正常な値です。LOGFONTの説明を良くよみましょう。

>しかし変更に関してはやはりWM_SETFONTではだめなようです。
では、自分でやるしかないですね。
WM_SETFONTを受け取ったら
1.DefwindowProc( hwnd, msg, wParam, lParam);
2.EnumChildWindows( hwnd, EnumProc, wParam); // wParam=HFONT
3.EnumProc()を実装
  BOOL CALLBACK EnumProc(
    HWND hwnd,      // 子ウィンドウのハンドル
    LPARAM lParam)  // = HFONT
  {   SendMessage( hwnd, WM_SETFONT, lParam, 1); return TRUE;  }
とかって、やるだけですよねぇ。超簡単(^^)/

編集 削除
sel  2010-11-29 19:30:32  No: 72099  IP: [192.*.*.*]

レスありがとうございます。
かなりやりたい事に近い内容のようで、参考になります。
ただ、見た感じリソースのフォント指定はあくまで「作成時」に使用されるようですね。
という事はやはり恐らく一括サイズ指定は作成時のみの可能性が高いのでしょうか?
作成しなおすとリサイズに比べて処理が増えるので、この方法でリサイズできるといいのですが、教えていただいた内容をちょっと調べてみます。

編集 削除
仲澤@失業者  2010-11-29 19:36:28  No: 72100  IP: [192.*.*.*]

>ただ、見た感じリソースのフォント指定はあくまで「作成時」に使用される>ようですね。
それは仕様です。WM_INITDIALOGの直前で行われるようです。

>という事はやはり恐らく一括サイズ指定は作成時のみの可能性が高いのでし>ょうか?
自分の提示したコードはDLG表示中は常に可能ですが、
DLG自身のサイズ変更を考慮していません。
大きなフォントを指定されたエディットは自身のサイズを大きく
するかもしれません。

編集 削除
sel  2010-11-29 19:42:06  No: 72101  IP: [192.*.*.*]

>仲澤@失業者さん
書き込んでいる間にレス頂いてました。

>>なぜか高さが-16と負値になっていますが、
>正常な値です。LOGFONTの説明を良くよみましょう。

MSDNを見てみたら確かに書いてますね、勉強不足でした。
(説明文を見てもいま一つ正負それぞれの意味が理解できませんでしたが・・。。)

>WM_SETFONTを受け取ったら
>1.DefwindowProc( hwnd, msg, wParam, lParam);
>2.EnumChildWindows( hwnd, EnumProc, wParam); // wParam=HFONT
>3.EnumProc()を実装

はい、一応今はそんな感じで実装しています。
フォントと、あとは位置とサイズも取得して調整しています。
リソースいじるよりはやはりこの方が簡単そうですね?
コンボボックスだけサイズ取得方法がちょっと違ったので、他にそういうコントロールがないか少し心配ではありますが。

編集 削除
sel  2010-11-29 19:47:30  No: 72102  IP: [192.*.*.*]

途中で送信してしまいました。

そういう心配があったので、リソースでの指定みたく一括でやる方法がないものかと思ったのですが、
なかなか楽はできないものですね。

編集 削除
gak  2010-11-30 18:05:08  No: 72103  IP: [192.*.*.*]

散発的に、本題かどうか関係無く幾つかの内容に対してレス。

> 1の、肝心のHFONTの取得が、SelectObject()で(したくないのに)変更する以外に
GetCurrentObject() を使うと HDC に結び付けられているgdiオブジェクトを直で参照できる。

> (GetDC()してSelectObject()で旧ハンドル取得とかやってました。お恥ずかしい限り)
WM_SETFONT したからと言って HDC にデフォルトで結びつけられるフォントが指定フォントになるワケじゃ無い。
文字書く時はこのフォントで書けと(システムに)指示するだけ。HDC に結びつけるのは別段階の処理。
なので SelectObject(GetCurrentObject)の戻り値を視ても意味は無かったりする…ハズ(少なくとも何年か前に検証した時は)

> そういう心配があったので、リソースでの指定みたく一括でやる方法がないものかと思ったのですが
無いわけではない。日本語よりC言語の方が伝わると思うのでソース貼付け。長いケド…

INT_PTR DialogBoxMk2(HINSTANCE instance, LPCTSTR id, HWND parent, DLGPROC func, short point, LPCWSTR typeface)
{
#pragma pack(push, 1)
    struct DLGTEMPLATEEX {
        WORD dlgVer;
        WORD signature;
        DWORD helpID;
        DWORD exStyle;
        DWORD style;
        WORD cDlgItems;
        short x;
        short y;
        short cx;
        short cy;
    };
#pragma pack(pop)

    INT_PTR result = 0;
    HRSRC src = ::FindResource(instance, id, RT_DIALOG);
    if (src != NULL) {
        HGLOBAL res = ::LoadResource(instance, src);
        if (res != NULL) {
            const DWORD length = ::SizeofResource(instance, src);
            BYTE* chg = new BYTE[length + 2 + 2 + 1 + 1 + sizeof(wchar_t) * (wcslen(typeface) + 1) + 3]; // 大は小を兼ねる
            if (chg != NULL) {
                BYTE* org = static_cast<BYTE*>(::LockResource(res));
                if (org != NULL) {
                    const bool ex = (*reinterpret_cast<WORD*>(org + 2) == 0xffff);
                    BYTE* src = org;
                    BYTE* dst = chg;
                    // skip DLGTEMPLATE or DLGTEMPLATEEX
                    src += ex ? sizeof(DLGTEMPLATEEX) : sizeof(DLGTEMPLATE);
                    // skip menu
                    if (*reinterpret_cast<WORD*>(src) == 0xffff) {
                        src += 4;
                    }
                    else {
                        src += sizeof(wchar_t) * (wcslen(reinterpret_cast<LPWSTR>(src)) + 1);
                    }
                    // skip class
                    if (*reinterpret_cast<WORD*>(src) == 0xffff) {
                        src += 4;
                    }
                    else {
                        src += sizeof(wchar_t) * (wcslen(reinterpret_cast<LPWSTR>(src)) + 1);
                    }
                    // skip title
                    BYTE* p = (ex ? src : org);
                    src += sizeof(wchar_t) * (wcslen(reinterpret_cast<LPWSTR>(src)) + 1);
                    memcpy(dst, org, src - org);
                    bool srcfontexist;
                    if (ex) {
                        srcfontexist = ((reinterpret_cast<DLGTEMPLATEEX*>(org)->style & DS_SETFONT) != 0);
                        reinterpret_cast<DLGTEMPLATEEX*>(chg)->style |= DS_SETFONT;
                    }
                    else {
                        srcfontexist = ((reinterpret_cast<DLGTEMPLATE*>(org)->style & DS_SETFONT) != 0);
                        reinterpret_cast<DLGTEMPLATE*>(chg)->style |= DS_SETFONT;
                    }
                    dst += src - org;
                    // skip font
                    if (srcfontexist) {
                        if (ex) {
                            p = src;
                        }
                        src += (ex ? 2 + 2 + 1 + 1 : 2);
                        src += sizeof(wchar_t) * (wcslen(reinterpret_cast<LPWSTR>(src)) + 1);
                    }
                    src = p + ((src - p + 3) / 4) * 4; // 4byte境界調整
                    // change font
                    p = (ex ? dst : chg);
                    *reinterpret_cast<short*>(dst) = point; // pointsize
                    dst += 2;
                    if (ex) {
                        *reinterpret_cast<short*>(dst) = FW_NORMAL; // weight
                        dst += 2;
                        *dst++ = 0; // italic
                        *dst++ = DEFAULT_CHARSET; // charset(面倒なので DEFAULT で…)
                    }
                    wcscpy(reinterpret_cast<LPWSTR>(dst), typeface); // fontface
                    dst += sizeof(wchar_t) * (wcslen(reinterpret_cast<LPWSTR>(dst)) + 1);
                    dst = p + ((dst - p + 3) / 4) * 4; // 4byte境界調整
                    // copy DLGITEMTEMPLATEs or DLGITEMTEMPLATEEXs
                    memcpy(dst, src, org + length - src);
                    result = ::DialogBoxIndirect(instance, reinterpret_cast<DLGTEMPLATE*>(chg), parent, func);
                }
                delete []chg;
            }
        }
    }
    return result;
}

void xxxxxxxx() {
    // dialog 表示
//  ::DialogBox(module, MAKEINTRESOURCE(IDD_DIALOG), parent, dialogProc);
    DialogBoxMk2(module, MAKEINTRESOURCE(IDD_DIALOG), parent, dialogProc, 16, L"メイリオ");
}

稼動実績あるコードじゃないんで当然bugは在るという前提で。

編集 削除
gak  2010-11-30 18:12:54  No: 72104  IP: [192.*.*.*]

んあ。同内容の回答を既にmさんがされてますね…

編集 削除
sel  2010-11-30 18:40:11  No: 72105  IP: [192.*.*.*]

レスありがとうございます。
ダイアログ作成時に好きなフォントでテンプレートを作成できるんですね、今回やりたい事とは少し違いましたが、とても参考になります。

GetCurrentObject()は、すっかり失念してました。ありがとうございます。
(ためしに過去に作ったコードを検索してみたらいくつかヒットしました・・・。
  使ったことがあるのに見つけられないとは。。)

今回は「ダイアログを作り直さずに」フォントを変えたかったので、リソースをいじる方法ではなく、個別に変更する方法でいく事にします。
教えていただいたリソースの方も便利なので自作ライブラリに加えてみようと思います。

仲澤@失業者さん、mさん、gakさんありがとうございました。

編集 削除