簡體   English   中英

從C ++ DLL(通過“ Dllimport”從C#加載)調用C#方法/函數

[英]Call a C# method/function from a C++ DLL (which is loaded from C# with “Dllimport”)

在單個標題中恢復它有點困難,所以這就是我的情況。

我正在構建一個加載C ++庫的C#應用​​程序。
我從該C ++ DLL調用函數。
但是我也想讓我的C ++ DLL從C#應用程序中調用函數(正在導入/運行它)...

這是一段使它更全面的代碼:

// I'm importing functions from my native code
[DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int returnIntValue(int value);
[DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern int returnIntArrValueAt(int[] values, int index);

// Here is the kind of code it should be
// [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)] 
// public static extern void callCSharpFunction( FunctionPointer fctPointer );

main    
{
    // I run the function
    int intValue1 = 
        MyAddIn.returnIntValue(147852369);
    int intValue2 = 
        MyAddIn.returnIntArrValueAt( new int[] { 9, 4, 3, 2, 1, 0 }, 5);

    // Here is an example function I use to call my C# func from C++
    // MyAddIn.returnIntValue( &myCSharpFunction );
}

// This is the method I'd like to call from my C++ imported library
static public void myCSharpFunction()
{
    MessageBox.Show("Called from C++ code !!");
}

因此,恢復:

  • C#代碼導入我的C ++ DLL
  • C#從C ++ DLL運行函數
  • C ++ DLL方法調用顯示消息框(即)的C#方法

這是我對這個問題的回答 ,這是相似的。 我的示例不對回調使用參數。 由於您的參數是整數,因此,您應該不會有任何問題。

基本上, Marshal.GetFunctionPointerForDelegate方法從委托創建一個IntPtr 該委托應具有與C ++庫中使用的函數指針相同的簽名。

// This is the delegate type that we use to marshal
// a function pointer for C to use. You usually need
// to specify the calling convention so it matches the
// convention of your C library. The signature of this
// delegate must match the signature of the C function
// pointer we want to use.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void FunctionPointer();

// This is the function we import from the C library.
[DllImport(dllFilePath)]
static extern void callCSharpFunction(IntPtr fctPointer);

// This is the placeholder for the function pointer
// we create using Marshal.GetFunctionPointerForDelegate
IntPtr FctPtr;

// This is the instance of the delegate to which our function
// pointer will point.
FunctionPointer MyFunctionPointer;

// This calls the specified delegate using the C library.
void CallFunctionPointer(FunctionPointer cb)
{
    // make sure the delegate isn't null
    if (null == cb) throw new ArgumentNullException("cb");

    // set our delegate place holder equal to the specified delegate
    MyFunctionPointer = cb;

    // Get a pointer to the delegate that can be passed to the C library
    FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer );

    // call the imported function with that function pointer.
    callCSharpFunction(FctPtr);
} 

可以做到,總的來說沒什么大不了的。 但是,有幾點需要考慮。

  • 由於您說的是C ++而不是C,因此請注意,除了靜態類,實例方法,朋友函數等之外,由於名稱修改,還有一些無法通過DllImport加載的函數。 避免使用COM,將C ++換行有時會用作一種更可移植的策略,以允許庫被其他語言.Net或其他語言包裝。 同樣,其中一些相同的注意事項也適用於您所包裝的庫中的回調。 盡管來自DLL的原型似乎沒有問題,但如果它確實是由C ++編譯器構建的,則可能值得查看導出的符號名稱以確保。

  • 在搜索時告訴幫助,您需要了解詞匯表。 通常,當一個函數將另一個要在函數調用期間或之后調用的函數作為參數時,稱為回調 在C和C ++的世界中,這是基於一種稱為功能指針的語言功能(該功能還用於回調以外的目的)。 在MS文檔中,通常將在運行時將不同功能動態綁定到不同調用者的整個過程稱為委托 與C#等效的函數指針是使用C#關鍵字proxy創建的稱為委托的對象。 我建議首先使用此功能在C#中創建一些實驗程序,以首先了解其工作原理。

  • 而且DllImport是實現的一部分,您正在使用的實際.Net工具是pinvoke 尋找更多信息時也應該如此。 然后,您要做的是通過將pinvoke 編組為函數指針來導出您的委托。 也就是說,.Net將為運行本機代碼創建一個填充函數 經常需要嘗試兩次的兩個大問題區域是:1)確保此shim函數/編組的委托本身具有正確的調用約定 ,以及2)此幕后shim函數的對象生存期使得它在以下情況下仍然存在:本機DLL將要使用它。 有時,此曲線球可能需要手動覆蓋垃圾回收行為,但這通常意味着僅保留對其的引用。

  • 我實際上認為, Mono文檔在這方面也比MSDN更好。

