4
3

More than 3 years have passed since last update.

C++で作成したDLLとC#/Javaとで文字列を授受する

Posted at

目的

諸事情あってC++で作成したライブラリ(DLL)をC#, Javaでも使用したい。
またその処理の都合上、文字列を引数で授受したい。

方法

ソースコード全体はGitHubに配置。

前提

  • C++, C# のビルドにはVisual Studio 2019を使用
  • Java は Open JDK 11 を使用
  • 各ソースコードはShift-JISで記述

呼び出される関数

定義

今回呼び出される関数(C++で作成)の定義を示す。

my_common_lib.h
#define MY_COMMON_FUNC_DECL_KWD __declspec(dllexport)
extern "C" MY_COMMON_FUNC_DECL_KWD bool __stdcall my_func_a(
    const char* in_str,
    char* out_str
);

仕様上のポイントは以下の通り。

  • 入力引数として文字列
  • 出力引数として文字列
  • 戻り値は論理値

実装上のポイントは以下の通り。

  • extern "C"として関数名がマングリングされるのを防ぐ。
  • __stdcallを指定する。

実装

デモとして以下のように関数を実装しておく。

my_common_lib.cpp
#include "my_common_lib.h"
#include <iostream>

bool my_func_a(const char* in_str, char* out_str) {
    std::cout << "in_str: " << in_str << std::endl;
    strcpy(out_str, "日本語(とASCII)を含む文字列");
    return (strlen(in_str) < 5);
}

呼び出す側(C#)

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace DemoApp {
    class Program {
        static void Main(string[] args) {
            StringBuilder outBuf = new StringBuilder(256);
            NativeMethods.my_func_a("日本語とEnglish", outBuf);
            Console.WriteLine(outBuf.ToString());
        }
    }

    static class NativeMethods {
        [DllImport("..\\..\\common-dll\\common-dll\\dest\\my_common_lib.dll", CallingConvention = CallingConvention.StdCall)]
        public extern static bool my_func_a(string in_str, StringBuilder out_str);
    }
}

実装のポイントは以下の通り。

  • 関数と同名のstaticメソッドDllImport属性を付与してDLL関数の受け皿とする
    • dllNameにファイルパスを指定すれば任意のDLLファイルをロードできる
    • 呼び出し規約はDLL関数に合わせておく(今回はStdCall)
    • 入力引数の文字列はstringを割り当てる
    • 出力引数の文字列はStringBuilderを割り当てる
  • 使用する際は単にstaticメソッドとして呼び出せばよい。

実行結果は以下のようになる(1行目はDLL関数内での出力)。

in_str: C#から文字列
result: False
out_str: 日本語(とASCII)を含む文字列

呼び出す側(Java)

Java Native Accessを利用する。

pom.xml
    <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna</artifactId>
      <version>5.8.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna-platform -->
    <dependency>
      <groupId>net.java.dev.jna</groupId>
      <artifactId>jna-platform</artifactId>
      <version>5.8.0</version>
    </dependency>
App.java
import com.sun.jna.Library;
import com.sun.jna.Native;

public class App 
{
    public static void main( String[] args )
    {
        byte[] buf = new byte[256];
        boolean result = NativeMethods.INSTANCE.my_func_a("Javaから文字列", buf);
        System.out.println("result: " + result);
        System.out.println("out_str: " + Native.toString(buf));
    }

    public interface NativeMethods extends Library {
        NativeMethods INSTANCE = Native.load("..\\..\\common-dll\\dest\\my_common_lib.dll", NativeMethods.class);
        public boolean my_func_a( String in_str, byte[] out_str );
    }
}

実装のポイントは以下の通り。

  • com.sun.jna.Libraryインターフェースを継承したインターフェースを作成する
    • 関数と同名のメソッドを作成する
      • 入力引数の文字列はStringを割り当てる
      • 出力引数の文字列はbyte[]を割り当てる→呼び出し後に文字列に変換する
    • メンバにインスタンスの実体を定義する(※)
      • nameにファイルパスを指定すれば任意のDLLファイルをロードできる
  • 使用する際はインターフェースのメンバを介してメソッドを実行する。
  • Native.toStringメソッドを利用し、出力引数のバイト列を文字列に変換する

※JNA 4.xで使用されていたloadLibraryは非推奨とされ、5.xではloadを使用することに注意(使い方は同じ)。

実行結果は以下のようになる(1行目はDLL関数内での出力)。

in_str: Javaから文字列
result: false
out_str: 日本語(とASCII)を含む文字列

参考

4
3
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
4
3