Edited at

【UWPアプリでOpenCVを無料で使おう!】C#(.NET) UWPアプリへのC++ダイナミックライブラリ(dll)の組み込み方

More than 1 year has passed since last update.


動機

UWPアプリを作っているとき,C#ではなくてC++による処理を組み込みたい...

例えば OpenCVをC++ベースで使いたい... なんて需要があったりします.

(自分はHololens開発者で,同様にHololensユーザーには大きな需要だと思います)

もちろん,C#(Unity)でOpenCVが使えるアセットもあります.

OpenCV for Unity - Asset Store -

そう,$95払えばね.

個人で開発してるとき

あなたが学生でお金がないなんてとき

これは困った事態です.

UWPアプリを作るとき,

実はC++製DLLをPluginとして組み込んでしまえば

あなたがC++が書けるなら,泣く泣く諭吉を飛ばすなんてことしなくてよいのです.

ただちょっとコツが要るので,ここで詳しく説明したいと思います.


環境

Visual Studio 2017 Community

Windows 10

Unity 2017.3.1f

OpenCV - Hololens 3.1.0 (NuGet, 32bitのOpenCV)

C++のDLLに組み込むライブラリとして,今回はOpenCVで試します.

64bitは確認していませんが,多分同じ手順で大丈夫なはずです

デプロイ先のデバイスのOSに合わせて,64bitか32bitか合わせて作ってください.

ちなみに,32bitで試す場合,64bitでしか動かないUnityEditorでは確認できません.

(Hololensは32bitOSであるため,これが前提条件となります)

また,HololensのOpenCVでは「OpenCV - Hololens」というNuGetのライブラリを使っています.


OpenCV for HololensではDLLスクリプトのデバッグができない

アプリケーションビルド用のpackage dllが含まれていて,

このためPC上でデバッグした際,ビルドエラーが出てしまいます(アプリケーション用のDLLがない,等により).

DLLスクリプトのデバッグ方法として,同じソリューション内にDLL用のプロジェクトと,デバッグ用の.exeのプロジェクトの2つを立てて,

同じスクリプトを #if DEBUG ...or #else と切り分けてデバッグする方法

等がありますが,これができない(やりにくい)ということになります.

別ソリューションを立てた方が無難だと思います.


DLL用のプロジェクト作成


Visual Studio 2017からプロジェクト作成

左の「Visual C++ -> ユニバーサル Windows」から

「DLL(ユニバーサル Windows)」を選択して作成します.

「Windows デスクトップ」のDLLでは失敗するので注意.

こちらでは.NETと互換性がないことが原因です.

スクリーンショット 2018-06-07 11.32.43.png

今回は「CppPugin」のプロジェクト名で作成しました.


OpenCVをNuGetからダウンロード

NuGetからOpenCV-Hololensをダウンロードします.

ソリューションエクスプローラからプロジェクト名を右クリック

「NuGetパッケージの管理」を開く

スクリーンショット 2018-06-07 12.49.57.png

「参照」タブから検索してOpenCV-Hololensをダウンロード

スクリーンショット 2018-06-07 12.38.11.png

これでHololens用のOpenCVの導入は完了です.

※普通のUWPアプリ等では,ターゲットと同じプラットフォームのOpenCVをincludeやリンカーで導入するので済みます.


DLLプロジェクトでのTestスクリプト

プロジェクトのプロパティ設定

プリコンパイル済ヘッダを無しにしたり,

OpenCV-HololensがSDLチェックに引っかかるので,それをオフしたり.

スクリーンショット 2018-06-07 12.44.12.png

デバッグはRelease x86に設定.

スクリーンショット 2018-06-07 12.46.19.png

次に既存のCppPlugin.cppをこう編集.


CppPlugin.cpp

#include "CppPlugin.h"

extern "C"

void __stdcall TestCpp(
uchar* data, int rows, int cols, int* res
)
{
cv::Mat mat(rows, cols, CV_8UC4, data);
if (mat.empty()) {
*res = -1;
}
else {
*res = 1;
}
}


また,CppPlugin.hを作成し,こちらも編集


CppPlugin.h

#pragma once

#include <opencv2/opencv.hpp>

#define DLLEXPORT __declspec(dllexport)
extern "C" {
DLLEXPORT void __stdcall TestCpp(
uchar* data, int rows, int cols, int* res
);

}


ビルドしたら,ソリューションフォルダ内のRelease\CppPlugin\下にリリースされたdllが入っております.

OpenCVのDLLもあります.


