Reputation: 149
I am having C++ .net wrapper for an unmanged MFC dll. This wrapper is used by a vb.net dll, called into my c# code. During runtime sometimes the wrapper throws an exception of Attempted to read or write protected memory.
System.AccessViolationException: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt
It seems to occur randomly in my "While" loop. Sometimes it throws at the beginning, sometimes in the middle and sometimes nothing is thrown.
HOW IT WORKS: my program need a MFC dll. I have a wrapper.dll(c++) and a myVbDll.dll(vb.net) referred into my program. i also added the MFC dll as content because it's not a valid COM component. So this is how it works:
myProgramm.exe->myVbDll.dll->wrapper.dll->myMFC.dll->myMFCfunction
INFO : If I set field = "WHATSOEVER";
just before to call MyWrappedFunction, the error is never thrown!!
UPDATE : After several changes, the problem still occurs. I look at this time converting unicode string to Ansi. There might be something to find ... Cause when you write text in a string as above, it works, but when the ToString function is used, it does not work.
Can some body tell why this occurs.
Part of my program in c#(reading 5000 lines from .csv file with TextFieldParser to get field) :
string[] fields;
string field ;
string temp = "";
TextFieldParser parser = new TextFieldParser(textbox_csv.Text, System.Text.Encoding.UTF8);
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(";");
while (!parser.EndOfData)
{
fields = parser.ReadFields();
field = fields[0];
temp = field.ToUpper();
field = myVbDll.MyWrappedFunction(ref temp, false);
}
Part of VB.net Dll, called by my c# program :
Public Class myVbDll
Public Declare Auto Function MyWrappedFunction Lib "myWrapper.dll" (ByVal name As String, ByVal opt As Boolean) As String
End Class
part of MFC wrapper, called by VB.net Dll (error is not into MFC dll for sure):
typedef void (*MYFUNCTION)(CString&, CString&, BYTE);
MYFUNCTION Myfunction;
LPWSTR _stdcall MyWrappedFunction(LPWSTR ValInput, BYTE opt)
{
HINSTANCE gLibtestDLL=NULL;
CString S_ValInput(ValInput);
CString S_resultat;
gLibtestDLL = AfxLoadLibrary(TEXT(".\\test.dll"));
if(gLibtestDLL == NULL)
{
MessageBox(NULL, TEXT("unable to load test.DLL"), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
return NULL;
}
Myfunction = (MYFUNCTION)GetProcAddress(gLibtestDLL, "Myfunction");
if (Myfunction == NULL)
{
MessageBox(NULL, TEXT("Can't find Myfunction."), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
return NULL;
}
//******************************************************************
S_resultat.LockBuffer();
S_resultat.Format("%64c", ' ');
Myfunction(S_ValInput , S_resultat , opt);
S_resultat.ReleaseBuffer();
S_resultat.LockBuffer();
S_resultat.TrimRight();
S_resultat.ReleaseBuffer();
// CString To UNICODE
USES_CONVERSION;
S_resultat.LockBuffer();
LPWSTR C_tmp= A2OLE(S_resultat.GetBuffer(S_resultat.GetLength()));
S_resultat.ReleaseBuffer();
AfxFreeLibrary(gLibtestDLL);
LPWSTR C_resultat=C_tmp;
//******************************************************************
return C_resultat;
}
Upvotes: 0
Views: 7550
Reputation: 149
Finally, I got rid of the problem!
The real reason of the problem was the encoding string. Here are all the changes I made to improve my code.
If you see suspicious things I do that could be improved, feel free to comment
my MainApplication:
private void processImport()
{
string[] fields;
string field ;
string temp = "";
byte[] tempByte;
TextFieldParser parser = new TextFieldParser(textbox_csv.Text, System.Text.Encoding.UTF8);
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(";");
while (!parser.EndOfData)
{
fields = parser.ReadFields();
field = fields[0];
temp = RemoveDiacritics(field).ToUpper();
//this line is very important. It seems to change the Encoding of my string to Unicode.
temp = temp.Normalize(NormalizationForm.FormC);
field = myVbDll.MyWrappedFunction(temp, false);
}
parser.Close();
}
//Transforms the culture of a letter to its equivalent representation in the 0-127 ascii table, such as the letter 'é' is substituted by an 'e'
public string RemoveDiacritics(string s)
{
string normalizedString = null;
StringBuilder stringBuilder = new StringBuilder();
normalizedString = s.Normalize(NormalizationForm.FormD);
int i = 0;
char c = '\0';
for (i = 0; i <= normalizedString.Length - 1; i++)
{
c = normalizedString[i];
if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().ToLower();
}
Part of VB.net Dll, called by my c# program :
Public Class myVbDll
<DllImport("Wrapper.dll", CharSet:=CharSet.Unicode)> Public Shared Function MyWrappedFunction (ByVal nom As String, ByVal opt As Boolean) As String
End Function
End Class
part of C++ wrapper, called by VB.net Dll :
typedef void (*MYFUNCTION)(CString&, CString&, BYTE);
MYFUNCTION Myfunction;
LPWSTR _stdcall MyWrappedFunction(LPWSTR ValInput, BYTE opt)
{
HINSTANCE gLibtestDLL=NULL;
CString S_ValInput(ValInput);
CString S_resultat;
gLibtestDLL = AfxLoadLibrary(TEXT(".\\test.dll"));
if(gLibtestDLL == NULL)
{
MessageBox(NULL, TEXT("unable to load test.DLL"), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
return NULL;
}
Myfunction = (MYFUNCTION)GetProcAddress(gLibtestDLL, "Myfunction");
if (Myfunction == NULL)
{
MessageBox(NULL, TEXT("Can't find Myfunction."), TEXT("Error"),MB_OK | MB_ICONINFORMATION);
return NULL;
}
//******************************************************************
S_resultat.Format("%64c", ' ');
Myfunction(S_ValInput , S_resultat , opt); //Run MFC function
S_resultat.TrimRight();
S_resultat.ReleaseBuffer();
char* tmp = (char*) CoTaskMemAlloc((S_resultat.GetLength()*2)+1);
memset(tmp,0,sizeof(tmp));
strcpy_s(tmp, (S_resultat.GetLength()+1), S_resultat.GetBuffer(S_resultat.GetLength()));
S_resultat.ReleaseBuffer();
wchar_t wtext[1024];
memset(wtext,0,sizeof(wtext));
mbstowcs(wtext, tmp, strlen(tmp)+1);//Plus \0
LPWSTR resultat = wtext;
AfxFreeLibrary(gLibtestDLL);
return resultat;
}
Upvotes: 1
Reputation: 942099
LPWSTR C_tmp= A2OLE(S_resultat.GetBuffer(S_resultat.GetLength()));
This is a very serious bug in your C++ code. You are returning the address of a local variable. A buffer created by A2OLE(). That invokes undefined behavior in C++ code. This tends to work by accident when you call this function from C++ code, you have some odds that there won't be another function call that overwrites the stack address where that local variable used to be. Those odds turn to zero when you pinvoke the function, the stack address gets obliterated by the pinvoke marshaller function calls. If that doesn't itself cause a crash then the pinvoke marshaller will make sure it crashes when it tries to release the string with CoTaskMemFree().
You will have to fix your C++ code first. And no, just copying the pointer into C_resultat is not a fix.
Do note that there isn't much point in trying to rescue this function. It doesn't do anything that you can't do in C#. Just write a [DllImport] attribute for that "MyFunction" function.
Upvotes: 2