MFCでdllを作成する 2

前回の記事

続・作成

Exe側の処理作成 (エディットボックスから数字読み取り)

エディットボックスから数字を取得します。

リソースビューから前回作成したIDD_DIALOG_CALCを開き、
これまた作った「計算実行」ボタンをダブルクリック。

Calc.cppに、ボタンが押下されたときに呼ばれる関数を追加します。

ここに、エディットボックスから値を取得し、数字に変換させる処理を入れます。

CString str1, str2;
GetDlgItemText(IDC_EDIT_NUM1, str1);	//エディットボックス1から文字列取得
GetDlgItemText(IDC_EDIT_NUM2, str2);	//エディットボックス2から文字列取得

int iNum1 = _ttoi(str1);				//文字列を数値に変換
int iNum2 = _ttoi(str2);

// ここにdllの処理を入れる

CString strDisp = _T("");
strDisp.Format(_T("%d + %d = "), iNum1, iNum2);	//数値を文字列に

CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_RESULT);
pStatic->SetWindowText(strDisp);

これで1回ビルドします

出来上がったダイアログに数字を入れて計算実行ボタンを押すと

ちゃんと想定通り動いてくれます。

プロジェクト作成 (dll側)

さて、やっと本題のdll処理です。

Visual Studioから
ファイル→新規作成→プロジェクト
を選択します。

今回、「MFC ダイナミックリンク ライブラリ」を選択します。

検索欄で「MFC dll」で出てくるかもしれません。

次へを選択し、プロジェクト名は「DllCalc」にでもしておきます。

ソリューションを「ソリューションに追加」に選択。

作成を押すとダイアログが出てきますが、そのままOKで構いません。

ソリューションエクスプローラを見ると、DllCalcが追加されているのが分かります。

Dll側の処理作成

さて、加算処理を書いていきます。

まずDllCalc.hに、以下を追記しておきます。

#ifdef DllCalc_EXPORTS
#define DllCalc_API __declspec(dllexport)
#else
#define DllCalc_API __declspec(dllexport)
#endif

extern "C" DllCalc_API int AddNumbers(int a, int b);

次いでDllCalc.cppに、以下を追加。

extern "C" DllCalc_API int AddNumbers(int a, int b)
{
	return a + b;
}

はい、dll側の処理はこれだけです。

Exe側の処理作成 (dllの呼び出し)

いよいよ最後、Exe側でdllを呼んでみます。

まずヘッダーCalc.hに定義を。

typedef int(*DLLPROC)(int, int);	//dll側の関数

次にCalc.cppに具体的な処理を。
先ほどの、計算実行ボタンを押下すると呼ばれる関数
(今回で言うとOnBnClickedButtonCalcstart)内に追記します。
後、strDispも少しだけ変更します。

int iResult;			// dllの戻り値
HINSTANCE hinstDLL = LoadLibrary(_T("DllCalc.dll"));
if (hinstDLL != NULL)
{
	DLLPROC ProcAdd = (DLLPROC)GetProcAddress(hinstDLL, "AddNumbers");
	if (NULL != ProcAdd)
	{
		iResult = ProcAdd(iNum1, iNum2);
	}
	FreeLibrary(hinstDLL);
}

CString strDisp = _T("");
strDisp.Format(_T("%d + %d = %d"), iNum1, iNum2, iResult);	//数値を文字列に

見ればわかると思いますが、
Exe側には加算処理は一切書いていません。

さて、動くかな…?

追記

iResultは念のため0で初期化すべきでしたね。

ビルド & 実行

ソリューションのビルドをします。

ExeDialog.exeを開き、計算ダイアログへ。

これで計算実行を押すと

