親牛の開発日記

ぼけ防止するため、開発メモを残そう

C#から文字列の配列をC/C++ DLLへの渡し方

■ネイティブ関数の引数が「char**」の場合

C関数

void init(int argc, char** argv);

C#

[DllImport("XXXXX.dll")]
public static extern int init(int argc, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex =1)]string argv);

これは無難だろう

 

■ネイティブ関数の引数が構造体であって、その中に「char**」があった場合

C構造体

struct ARGS

{

 int argc;

 char** argv;

};

void init(struct ARGS * pArgs);

 

C#

public struct ARGS
{

int Argc;
IntPtr Argv;

public ARGS(string argv)
{
  Argc = argv.Length;
  Encoding encoding = Encoding.GetEncoding(932);
  List<string> list = new List<string>(argv);
  Argv = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr)) * list.Count);

  for (int index = 0; index < list.Count; index++)
   {
      int len = encoding.GetByteCount(list[index]) + 1;
      IntPtr point = Marshal.AllocCoTaskMem(len);
      Marshal.Copy(encoding.GetBytes(list[index]), 0, point, len - 1);

      Marshal.WriteByte(point, len - 1, 0);

      Marshal.WriteIntPtr(Argv, index * Marshal.SizeOf(typeof(IntPtr)), point);
   }
}

}

[DllImport("XXXXXX.dll")]
public static extern int Init(ref ARGS args);

JAVAのNative開発(JNI)もろもろ注意

■2番目の引数って何?

Javaから呼び出されるjni関数は、だいたい以下の形式になっている

jxxxxx Java_package_name_クラス名_メソッド名(JEnv*, j???, メソッド引数)

中には2番目の引数(j???)は、Java側のメソッドの宣言によって型が変わってくる

 

private native String A();

jobject Java_package_name_クラス名_A(JEnv*, jobject..)になる

しかし、Aメソッドが「static」で宣言された場合

private static native String A();

jobject Java_package_name_クラス名_A(JEnv*, jclass..)になる

つまり、Java側のメソッドがstatic/非staticによって、2番目の引数は

jobject/jclassに変わってくる。これを注意したい。

 

Android Studio 開発メモ Javahとの連携

  ご存じ通り、Android開発中にJavaクラスからNativeコードを呼び出したり、逆にしたりする際に、JNI規格に従いC Headerファイルの作成やJava側のラッパクラスの用意が欠かせない作業でありますが、とても面倒だと思います。幸いJDK側が「Javah」という便利なツールを提供してくれてコマンドラインで地道に叩けば何とかなりますが、それでも不便と思う方が多いと思います。この作業をAndroid Studioに取り込み開発の効率の向上に繋がればと思います。

 

取り込み手順

1.プロジェクトを開いた状態で、「File」⇒「Settings」メニューをクリックし、「Settings」画面を表示させる。

2.「Settings」画面にて下図のように、「Tools」⇒「External Tools」パネルを特定し、「」アイコンをクリックし、「Create Tool」画面を表示させる。

f:id:itroad:20170218202739p:plain

3.「Create Tool」画面にて、下記情報を入力する。

 ①Name

  ⇒JavaClass To C Header

 ②Description

  ⇒Generate JNI Special C Header File From JavaClass

 ③Program

  ⇒$JDKPath$/bin/javah

 ④Parameters

  ⇒-classpath $Classpath$ -v -jni $FileClass$

 ⑤Working directory

  ⇒$SourcepathEntry$\..\cpp

 

$...$で囲んているのは、マクロ変数である。③~⑤項目の右側にある「Insert macro…」ボタンをクリックすることで、「Macros」画面を呼び出し、そこから自分に必要なマクロ変数を選んで、追加できる。(画面操作についてここで割愛)

※「Options」および「Show in」項目は一応全部入れて、より詳しい情報を得るには無難でしょう。

f:id:itroad:20170218202839p:plain

f:id:itroad:20170218202910p:plain

使用上に注意事項

  基本的に、プロジェクト上にあるJavaクラスファイルを選んで、右クリックして、コンテキストメニューからの「External Tools」⇒「JavaClass To C Header」を呼び出すだけで済みます。

  ただ、Android Studioは、プロジェクトをビルドしないとJavaクラスをコンパイルしてくれないクセがあるので、使用前に自ら「Build」⇒「Make Project」を行うほうが無難でしょう。特にJavah」が下記エラーが発生する場合、必ず自前でビルドしてください。

エラー:’クラスのフルネーム’ のクラス・ファイルが見つかりませんでした。