C++ 数据类型在C# 中转换的问题,尝试读取或写入受保护的内存。这通常指示其他内存已损坏

.Net技术 码拜 10年前 (2015-05-10) 1904次浏览 0个评论

小弟目前在用C#做一个调用C++开发的DLL的功能模块。其中数据类型转换的问题不是太懂。在这里请教一下各位大侠。

运行一直报错:“尝试读取或写入受保护的内存。这通常指示其他内存已损坏。”
初步估计是类型转换的问题。这里主要请教一下在C#中定义下面类型的数据是不是有问题?主要是结构体中的数据类型和枚举的使用。

先贴一下代码吧。
C++代码:实现登录的功能。  

/** 注册平台.
 @param   IN	nPDLLHandle		SDK句柄
 @param   IN	pLoginInfo		用户登录信息
 @param   IN	nTimeout		超时时长,单位毫秒
 @return  函数返回错误类型,参考dpsdk_retval_e
 @remark	  int32_t 定义为 int
*/
DPSDK_DLL_API int32_t DPSDK_CALL_METHOD DPSDK_Login( IN int32_t nPDLLHandle, 
													 IN Login_Info_t* pLoginInfo, 
													 IN int32_t nTimeout = DPSDK_CORE_DEFAULT_TIMEOUT );
/** nPDLLHandle 在初始化得到值为1 **/

/** 其中Login_Info 的结构如下 **/
// 登录信息
typedef struct tagLoginInfo
{
    char								szIp[DPSDK_CORE_IP_LEN];					// 服务IP,或者是域名DPSDK_CORE_IP_LEN为48
	uint32_t							nPort;										// 服务端口
    char								szUsername[DPSDK_CORE_USER_NAME_LEN];		// 用户名DPSDK_CORE_USER_NAME_LEN为64
	char								szPassword[DPSDK_CORE_PASSWORD_LEN];		// 密码DPSDK_CORE_PASSWORD_LEN为64
	dpsdk_protocol_version_e			nProtocol;									// 协议类型
	uint32_t							iType;										// 登陆类型,1为PC客户端, 2为手机客户端
}Login_Info_t;

/** 枚举 dpsdk_protocol_version_e 代码如下 **/
// 协议版本
typedef enum
{
	DPSDK_PROTOCOL_VERSION_I					= 1,					// 一代协议
	DPSDK_PROTOCOL_VERSION_II					= 2,					// 二代协议
}dpsdk_protocol_version_e;

// C#代码如下


        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="m_nDLLHandle">SDK句柄</param>
        /// <param name="stuLoginInfo">用户登录信息</param>
        /// <param name="timeout">无响应超时时间</param>
        /// <returns>登录状态</returns>
        [DllImport("C:\DLLSDK\DPSDK_Core.dll", CharSet = CharSet.Auto, SetLastError = true)]//, CallingConvention = CallingConvention.Cdecl  , CallingConvention = CallingConvention.StdCall
        public static extern int DPSDK_Login(int m_nDLLHandle, Login_Info_t stuLoginInfo, int timeout = 10000);

//定义 Login_Info_t 结构体如下:
    /// <summary>
    /// 用户登录信息
    /// </summary>
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    public struct Login_Info_t
    {
        /// <summary>
        /// IP
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)]
        public char[] szIp;
        /// <summary>
        /// 端口
        /// </summary>
        public uint nPort;
        /// <summary>
        /// 登录用户名
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] szUsername;
        /// <summary>
        /// 登录密码
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
        public char[] szPassword;
        /// <summary>
        /// 协议类型
        /// </summary>
        public dpsdk_protocol_version_e nProtocol;
        /// <summary>
        /// 登陆类型,1为PC客户端, 2为手机客户端
        /// </summary>
        public uint iType;
    }

//定义枚举如下:
    /// <summary>
    /// 协议类型
    /// </summary>
    public enum dpsdk_protocol_version_e
    {
        /// <summary>
        /// 一代协议
        /// </summary>
        DPSDK_PROTOCOL_VERSION_I = 1,					// 一代协议
        /// <summary>
        /// 二代协议
        /// </summary>
        DPSDK_PROTOCOL_VERSION_II = 2,					// 二代协议
    }


//实现登录方法如下:

        /// <summary>
        /// 登录平台
        /// </summary>
        /// <param name="strIp">平台IP</param>
        /// <param name="strUserName">用户名</param>
        /// <param name="strPassword">密码</param>
        /// <param name="strPort">端口号</param>
        /// <param name="nProtocol">协议类型</param>
        /// <param name="iType">登录类型</param>
        /// <returns></returns>
        public int DLLSDK_Login(string strIp, string strUserName, string strPassword, uint strPort, dpsdk_protocol_version_e nProtocol = dpsdk_protocol_version_e.DPSDK_PROTOCOL_VERSION_II, uint iType = 1)
        {
            //用户登录信息
            Login_Info_t login_info = new Login_Info_t();
            login_info.szIp = StringToCharArray(strIp,48);
            login_info.szUsername = StringToCharArray(strUserName, 64);
            login_info.szPassword = StringToCharArray(strPassword, 64);
            login_info.nPort = strPort;
            login_info.nProtocol = nProtocol;
            login_info.iType = iType;
            //登录  m_Dll_Hanlde 为全局变量 已经赋值为1
            int result = DPSDK_Login(m_Dll_Hanlde, login_info, 5000);
            return result;
        }

       /// <summary>
        /// 字符串转换成指定长度的字符数组
        /// </summary>
        /// <param name="str"></param>
        /// <param name="ToLength"></param>
        /// <returns></returns>
        public char[] StringToCharArray(string str, int ToLength)
        {
            char[] newArray = new char[ToLength];
            if (str != null)
            {
                if (str.Length > ToLength)
                {
                    str = str.Substring(0, ToLength - 1);
                }
                char[] oldArray = str.ToCharArray();
                //byte[] buffer = Encoding.ASCII.GetBytes(str);
                //string s = Encoding.ASCII.GetString(buffer);
                //oldArray = s.ToCharArray();
                int i = 0;
                foreach (char item in oldArray)
                {
                    newArray[i] = item;
                    i++;
                }
            }
            return newArray;
        }