よーしよしよし(∩´∀`)∩

初めて作ったのですが、dllと連携できてよかったです。

念のため、デバッグしてみます。

exe側で、ProcAddを呼んでる箇所にいったんブレークポイントを張ります。

プロセスにアタッチをし、計算実行ボタンを押下します。

そりゃ当然止まってくれます。

ここでステップインすると
(Visual StudioはF11でステップインできます)

ちゃんとdllのソースへ飛んでくれます。

値もちゃんとわたってきてくれてました。

終わりに

今回dllを作り、exe側とデータのやり取りを行ってみました。

加算処理という超簡単なものですが動かせることができて良かったです。

ものすごい計算量をしなければいけないシステムなんかは、
計算専用のdllを作成するとかが好ましいんですかね…?

今回はここまでです。

減算、乗算、除算も加えたソースも載せて終わりです。

割り算はint型で

ExeDialogDlg.cpp (最初のダイアログ。計算ダイアログを表示させます。)


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

#include "pch.h"
#include "framework.h"
#include "ExeDialog.h"
#include "ExeDialogDlg.h"
#include "afxdialogex.h"
#include "Calc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// アプリケーションのバージョン情報に使われる 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()


// CExeDialogDlg ダイアログ



CExeDialogDlg::CExeDialogDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_EXEDIALOG_DIALOG, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

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

BEGIN_MESSAGE_MAP(CExeDialogDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON_CALCDLG, &CExeDialogDlg::OnBnClickedButtonCalcdlg)
END_MESSAGE_MAP()


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

BOOL CExeDialogDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

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

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

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		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: 初期化をここに追加します。

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

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

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

void CExeDialogDlg::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
	{
		CDialogEx::OnPaint();
	}
}

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



void CExeDialogDlg::OnBnClickedButtonCalcdlg()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CCalc dlg;
	dlg.DoModal();
}

Calc.h

#pragma once
#include "afxdialogex.h"


// CCalc ダイアログ

class CCalc : public CDialog
{
	DECLARE_DYNAMIC(CCalc)

public:
	CCalc(CWnd* pParent = nullptr);   // 標準コンストラクター
	virtual ~CCalc();

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

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

	DECLARE_MESSAGE_MAP()
public:
	virtual BOOL OnInitDialog();
	afx_msg void OnBnClickedButtonCalcstart();

	typedef int(*DLLPROC)(int, int);	//dll側の関数
};

Calc.cpp

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

#include "pch.h"
#include "ExeDialog.h"
#include "afxdialogex.h"
#include "Calc.h"


// CCalc ダイアログ

IMPLEMENT_DYNAMIC(CCalc, CDialog)

CCalc::CCalc(CWnd* pParent /*=nullptr*/)
	: CDialog(IDD_DIALOG_CALC, pParent)
{

}

CCalc::~CCalc()
{
}

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


BEGIN_MESSAGE_MAP(CCalc, CDialog)
	ON_BN_CLICKED(IDC_BUTTON_CALCSTART, &CCalc::OnBnClickedButtonCalcstart)
END_MESSAGE_MAP()


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


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

	// TODO: ここに初期化を追加してください
	CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_RESULT);
	pStatic->SetWindowText(_T("計算を行います。"));

	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}


void CCalc::OnBnClickedButtonCalcstart()
{
	// TODO: ここにコントロール通知ハンドラー コードを追加します。
	CString str1, str2;
	GetDlgItemText(IDC_EDIT_NUM1, str1);	//エディットボックス1から文字列取得
	GetDlgItemText(IDC_EDIT_NUM2, str2);	//エディットボックス2から文字列取得

	int iNum1 = _ttoi(str1);				//文字列を数値に変換
	int iNum2 = _ttoi(str2);

	int iResultAdd = 0;			// dllの戻り値格納
	int iResultSubtract = 0;			// dllの戻り値格納
	int iResultMultiple = 0;			// dllの戻り値格納
	int iResultDivide = 0;			// dllの戻り値格納
	HINSTANCE hinstDLL = LoadLibrary(_T("DllCalc.dll"));
	if (hinstDLL != NULL)
	{
		DLLPROC ProcAdd = (DLLPROC)GetProcAddress(hinstDLL, "AddNumbers");
		if (NULL != ProcAdd)
		{
			iResultAdd = ProcAdd(iNum1, iNum2);		//加算
		}
		DLLPROC ProcSubtract = (DLLPROC)GetProcAddress(hinstDLL, "SubtractNumbers");
		if (NULL != ProcAdd)
		{
			iResultSubtract = ProcSubtract(iNum1, iNum2);	//減算
		}
		DLLPROC ProcMultiple = (DLLPROC)GetProcAddress(hinstDLL, "MultipleNumbers");
		if (NULL != ProcAdd)
		{
			iResultMultiple = ProcMultiple(iNum1, iNum2);	//乗算
		}
		DLLPROC ProcDivide = (DLLPROC)GetProcAddress(hinstDLL, "DivideNumbers");
		if (NULL != ProcAdd)
		{
			iResultDivide = ProcDivide(iNum1, iNum2);		//除算
		}
		FreeLibrary(hinstDLL);
	}

	CString strDisp = _T("");
	strDisp.Format(_T("%d + %d = %d\r\n%d - %d = %d\r\n%d × %d = %d\r\n%d ÷ %d = %d\r\n"),
					iNum1, iNum2, iResultAdd,
					iNum1, iNum2, iResultSubtract, 
					iNum1, iNum2, iResultMultiple, 
					iNum1, iNum2, iResultDivide);	//数値を文字列に

	CStatic* pStatic = (CStatic*)GetDlgItem(IDC_STATIC_RESULT);
	pStatic->SetWindowText(strDisp);
}

DllCalc.h

// DllCalc.h : DllCalc DLL のメイン ヘッダー ファイル
//

#pragma once

#ifndef __AFXWIN_H__
	#error "PCH に対してこのファイルをインクルードする前に 'pch.h' をインクルードしてください"
#endif

#include "resource.h"		// メイン シンボル


// CDllCalcApp
// このクラスの実装に関しては DllCalc.cpp をご覧ください
//

class CDllCalcApp : public CWinApp
{
public:
	CDllCalcApp();

// オーバーライド
public:
	virtual BOOL InitInstance();

	DECLARE_MESSAGE_MAP()
};

#ifdef DllCalc_EXPORTS
#define DllCalc_API __declspec(dllexport)
#else
#define DllCalc_API __declspec(dllexport)
#endif

extern "C" DllCalc_API int AddNumbers(int a, int b);
extern "C" DllCalc_API int SubtractNumbers(int a, int b);
extern "C" DllCalc_API int MutipleNumbers(int a, int b);
extern "C" DllCalc_API int DivideNumbers(int a, int b);

DllCalc.cpp

// DllCalc.cpp : DLL の初期化ルーチンを定義します。
//

#include "pch.h"
#include "framework.h"
#include "DllCalc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
//TODO: この DLL が MFC DLL に対して動的にリンクされる場合、
//		MFC 内で呼び出されるこの DLL からエクスポートされたどの関数も
//		関数の最初に追加される AFX_MANAGE_STATE マクロを
//		持たなければなりません。
//
//		例:
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// 通常関数の本体はこの位置にあります
//		}
//
//		このマクロが各関数に含まれていること、MFC 内の
//		どの呼び出しより優先することは非常に重要です。
//		it は、次の範囲内で最初のステートメントとして表示されるべきです
//		らないことを意味します、コンストラクターが MFC
//		DLL 内への呼び出しを行う可能性があるので、オブ
//		ジェクト変数の宣言よりも前でなければなりません。
//
//		詳細については MFC テクニカル ノート 33 および
//		58 を参照してください。
//

// CDllCalcApp

BEGIN_MESSAGE_MAP(CDllCalcApp, CWinApp)
END_MESSAGE_MAP()


// CDllCalcApp の構築

CDllCalcApp::CDllCalcApp()
{
	// TODO: この位置に構築用コードを追加してください。
	// ここに InitInstance 中の重要な初期化処理をすべて記述してください。
}


// 唯一の CDllCalcApp オブジェクト

CDllCalcApp theApp;


// CDllCalcApp の初期化

BOOL CDllCalcApp::InitInstance()
{
	CWinApp::InitInstance();

	return TRUE;
}

extern "C" DllCalc_API int AddNumbers(int a, int b)
{
	return a + b;
}

extern "C" DllCalc_API int SubtractNumbers(int a, int b)
{
	return a - b;
}

extern "C" DllCalc_API int MultipleNumbers(int a, int b)
{
	return a * b;
}

extern "C" DllCalc_API int DivideNumbers(int a, int b)
{
	return a / b;
}

コメント

タイトルとURLをコピーしました