好的,經過幾次測試,從零開始,我終於設法使此回調運行!

所以這是我創建的使用回調的測試項目。

在C ++方面

export.def

LIBRARY TestCallBack
    EXPORTS
        callCSharpFunction

TestCallBack.cpp

__declspec(dllexport) void callCSharpFunction ( void *fctPointer(int) )
{
    fctPointer(123456);
}

該C ++項目將作為“ DLL”文件構建,並放在C#的項目文件夾中的“ lib”項目中。

在C#方面

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    class Program
    {
        // Set the library path
        const string dllFilePath = 
        "C:\\PathToProject\\TestCallBack\\ConsoleApp\\lib\\TestCallBack.dll";

        // This is the delegate type that we use to marshal
        // a function pointer for C to use. You usually need
        // to specify the calling convention so it matches the
        // convention of your C library. The signature of this
        // delegate must match the signature of the C function
        // pointer we want to use.
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        delegate void FunctionPointer( int nb);

        // This is the function we import from the C library.
        //[DllImport(dllFilePath)]
        [DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
        public static extern void callCSharpFunction(IntPtr fctPointer);

        // This is the placeholder for the function pointer
        // we create using Marshal.GetFunctionPointerForDelegate
        static IntPtr FctPtr;

        // This is the instance of the delegate to which our function
        // pointer will point.
        FunctionPointer MyFunctionPointer;

        // This calls the specified delegate using the C library.
        void CallFunctionPointer(FunctionPointer cb)
        {
            // make sure the delegate isn't null
            if (null == cb) throw new ArgumentNullException("cb");

            // set our delegate place holder equal to the specified delegate
            MyFunctionPointer = cb;

            // Get a pointer to the delegate that can be passed to the C lib
            FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer);

            // call the imported function with that function pointer.
            callCSharpFunction(FctPtr);
        } 

        static void Main(string[] args)
        {
            // This is the instance of the delegate to which our function
            // pointer will point.
            FunctionPointer printInConsoleDelegate;

            // Create the delegate object "MyFunctionPointer" that references 
            printInConsoleDelegate = new FunctionPointer(printInConsole);

            // Get a pointer to the delegate that can be passed to the C lib
            IntPtr printInConsolePtr = 
                Marshal.GetFunctionPointerForDelegate(printInConsoleDelegate);

            Console.WriteLine(
                "Call C++ which's calling back C# func \"printInConsole\"");

            // Call C++ which calls C#
            callCSharpFunction(printInConsolePtr);

            // Stop the console until user's pressing Enter
            Console.ReadLine();
        }

        public static void printInConsole(int nb) 
        {
            // Write the parameter in the console
            Console.WriteLine("value = " + nb + "; ");
        }
    }
}

不受管理的出口項目可能會幫助您:
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports

基本上,它允許您使用DllExport屬性從程序集中導出函數。 然后可以像正常的本機Dll導出一樣使用這些功能。 這有點像缺少DllImport屬性的對應項。

然后,您將這樣聲明您的方法:

[DllExport("myCSharpFunction")]
static public void myCSharpFunction()
{
    MessageBox.Show("Called from C++ code !!");
}

但是,這種雙向依賴關系在我看來通常也是可疑的。 在您的情況下,也許也可以使用回調,例如ken建議。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM