0

Hello I'm wrapping C++ library with C#. Next function in C++:

    SCREENCAPTUREDLL_API wchar_t** getAudioDeviceList(int* listSize) {
        static std::vector<wchar_t*> descriptionList;
        AudioCaptureList::getInstance().Update();
        AudioCaptureList::getInstance().getList(&descriptionList);

        *listSize = descriptionList.size();
        return &descriptionList[0];
    }

Wrapping with next C# code:

    [DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
    private static extern IntPtr getAudioDeviceList(ref int arrayCount);

    public static string[] GetAudioDeviceList()
    {
        IntPtr outputStr;
        int length = 0;

        outputStr = getAudioDeviceList(ref length);
        string[] resultArray = new string[length];
        for (int j = 0; j < length; j++)
        {
            resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
        }

        return resultArray;
    }

That works perfect, exactly as I expected, but I was about to change the way I returning value from function itself to variable by reference, so I changing my code to:

C++

    SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** list, int* listSize) {
        static std::vector<wchar_t*> descriptionList;
        AudioCaptureList::getInstance().Update();
        AudioCaptureList::getInstance().getList(&descriptionList);

        *listSize = descriptionList.size();
        list = &descriptionList[0];
    }

C#

    [DllImport(screenCaptureDLLPath, CallingConvention = callConversion)]
    private static extern void getAudioDeviceList(out IntPtr listRef, ref int arrayCount);

    public static string[] GetAudioDeviceList()
    {
        IntPtr outputStr;
        int length = 0;

        getAudioDeviceList(out outputStr, ref length);
        string[] resultArray = new string[length];
        for (int j = 0; j < length; j++)
        {
            resultArray[j] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(outputStr, 4 * j));
        }

        return resultArray;
    }

But I got error, returned memory address is zero. What is the problem here? Please help me understood what cause the problem and how to fix that, thanks!

Liastre
  • 1,281
  • 11
  • 29

1 Answers1

1

Why doesn't Pinvoke work? Because you are trying to interpret a pointer to a string as a pointer to a set of strings. But there is nothing wrong with PInvoke - it happens because there is actually a problem with new function signature and its internal code.

See:

SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize);

can't provide the same data like

DLL_API wchar_t** getAudioDeviceList(int* listSize)

Because original definition basically returned pointer to a set of pointers to strings(C style strings, I mean), while wchar_t** listRef can only allow to return a single pointer to a string.

SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t** listRef, int* listSize)
{
     ...
     *listRef = "string";

I don't know what is going inside new version of the function(you didn't show the code), but listRef = &descriptionList[0]; will compile though won't do anything, and even if *listRef = &descriptionList[0]; somehow compiles it won't contain what you want.

So function signature should containt triple pointer to allow return of a set of strings.

SCREENCAPTUREDLL_API void getAudioDeviceList(wchar_t*** listRef, int* listSize)
{
    ...
    *listRef = &descriptionList[0];
}

Then your PInvoke would work correctly because it will have the same pointer to a set of string pointers.

Community
  • 1
  • 1
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
  • @Liastre If this answer helped you to solve the problem, then you can [mark it as accepted](http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). – Eugene Podskal Jan 12 '17 at 20:25