Unityプロジェクト

いつもの手順でUnityプロジェクト「TestUWP」を作成.

mainシーンを作成.

HoloToolkit(MixedRealityToolkit)をインポートし,

お決まりの以下の設定をします.


  • Mixed Reality Toolkit -> Apply Mixed Reality Scene Settings

  • Mixed Reality Toolkit -> Apply Mixed Reality Project Settings

  • Edit -> Project Settings -> Quality : Windowsアイコンの方のLevelをUltraに

  • Edit -> Project Settings -> Player : Windowsアイコンタブ -> XR Settings -> XR Settingがenabled, Windows Mixed Realityが入ってることを確認

  • File -> Build Settings : Target DeviceがHololens, Debugging Unity C# Project がenabledになってることを確認

Canvasオブジェクトを作成して置き,子要素にTextを配置.

Textの色は白に設定.

Canvasは以下のように設定


  • Renderer Mode: Screen Space - Camera

  • Camera: SceneからMixedRealityCamera

CanvasオブジェクトにはC#スクリプト「TestMangaer.cs」を作成してアタッチ


TestManager.cs

using System.Collections;

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;

public class TestManager : MonoBehaviour {

[DllImport("CppPlugin")]
unsafe private static extern void CppTest(
byte[] data, int rows, int cols, int channels, int[] res
);

Text text;
int[] res = { 0 };

// Use this for initialization
void Start () {
text = GetComponentInChildren<Text>();
byte[] data = { 128, 128, 128, 255 };
int rows = 1;
int cols = 1;
int channels = 4;
CppTest(data, rows, cols, channels, res);
text.text = res.ToString();
}

// Update is called once per frame
void Update () {

}
}


また,Asset下にPluginsフォルダを作り,

先ほどのビルドでReleaseされたDLL群(Release\CppPlugin\下のファイル全てでOK)をコピーします.

結果,Unityの画面は以下のようになっているはずです.

スクリーンショット 2018-06-07 13.37.13.png

このとき,x86のDLLを読み込んでいるので,UnityEditorでPlayはできないです.

必要なら#if等で切り分けてください.

この後BuildSettingsからビルドします.

時間がかかるので,紅茶でも淹れてのんびりしましょう.


Hololens実機にデプロイ

HololensをUSBでPCに繋ぎます.

ビルドしたフォルダ下の.Unityプロジェクト名と同じ名前(ここではTestUWP)のソリューションを開き,

x86, Release, Deviceに設定し

ビルド(失敗するならリビルドでなんとかなることもある)-> デバッグなしで開始 により,実機にデプロイできます.

(IPアドレス打ち込みでRemoteコンピュータもできる)

目の前に"1"と表示されたら成功です.


DLLが読み込めないときに確認すること


依存関係にあるDLLも全てPlugins\下にあることを確認.

DLLが足りてないと,ビルド後のVSソリューションのTestUWPプロジェクト下のCppPlugin.dllに赤いエラーマークがついている.


そもそもDLLがきちんとビルドできてなくて,関数がなかったり

関数の有無を確認するにはdllexp.exeがおすすめです.私の知る限りでは,これ以外で関数をGUIで確認できるものはありません.

DLL Export Viewer - view exported functions list in Windows DLL

そもそもDLLにビルドできるということはスクリプトエラーはなくて,headerとsourceで関数定義が違っていたり,読み込む関数名を間違えていたり(大文字小文字とか),現れないほうのエラーです.

Releaseでビルドしていること,また該当のプラットフォーム下のDLLファイルを使っていることを確認してください(x64間違いなど)

extern "C"等の記法も確認してください.別のソリューションでデバッグしてからDLL用のソリューションにコピペしたりすると,忘れることもあるののでは.


DllImportの書き方

DllImport() カッコ内は.dllを抜かした名前です.


上手なデバッグ方法

DLLのソリューション(CppPlugin)のプロジェクトのプロパティより,出力先フォルダを

(Unityのプロジェクトフォルダ)\(ビルドしたフォルダ)\(Unityのプロジェクトフォルダ)\

※ここでは
(Unityプロジェクトフォルダへのパス)\TestUWP\App\TestUWP\

とすると,DLLを変更するたびUnityからいちいちビルドし直す必要がなくなります.

スクリーンショット 2018-06-07 14.05.25.png

もっと言うと,Unityビルド後のVSソリューションにDLLのプロジェクトを追加してしまうと1画面で管理できます.

スクリーンショット 2018-06-07 14.03.54.png

楽~~!!!