首页新闻找找看学习计划

C#调用C++ DLL的难题

0
悬赏园豆:120 [已解决问题] 解决于 2012-01-12 23:44

请教大家一个问题,现在有个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;

 

请问红色部分还有其他写法?

C#S的主页 C#S | 初学一级 | 园豆:44
提问于:2012-01-10 16:03
< >
分享
最佳答案
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

收获园豆:60
2012 | 专家六级 |园豆:18932 | 2012-01-10 17:32

谢谢您提供的资料和工具!

C#S | 园豆:44 (初学一级) | 2012-01-11 11:09

@小铭: 

你的例子测试通过的代码:http://files.cnblogs.com/yeerh/PCallTest.7z

用MarshalAs,就是系统帮你包装而已,那些属性就是你告诉他要咱个封装。 

如果理解了内存布局,就可以自己用指针写结构直接调用,那才是最方便快捷的。

边城浪 | 园豆:264 (菜鸟二级) | 2012-01-13 10:00
其他回答(6)
0

既然都有dll的C++源码,用VS跟踪进去可以调试的,或者用VC6也可以调试,看参数传进C++时,C++到底接收到什么数据。

咖啡色 | 园豆:208 (菜鸟二级) | 2012-01-10 17:00
0

这两个地方应该是指针的。你的结构定义有问题。

  [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);
        }

收获园豆:30
边城浪 | 园豆:264 (菜鸟二级) | 2012-01-10 18:35
0

extern BOOL  _stdcallPci_InitDevice(int mDev,PDEV_CTRL_PARAM pParam);

等价于:

 

[DllImport("PCIDLL.dll")]  

static extern int Pci_InitDevice(int mDev, IntPtr pParam);

Launcher | 园豆:45040 (高人七级) | 2012-01-11 09:19
0

C++有许多优秀的类库,效率高,用C#是可以调用的,可以参考:http://www.docin.com/p-184985356.htmlhttp://www.docin.com/p-271676146.htmlhttp://www.docin.com/p-317759855.htmlhttp://www.cnblogs.com/Jianchidaodi/archive/2009/03/11/1407270.htmlhttp://blog.csdn.net/huwei2003/article/details/3882473等这些资料。需要注意数据类型的置换(指针和结构体),很多时候问题出现在这个地方。另外,还有一个思路,你可以参考:就是把进一步封闭C++DLL为webservice标准接口,这样无论什么样的语言都可以很好调用。稍后如果你仍没有解决,我给贴些例子。

收获园豆:30
lonely_rain | 园豆:752 (小虾三级) | 2012-01-12 09:37
0

[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;
}

keepsilence | 园豆:427 (菜鸟二级) | 2012-01-12 14:16
0

调DLL,还是用VB.Net来的方便,然后封装一下C#也可以调了。

Donaldxu | 园豆:207 (菜鸟二级) | 2012-01-12 15:12
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册