最近在开发过程中需要使用C#调用C写的DLL,但在调用过程中遇到这个问题:“尝试读取或写入受保护的内存 这通常指示其它内存已损坏”,出现错误的地方可能为C#定义的结构体与C定义的结构体转换错误。详细如下:
1、引用外部C的DLL:
        [DllImport("UniAPIDll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl, EntryPoint = "InitSgipAPI")]
        private static extern int InitSgipAPI(StringBuilder path, loginInfo _LoginInfo);2、C#中定义的loginInfo:        public const int LOGINFO_IP_LEN = 15;
        public const int LOGINFO_USER_LEN = 16;
        
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
        public struct loginInfo 
        { 
            [MarshalAs(UnmanagedType.U4)] 
            public uint spNodeId;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_IP_LEN + 1)] 
            public string gwIp; 
            [MarshalAs(UnmanagedType.I4)] 
            public  int gwPort;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_USER_LEN + 1)] 
            public string loginGwUser;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_USER_LEN + 1)] 
            public string loginGwPwd;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_IP_LEN + 1)] 
            public string appIp; 
            [MarshalAs(UnmanagedType.I4)] 
            public int appPort;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_USER_LEN + 1)] 
            public string gwLinkUser;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = LOGINFO_USER_LEN + 1)] 
            public string gwLinkPwd; 
        }
3、C的loginInfo的结构如下:#define LOGINFO_IP_LEN             ((int)15)
#define LOGINFO_USER_LEN           ((int)16)
#define LOGINFO_PWD_LEN            ((int)16)typedef struct loginInfo_tag
{
     unsigned int spNodeId;                              // sp node code, should be assigned by gw
     char         gwIp[LOGINFO_IP_LEN + 1];
     int          gwPort;
     char         loginGwUser[LOGINFO_USER_LEN + 1];
     char         loginGwPwd[LOGINFO_PWD_LEN + 1];
     char         appIp[LOGINFO_IP_LEN + 1];             // provide sgip server on the ip.
     int          appPort;                               // provide sgip server at the port
     char         gwLinkUser[LOGINFO_USER_LEN + 1];      // gw use the username to link app  
     char         gwLinkPwd[LOGINFO_PWD_LEN + 1];        // gw use the password to link app
} LoginInfo;
4、在使用
        public int InitAPI(loginInfo _LoginInfo) 
        {
            if (this._path == "" || this._path == null) return -1;
            StringBuilder _StringBuilderPath = new StringBuilder(this._path);
            return InitSgipAPI(_StringBuilderPath,  _LoginInfo);
        }
抛出“尝试读取或写入受保护的内存 这通常指示其它内存已损坏”的异常,请问大师们这为何?如何解决?

解决方案 »

  1.   

    很少见到按值传一个大结构的。把int InitSgipAPI()的原型贴出来,很可能是按引用传的:InitSgipAPI(..., ref loginInfo info);
      

  2.   

    如果单单是一个结构体指针的话,c#中可以使用ref或者out修饰,如果c++中是结构体数组的话,这个就要看情况了,c#中可能需要加上OUT IN修饰数组,而且c++的调用方式需要时stdcall
      

  3.   

    函数原型:int InitSgipAPI( char *path, LoginInfo *pLoginInfo );
      

  4.   


    参考一下吧using System;
    using System.Text;
    using System.Runtime.InteropServices;namespace testStructureToPtr
    ...{
        public static class define  //define some constant
        ...{        
            public const int MAX_LENGTH_OF_IDENTICARDID = 20;   //maximum length of identicardid
            public const int MAX_LENGTH_OF_NAME = 50;           //maximum length of name
            public const int MAX_LENGTH_OF_COUNTRY = 50;        //maximum length of country
            public const int MAX_LENGTH_OF_NATION = 50;         //maximum length of nation
            public const int MAX_LENGTH_OF_BIRTHDAY = 8;        //maximum length of birthday
            public const int MAX_LENGTH_OF_ADDRESS = 200;       //maximum length of address
        }    public struct PERSON    //person structure
        ...{
            //MarshalAs:指示如何在托管代码和非托管代码之间封送数据
            //UnmanagedType:指定如何将参数或字段封送到非托管内存块
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_IDENTICARDID)]
            public byte[] identicardid;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NAME)]
            public byte[] name;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_COUNTRY)]
            public byte[] country;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_NATION)]
            public byte[] nation;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_BIRTHDAY)]
            public byte[] birthday;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = define.MAX_LENGTH_OF_ADDRESS)]
            public byte[] address;
        }    class testProgram
        ...{
            private static byte _fillChar = 0;      //the fill character        //convert string to byte array in Ascii with length is len        
            public static byte[] CodeBytes(string str, int len)
            ...{
                if (string.IsNullOrEmpty(str))
                ...{
                    str = string.Empty;
                }
     
                byte[] result = new byte[len];
                byte[] strBytes = Encoding.Default.GetBytes(str);            //copy the array converted into result, and fill the remaining bytes with 0
                for (int i = 0; i < len; i++)
                    result[i] = ((i < strBytes.Length) ? strBytes[i] : _fillChar);
                
                return result;
            }        //show the person information
            public static void ShowPerson(PERSON person)
            ...{
                Console.WriteLine("cardid   :" + Encoding.ASCII.GetString(person.identicardid));
                Console.WriteLine("name     :" + Encoding.ASCII.GetString(person.name));
                Console.WriteLine("country  :" + Encoding.ASCII.GetString(person.country));
                Console.WriteLine("nation   :" + Encoding.ASCII.GetString(person.nation));
                Console.WriteLine("birthday :" + Encoding.ASCII.GetString(person.birthday));
                Console.WriteLine("address  :" + Encoding.ASCII.GetString(person.address));
            }        static void Main(string[] args)
            ...{
                PERSON person;
                person.identicardid = CodeBytes("123456198001011111", define.MAX_LENGTH_OF_IDENTICARDID);
                person.name = CodeBytes("jackson", define.MAX_LENGTH_OF_NAME);
                person.country = CodeBytes("China", define.MAX_LENGTH_OF_COUNTRY);
                person.nation = CodeBytes("HanZu", define.MAX_LENGTH_OF_NATION);
                person.birthday = CodeBytes("19800101", define.MAX_LENGTH_OF_BIRTHDAY);
                person.address = CodeBytes("Luoshan Road, Shanghai", define.MAX_LENGTH_OF_ADDRESS);            int nSizeOfPerson = Marshal.SizeOf(person);
                IntPtr intPtr = Marshal.AllocHGlobal(nSizeOfPerson);
                
                Console.WriteLine("The person infomation is as follows:");
                ShowPerson(person);            try
                ...{
                    //将数据从托管对象封送到非托管内存块,该内存块开始地址为intPtr
                    Marshal.StructureToPtr(person, intPtr, true);                //将数据从非托管内存块封送到新分配的指定类型的托管对象anotherPerson
                    PERSON anotherPerson = (PERSON)Marshal.PtrToStructure(intPtr, typeof(PERSON));                Console.WriteLine("The person after copied is as follows:");
                    ShowPerson(anotherPerson);
                }
                catch (ArgumentException)
                ...{
                    throw;
                }
                finally
                ...{
                    Marshal.FreeHGlobal(intPtr);    //free tha memory
                }
            }
        }
    }
      

  5.   

    LoginInfo *pLoginInfo 
    要用ref LoginInfo。
      

  6.   

    gomoku,我也加ref试过了,结果一样错误,你还有其他办法吗?就靠你们了