none
AccessViolationException “Attempted to read or write protected memory” in using C++ DLL from C#

    Question

  • I have a some problem trying to use a DLL written in C++ from a C# application

    I want to integrate this C++ method:

       
    int MyMethod(unsigned char* materials, 
                 unsigned int materialCount,            	   
                 unsigned int materialLength,            	 
                 char* filePath,
                 unsigned int shift,            	 
                 unsigned int length,             	 
                 unsigned char** seq,            	 
                 unsigned int* seqLength);
    C# code:

      
    [DllImport("MyDll.dll")]
    public static extern int MyMethod(byte[] materials,
                	                      uint materialCount,
                	                      uint materialLength,
                	                      string filePath,
                	                      uint shift,
                	                      uint length, 
                	                      ref byte[] seq,
                	                      ref uint seqLength);
    But when I trying to execute MyMethod - I getting error : AccessViolationException "Attempted to read or write protected memory. This is often an indication that other memory is corrupt"

    Help please to solve my problem...
    Saturday, October 27, 2012 9:52 PM

Answers

  • You can't return arrays that were allocated in C++ to use in C# unless you are using "unsafe" mode. Allocate all of your memory in C# first. You can either declare your parameters as byte[] in C# or you can use IntPtr and marshal the objects manually. See this post for more detail:

    How to pass an array of bytes


    Dan Randolph - My Code Samples List

    Saturday, October 27, 2012 10:16 PM
  • Hello dsnj,


    1. There are 2 important points about the MyMethod() function as described in the OP :

    1.1 How to properly marshal the various reference type parameters (e.g. the byte arrays and the string) to and from the C++ function.

    1.2 The direction of parameter flow.

    1.3 I shall expound on these 2 issues as I touch on (in the sections below) the 3 important parameters : "materials", "filePath" and "seq".


    2. Concerning the "materials" Parameter.

    2.1 From the declaration of MyMethod(), it appears that "materials" is passed as an "in" parameter. That is, it is passed as read-only to MyMethod(). It also appears that the second parameter "materialCount" indicates the number of elements that the "materials" array holds.

    2.2. Hence in the DllImport declaration for MyMethod, "materials" can be declared as a byte[] but it must be decorated with the MarshalAsAttribute indicating that it is to be marshaled as a C-style array, e.g. :

    [In, MarshalAs(UnmanagedType.LPArray)] byte[] materials

    2.3 When MyMethod() is called, the interop marshaler will either internally allocate an unmanaged memory buffer to contain all the byte values of "materials" or it may optimize and pin the managed memory buffer of "materials". Either way, a pointer to this memory will be passed to MyMethod().

    2.4 MyMethod() must not touch this memory and must treat it as read-only.

    2.5 When MyMethod() returns, the interop marshaler will delete the memory that was used to hold the values of the "materials" array (if this memory was indeed allocated). If the "materials" array was pinned and the pointer to this memory was passed to MyMethod(), then the memory is not deleted.


    3. Concerning the "filePath" Parameter.

    3.1 This parameter also appears to be an "in" parameter.

    3.2. Hence in the DllImport declaration for MyMethod, "filePath" can be declared as a string. It also will be helpful (for documentation purposes) that it be decorated with the MarshalAsAttribute indicating that it is to be marshaled as a C-style NULL-terminated string (LPSTR), e.g. :

    [In, MarshalAs(UnmanagedType.LPStr)] string filePath

    However, this MarshalAsAttribute may not actually be necessary because a managed string is marshaled as a C-style NULL-terminated string by default.

    3.3 At runtime, when MyMethod() is called, the interop marshaler will internally allocate a temporary memory buffer and copy the value of "filePath" to this buffer. A pointer to this buffer will be passed to MyMethod(). When MyMethod() returns, the interop marshaler will delete this temporary memory buffer.

    3.4 Because it is an "in" parameter, MyMethod() must treat it as read-only and must not modify it.

    4. Concerning the "seq" Parameter.

    4.1 This parameter appears to be passed as an "in" and "out" parameter (hence the "ref" keyword).

    4.2 I take it that an array of bytes is to be allocated on the C# code which is then passed to MyMethod(). MyMethod() will then modify this array of bytes (increasing its array size, changing the values of its elements) and may return a totally different pointer to the C# client code.

    4.3 It also appears that the "seqLength" parameter will indicate the size of the array on entering MyMethod() and will report back a new size once MyMethod() returns.

    4.4 In this case, in the DllImport declaration for MyMethod, it is not possible to declare "seq" as a byte[]. There are 5 important steps to be taken in relation to passing "seq" and "seqLength" :

    4.4.1 "seq" must be declared as of type IntPtr and is to be marshaled as an "in" and "out" parameter, e.g. :

    [In, Out] ref IntPtr seq

    Notice that the "ref" keyword is important and it indicates that the value in "seq" can change on return from MyMethod().

    4.4.2 "seqLength" is declared correctly as a "ref" parameter but it should be declared with the InAttribute and the OutAttribute (for documentation purposes) to indicate marshaling direction, e.g. :

    [In, Out] ref uint seqLength

    4.4.3 An unmanaged buffer must be allocated to store the bytes to be passed to MyMethod(). Then a pointer to this buffer is passed as the parameter. I will provide a demonstration of this in the example code given in section 5 below.

    4.4.4 MyMethod() is free to change the values inside the allocated buffer. It may even free this buffer and re-allocate a new buffer which will be returned to the C# client. The new size of the buffer will be returned via "uiseqLength". Example of this will be given in section 5.

    4.4.5 When MyMethod() returns, the newly allocated buffer must be freed. Example of this will be given in section 5.

    5. Example Code.

    5.1 In this section, I shall provide some sample implementation code.

    5.2 Listed below is a sample C++ implementation code for MyMethod() that I used for testing purposes :

    extern "C" __declspec(dllexport) int __stdcall MyMethod
    (
        /*[in]*/ unsigned char* materials, 
        /*[in]*/ unsigned int materialCount,            	   
        /*[in]*/ unsigned int materialLength,            	 
        /*[in]*/ char* filePath,
        /*[in]*/ unsigned int shift,            	 
        /*[in]*/ unsigned int length,             	 
        /*[in, out]*/ unsigned char** seq,            	 
        /*[in, out]*/ unsigned int* seqLength
    )
    {
    	int i = 0;
    
    	// Display all values inside the materials byte array.	
    	for (i = 0; i < (int)materialCount; i++)
    	{
    		printf ("material[%d] : [%d].\r\n", i, materials[i]);
    	}
    	
    	printf ("materialLength : [%d].\r\n", materialLength);
    	printf ("filePath : [%s].\r\n", filePath);
    	printf ("shift : [%d].\r\n", shift);
    	printf ("length : [%d].\r\n", length);
    	
    	if (*seq)
    	{
    		// Display the original bytes in "seq".
    		for (i = 0; i < (int)(*seqLength); i++)
    		{
    			printf ("Original seq[%d] : [%d].\r\n", i, (*seq)[i]);
    		}
    	}	
    	
    	// Set the new size of the array to be returned.
    	*seqLength = 20;
    	
    	// Free the memory buffer pointed to by "seq".
    	if (*seq)
    	{
    		CoTaskMemFree(*seq);
    	}
    	
    	// Allocate a new memory buffer and make "seq" point to it.
    	*seq = (unsigned char*)CoTaskMemAlloc(sizeof(unsigned char) * (*seqLength));
    	
    	// Set values in the new buffer.
    	for (i = 0; i < (int)(*seqLength); i++)
    	{
    		(*seq)[i] = i;
    	}	
    	
    	return 0;
    }
    

    5.3 Listed below is a sample C# client code that I used to test MyMethod() :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace CSConsoleClient
    {
        class Program
        {
            [DllImport("MyDll.dll", CallingConvention=CallingConvention.StdCall)]
            public static extern int MyMethod
            (
                [In, MarshalAs(UnmanagedType.LPArray)] byte[] materials,  // Array of bytes.
                [In] uint materialCount,// count of items in materials array of bytes.
                [In] uint materialLength,
                [In, MarshalAs(UnmanagedType.LPStr)] string filePath,
                [In] uint shift,
                [In] uint length,
                [In, Out] ref IntPtr seq, // Pointer to an array of bytes passed to and changed by unmanaged function. 
                [In, Out] ref uint seqLength // Pointer to a uint which indicates the size of array passed to and changed by unmanaged function.
            );
            
            static void DoTest()
            {
                byte[] materials = new byte[5] { 0, 1, 2, 3, 4 };
                byte[] seq = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                
                // Allocate unmanaged memory that will contain a copy of the seq array.
                IntPtr pseq = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(byte)) * seq.Length);
                // Copy all bytes from seq array to unmanaged memory buffer pointed to by pseq.
                Marshal.Copy(seq, 0, pseq, seq.Length);
                uint uiseqLength = (uint)seq.Length;
                
                int iRet = MyMethod
                (
                    materials,  // Pointer to an array of bytes.
                    (uint)materials.Length,// count of items in materials array of bytes.
                    0,
                    "c:\\test\\test.txt",
                    0,
                    0,
                    ref pseq, // array of bytes passed to and changed by unmanaged function. 
                    ref uiseqLength // size of array passed to and changed by unmanaged function.
                );
                
                byte [] new_seq = new byte[uiseqLength];
                // Copy all bytes from unmanaged memory buffer pointed to by pseq to new_seq.
                Marshal.Copy(pseq, new_seq, 0, (int)uiseqLength);
    
                // Free the unmanaged memory buffer pointed to by pseq.
                Marshal.FreeCoTaskMem(pseq);
                pseq = IntPtr.Zero;
                
                // Print out all new values of pseq.
                for (int i = 0; i < (int)uiseqLength; i++)
                {
                    Console.WriteLine("New SEQ[{0:D}] : [{1:D}].", i, new_seq[i]);
                }
            }
            
            static void Main(string[] args)
            {
                DoTest();
            }
        }
    }
    

    Please refer to the explanatory comments in the code above.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Sunday, October 28, 2012 5:42 AM

