C#调用C++的dll,PInvoke问题和回调函数抛出异常access violation

不磨叽,直接上代码。

C++动态库中函数定义

#ifndef  _AUDIOSYSSDK_H
#define  _AUDIOSYSSDK_H


typedef void PlayResFunc(void *powner,int filestate, int playlen);


extern "C"
{
	
	int _stdcall Audio_OpenChannel(char *pLocalAddr,PlayResFunc *pFun,void *powner);
	int _stdcall Audio_CloseChannel(int idx);
	
}

在C#中的调用的代码

#warning 这个委托一定要标记为Cdecl
    /*
     这个委托一定要标记为[System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.Cdecl)],
     * 否则在C#调用时在回调函数执行一次后会退出或会抛出异常Access violation
     */

    /// Return Type: void
    ///powner: void*
    ///filestate: int
    ///playlen: int    
    [System.Runtime.InteropServices.UnmanagedFunctionPointerAttribute(System.Runtime.InteropServices.CallingConvention.Cdecl)]
    public delegate void PlayResFunc(System.IntPtr powner, int filestate, int playlen);

public class AudioSysSdkHelper
    {
[System.Runtime.InteropServices.DllImportAttribute("AudioSysSDK.dll", EntryPoint = "Audio_OpenChannel", CallingConvention = CallingConvention.StdCall)]
        public static extern int Audio_OpenChannel(string pLocalAddr,PlayResFunc pFun,System.IntPtr powner);
}


Audio_OpenChannel方法前的特性标记不能遗漏,特别是EntryPoint和CallingConvention,EntryPoint是C++中头文件定义的方法名,由于C++中是StdCall,所以这里也用StdCall,但是回调函数中的委托要标记为Cdecl,如果委托不标记,那么会抛出异常Access violation。




winform程序调用的代码

public PlayResFunc PlayResFuncObj = null;

        private void button3_Audio_OpenChannel_Click(object sender, EventArgs e)
        {
            string pLocalAddr = "192.168.7.22";            
            PlayResFuncObj =PlayRes;

            
            channelIndex = AudioSysSdkHelper.Audio_OpenChannel(pLocalAddr, PlayResFuncObj, IntPtr.Zero);
            if (channelIndex >= 0)
            {
                SetTextShow(string.Format("打开通道成功,通道号{0}", channelIndex));
            }
            else
            {
                SetTextShow(string.Format("打开通道失败,返回值{0}", channelIndex));
            }
        }

public void PlayRes(IntPtr powner, int filestate, int playlen)
        {
            try
            {
                int groupid = (int)powner;
                ePlayStatus state = (ePlayStatus)filestate;
                switch (state)
                {
                    case ePlayStatus.PS_PlayDone:
                        SetTextShow("播放完毕");
                        //System.Diagnostics.Trace.WriteLine("播放完毕");
                        break;
                    case ePlayStatus.PS_PlayPause:
                        SetTextShow("播放暂停");
                        //System.Diagnostics.Trace.WriteLine("播放暂停");
                        break;
                    case ePlayStatus.PS_PlayStatus:
                        SetTextShow(string.Format("播放了百分之{0}", playlen));
                        //System.Diagnostics.Trace.WriteLine(string.Format("播放了百分之{0}", playlen));                        
                        break;
                }
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

这个问题耗了我一天半的时间,这里Mark一下,备忘。

C#调用C++动态库的坑还是很多的。童鞋们注意自身安全!



posted on 2016-06-22 16:27  V吃饭睡觉打豆豆V  阅读(366)  评论(0编辑  收藏  举报

导航