Sanjeev Kumar
Sanjeev Kumar

Reputation: 55

Access violation read location 0x00000004 when sending calllback from C++ code to C# code

I am developing a windows 8 phone application, in which I need to send callback event messages from C++ code to the UI code in C#. Below is the C# code in which I am creating the callback function and sending it's pointer to the C++ code. The C++ code can send asynchronous event messages to UI using this function pointer.

C# Code

public partial class MainPage
{
        public MainPage()
        {
            InitializeComponent();
        }
        public delegate void CallBack([In][MarshalAs(UnmanagedType.LPStr)] string strParam);
        [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern void PerformActionWithCallBack(int x);

        void DemonstrateCallBack()
        {
            int x;
            CallBack callback_delegate = new CallBack(CallBackFunction);
            // Converting callback_delegate into a function pointer that can be
            // used in unmanaged code.
            IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(callback_delegate);

            //Getting the pointer into integer value and sending it to C++

            x = (int)intptr_delegate;

            testApp.PerformActionWithCallBack(x);
        }

        //CallBack function in which event messages will be received
        void CallBackFunction([In][MarshalAs(UnmanagedType.LPStr)] string strParam)
        {
            if (Dispatcher.CheckAccess() == false)
            {
                //Updating the text block with the received message
                Dispatcher.BeginInvoke(() => MyTextBlock.Text = strParam);
            }
            else
            {
                MyTextBlock.Text = "Update directly";
            }
        }
}

Below is the C++ code which will send event messages to C#

//Callback function prototype
typedef void(__stdcall *PCallBack)(LPSTR s);
LPSTR cbMsg = NULL;

//Global instance callback function
PCallBack gUICallback;

void TestApp::PerformActionWithCallBack(int x)
{
    //Assigning the received calllback function pointer from C# to global function pointer
    gUICallback = (PCallBack)x;
}

//This function will send event messages to C#
void SendCallbackMsg()
{
    while(1)
    {
         cbMsg = "Hello";
         Sleep(100);
         gUICallback(cbMsg);
         cbMsg = "Hi";
         Sleep(100);
         gUICallback(cbMsg);
    }
 }

With this code I am able to get the event messages in C# succcessfully, But after sending 650-700 calllbacks my application gives access violation exception and after that nothing works. I am suspecting the way I am passing the function pointer from C# to C++ but not able to resolve it.

Upvotes: 1

Views: 491

Answers (1)

V. Kravchenko
V. Kravchenko

Reputation: 1904

Your problem is, callback_delegate gets garbage-collected.

MSDN says

You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track reference to unmanaged code.

Try this

public partial class MainPage
{
    private CallBack _callBackDelegate = null;
    public MainPage()
    {
        InitializeComponent();
    }
    public delegate void CallBack([In][MarshalAs(UnmanagedType.LPStr)] string strParam);
    [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void PerformActionWithCallBack(int x);

    void DemonstrateCallBack()
    {
        _callBackDelegate = new CallBack(CallBackFunction);
        int x;

        // Converting _callBackDelegate  into a function pointer that can be
        // used in unmanaged code.
        IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(_callBackDelegate );

        //Getting the pointer into integer value and sending it to C++

        x = (int)intptr_delegate;

        testApp.PerformActionWithCallBack(x);
    }

    //CallBack function in which event messages will be received
    void CallBackFunction([In][MarshalAs(UnmanagedType.LPStr)] string strParam)
    {
        if (Dispatcher.CheckAccess() == false)
        {
            //Updating the text block with the received message
            Dispatcher.BeginInvoke(() => MyTextBlock.Text = strParam);
        }
        else
        {
            MyTextBlock.Text = "Update directly";
        }
    }
}

Upvotes: 3

Related Questions