C#.NET. How Do I get values of an array of longs in a C++ DLL?

Hello

Im developing a C#.NET application using Visual Studio 2008.

Im having trouble accessing data in a C++ DLL within my solution.

The C++ DLL is called myDLL.dll.  Ive defined this in my C#.NET code thus;

[DllImport(@"myDLL.dll")]
static extern int GetArray(UInt32[] pBuffer, int Size);

In the C++ source the code is like;

int GetArray(unsigned long* pBuffer, long BufferSz)
{
     int result = 0;
     //  
    // getValues in a manufacturer DLL. It returns a pointer to "unsigned long*"
    pBuffer = GetValues();

     return(result);
}

In my C++ DLL the GetArray function is exported thus;

__declspec(dllexport) int GetArray(unsigned long* pBuffer, long BufferSz);



I now try and access the values from my C#.NET code. ie.

int size = 1000;
UInt32[] pBuf = new UInt32[size];
int = GetArray(pBuf, size);

However I get an exception when I call GetArray. It is
"Attempted to read or write protected memory. This is often an indication that other memory is corrupt"

Can anyone spot why this may not be working? Note we can't put a breakpoint in this particular DLL for some reason. We've spent ages trying to fix this but it seems its a known bug in Visual Studio.
PingPhotonicsAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

käµfm³d 👽Commented:
Have you tried marking pBuffer as a "ref" param and passing as such?

[DllImport(@"myDLL.dll")]
static extern int GetArray(ref UInt32[] pBuffer, int Size);

Open in new window

int size = 1000;
UInt32[] pBuf = new UInt32[size];
int = GetArray(ref pBuf, size);

Open in new window

EDDYKTCommented:
may be use out instead of ref since c++ alloc memory
PingPhotonicsAuthor Commented:
Hi thanks for the suggestion.
I tried it and when executing the GetArray function the debugger shows pBuf as containing uint[0].
ie. an array with one element set to zero.

Thats not what Im expecting. Im hoping to see  an array of 1000 elements.

Rowby Goren Makes an Impact on Screen and Online

Learn about longtime user Rowby Goren and his great contributions to the site. We explore his method for posing questions that are likely to yield a solution, and take a look at how his career transformed from a Hollywood writer to a website entrepreneur.

jkrCommented:
IMO that shoule be
int GetArray(unsigned long* pBuffer, long BufferSz)
{
     int result = 0;
     //  
    // getValues in a manufacturer DLL. It returns a pointer to "unsigned long*"
    memcpy(pBuffer,GetValues(), actual_number_of_values * sizeof(unsigned long));

     return(result);
}

Open in new window

käµfm³d 👽Commented:
@EDDYKT
may be use out instead of ref since c++ alloc memory
That shouldn't matter. The difference between "out" and "ref" is that when "ref" is used, the variable would be required to be initialized prior to passing into the function, which according to the above, it is. I concede that you could remove the initialization and then use "out" instead to save a small bit of processing (i.e. the managed initialization of the variable prior to the call).
käµfm³d 👽Commented:
I'll ask the obvious question:  are you sure the C++ is allocating the appropriate number of elements?

Just trying to cover all the bases  : )

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
PingPhotonicsAuthor Commented:
kaufmed
I suspect you may be correct. Im investigating.
Todd GerbertIT ConsultantCommented:
Okay, looks like you've got a couple things going on here.

1. You didn't specify a calling convention in your C++ code so it defaults to __cdecl; and you also didn't specify a calling convention in the DllImport attribute in your C# code and so it defaults to __stdcall.  These can't be mis-matched, either declare your C++ function as int __stdcall GetArray(unsigned long* pBuff, long cBuffLen) or change your C# DllImport to [DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int GetArray(uint[] pBuff, int cBuff)



2. That C++ function doesn't really make sense.  Since there's no bounds checking in C++ normally the only way to know how many elements are in an array is by specifying with a separate parameter how many there are, but that doesn't seem to be the case with your GetValues() function - does it return a known number of elements?  I would expect something more like:
[DllImport("MyDll.dll")]
private static extern int GetArray(uint[] pBuffer, int cBufferLen);

static void Main(string[] args)
{
	uint[] buff = new uint[4];

	int result = GetArray(buff, buff.Length);
}

//////////////////////////////////////////////////////////////////////////////
unsigned long* GetValues();

