LoginSignup
6
5

More than 5 years have passed since last update.

Visual Studio 2017 Visual C++ による Win32 DLL の開発 (リソースのみの DLL)

Posted at

はじめに

DLL には関数やクラスの他、リソースも含めることができます。そして、リソースだけからなる DLL も作成できます。

例えば、企業の業務用アプリ間でリソース DLL を共有することで、アイコンやビットマップ、メッセージなどを共通化するのに役立ちます。

プロジェクトの作成

リソースのみの DLL のプロジェクトは、通常の(関数用の) Win32 DLL と同じです。「新しいプロジェクト」ダイアログで Fig.1 のように DLL プロジェクトを選び、名前を適切に変更して OK ボタンをクリックします。

VCProj_Win32DLL.png
Fig.1 新しいプロジェクトダイアログ

エントリーポイント (main 関数) は不要なので、プロジェクトのプロパティでエントリーポイントが存在しないように設定します。(Fig.2)

ResouceDLLProp.png
Fig.2 エントリーポイントなしの設定

プロジェクトは元々、関数の DLL 用なので、不要なコードが含まれています。そのコードをコメントアウトします。(ファイルは dllmain.cpp)

不要なコードのコメントアウト (dllmain.cpp)

// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。
#include "stdafx.h"

//BOOL APIENTRY DllMain( HMODULE hModule,
//                       DWORD  ul_reason_for_call,
//                       LPVOID lpReserved
//                     )
//{
//    switch (ul_reason_for_call)
//    {
//    case DLL_PROCESS_ATTACH:
//    case DLL_THREAD_ATTACH:
//    case DLL_THREAD_DETACH:
//    case DLL_PROCESS_DETACH:
//        break;
//    }
//    return TRUE;
//}

リソースの追加

ソリューションエクスプローラでプロジェクトを選び、「追加」コンテキストメニューから「リソース」を実行します。Fig.3 のようなダイアログが開くので、必要なリソースを追加します。新しく作成する場合は「新規作成」、既存のアイコンなどを利用する場合は、「インポート」を実行します。

ResouceAddDlg.png
Fig.3 リソースの追加ダイアログ

プログラムはないので、コーディングは不要です。このままビルドを行い DLL ファイルができていることを確認します。

リソース DLL の使用例

このサンプルは DLL からアイコンをロードして表示する MFC アプリケーションです。このサンプルの要点を以下に示します。

  • OnInitDialog ハンドラの中で LoadLibrary API 関数で DLL をロードします。(DLL のロードは必ず動的に行います)
  • この後、DLL のモジュールハンドルを使って、DLL のアイコンを LoadImage API 関数 でロードして、ハンドルを取得します。このとき、アイコンの ID は DLL の中のものなので、シンボルでなく具体的な数を指定します。リソースビューでリソースファイル (.rc) を選択してコンテキストメニューで「リソースシンボル」メニューを実行すると Fig.4 のようなダイアログが開くので、シンボルの具体的な番号を知ることができます。
  • LoadImage API 関数でアイコンの ID はそのまま使用できないので、MAKEINTRESOURCE マクロを使って渡します。
  • アイコンの表示は、OnPaint ハンドラの中で DrawIcon API 関数で行っています。
  • アプリが終了するとき、OnClose ハンドラで FreeLibrary API 関数で DLL を解放します。

ResourceSymbol.png
Fig.4 リソースシンボル

ソースファイル

// TestDllResourceDlg.cpp : 実装ファイル
//

#include "stdafx.h"
#include "TestDllResource.h"
#include "TestDllResourceDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define DLLPATH L"C:\\workspace\\project2017\\Win32Projects\\Debug\\DllResource.dll"

// アプリケーションのバージョン情報に使われる CAboutDlg ダイアログ

class CAboutDlg : public CDialogEx
{
public:
    CAboutDlg();

// ダイアログ データ
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_ABOUTBOX };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート

// 実装
protected:
    DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CTestDllResourceDlg ダイアログ



CTestDllResourceDlg::CTestDllResourceDlg(CWnd* pParent /*=NULL*/)
    : CDialog(IDD_TESTDLLRESOURCE_DIALOG, pParent)
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CTestDllResourceDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CTestDllResourceDlg, CDialog)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_CLOSE()
END_MESSAGE_MAP()


