请教大家一个问题,现在有个C++ PCIDLL.dll文件,在C#中调用 dll,出现错误如下:System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
请问C#代码哪里有误,烦请高手指点,谢谢!
C++ Code:
结构体:
typedef struct _DEV_CTRL_PARAM { intScanType;
intSelChannel[12];
intmParamType;
int*mEtalonPeakNum;
float*p_EtalonPeakVal;
}DEV_CTRL_PARAM,*PDEV_CTRL_PARAM;
externBOOL _stdcallPci_InitDevice(int mDev,PDEV_CTRL_PARAM pParam);
调用:
int mEtalonPeakNum = 0;
floatEtalonPeakVal[400];
DEV_CTRL_PARAM mParam;
int temptype[7] ={6,5,4,3,2,1,0};
mParam.ScanType=3;
mParam.mParamType = 0;
mParam.mEtalonPeakNum = &mEtalonPeakNum;
mParam.p_EtalonPeakVal = EtalonPeakVal;
mParam.SelChannel[0] = 1;
mParam.SelChannel[1] = 1;
mParam.SelChannel[2] = 1;
mParam.SelChannel[3] = 1;
mParam.SelChannel[4] = 1;
mParam.SelChannel[5] = 1;
mParam.SelChannel[6] = 1;
mParam.SelChannel[7] = 1;
mParam.SelChannel[8] = 1;
mParam.SelChannel[9] = 1;
mParam.SelChannel[10] = 0;
mParam.SelChannel[11] = 0;
Pci_InitDevice(0,&mParam);
----------------------------------------------------------------------------------
C# Code:
结构体:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEV_CTRL_PARAM
{
[MarshalAs(UnmanagedType.I4)]
public int ScanType;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I4, SizeConst = 12)]
public int[] SelChannel;
[MarshalAs(UnmanagedType.I4)]
public int mParamType;
[MarshalAs(UnmanagedType.I4)]
public int mEtalonPeakNum;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.R4, SizeConst = 400)]
public Single[] p_EtalonPeakVal;
}
[DllImport("PCIDLL.dll")]
public static extern bool Pci_InitDevice(int mDev, ref DEV_CTRL_PARAM pParam);
调用:
DEV_CTRL_PARAM mParam;
mParam.SelChannel = new int[12];
int mEtalonPeakNum = 0;
float[] EtalonPeakVal = new float[400];
int[] temptype = new int[7] { 6, 5, 4, 3, 2, 1, 0 };
mParam.ScanType = 3;
mParam.mParamType = 0;
mParam.mEtalonPeakNum = mEtalonPeakNum;
mParam.p_EtalonPeakVal = EtalonPeakVal;
mParam.SelChannel[0] = 1;
mParam.SelChannel[1] = 1;
mParam.SelChannel[2] = 1;
mParam.SelChannel[3] = 1;
mParam.SelChannel[4] = 1;
mParam.SelChannel[5] = 1;
mParam.SelChannel[6] = 1;
mParam.SelChannel[7] = 1;
mParam.SelChannel[8] = 1;
mParam.SelChannel[9] = 1;
mParam.SelChannel[10] = 0;
mParam.SelChannel[11] = 0;
Pci_InitDevice(0, ref mParam);
已将结构体修改为:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEV_CTRL_PARAM
{
public int ScanType;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 12, ArraySubType = UnmanagedType.I4)]
public int[] SelChannel;
public int mParamType;
public IntPtr mEtalonPeakNum;
public IntPtr p_EtalonPeakVal;
}
调用:
DEV_CTRL_PARAM mParam = new DEV_CTRL_PARAM();
mParam.SelChannel = new int[12];
int mEtalonPeakNum = 0;
float[] EtalonPeakVal = new float[400];
int[] temptype = new int[7] { 6, 5, 4, 3, 2, 1, 0 };
mParam.ScanType = 3;
mParam.mParamType = 0;
mParam.mEtalonPeakNum = new IntPtr(mEtalonPeakNum);
mParam.p_EtalonPeakVal = Marshal.UnsafeAddrOfPinnedArrayElement(EtalonPeakVal, 0);
mParam.SelChannel[0] = 1;
mParam.SelChannel[1] = 1;
mParam.SelChannel[2] = 1;
mParam.SelChannel[3] = 1;
mParam.SelChannel[4] = 1;
mParam.SelChannel[5] = 1;
mParam.SelChannel[6] = 1;
mParam.SelChannel[7] = 1;
mParam.SelChannel[8] = 1;
mParam.SelChannel[9] = 1;
mParam.SelChannel[10] = 0;
mParam.SelChannel[11] = 0;
请问红色部分还有其他写法?
使用结构是有点复杂,参考http://msdn.microsoft.com/zh-cn/library/aa686045.aspx 官方的说明
或者嫌麻烦可以使用一个工具,参考 http://www.cnblogs.com/lbq1221119/archive/2011/12/10/1040958.html 可以自动生成调用
这个错误的原因可能是结构的大小和指针的问题
http://stackoverflow.com/questions/941440/c-sharp-p-invoke-difficulties-marshalling-pointers
谢谢您提供的资料和工具!
@小铭:
你的例子测试通过的代码:http://files.cnblogs.com/yeerh/PCallTest.7z
用MarshalAs,就是系统帮你包装而已,那些属性就是你告诉他要咱个封装。
如果理解了内存布局,就可以自己用指针写结构直接调用,那才是最方便快捷的。
既然都有dll的C++源码,用VS跟踪进去可以调试的,或者用VC6也可以调试,看参数传进C++时,C++到底接收到什么数据。
这两个地方应该是指针的。你的结构定义有问题。
[MarshalAs(UnmanagedType.I4)]
public int mEtalonPeakNum;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.R4, SizeConst = 400)]
public Single[] p_EtalonPeakVal;
可以这样子
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
unsafe public struct DEV_CTRL_PARAM
{
public int ScanType;
public int SelChannel0;
public int SelChannel1;
public int SelChannel2;
public int SelChannel3;
public int SelChannel4;
public int SelChannel5;
public int SelChannel6;
public int SelChannel7;
public int SelChannel8;
public int SelChannel9;
public int SelChannel10;
public int SelChannel11;
public int mParamType;
public int* mEtalonPeakNum;
public Single* p_EtalonPeakVal;
}
调用:
[DllImport("PCIDLL.dll")]
public static extern bool Pci_InitDevice(int mDev, ref DEV_CTRL_PARAM pParam);
unsafe static void Main(string[] args)
{
DEV_CTRL_PARAM mParam;
int[] mEtalonPeakNum = new int[18];
float[] EtalonPeakVal = new float[400];
int[] temptype = new int[7] { 6, 5, 4, 3, 2, 1, 0 };
mParam.ScanType = 3;
mParam.mParamType = 0;
mParam.SelChannel0 = 1;
mParam.SelChannel1 = 1;
mParam.SelChannel2 = 1;
mParam.SelChannel3 = 1;
mParam.SelChannel4 = 1;
mParam.SelChannel5 = 1;
mParam.SelChannel6 = 1;
mParam.SelChannel7 = 1;
mParam.SelChannel8 = 1;
mParam.SelChannel9 = 1;
mParam.SelChannel10 = 0;
mParam.SelChannel11 = 0;
fixed (int* d = mEtalonPeakNum)
{
fixed (float* dd = EtalonPeakVal)
{
mParam.mEtalonPeakNum = d;
mParam.p_EtalonPeakVal = dd;
Pci_InitDevice(0, ref mParam);
}
}
int count = sizeof(DEV_CTRL_PARAM);
Console.WriteLine(count);
}
extern BOOL _stdcallPci_InitDevice(int mDev,PDEV_CTRL_PARAM pParam);
等价于:
[DllImport("PCIDLL.dll")]
static extern int Pci_InitDevice(int mDev, IntPtr pParam);
C++有许多优秀的类库,效率高,用C#是可以调用的,可以参考:http://www.docin.com/p-184985356.html,http://www.docin.com/p-271676146.html,http://www.docin.com/p-317759855.html,http://www.cnblogs.com/Jianchidaodi/archive/2009/03/11/1407270.html,http://blog.csdn.net/huwei2003/article/details/3882473等这些资料。需要注意数据类型的置换(指针和结构体),很多时候问题出现在这个地方。另外,还有一个思路,你可以参考:就是把进一步封闭C++DLL为webservice标准接口,这样无论什么样的语言都可以很好调用。稍后如果你仍没有解决,我给贴些例子。
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
public struct _DEV_CTRL_PARAM {
/// int
public int ScanType;
/// int[12]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=12, ArraySubType=System.Runtime.InteropServices.UnmanagedType.I4)]
public int[] SelChannel;
/// int
public int mParamType;
/// int*
public System.IntPtr mEtalonPeakNum;
/// float*
public System.IntPtr p_EtalonPeakVal;
}
调DLL,还是用VB.Net来的方便,然后封装一下C#也可以调了。