...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

DFL: リソースファイル読み込みのサンプルコード

ようやく dfl.resources モジュールのサンプルコードに着手した。

リソースコンパイラ rc.exe が必要なので、この記事のコードは、Visual Studio がインストールされていないと実行できない。

リソースのバイナリファイルもリポジトリに含めてあるので、プロジェクト全体をこちらからダウンロードして試した方がよい。

sample.rc リソースファイル

こういうリソースファイルを作成したとする。

256   ICON  "image/icon.ico"
257   BITMAP "image/dman-error.bmp"
258   RCDATA
{
    "Here is an ANSI string\0",    // explicitly null-terminated 
    L"Here is a Unicode string\0", // explicitly null-terminated 
    1024,                          // integer, stored as WORD 
    7L,                            // integer, stored as DWORD 
    0x0010,                        // hex integer as WORD
    0o010,                         // octal integerr as WORD
}
259 CURSOR  "image/d-cursor.cur"
STRINGTABLE
{
    260, "Hoge\0"
    261, "Fuga\0"
}

この sample.rc が ./resource フォルダに入っているとして、次を実行すると、 sample.res ファイルが作成される。 もちろん rc.exe にパスが通っていないとダメ。

> rc /v resource\sample.rc

resources_sample.d

メインのコードはこちら。

import dfl;

import dfl.internal.dlib;

import core.sys.windows.winnt;
import core.sys.windows.winuser;

import std.conv;

version(Have_dfl) // For DUB.
{
}
else
{
    pragma(lib, "dfl.lib");
}

// RCDATA の項目に合わせて定義した構造体
struct RCData
{
    string ansiString;
    wstring unicodeString;
    WORD wordInt;
    DWORD dwordInt;
    WORD wordHexInt;
    WORD wordOctInt;
}

// リソースファイルから読み出して構造体に埋めていく
// RCData* dat = cast(RCData*)pBuffer; みたいなことはできないので頑張る
RCData parseRCData(const(ubyte)* ptr, size_t size)
{
    RCData data;
    size_t offset;

    // string ansiString: ANSI null-terminated string
    auto start = offset;
    while (offset < size && ptr[offset] != 0)
        offset += char.sizeof;
    data.ansiString = cast(string)ptr[start .. offset];
    offset += char.sizeof; // skip null terminator

    // wstring unicodeString: Unicode null-terminated string
    start = offset;
    while (offset + 1 < size && (ptr[offset] != 0 || ptr[offset + 1] != 0))
        offset += wchar.sizeof;
    data.unicodeString = cast(wstring)ptr[start .. offset];
    offset += wchar.sizeof; // skip null terminator

    // WORD wordInt
    data.wordInt = *cast(WORD*)&ptr[offset];
    offset += WORD.sizeof;

    // DWORD dwordInt
    data.dwordInt = *cast(DWORD*)&ptr[offset];
    offset += DWORD.sizeof;

    // WORD wordHexIn
    data.wordHexInt = *cast(WORD*)&ptr[offset];
    offset += WORD.sizeof;

    // WORD wordOctInt
    data.wordOctInt = *cast(WORD*)&ptr[offset];
    offset += WORD.sizeof;

    return data;
}

class MainForm : Form
{
    Bitmap _bmp;
    Cursor _cur;
    Icon _ico;
    string[] _str;
    RCData _rcdata;

    public this()
    {
        this.text = "Resources example";
        this.size = Size(600, 600);

        Resources r = new Resources(Application.getInstance());

        _bmp = r.getBitmap(257);
        
        _cur = r.getCursor(259);

        _ico = r.getIcon(256, 32, 32);
        this.icon = _ico;

        _str = [r.getString(260), r.getString(261)];

        void[] dat = r.getData(RT_RCDATA, 258);
        _rcdata = parseRCData(cast(ubyte*)dat, dat.length);
    }

    override void onPaint(PaintEventArgs e)
    {
        Font fon = new Font("msgothic", 12);
        e.graphics.drawText(_str[0] ~ " " ~ _str[1], fon, Color.black, Rect(0, 0, width, height));

        e.graphics.drawText(_rcdata.ansiString, fon, Color.black, Rect(0, 50, width, height));
        e.graphics.drawText(_rcdata.unicodeString.to!string(), fon, Color.black, Rect(0, 70, width, height));
        e.graphics.drawText(_rcdata.wordInt.to!string(), fon, Color.black, Rect(0, 90, width, height));
        e.graphics.drawText(_rcdata.dwordInt.to!string(), fon, Color.black, Rect(0, 110, width, height));
        e.graphics.drawText(_rcdata.wordHexInt.to!string(), fon, Color.black, Rect(0, 130, width, height));
        e.graphics.drawText(_rcdata.wordOctInt.to!string(), fon, Color.black, Rect(0, 150, width, height));

        _bmp.draw(e.graphics, Point(100, 200));
    }

    override void wndProc(ref Message msg)
    {
        if (msg.msg == WM_SETCURSOR)
        {
            Cursor.current = _cur;
            return; // Returns so that the class cursor is not displayed.
        }
        super.wndProc(msg);
    }
}

static this()
{
    Application.enableVisualStyles();

    // おまけ
    import dfl.internal.dpiaware;
    // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); // OK
    // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED); // OK
    // SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); // Windows suppresses the display of balloon tips.
    SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); // ditto.
}

void main()
{
    Application.run(new MainForm());
}

dub.json

DUB でビルドするときにリソースファイルがリンクされるように;

"lflags-windows": ["resource/sample.res"]

を追記している。

{
    "authors": ["haru-s"],
    "copyright": "Copyright (C) 2025 haru-s",
    "description": "DFL sample code.",
    "name": "resources_sample",
    "targetType": "executable",
    "targetPath": "bin",
    "dependencies": {
        "dfl": {
            "path": "../../../dfl"
        }
    },
    "lflags-windows": ["resource/sample.res"],
    "lflags-windows-x86_mscoff-dmd": ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"],
    "lflags-windows-x86_64-dmd":     ["/SUBSYSTEM:WINDOWS", "/ENTRY:mainCRTStartup"]
}

DFL のダウンロード

github.com

DUB のパッケージ

code.dlang.org