Heyjee
Heyjee

Reputation: 149

C++ .NET Wrapper: Attempted to read or write protected memory. This is often an indication that other memory is corrupt

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

Answers (2)

Heyjee
Heyjee

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

Hans Passant
Hans Passant

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

Related Questions