Click to See Complete Forum and Search --> : gcnew, interop, and OutOfMemoryException


TFobear
August 4th, 2010, 01:00 PM
Hello all,

I am getting a System.OutOfMemoryException in my client program after I make a call to a managed dll wrapper (the dll is mixed mode c++/cli), the client is VB.Net)

The offending dll function is:



String^ Toolkit::Twain::NextSourceName()
{
return gcnew String(::TWAIN_NextSourceName());
}



TWAIN_NextSourceName() returns a LPCSTR

It is not throwing the exception after this function is called, rather at the end of Form1's constructor (sub New)

MSDN says OutOfMemoryException can occur when .newobj or .newarr (is generated by gcnew)
Task Managed shows memory usage for the client is fairly low, when NextSourceName is called there is no spike in memory usage..

commenting out Toolkit.Twain.NextSourceName allows the program to run fine.

One last thing, when there is no more TWAIN sources to be found TWAIN_NextSourceName returns an empty null terminated string, so when no more sources are found it calls gcnew String(""); I dont see that being a problem though

*Edit*

There is another function that might be problematic:


const char * SystemStringToCSTR(System::String^ sz)
{
IntPtr hglobal = Marshal::StringToHGlobalAnsi(sz);
const char * ret = (char*)hglobal.ToPointer();
Marshal::FreeHGlobal(hglobal);
return ret;
}

Boolean Toolkit::Twain::GetDefaultSourceName(System::String ^pzName)
{
return (0 == ::TWAIN_GetDefaultSourceName((LPSTR)SystemStringToCSTR(pzName)) ? false : true);
}


The overall goal for me is to be able to reference native global functions TWAIN_NextSourceName and TWAIN_GetDefaultSourceName in a managed client.

my questions are:

1.) Do I want to use char * pzName as a parameter for GetDefaultSourceName and pass it to native TWAIN_GetDefaultSourceName using pin_ptr<char>?

2.) if I use char * pzName as a parameter when I call this function from VB.Net will it map to System::String?

3.) if above case is true should I change the previous functions signature to char * NextSourceName();?

TFobear
August 4th, 2010, 03:57 PM
Im beginning to talk to myself, but thats ok hopefully someone can use this information.

The problem is definately the GetDefaultSourceName function. I have updated it so it gets a string from the native function, but I am having trouble exporting the value from the managed function now. I need a way to copy the data from the array<LPSTR> to my String^ parameter:


bool Toolkit::Twain::GetDefaultSourceName(String^ pzName)
{
array<LPSTR>^ name = gcnew array<LPSTR>(255);
pin_ptr<LPSTR> temp = &name[0];
BOOL b = TWAIN_GetDefaultSourceName((LPSTR)temp);
pzName = gcnew String((LPSTR)temp); // does not copy temp to pzName, I need temp copied into pzName
return ( 0 == b ? false : true );
}


temp gets filled out with the name of my scanner, and when I pass it to pzName it shows the string. But in the client it shows the string i pass = Nothing. This tells me the String constructor did not copy the characters to pzName.

Is there a good way to copy the char array to pzName?

Alex F
August 5th, 2010, 08:14 AM
String class is immutable. This means, you cannot change its value one it is created. String^ pzName parameter may be used only is input parameter. Setting new value to this parameter has only local effect.

pzName = gcnew String((LPSTR)temp);

This actually means: pzName is reference to the string passed by function caller. You replace it with new string reference, original string is not changed.

The simplest way to fix this is to use String^ as return value. Return nullptr instead of false. If you want output string parameter, use tracking reference: String^%, which is equal to ref parameter in C#, and ByRef in VB. For String^% parameter, setting new value to pzName is visible to caller.

TFobear
August 5th, 2010, 08:17 AM
Thanks again alex! so ^% is equal to & in a c++ declaration?

Alex F
August 5th, 2010, 08:38 AM
I think about this in terms of pointers. Passing ^ parameter is like * in C/C++. Referenced object is passed by reference, but reference itself is passed by value. This is why reassinging the reference is not visible to a caller.
^% (ByRef, ref) is the same as ** in C/C++. In this case you can change both referenced object (not for String, which is immutable), and also change the reference itself. Reference is passed by reference.
You can test it first in VB: compare function behavior with "s as String" and "ByRef s as String" parameters.

TFobear
August 5th, 2010, 10:05 AM
I think I am getting the hang of memory in c++/cli. So this native function:


int WriteToBuffer(HDIB hdib, int nFormat, BYTE* pBuffer, int nbMax);


can be wrapped like this:


int Toolkit::Dib::WriteToBuffer(System::IntPtr hdib, int nFormat, array<Byte>^% pBuffer, int nbMax)
{
pin_ptr<Byte> p = &pBuffer[0];
return ::WriteToBuffer((HDIB)hdib.ToPointer(), nFormat, (BYTE*)p, nbMax);
}


and memcpy can be used to convert a String^ to a char* like this:


const char * SystemStringToCSTR(System::String^ sz)
{
char ret [255];
IntPtr hglobal;

hglobal = Marshal::StringToHGlobalAnsi(sz);
memcpy_s(ret, 255, (char*)hglobal.ToPointer(), strlen((char*)hglobal.ToPointer()));
Marshal::FreeHGlobal(hglobal);

return ret;
}


(*Note*)
the above function does not work, because ret is an adress of memory that does not last beyond this function. To surcumvent this for the time being I made "char ret" into "static char ret", however this has an issue. If you were to call this function twice in the same callee, it will overright itself:


char * one = SystemStringToCSTR(first);
char * two = SystemStringToCSTR(last);


one now is overwritten. For my case it (sort of) works because I am only calling it once per function so far. however, this is not very scalable.

mani3355
August 12th, 2010, 01:28 AM
hi,

sorry I am not able to answer it because I am new member, and I need to suggestions



I hope to soon reply and thanks in advance.


regards,
phe9oxis,
http://www.guidebuddha.com