Dot-Net

C++/CLI 委託作為函式指針 (System.AccessViolationException)

  • December 30, 2011

我一直在嘗試使用 C++/CLI 委託(因為我正在嘗試製作一個 .NET 參考庫),並且一直遇到以下問題。

我在 C++/CLI 中定義了一個委託,然後在 C# 中創建了一個委託的實例,然後通過函式指針通過非託管 C++ 呼叫該委託的實例。這一切都按預期工作。

說明這一點的程式碼(首先是我的 C#)

using System;

namespace TestProgram
{
   class Program
   {
       static void Main(string[] args)
       {
           Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message);
           Library.Test test = new Library.Test(messageDelegate);
           test.WriteMessage();
           Console.Read();
       }

       static void Message()
       {
           Console.WriteLine(1024);
       }
   }
}

接下來是我的託管 c++ 文件 (Managed.cpp)

#include "Unmanaged.hpp"
#include <string>

namespace Library
{
   using namespace System;
   using namespace System::Runtime::InteropServices;

   public ref class Test
   {
   public:
       delegate void MessageDelegate();
   internal:
       MessageDelegate^ Message;
       void* delegatePointer;

   public:
       Test(MessageDelegate^ messageDelegate)
       {
           delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer();
       }

       void WriteMessage()
       {
           Unmanaged::WriteMessage(delegatePointer);
       }
   };
}

還有我的非託管 C++ 文件 (Unmanaged.cpp)

#include "Unmanaged.hpp"

namespace Unmanaged
{
   typedef void (*WriteMessageType)();
   WriteMessageType WriteMessageFunc;

   void WriteMessage(void* Function)
   {
       WriteMessageType WriteMessageFunc = (WriteMessageType)(Function);
       WriteMessageFunc();
   }
}

此程式碼按預期工作,輸出為“1024”,因為 Method() 由指向委託方法的函式指針呼叫。

當嘗試對帶參數的委託應用相同的方法時,會出現我的問題,即:

delegate void MessageDelegate(int number);

我的程式碼現在如下(C#):

using System;

namespace AddProgram
{
   class Program
   {
       static void Main(string[] args)
       {
           Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message);
           Library.Test test = new Library.Test(messageDelegate);
           test.WriteMessage(1024);
           Console.Read();
       }

       static void Message(int number)
       {
           Console.WriteLine(number);
       }
   }
}

我的託管 C++ 文件:

#include "Unmanaged.hpp"
#include <string>

namespace Library
{
   using namespace System;
   using namespace System::Runtime::InteropServices;

   public ref class Test
   {
   public:
       delegate void MessageDelegate(int number);
   internal:
       MessageDelegate^ Message;
       void* delegatePointer;

   public:
       Test(MessageDelegate^ messageDelegate)
       {
           delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer();
       }

       void WriteMessage(int number)
       {
           Unmanaged::WriteMessage(delegatePointer, number);
       }
   };
}

還有我的非託管 C++ 文件:

#include "Unmanaged.hpp"

namespace Unmanaged
{
   typedef void (*WriteMessageType)(int number);
   WriteMessageType WriteMessageFunc;

   void WriteMessage(void* Function, int number)
   {
       WriteMessageType WriteMessageFunc = (WriteMessageType)(Function);
       WriteMessageFunc(number);
   }
}

當我執行程序時,我收到以下錯誤:

非託管庫 Test.dll 中出現“System.AccessViolationException”類型的未處理異常

附加資訊:試圖讀取或寫入受保護的記憶體。這通常表明其他記憶體已損壞。

順便說一句,控制台視窗確實顯示 1024,但後面跟著一個隨機整數(~1000000),然後我得到了錯誤。

我可以開始想像我收到此錯誤的一些原因,但我不確定並且很難找出。如果有人能告訴我為什麼會出現這個錯誤,以及我能做些什麼來解決它,我將不勝感激。

void WriteMessage(void* Function, int number)

將函式指針作為 void* 傳遞是一個非常糟糕的主意。它可以防止編譯器檢查你做錯了什麼。儘管在這種特定情況下編譯器無法檢測到,但還是有問題。委託被編組為使用 __stdcall 呼叫約定的函式指針,您的實際函式指針使用 __cdecl 呼叫約定,本機程式碼的預設值。這會導致堆棧在呼叫時變得不平衡。

您可以通過將[UnmanagedFunctionPointer] 屬性應用於委託聲明來修復它,指定 CallingConvention::Cdecl。

引用自:https://stackoverflow.com/questions/8675638