您的位置:首页 > 编程语言 > C语言/C++

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

2016-06-22 16:27 676 查看
不磨叽,直接上代码。

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++动态库的坑还是很多的。童鞋们注意自身安全!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C# C++ dll