0
votes

I'm trying to call a native Windows API from managed C++/CLI. One of the arguments is a void**. The idea is that the function will allocate a memory structure and return a void pointer to the caller, which should be passed back to the API on the next call. So I need to allocate storage for a pointer on the managed side and pass a reference to the C API. I can't figure out how to do this.

I've tried declaring a void * in the caller and passing a reference via various operators: &, internal_ptr<>, pin_ptr<>. I did the same with an IntPtr. I get errors saying the compiler can't convert this to a void**.

Here's one attempt using IntPtr and pin_ptr. I get the following compile error on line 28 (the line that declares the pin_ptr):

E0144 a value of type "interior_ptr<System::IntPtr>" cannot be used to initialize an entity of type "cli::pin_ptr<void *>"

#include <msclr\marshal.h>
using namespace msclr::interop;
using namespace System;

namespace CLRStorage
{
    public ref class CompoundFile
    {
    private:
        String ^ pathname;
        IntPtr pRootStorage;
    public:
        CompoundFile CompoundFile::Create(String^ path)
        {
            STGOPTIONS stgOptions;
            stgOptions.usVersion = 1;
            stgOptions.reserved = 0;
            stgOptions.ulSectorSize = 4096;
            stgOptions.pwcsTemplateFile = NULL;

            auto cf = gcnew CompoundFile();
            cf->pathname = path;
            marshal_context^ context = gcnew marshal_context();
            pin_ptr<void*> ppRootStorage = &cf->pRootStorage;
            StgCreateStorageEx(
                context->marshal_as<WCHAR*>(path),
                STGM_READWRITE & STGM_CREATE,
                STGFMT_DOCFILE,
                0,
                &stgOptions,
                NULL,
                IID_IStorage,
                ppRootStorage);
        }
    };
}
1

1 Answers

0
votes

IntPtr can be converted to and from void*, but it isn't the same type.

Since the parameter is out-only, the simple solution is just to use a temporary:

void* pRootStorage;
StgCreateStorageEx(
            context->marshal_as<WCHAR*>(path),
            STGM_READWRITE & STGM_CREATE,
            STGFMT_DOCFILE,
            0,
            &stgOptions,
            NULL,
            IID_IStorage,
            &pRootStorage);
cf->pRootStorage = IntPtr(pRootStorage);

This will actually be a tiny bit faster as well, because no pinning is needed.

You also have a separate problem with bad member function syntax. You want

static CompoundFile^ Create(String^ path)

instead of

CompoundFile CompoundFile::Create(String^ path)

and don't forget to

return cf;

Then, marshal_context is not a ref class, so this line is wrong:

marshal_context^ context = gcnew marshal_context();

Instead use

marshal_context context;

and since it is not a pointer,

context.marshal_as<WCHAR*>(path)