All replies

  • You can't return arrays that were allocated in C++ to use in C# unless you are using "unsafe" mode. Allocate all of your memory in C# first. You can either declare your parameters as byte[] in C# or you can use IntPtr and marshal the objects manually. See this post for more detail:

    How to pass an array of bytes


    Dan Randolph - My Code Samples List

    Saturday, October 27, 2012 10:16 PM
  • Hello dsnj,


    1. There are 2 important points about the MyMethod() function as described in the OP :

    1.1 How to properly marshal the various reference type parameters (e.g. the byte arrays and the string) to and from the C++ function.

    1.2 The direction of parameter flow.

    1.3 I shall expound on these 2 issues as I touch on (in the sections below) the 3 important parameters : "materials", "filePath" and "seq".


    2. Concerning the "materials" Parameter.

    2.1 From the declaration of MyMethod(), it appears that "materials" is passed as an "in" parameter. That is, it is passed as read-only to MyMethod(). It also appears that the second parameter "materialCount" indicates the number of elements that the "materials" array holds.

    2.2. Hence in the DllImport declaration for MyMethod, "materials" can be declared as a byte[] but it must be decorated with the MarshalAsAttribute indicating that it is to be marshaled as a C-style array, e.g. :

    [In, MarshalAs(UnmanagedType.LPArray)] byte[] materials

    2.3 When MyMethod() is called, the interop marshaler will either internally allocate an unmanaged memory buffer to contain all the byte values of "materials" or it may optimize and pin the managed memory buffer of "materials". Either way, a pointer to this memory will be passed to MyMethod().

    2.4 MyMethod() must not touch this memory and must treat it as read-only.

    2.5 When MyMethod() returns, the interop marshaler will delete the memory that was used to hold the values of the "materials" array (if this memory was indeed allocated). If the "materials" array was pinned and the pointer to this memory was passed to MyMethod(), then the memory is not deleted.


    3. Concerning the "filePath" Parameter.

    3.1 This parameter also appears to be an "in" parameter.

    3.2. Hence in the DllImport declaration for MyMethod, "filePath" can be declared as a string. It also will be helpful (for documentation purposes) that it be decorated with the MarshalAsAttribute indicating that it is to be marshaled as a C-style NULL-terminated string (LPSTR), e.g. :

    [In, MarshalAs(UnmanagedType.LPStr)] string filePath

    However, this MarshalAsAttribute may not actually be necessary because a managed string is marshaled as a C-style NULL-terminated string by default.

    3.3 At runtime, when MyMethod() is called, the interop marshaler will internally allocate a temporary memory buffer and copy the value of "filePath" to this buffer. A pointer to this buffer will be passed to MyMethod(). When MyMethod() returns, the interop marshaler will delete this temporary memory buffer.

    3.4 Because it is an "in" parameter, MyMethod() must treat it as read-only and must not modify it.

    4. Concerning the "seq" Parameter.

    4.1 This parameter appears to be passed as an "in" and "out" parameter (hence the "ref" keyword).

    4.2 I take it that an array of bytes is to be allocated on the C# code which is then passed to MyMethod(). MyMethod() will then modify this array of bytes (increasing its array size, changing the values of its elements) and may return a totally different pointer to the C# client code.

    4.3 It also appears that the "seqLength" parameter will indicate the size of the array on entering MyMethod() and will report back a new size once MyMethod() returns.

    4.4 In this case, in the DllImport declaration for MyMethod, it is not possible to declare "seq" as a byte[]. There are 5 important steps to be taken in relation to passing "seq" and "seqLength" :

    4.4.1 "seq" must be declared as of type IntPtr and is to be marshaled as an "in" and "out" parameter, e.g. :

    [In, Out] ref IntPtr seq

    Notice that the "ref" keyword is important and it indicates that the value in "seq" can change on return from MyMethod().

    4.4.2 "seqLength" is declared correctly as a "ref" parameter but it should be declared with the InAttribute and the OutAttribute (for documentation purposes) to indicate marshaling direction, e.g. :

    [In, Out] ref uint seqLength

    4.4.3 An unmanaged buffer must be allocated to store the bytes to be passed to MyMethod(). Then a pointer to this buffer is passed as the parameter. I will provide a demonstration of this in the example code given in section 5 below.

    4.4.4 MyMethod() is free to change the values inside the allocated buffer. It may even free this buffer and re-allocate a new buffer which will be returned to the C# client. The new size of the buffer will be returned via "uiseqLength". Example of this will be given in section 5.

    4.4.5 When MyMethod() returns, the newly allocated buffer must be freed. Example of this will be given in section 5.

    5. Example Code.

    5.1 In this section, I shall provide some sample implementation code.

    5.2 Listed below is a sample C++ implementation code for MyMethod() that I used for testing purposes :

    extern "C" __declspec(dllexport) int __stdcall MyMethod
    (
        /*[in]*/ unsigned char* materials, 
        /*[in]*/ unsigned int materialCount,            	   
        /*[in]*/ unsigned int materialLength,            	 
        /*[in]*/ char* filePath,
        /*[in]*/ unsigned int shift,            	 
        /*[in]*/ unsigned int length,             	 
        /*[in, out]*/ unsigned char** seq,            	 
        /*[in, out]*/ unsigned int* seqLength
    )
    {
    	int i = 0;
    
    	// Display all values inside the materials byte array.	
    	for (i = 0; i < (int)materialCount; i++)
    	{
    		printf ("material[%d] : [%d].\r\n", i, materials[i]);
    	}
    	
    	printf ("materialLength : [%d].\r\n", materialLength);
    	printf ("filePath : [%s].\r\n", filePath);
    	printf ("shift : [%d].\r\n", shift);
    	printf ("length : [%d].\r\n", length);
    	
    	if (*seq)
    	{
    		// Display the original bytes in "seq".
    		for (i = 0; i < (int)(*seqLength); i++)
    		{
    			printf ("Original seq[%d] : [%d].\r\n", i, (*seq)[i]);
    		}
    	}	
    	
    	// Set the new size of the array to be returned.
    	*seqLength = 20;
    	
    	// Free the memory buffer pointed to by "seq".
    	if (*seq)
    	{
    		CoTaskMemFree(*seq);
    	}
    	
    	// Allocate a new memory buffer and make "seq" point to it.
    	*seq = (unsigned char*)CoTaskMemAlloc(sizeof(unsigned char) * (*seqLength));
    	
    	// Set values in the new buffer.
    	for (i = 0; i < (int)(*seqLength); i++)
    	{
    		(*seq)[i] = i;
    	}	
    	
    	return 0;
    }
    

    5.3 Listed below is a sample C# client code that I used to test MyMethod() :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace CSConsoleClient
    {
        class Program
        {
            [DllImport("MyDll.dll", CallingConvention=CallingConvention.StdCall)]
            public static extern int MyMethod
            (
                [In, MarshalAs(UnmanagedType.LPArray)] byte[] materials,  // Array of bytes.
                [In] uint materialCount,// count of items in materials array of bytes.
                [In] uint materialLength,
                [In, MarshalAs(UnmanagedType.LPStr)] string filePath,
                [In] uint shift,
                [In] uint length,
                [In, Out] ref IntPtr seq, // Pointer to an array of bytes passed to and changed by unmanaged function. 
                [In, Out] ref uint seqLength // Pointer to a uint which indicates the size of array passed to and changed by unmanaged function.
            );
            
            static void DoTest()
            {
                byte[] materials = new byte[5] { 0, 1, 2, 3, 4 };
                byte[] seq = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
                
                // Allocate unmanaged memory that will contain a copy of the seq array.
                IntPtr pseq = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(byte)) * seq.Length);
                // Copy all bytes from seq array to unmanaged memory buffer pointed to by pseq.
                Marshal.Copy(seq, 0, pseq, seq.Length);
                uint uiseqLength = (uint)seq.Length;
                
                int iRet = MyMethod
                (
                    materials,  // Pointer to an array of bytes.
                    (uint)materials.Length,// count of items in materials array of bytes.
                    0,
                    "c:\\test\\test.txt",
                    0,
                    0,
                    ref pseq, // array of bytes passed to and changed by unmanaged function. 
                    ref uiseqLength // size of array passed to and changed by unmanaged function.
                );
                
                byte [] new_seq = new byte[uiseqLength];
                // Copy all bytes from unmanaged memory buffer pointed to by pseq to new_seq.
                Marshal.Copy(pseq, new_seq, 0, (int)uiseqLength);
    
                // Free the unmanaged memory buffer pointed to by pseq.
                Marshal.FreeCoTaskMem(pseq);
                pseq = IntPtr.Zero;
                
                // Print out all new values of pseq.
                for (int i = 0; i < (int)uiseqLength; i++)
                {
                    Console.WriteLine("New SEQ[{0:D}] : [{1:D}].", i, new_seq[i]);
                }
            }
            
            static void Main(string[] args)
            {
                DoTest();
            }
        }
    }
    

    Please refer to the explanatory comments in the code above.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Sunday, October 28, 2012 5:42 AM
  • Hello, Lim Bio Liong!

    Recommendations that you described doesn't change my situation to the best.. 

    I got the same 

    error : AccessViolationException "Attempted to read or write protected memory. This is often an indication that other memory is corrupt"

    Why so happend? I can't understand...

    Sunday, October 28, 2012 11:51 AM
  • Hello dsnj,

    1. First please use my version of the C++ function MyMethod() (as presented in my last post) and the C# client code as references. You may want to build a special version of MyDll.dll (with my version of MyMethod()) for testing and learning purposes.

    1.1 Note that the C# codes I have presented are for testing and demonstrative purposes. My C# code will work with my version of MyMethod() but may not work with the actual MyMethod() in your MyDLL.dll.

    1.2 You need to study and understand the logic behind my recommendations and match them against your actual situations. Do not follow my C# code blindly.

    2. Next, I would like to clarify/confirm a few matters :

    2.1 MyMethod() is a global exported function, correct ? May I confirm that it is not a class method ?

    2.2 Do you have the source codes of MyMethod() ? If you do, then would I be correct to assume that you would be able to confirm the direction of parameter flow for the "materials", "filePath" and "seq" parameters ?

    2.3 If you do not have the source codes for MyMethod(), would you be able to inquire the parameter direction from the actual developers ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Sunday, October 28, 2012 2:24 PM
  • Hello dsnj,

    1. First please use my version of the C++ function MyMethod() (as presented in my last post) and the C# client code as references. You may want to build a special version of MyDll.dll (with my version of MyMethod()) for testing and learning purposes.

    1.1 Note that the C# codes I have presented are for testing and demonstrative purposes. My C# code will work with my version of MyMethod() but may not work with the actual MyMethod() in your MyDLL.dll.

    1.2 You need to study and understand the logic behind my recommendations and match them against your actual situations. Do not follow my C# code blindly.

    2. Next, I would like to clarify/confirm a few matters :

    2.1 MyMethod() is a global exported function, correct ? May I confirm that it is not a class method ?

    2.2 Do you have the source codes of MyMethod() ? If you do, then would I be correct to assume that you would be able to confirm the direction of parameter flow for the "materials", "filePath" and "seq" parameters ?

    2.3 If you do not have the source codes for MyMethod(), would you be able to inquire the parameter direction from the actual developers ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Hello, Bio!

    MyMethod() is not class method. This is a global exported function..

    I don't have source codes for MyMethod(), but I have a prototype of MyMethod() and  I provide its description in the beginning :

    int MyMethod(unsigned char* materials, 
                 unsigned int materialCount,            	   
                 unsigned int materialLength,            	 
                 char* filePath,
                 unsigned int shift,            	 
                 unsigned int length,             	 
                 unsigned char** seq,            	 
                 unsigned int* seqLength);

    Sunday, October 28, 2012 8:19 PM
  • Hello dsnj,

    1. First please use my version of the C++ function MyMethod() (as presented in my last post) and the C# client code as references. You may want to build a special version of MyDll.dll (with my version of MyMethod()) for testing and learning purposes.

    1.1 Note that the C# codes I have presented are for testing and demonstrative purposes. My C# code will work with my version of MyMethod() but may not work with the actual MyMethod() in your MyDLL.dll.

    1.2 You need to study and understand the logic behind my recommendations and match them against your actual situations. Do not follow my C# code blindly.

    2. Next, I would like to clarify/confirm a few matters :

    2.1 MyMethod() is a global exported function, correct ? May I confirm that it is not a class method ?

    2.2 Do you have the source codes of MyMethod() ? If you do, then would I be correct to assume that you would be able to confirm the direction of parameter flow for the "materials", "filePath" and "seq" parameters ?

    2.3 If you do not have the source codes for MyMethod(), would you be able to inquire the parameter direction from the actual developers ?

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Hello, Bio!

    MyMethod() is not class method. This is a global exported function..

    I don't have source codes for MyMethod(), but I have a prototype of MyMethod() and  I provide its description in the beginning :

    int MyMethod(unsigned char* materials,  unsigned int materialCount,            unsigned int materialLength,            char* filePath,           unsigned int shift,            unsigned int length,             unsigned char** seq,            unsigned int* seqLength);

    Sunday, October 28, 2012 11:46 PM
  • Hello dsnj,

    1. >> I don't have source codes for MyMethod()...

    1.1 You will have to inquire of the original developers or obtain proper documentation for MyMethod() especially on how to pass the important parameters "materials", "filePath" and "seq".

    1.2 The "seq" parameter is especially important because it is possible that it points to an array that is modifiable inside the MyMethod() function. If so, you need to know whether it is possible that this array is re-allocated. And if so, how it is re-allocated (e.g. via GlobalAlloc(), CoTaskMemAlloc(), etc). This is so that on the C# side you can determine how to delete away array memory if necessary.

    2. Basically, more knowledge is needed on how MyMethod() functions. Otherwise you will be left groping in the dark. Good luck.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Monday, October 29, 2012 4:28 AM