結果だけでなく過程も見てください

日々の奮闘を綴る日記です。

Windowsで超シンプルなC/C++のDLL作成方法、C/C++からDLLの関数を呼ぶ方法、JavaからDLLの関数を呼ぶ方法、DLLに含まれる関数の調べ方

今更な内容の個人的メモ。極力手短な手順で。環境はWindows 10(64-bit)です。
例としてVisual Studio Community 2019を使って、2つの整数を受け取り、その和を返す関数を作ります。
JavaEclipse(Pleiades)を使ってます。

C/C++のDLL作成方法

1.[ファイル]→[新規作成]→[プロジェクト]→「dll」で検索して出てきたテンプレートを選択する。プロジェクト名は「sum」とする。
2.ヘッダファイルを作成する。名前はfunc.hとする。内容は以下。

#pragma once
#ifndef __FUNC_H
#define __FUNC_H

#ifdef DLLEXPORTS
#define DllExport extern "C" __declspec(dllexport)
#else
#define DllExport extern "C" __declspec(dllimport)
#endif

DllExport int __cdecl sum(int a, int b);

#endif

3.プロジェクトを右クリック→[プロパティ]→[C/C++]→[プリプロセッサ]→[プリプロセッサの定義]に「DLLEXPORTS」というマクロを定義する
4.dllmain.cppに関数の定義を書く。内容は以下。
ポイントとして必ず上記func.hをincludeすること!(includeしないと関数はexportされません)

#include "func.h"

int __cdecl sum(int a, int b)
{
    return a+b;
}

5.ビルドしてsum.dllを生成する
※以降で使うので同じ場所に生成されたsum.libがちゃんと生成されてるかも確認してください

C/C++からDLLの関数を呼ぶ方法

1.C/C++のプロジェクトを適当に作る
2.プロジェクトを右クリック→[プロパティ]→[リンカー]→[追加のライブラリ ディレクトリ]に上記「C/C++のDLL作成方法」で生成されたsum.libが存在するディレクトリを指定する
3.プロジェクトを右クリック→[プロパティ]→[リンカー]→[入力]→[追加の依存ファイル]に「sum.lib」を追加する
4.上記「C/C++のDLL作成方法」で作成したfunc.hをプロジェクトに追加する。
5.ビルドして実行ファイルを生成する
6.実行ファイルと同じ場所に「sum.dll」を配置してから、実行ファイルを実行する

JavaからDLLの関数を呼ぶ方法

ここの記事はあまりシンプルにまとまっていないかもしれません。ごめんなさい。
1.Eclipse(pleiades)で[ファイル]→[新規]→[Javaプロジェクト]で適当なプロジェクトを作る。ここではプロジェクト名をUseDllとする。
2.プロジェクトを右クリック→[新規]→[その他]→[Java]→[クラス]を選び、パッケージは「samplepkg」など適当なものにし、[名前]のところは「UseDll」とする。また画面下の方から「public static void main」を生成するように設定する。

3.以下にアクセスしてJavaからC/C++のDLLを呼び出すためのJNAのjarファイルをダウンロードする。わかりにくいけど「JNA Platform」あたりにあるjna-platform-5.11.0.jar(バージョンは時期によって異なる)あたりからjarをダウンロードできる。
github.com
4.プロジェクトを右クリック→[プロパティ]→[Javaのビルド・パス]→[ライブラリー]→[クラスパス]→[外部jarの追加]を選び、3.でダウンロードしたjna-<バージョン>.jarを指定する。[適用して閉じる]でウィンドウを閉じる。
5.DLLをJavaから見える場所に配置する。ここがちょっとややこしい頑張ってください。EclipseでworkspaceにプロジェクトUseDllを作っているとしたら「(Eclipse)インストールパス\workspace\UseDll\bin\」の場所にsum.dllを配置してください。
6.UseDll.javaが以下のソースになるようにする。

package samplepkg;

import com.sun.jna.Library;
import com.sun.jna.Native;

public class UseDll 
{
	public interface CLibrary extends Library 
	{
		public int sum(int a, int b);
	}

	public static void main(String[] args)
	{
		// TODO 自動生成されたメソッド・スタブ
		final CLibrary clib = (CLibrary)Native.loadLibrary("sum", CLibrary.class);				
		System.out.println("result = " + clib.sum(10, 32));
	}
}

7.ここからがあまり慣れていない点ですが、ちょっと手探りです。とりあえずうまく動いたので記事は書いていますが、32-bitのJavaをインストールする必要があったり、少し面倒な印象。なんとかならないのかな…。シンプルでなくてごめんなさい。Visual Studio Communityで何も考えずにDLLを作るとどうやらDLLが32-bit版になるようです。で、64-bitのJavaから32-bitのDLLはどうやらコールできないようで・・・Javaも32-bitにする必要があるようです。今回は古い32-bitのDLL(再作成が不可な状況)を想定していますので、Javaを32-bitにする方法を採用したいと思います。
[https://www.java.com/en/download/manual.jsp:title=Java Archive Downloads - Java SE 8 | Oracle 日本]
ここから「Windows x86」(つまり32-bit版)をダウンロードしてインストールします。
8.Eclipseに7.でインストールしたJavaを追加する。[ウィンドウ]→[設定]→[Java]→[インストール済みのJRE]→[追加]自分は「C:\Program Files (x86)\Java\jre1.8.0_333」を追加した。
9.プロジェクトを右クリック→[プロパティー]→[Javaのビルド・パス]→[ライブラリー]でJRE システム・ライブラリーを選択して[編集]ボタン→代替JRE→8.で追加したJREを指定
10.UseDll.javaを右クリック→[実行]→[Javaアプリケーション]

おまけ

DLLに含まれる関数の調べ方

1.VisualStudioの開発者用のコマンドプロンプト(英語でインストールされているときはDeveloper Command Prompt)を起動する
2.dumpbin /exports <対象のDLLファイル>
3.以下のように「ordinal hint RVA name」みたいなヘッダがあれば関数がエクスポートされている(DLLの関数として使える)ことになります。

DLLが64bitか32bitかを調べる方法

1.VisualStudioの開発者用のコマンドプロンプト(英語でインストールされているときはDeveloper Command Prompt)を起動する
2.dumpbin /headers <対象のDLLファイル> | findstr machine
3.「machine (x86)」のような表記の場合は32-bit、「machine (x64)」のような表記の場合は64-bitだとわかる

トラブルシューティング - libファイルが生成されない

おそらくなんらかの理由で関数がエクスポートされていません。
このあたりのコードが抜けていませんか?

#include "func.h"
#define DLLEXPORTS

DLLとして使用できる関数がない場合、libファイルは生成されません。
↑で紹介したdumpbinコマンドで関数がエクスポートされているか確認してください。
libが生成されない場合おそらくは関数のエントリポイントが存在しないはずです。

トラブルシューティング - JavaでDLLが見つからないときのエラーメッセージ

以下のように「指定されたモジュールが見つかりません」と言われます。

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'sum':
指定されたモジュールが見つかりません。

指定されたモジュールが見つかりません。

指定されたモジュールが見つかりません。

Native library (win32-x86/sum.dll) not found in resource path ([file:/C:/pleiades/2022-03/workspace/UseDll/bin/, file:/C:/temp/jna-5.11.0.jar])
:
:

エクスポート/インポートを自動で切り替えるマクロについて

プロジェクトで「DLLEXPORTS」というマクロを定義しておくとエクスポートになり、定義していないと自動的にインポートになるようなマクロです。DLL関数を定義する際によく使う手法です。

プライバシーポリシー お問い合わせ