自己先顶一下
60分
stuLoginInfo
这个参数类型应该用IntPtr
通过Mashal.StructureToPtr把你的结构转换了再传进去
引用 2 楼 shingoscar 的回复:

stuLoginInfo
这个参数类型应该用IntPtr
通过Mashal.StructureToPtr把你的结构转换了再传进去

我对C++和C#的转换还是个小白,请问能说具体点吗或者能给我贴个示例代码吗?

引用 2 楼 shingoscar 的回复:

stuLoginInfo
这个参数类型应该用IntPtr
通过Mashal.StructureToPtr把你的结构转换了再传进去

            //用户登录信息
            Login_Info_t login_info = new Login_Info_t();
            login_info.szIp = StringToCharArray(strIp, 48);//StringToCharArray(strIp, 48);
            login_info.szUsername = StringToCharArray(strUserName, 64);
            login_info.szPassword = StringToCharArray(strPassword, 64);
            login_info.nPort = strPort;
            login_info.nProtocol = nProtocol;
            login_info.iType = iType;

            int nSizeOfLogin = Marshal.SizeOf(login_info);
            IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfLogin);

            //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
            Marshal.StructureToPtr(login_info, intPtr, true);
            //登录
            int result = -1;
            try
            {
                result = DaHua_SDK_API.DPSDK_Login(m_Dll_Hanlde, intPtr, 5000);
            }
            catch
            { result = -2; }
            finally
            {
                Marshal.FreeHGlobal(intPtr);    //free tha memory
            }
            return result;

我这样做了一下好像还是不行。。

引用 4 楼 web_asp_net 的回复:
Quote: 引用 2 楼 shingoscar 的回复:

stuLoginInfo
这个参数类型应该用IntPtr
通过Mashal.StructureToPtr把你的结构转换了再传进去

            //用户登录信息
            Login_Info_t login_info = new Login_Info_t();
            login_info.szIp = StringToCharArray(strIp, 48);//StringToCharArray(strIp, 48);
            login_info.szUsername = StringToCharArray(strUserName, 64);
            login_info.szPassword = StringToCharArray(strPassword, 64);
            login_info.nPort = strPort;
            login_info.nProtocol = nProtocol;
            login_info.iType = iType;

            int nSizeOfLogin = Marshal.SizeOf(login_info);
            IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfLogin);

            //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
            Marshal.StructureToPtr(login_info, intPtr, true);
            //登录
            int result = -1;
            try
            {
                result = DaHua_SDK_API.DPSDK_Login(m_Dll_Hanlde, intPtr, 5000);
            }
            catch
            { result = -2; }
            finally
            {
                Marshal.FreeHGlobal(intPtr);    //free tha memory
            }
            return result;

我这样做了一下好像还是不行。。

可以了。。不过需要将  C++ Login_Info_t 结构体中的 char 类型 定义为C# 中的 byte[] 类型
登录修改代码如下:

        /// <summary>
        /// 字符串转换成指定长度的字符数组
        /// </summary>
        /// <param name="str"></param>
        /// <param name="ToLength"></param>
        /// <returns></returns>
        public byte[] StringToByteArray(string str, int ToLength)
        {
            byte[] newArray = new byte[ToLength];
            if (str != null)
            {
                if (str.Length > ToLength)
                {
                    str = str.Substring(0, ToLength - 1);
                }
                byte[] buffer = Encoding.ASCII.GetBytes(str);
                int i = 0;
                foreach (byte item in buffer)
                {
                    newArray[i] = item;
                    i++;
                }
            }
            return newArray;
        }

        /// <summary>
        /// 登录平台
        /// </summary>
        /// <param name="strIp">平台IP</param>
        /// <param name="strUserName">用户名</param>
        /// <param name="strPassword">密码</param>
        /// <param name="strPort">端口号</param>
        /// <param name="nProtocol">协议类型</param>
        /// <param name="iType">登录类型</param>
        /// <returns></returns>
        public int DLLSDK_Login(string strIp, string strUserName, string strPassword, uint strPort, dpsdk_protocol_version_e nProtocol = dpsdk_protocol_version_e.DPSDK_PROTOCOL_VERSION_II, uint iType = 1)
        {
            //用户登录信息
            Login_Info_t login_info = new Login_Info_t();
            login_info.szIp = StringToByteArray(strIp, 48);//StringToCharArray(strIp, 48);
            login_info.szUsername = StringToByteArray(strUserName, 64);
            login_info.szPassword = StringToByteArray(strPassword, 64);
            login_info.nPort = strPort;
            login_info.nProtocol = nProtocol;
            login_info.iType = iType;

            int nSizeOfLogin = Marshal.SizeOf(login_info);
            IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfLogin);

            //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
            Marshal.StructureToPtr(login_info, intPtr, true);
            //登录
            int result = -1;
            try
            {
                result =DPSDK_Login(m_Dll_Hanlde, intPtr, 5000);
            }
            catch
            { result = -2; }
            finally
            {
                Marshal.FreeHGlobal(intPtr);    //free tha memory
            }
            return result;
        }

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C++ 数据类型在C# 中转换的问题,尝试读取或写入受保护的内存。这通常指示其他内存已损坏
喜欢 (0)
[1034331897@qq.com]
分享 (0)

文章评论已关闭!