LoginSignup
0
0

More than 1 year has passed since last update.

[C#/C++/pinvoke] 関数ポインタを受け取る関数をpinvokeでC#から使う

Last updated at Posted at 2021-06-18

もくじ

やりたいこと

関数ポインタを受け取る関数をpinvokeでC#から使うときのお作法をメモしておきたい。

やり方

下記の流れでおこなう。

  • C#の中に、関数ポインタの型に相当するdelegateを宣言する。
  • それを、[DllImport(・・を付けたメソッド(関数)本体の宣言のところの引数で使用する。
  • 使うときは、そこにメソッドなりラムダを渡してやる。

具体的には、下記のような感じ。

サンプル

C++側はdll。
1秒おきに関数ポインタで登録した関数を呼ぶ。
登録と秒数カウント開始のための関数と、カウント停止のための関数を持っておく。

C#側はコンソールアプリ。
C++がエクスポートしてる関数をpinvokeで呼ぶ。
カウント開始のための関数を呼ぶ際、ラムダでメソッドを記述し、登録する。

DelegateParamFunc.cpp
// VS2019 プロジェクトの設定で「プリコンパイル済みヘッダーを使用しない」にして、stdafx.hのincludeを削除する必要あり
#include <windows.h>
#include <stdio.h>
#include <thread>

#define VC_DLL_EXPORTS
#include "DelegateParamFunc.h"

// public variable
// なし

// private variable
void (*func)(int count);
std::thread th;
BOOL isStop = false;
int count = 0;

// public function

// 関数の登録とカウントの開始
void __cdecl StartCountDownAndRegisterFunction(void(*f)(int))
{
    if (f == NULL)
        return;
    func = f;

    isStop = false;

    th = std::thread([]()
        {
            while (!isStop)
            {
                Sleep(1000);
                count++;
                if (func != NULL)
                    func(count);

            }
        });
}

// 関数の登録解除とカウントの停止
void __cdecl StopCountDownAndUnregisterFunction()
{
    isStop = true;
    count = 0;
    func = NULL;
    th.join();
}
DelegateParamFunc.h
#pragma once
#include <Windows.h>

// エクスポートとインポートの切り替え
#ifdef VC_DLL_EXPORTS
#undef VC_DLL_EXPORTS
#define VC_DLL_EXPORTS extern "C" __declspec(dllexport)
#else
#define VC_DLL_EXPORTS extern "C" __declspec(dllimport)
#endif

// エクスポート関数のプロトタイプ宣言
VC_DLL_EXPORTS void __cdecl StartCountDownAndRegisterFunction(void(*f)(int));
VC_DLL_EXPORTS void __cdecl StopCountDownAndUnregisterFunction();
Program.cs
using System;
using System.Runtime.InteropServices;

namespace PInvokeTest_deleate_CsApp
{
    class Program
    {
        static void Main(string[] args)
        {
            NativeMethods.StartCountDownAndRegisterFunction((count)=>
            {
                Console.WriteLine(count);
            });

            Console.ReadLine();

            NativeMethods.StopCountDownAndUnregisterFunction();

            Console.ReadLine();
        }
    }

    public static class NativeMethods
    {
        // 引数で渡すメソッドの型をdelegateで宣言
        public delegate void DELEGATE_PARAM_FUNC(int count); 

        // 関数本体の宣言
        [DllImport("PInvokeTest_delegate.dll", CallingConvention = CallingConvention.Cdecl)]
        public extern static void StartCountDownAndRegisterFunction(DELEGATE_PARAM_FUNC func);

        [DllImport("PInvokeTest_delegate.dll", CallingConvention = CallingConvention.Cdecl)]
        public extern static void StopCountDownAndUnregisterFunction();

    }
}

こまかい話メモ

C++のプロジェクトをビルドするとき、「pch.hをインクルードしましたか?」みたいなエラーがでるので、プロジェクトの設定で「プリコンパイル済みヘッダーを使用しない」にして、stdafx.hのincludeを削除する必要あり。
image.png

dllのPJは、できたdllをC#のPJの出力フォルダにビルド後イベントでコピーしてやらないといけない。
copy /y $(TargetPath) $(SolutionDir)PInvokeTest_deleate_CsApp\bin\$(Platform)\$(Configuration)\
のような感じでコピーする。

コード

0
0
1

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
0
0