端くれプログラマの備忘録 C# [C#] C#からC++のDLLを呼ぶ方法

[C#] C#からC++のDLLを呼ぶ方法

最近は新規Windowsアプリ開発はC#でやるようになった。だけど、「既存のC/C++ライブラリ使い回す代わりに工数減らして」と発注元から要求されたり、サードパーティから購入したC言語DLLの商用ライブラリをリンクしないといけなかったりと、いまだにC/C++との連携は避けられない。

というわけで、C#からC/C++のDLL関数を呼び出す手順を覚え書きとして記しておく。

サンプルコード

以下のような簡単なテスト関数をC++で書いてDLLをビルドした。このDLLの関数をC#のテストアプリから呼び出してみる。

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include "CppDLL.h"
 
extern "C" {
 
// No argument
CPPDLL_API int func1()
{
    return 11;
}
 
// Passes values
CPPDLL_API int func2(int a, int b)
{
    return a + b;
}
 
// Passes values and receives a value
CPPDLL_API int func3(int a, int b, int* c)
{
    *c = a + b;
    return 33;
}
 
// Passes a fixed-length string
CPPDLL_API int func4(const char* psz)
{
    printf("%s\n", psz);
    return 44;
}
 
// Receives a string
CPPDLL_API int func5(char* psz, int len)
{
    strcpy_s(psz, len, "Oh my goodness!");
    return 55;
}
 
// Passes a structure
CPPDLL_API int func6(Data* data)
{
    data->i3 = data->i1 + data->i2;
    data->u3 = data->u1 + data->u2;
    data->c3 = data->c1 + data->c2;
 
    strcpy_s(data->sz3, 64, data->sz1);
    strcat_s(data->sz3, 64, data->sz2);
 
    return 66;
}
 
} // extern "C"
#pragma once
 
extern "C" {
 
#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif
 
CPPDLL_API int func1();
CPPDLL_API int func2(int a, int b);
CPPDLL_API int func3(int a, int b, int* c);
CPPDLL_API int func4(const char* psz);
CPPDLL_API int func5(char* psz, int len);
 
typedef struct _Data {
    int i1;
    int i2;
    int i3;
    unsigned short u1;
    unsigned short u2;
    unsigned short u3;
    unsigned char c1;
    unsigned char c2;
    unsigned char c3;
    char sz1[32];
    char sz2[32];
    char sz3[64];
} Data;
 
CPPDLL_API int func6(Data* data);
 
} // extern "C"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
 
namespace TestDLLCSharp
{
    class Program
    {
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func1();
 
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func2(int a, int b);
 
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func3(int a, int b, ref int c);
 
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func4(string str);
 
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func5(StringBuilder sb, int len);
 
        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct DATA
        {
            public int i1;
            public int i2;
            public int i3;
            public ushort u1;
            public ushort u2;
            public ushort u3;
            public byte c1;
            public byte c2;
            public byte c3;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string sz1;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
            public string sz2;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
            public string sz3;
        }
 
        [DllImport("CppDLL.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int func6(ref DATA data);
 
        static void Main(string[] args)
        {
            int c = func1();
            Console.WriteLine("func1 returned {0}", c);
 
            c = func2(12, 10);
            Console.WriteLine("func2 returned {0}", c);
 
            int d = 0;
            c = func3(13, 11, ref d);
            Console.WriteLine("func3 returned {0} ({1})", c, d);
 
            c = func4("Hello World");
            Console.WriteLine("func4 returned {0}", c);
 
            StringBuilder sb = new StringBuilder(512);
            c = func5(sb, 512);
            Console.WriteLine("func5 returned {0} ({1})", c, sb);
 
            DATA data = new DATA();
            data.i1 = 25;
            data.i2 = 27;
            data.i3 = 0;
            data.u1 = 15;
            data.u2 = 17;
            data.u3 = 0;
            data.c1 = 5;
            data.c2 = 7;
            data.c3 = 0;
            data.sz1 = "HELLO+";
            data.sz2 = "WORLD";
            data.sz3 = "";
            c = func6(ref data);
            Console.WriteLine("func6 returned {0}", c);
            Console.WriteLine("data.i3 = {0}", data.i3);
            Console.WriteLine("data.u3 = {0}", data.u3);
            Console.WriteLine("data.c3 = {0}", data.c3);
            Console.WriteLine("data.sz3 = {0}", data.sz3);
 
            Console.WriteLine("done!");
        }
    }
}

/* result
func1 returned 11
func2 returned 22
func3 returned 33 (24)
Hello World
func4 returned 44
func5 returned 55 (Oh my goodness!)
func6 returned 66
data.i3 = 52
data.u3 = 32
data.c3 = 12
data.sz3 = HELLO+WORLD
*/

参考サイト

チュートリアル: ダイナミック リンク ライブラリの作成と使用 (C++)
http://msdn.microsoft.com/ja-jp/library/ms235636.aspx

pInvokeStackImbalance MDA
http://msdn.microsoft.com/ja-jp/library/0htdy0k3(v=vs.110).aspx