Pankaj
Pankaj

Reputation: 234

Passing list of struct to C++ DLL from C# Application

I am trying to pass struct as a parameter from C# application to the C++ MFC DLL. DLL fills records in the struct object and return back to the C# application. So here I used "out" keyword in C# application to call C++ method. When I execute it, it fails with error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." I am allocating memory at C++ DLL to store the records and assign it to out parameter. I could send data in struct object and print it in C++ DLL but not able to do modification in struct object at DLL side and return back to C# application.

Could anybody please help on this.

NativeDLLHelper.cs

class NativeDLLHelper
    {
        const int FIELD_LENGTH = 255;

        [StructLayout(LayoutKind.Sequential)]
        public struct APPDATA
        {
            public int ID;
            public int Version;
            [MarshalAs(UnmanagedType.LPTStr, SizeConst = FIELD_LENGTH)]
            public string AppName;
        };

        [DllImport("Parser.dll", EntryPoint = "?FillLstStructData@DLLWrapper@@QAEXAAPAU_APPDATA@1@@Z")]
        public static extern void FillLstStructData(out APPDATA[] AppDataStruct);
}

Main.cs

NativeDLLHelper.APPDATA[] lstFillAppDataStruct;
NativeDLLHelper.FillLstStructData(out lstFillAppDataStruct);
for (int i = 0; i < lstFillAppDataStruct.Length; i++)
{
    Console.WriteLine(" ID[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].ID);
    Console.WriteLine(" Version[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].Version);
    Console.WriteLine(" AppName[{0:G}]:{1:G}", i, lstFillAppDataStruct[i].AppName);
}

C++ DLL:

static const int FIELD_LENGTH = 128;
typedef struct _APPDATA
{
    int ID;
    double Version;
    TCHAR AppName[FIELD_LENGTH];
}APPDATA;

extern "C" __declspec(dllexport) FillLstStructData(APPDATA* &pAppData)
{
    APPDATA *localAppDataStruct = new APPDATA[2];

    localAppDataStruct[0].ID = 1;
    localAppDataStruct[0].Version = 1.1;
    _tcscpy(localAppDataStruct[0].AppName, L"MS Visual Studio 2010");

    localAppDataStruct[1].ID = 2;
    localAppDataStruct[1].Version = 2.1;
    _tcscpy(localAppDataStruct[1].AppName, L"MS Office 2010");

    pAppData = localAppDataStruct;
}

Upvotes: 2

Views: 1483

Answers (1)

Hans Passant
Hans Passant

Reputation: 941277

Your exported function unmangles to:

  void __thiscall DLLWrapper::FillLstStructData(struct DLLWrapper::_APPDATA * &)

That makes it an instance method of a C++ class. You cannot pinvoke such methods, they require the C++ object to be created first and you cannot do this reliably with pinvoke. Only static C++ member functions can be pinvoked. Do note that this function doesn't need to be an instance method at all, it doesn't actually use instance members of the class.

That isn't the only problem, there's also a nasty memory management issue. The function allocates memory with ::operator new and memory needs to be released by the caller. But a C# program cannot do this, it doesn't have access to the ::operator delete that the C++ code uses. This kind of function is very hard to use reliably from a C++ program for that reason, it doesn't get better when you do it from C#. The proper way to do it is to allow the caller to pass the array. In other words, APPDATA[] and an extra argument that says how long the passed array is.

If you can't rewrite this function then you must write a wrapper for this class using the C++/CLI language. Very important that this wrapper uses the exact same compiler and CRT version as the C++ DLL.

Upvotes: 2

Related Questions