// CTestDllResourceDlg メッセージ ハンドラー

BOOL CTestDllResourceDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // "バージョン情報..." メニューをシステム メニューに追加します。

    // IDM_ABOUTBOX は、システム コマンドの範囲内になければなりません。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // このダイアログのアイコンを設定します。アプリケーションのメイン ウィンドウがダイアログでない場合、
    //  Framework は、この設定を自動的に行います。
    SetIcon(m_hIcon, TRUE);         // 大きいアイコンの設定
    SetIcon(m_hIcon, FALSE);        // 小さいアイコンの設定

    // TODO: 初期化をここに追加します。
    m_hModule = LoadLibrary(DLLPATH);
    m_hIcon1 = (HICON)LoadImage(m_hModule, MAKEINTRESOURCE(101), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR| LR_DEFAULTSIZE);

    return TRUE;  // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}

void CTestDllResourceDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
    if ((nID & 0xFFF0) == IDM_ABOUTBOX)
    {
        CAboutDlg dlgAbout;
        dlgAbout.DoModal();
    }
    else
    {
        CDialog::OnSysCommand(nID, lParam);
    }
}

// ダイアログに最小化ボタンを追加する場合、アイコンを描画するための
//  下のコードが必要です。ドキュメント/ビュー モデルを使う MFC アプリケーションの場合、
//  これは、Framework によって自動的に設定されます。

void CTestDllResourceDlg::OnPaint()
{
    if (IsIconic())
    {
        CPaintDC dc(this); // 描画のデバイス コンテキスト

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // クライアントの四角形領域内の中央
        int cxIcon = GetSystemMetrics(SM_CXICON);
        int cyIcon = GetSystemMetrics(SM_CYICON);
        CRect rect;
        GetClientRect(&rect);
        int x = (rect.Width() - cxIcon + 1) / 2;
        int y = (rect.Height() - cyIcon + 1) / 2;

        // アイコンの描画
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        POINT pos;
        pos.x = 10;
        pos.y = 10;
        CPaintDC dc(this);
        dc.DrawIcon(pos, m_hIcon1);
        // CDialog::OnPaint();
    }
}

// ユーザーが最小化したウィンドウをドラッグしているときに表示するカーソルを取得するために、
//  システムがこの関数を呼び出します。
HCURSOR CTestDllResourceDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}

void CTestDllResourceDlg::OnClose()
{
    // TODO: ここにメッセージ ハンドラー コードを追加するか、既定の処理を呼び出します。
    FreeLibrary(m_hModule);
    CDialog::OnClose();
}

ヘッダーファイル

// TestDllResourceDlg.h : ヘッダー ファイル
//

#pragma once


// CTestDllResourceDlg ダイアログ
class CTestDllResourceDlg : public CDialog
{
// コンストラクション
public:
    CTestDllResourceDlg(CWnd* pParent = NULL);  // 標準コンストラクター

// ダイアログ データ
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_TESTDLLRESOURCE_DIALOG };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート


// 実装
protected:
    HICON m_hIcon;
    HMODULE m_hModule;
    HICON m_hIcon1;

    // 生成された、メッセージ割り当て関数
    virtual BOOL OnInitDialog();
    afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnClose();
};

実行例

TestResouceDLL.png

ExtractIcon や ExtractIconEx を使ってアイコンを取り出す

DLL からアイコンを取り出す場合は、ExtractIcon や ExtractIconEx API 関数を使う方法もあります。InitDialog ハンドラのところで、LoadImage API 関数をこれらの関数に置き換えます。

次のコードは ExtractIcon を使用する例ですが、直接、DLL のパス名を指定するので、LoadLibrary 関数の呼び出しは不要になります。その代りインスタンスハンドルがパラメータとして必要です。

また、リソース ID は使用せず、アイコンの番号(先頭は 0)でアイコンを特定します。

// TODO: 初期化をここに追加します。
HINSTANCE hInst = AfxGetInstanceHandle();
m_hIcon1 = ExtractIcon(hInst, DLLPATH, 0);

-

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5