views:

355

answers:

2

Hi,

I am working on a C# app that is trying to use functionality provided via a C++ DLL. I am having a bit of a hard time getting the DLLImport definitions to work right at the moment.

Here's the C++ side of the equation:

struct Result
{
    FLOAT   first;
    FLOAT   second;
};

struct ResultData
{
    UINT            uint1;
    UINT            uint2;
    Result          result;
    Result*         pResults;
};

#define DllImport __declspec(dllimport)    
extern "C"
{
    DllImport HRESULT APIENTRY Process(const PCWSTR FileName, const PCWSTR logfileFileName, const PCWSTR DataFileName, ResultData** ppResults);
    DllImport HRESULT APIENTRY Release(ResultData* pResults);
}

On the C# side, here's what I have done so far:

    [StructLayout(LayoutKind.Sequential)]
    public struct Result
    {
        public float first;
        public float second;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ResultData
    {
        public uint uint1;
        public uint uint2;
        public Result result;
        public Result[] Results;
    }    

        DllImport("MyDLL.dll")]
        static extern uint Release(ResultData pResults);

        [DllImport("MyDLL.dll")]
        static extern uint Process(string FileName, string logfileFileName, string DataFileName, ref ResultData ppResults);

Is this the correct way to do it? The thing I am most concerned about is that pResults member of the ResultData structure. I don't want that copied by value as it will be a large amount of data and I don't want to replicate memory... how can I make sure that doesn't happen?

I appreciate your help.

+3  A: 

The most immediate problem that jumps out at me is the Results member in ResultData. The native type is a Result* but you've translated it as an array. This may or may not work (can't remember off the top of my head). What will work though is marshalling it as an IntPtr type.

[StructLayout(LayoutKind.Sequential)]
public struct ResultData
{
    public uint uint1;
    public uint uint2;
    public Result result;
    public IntPtr RawResults;
    public Result Results { get { return (Result)Marshal.PtrToStructure(RawResults,typeof(Result)); }
}

This is assuming that it's a single value. If it's more than one value a more complex marshalling will be needed.

Also the native Release method takes a ResultData* but you have a simple by value ResultData type in the managed signature. It needs to have the same level of indirection. You can achieve this by making it a ref param.

DllImport("MyDLL.dll")]
static extern uint Release(ref ResultData pResults);
JaredPar
Hummm, single value == one value, afaict. You missed 'more than' in the last sentence I presume
Vinko Vrsalovic
@Vinko, correct. It's amazing how well it reads to yourself until someone points out the obvious inconsistency :)
JaredPar
A: 

If you can keep the interface to pure C primitive types, you won't have to do any marshalling. This will save you a lot of heartache.

Dustin Getz