0

Problem:

I have a test application that is looking for a scanner attached to a PC. The European manufacturer supplies an SDK written in C++ and provides some header files and other information. Their C++ API populates a structure as shown below. My concern is de-referencing (Marshaling) the returned char** inside of that structure into a set of strings.

//
//The struct, as per the manufacturer's C++ header file:
//
typedef struct {
    int count;
    char** serialNumbers;
} ScannerList;

//
// My interpretation of the struct
//
struct ScannerList
{
    public int count;
    public IntPtr serialNumbers;
}

//
// C++ API Method Signature:
//
bool SCN_GetScannerList( ScannerList** scannerList );

//
// My DLLImport:
//
[DllImport(@"C:\ScannerSDK\Scanner.dll",  CharSet = CharSet.Unicode)]
public static extern bool SCN_GetScannerList(out IntPtr scannerList);

Inside my C# wrapper class, I have several classes that wrap each specific set of Scanner API calls. For this method, I'm using the ScannerNative class that the DllImport resides in, along with the struct. This is the method charged with summoning the list:

private object GetScanlistFromDLL()
{
    // our managed code return object - class instead of struct
    ScannerNative.ScannerListSafe safeList = new ScannerNative.ScannerListSafe(); 

    // IntPtr to give to the API
    IntPtr ptr = new IntPtr();

    // unmanaged code struct
    ScannerNative.ScannerList scanList = new ScannerNative.ScannerList();

    // Call the API with our IntPtr
    bool success = ScannerNative.GetScannerList(out ptr);

    if(success)
    {
        // Map the pointer to the type of structure from the API call
        scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));

        // copy the unsafe struct members to our safe class
        safeList.count = scanList.count;

        //Problem - the marshaling returns a string full of garbage
        //likely because char** is an array of char*  ...
        //Not sure how to Marshal a pointer to a pointer to a string :-\
        safeList.serialNumbers = Marshal.PtrToStringUni(scanList.serialNumbers);
    }
    else
    {
        return null;
    }

    //
    // API call to release unmanaged scanner list ..
    //

    return safeList;
}

Results:

I do get what appears to be a valid structure back from the API call

bool success = ScannerNative.GetScannerList(out ptr);

The subsequent casting in the if(success) block:

    if(success)
    {
        // Map the pointer to the type of structure from the API call
        scanList = (ScannerNative.ScannerList)Marshal.PtrToStructure(ptr, typeof(ScannerNative.ScannerList));

This works all the way until we get to our IntPtr stored in the scanList.serialNumbers member, which is holding a reference to the char** from the API struct.

scanList.count shows a correct value of '1' (when the scanner is attached) but I'm not able to de-ref the char** held in the scanList.serialNumbers member.

I've tried attacking this in a couple ways, but nothing is coming out and either I'm using the wrong search engines, or trying to find "c# DllImport Char** Marshal" and variants is asking the wrong question.

Community
  • 1
  • 1
Redgum
  • 388
  • 5
  • 14
  • I would expect `char**` to be marshalled into a `string[]` based on [this](https://msdn.microsoft.com/en-us/library/hk9wyw21(v=vs.110).aspx) – rene Dec 31 '15 at 05:19
  • 1
    You need Marshal.ReadIntPtr() to read the content of the array of strings, *count* times. Your *safeList* needs to be a `List`. – Hans Passant Dec 31 '15 at 08:03
  • @Hans - That solved the issue. Once I passed scanList.serialNumbers into Marshal.ReadIntPtr, the IntPtr result was marshaled correctly into a string when put through Marshal.PtrToStringAnsi(). – Redgum Dec 31 '15 at 18:55

0 Answers0