int WINAPI GetArray(unsigned long* pBuffer, long cBufferLen)
{
	// Note that C++ has no way to know how big the array
	// passed in from C# is (or passed in from anywhere, really)
	// so you have to depend on cBufferLen to know how
	// big the array is

	// For the sake of this example I'm going to ASSUME that
	// GetValues() always returns a pointer to four unsigned long's
	if (cBufferLen < 4)
		return FALSE;
	
	// Get the pointer to the values
	unsigned long* pValues = GetValues();
	
	// Copy the block of memory pointed to by pValues
	// to the array passed in from c#
	memcpy(pBuffer, pValues, sizeof(unsigned long) * 4);

	// Free the pValues memory - this might be
	// a call from the mfgr's DLL, like FreeValuesBuffer(),
	// or just a plain-old free()
	free(pValues);

	// Return number of ulongs copied
	return 4;
}

unsigned long* GetValues()
{
	unsigned long *pRetVal = (unsigned long*)calloc(4, sizeof(unsigned long));

	pRetVal[0] = 13;
	pRetVal[1] = 26;
	pRetVal[2] = 52;
	pRetVal[3] = 104;

	return pRetVal;
}

Open in new window



There's other stuff, but before diving into that let's see if we can't get some detailed documentation on GetValues()?
Todd GerbertIT ConsultantCommented:
Quickly here's another example of how this code might work.  Really you need to find out from the vendor how GetValues() is supposed to behave and post that information here, without that we're all just shooting in the dark.

class Program
{
	[DllImport("MyDll.dll")]
	private static extern int GetArray(ref IntPtr pBuffer, ref int cBufferLen);// Note the ref keywords
	[DllImport("Kernel32.dll")]
	private static extern bool HeapFree(IntPtr hHeap, int dwFlags, IntPtr lpBuff);
	[DllImport("Kernel32.dll")]
	private static extern IntPtr GetProcessHeap();

	static void Main(string[] args)
	{
		IntPtr buff = IntPtr.Zero;

		int cbLen = 0;
		int result = GetArray(ref buff, ref cbLen);

		// After GetArray returns cbLen will tell us how big our int[] array needs to be
		int[] values = new int[cbLen];

		// Copy the memory pointed to by buff into the array "values"
		Marshal.Copy(buff, values, 0, values.Length);

		// Free the memory that was allocated by MyDll.dll
		bool success = HeapFree(GetProcessHeap(), 0, buff);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Note the double-indirection on pBuffer, it's a pointer-to-a-pointer-to-a-ulong
// And pcBufferLen is a pointer to a long (C++ doesn't have a "ref" keyword
// so the same effect is achieved with pointers and pointers-to-pointers)
int WINAPI GetArray(unsigned long** pBuffer, long* pcBufferLen)
{
	// Note that C++ has no way to know how big the array
	// passed in from C# is (or passed in from anywhere, really)
	// so you have to depend on cBufferLen to know how
	// big the array is

	
	// Get the pointer to the values
	unsigned long* pValues = GetValues();
	
	// Copy the pointer
	*pBuffer = GetValues();
	*pcBufferLen = 4; // Indicate how many elements were copied

	// Return success without freeing the memory
	// the C# application will be responsible for freeing it
	return TRUE;
}

unsigned long* GetValues()
{
	// Allocate memory on the process heap
	unsigned long *pRetVal = (unsigned long*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(unsigned long) * 4);

	pRetVal[0] = 13;
	pRetVal[1] = 26;
	pRetVal[2] = 52;
	pRetVal[3] = 104;

	return pRetVal;
}

Open in new window

jkrCommented:
I thought I pointed to 'memcpy()' earler, didn't I?
Todd GerbertIT ConsultantCommented:
Yup, but I hadn't noticed any non-C# responses as I scanned the question earlier, to be honest I just missed your comment.

At any rate, no one had yet to mention the calling convention issue, plus my comment slightly elaborated on yours (or at least attempts to do so) - so not quite a duplicate.
käµfm³d 👽Commented:
Curses!    I was going to mention calling convention, but I thought that's what "__declspec" was doing...   too much managed code for me, I guess  ; )
PingPhotonicsAuthor Commented:
Hi
Thanks for the responses.

I tried changing the C# DLLImport definition to

 [DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)] private static extern int GetArray(uint[] pBuff, int cBuff)

This didn't affect the result. Im now trying to get information from the vendors to tell me what the GetValues() function is doing. I'll report back when I hear from them.
PingPhotonicsAuthor Commented:
Hi
Okay I managed to fix this. Due to poor documentation I had failed to set a parameter which caused the camera capture to fail and therefore the pointer didn't point to anything.
 Im not sure how to award points in this case so Im just going to award points to several of the answers which were generally useful and were relevant to the code I posted.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.