[翻訳]:C/C++コードからC#をどのようにコールバックするか

12717 ワード

声明:ネット上で类似の中国语のブログは大いに存在して、本人の知识のレベルは有限で、アマチュアの趣味、同じくバックアップのために収蔵しますHow to make a callback to C#from C/C++code
知識を共有する初志に基づいて、翻訳してみんなに参考にして、読みやすくて拗ねないように、原文に従って直訳していないで、届かないところあるいは翻訳が間違っているので、まだ噴き出さないでください.
 
ほとんどの人が非管理DLLの関数を呼び出す方法を知っていますが、C/C++コードからC#コードを呼び出すことを望んでいる場合があります.Engineというシーンを想像してみてください.dllのネイティブC言語DLLのC#アプリケーションを記述します.DLLにDoWorkという名前の関数エントリポイントがあります.呼び出す必要があります.Engine.dllで「DoWork」を呼び出すのは、C#コードで次のように簡単です.
[DllImport("Engine.dll")]
public static extern void DoWork(); 

このコードの実行は非常に良好になりますが、DoWorkは継続的に実行されるタスクであり、ユーザー側が更新されることを保証するために、進捗状況を表示したいと考えています.これを実現するには、次のステップが必要です.
 1.C#コードに類似の非管理コード委任を定義する
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ProgressCallback(int value);

2.Cコードにコールバック署名を定義する
typedef void (__stdcall * ProgressCallback)(int);

3.CコードでDoWorkの署名を変更してProgressCallbackのアドレスを受信する
DLL void DoWork(ProgressCallback progressCallback)

注意:DLLマクロの宣言は次のとおりです.
#define DLL __declspec(dllexport)

4.C#コードでは、管理されていない委任タイプの委任を作成する必要があります.
ProgressCallback callback =
    (value) =>
    {
        Console.WriteLine("Progress = {0}", value);
    };

5.そして、DoWorkを呼び出すためには、このようにする必要があります.
DoWork(callback);

ここでは、単純なアプリケーションのソースコードの例を示す.このコードセグメントには、ProcessFile関数という名前のCコードがC#にコールバックされ、さらなる処理のためにファイルパスを取得する必要があります.現在の状況では、印刷ファイルの内容をコンソールに印刷します.
Engine.dll/Main.h
#include "Windows.h"

#ifdef __cplusplus
extern "C"
{
#endif
 
    #define DLL __declspec(dllexport)
    typedef void (__stdcall * ProgressCallback)(int);
    typedef char* (__stdcall * GetFilePathCallback)(char* filter);
 
    DLL void DoWork(ProgressCallback progressCallback);
    DLL void ProcessFile(GetFilePathCallback getPath);
 
#ifdef __cplusplus
}
#endif

Engine.dll/Main.c
 
#include "Main.h"
#include 

DLL void DoWork(ProgressCallback progressCallback)
{
    int counter = 0;
 
    for(; counter<=100; counter++)
    {
        // do the work...

        if (progressCallback)
        {
            // send progress update
            progressCallback(counter);
        }
    }
}
 
DLL void ProcessFile(GetFilePathCallback getPath)
{
 
    if (getPath)
    {
        // get file path...
        char* path = getPath("Text Files|*.txt");
        // open the file for reading
        FILE *file = fopen(path, "r");
        // read buffer
        char line[1024];
 
        // print file info to the screen
        printf("File path: %s
", path ? path : "N/A"); printf("File content:
"); while(fgets(line, 1024, file) != NULL) { printf("%s", line); } // close the file fclose(file); } }

 
TestApp.exe/Program.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
 
class Program
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate void ProgressCallback(int value);
 
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    delegate string GetFilePathCallback(string filter);
 
    [DllImport("Engine.dll")]
    public static extern void DoWork([MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback callbackPointer);
 
    [DllImport("Engine.dll")]
    public static extern void ProcessFile([MarshalAs(UnmanagedType.FunctionPtr)] GetFilePathCallback callbackPointer);
 
    [STAThread]
    static void Main(string[] args)
    {
        // define a progress callback delegate
        ProgressCallback callback =
            (value) =>
            {
                Console.WriteLine("Progress = {0}", value);
            };
 
        Console.WriteLine("Press any key to run DoWork....");
        Console.ReadKey(true);
        // call DoWork in C code
        DoWork(callback);
 
        Console.WriteLine();
        Console.WriteLine("Press any key to run ProcessFile....");
        Console.ReadKey(true);
 
        // define a get file path callback delegate
        GetFilePathCallback getPath =
            (filter) =>
            {
                string path = default(string);
 
                OpenFileDialog ofd =
                    new OpenFileDialog()
                {
                    Filter = filter
                };
 
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    path = ofd.FileName;
                }
 
                return path;
            };
 
        // call ProcessFile in C code
        ProcessFile(getPath);
    }
}

以下は私がコンパイルしたコードを添付して、原文と少し出入りして、主に私が習慣的に使うためです.NET 2.0は、コンパイル中にコンパイラをスムーズに通過するためのものもあります.
コードはVisual Studio 2010+VC 6を使用する.0作成
ダウンロードアドレス:1.どうやってC_からC++コードでC#をコールバックする.rar
            2.どうやってC_からCppコードでCSharpをコールバックする.rar