views:

291

answers:

1

I've been trying to figure out for nearly a week now how to call StgCreateStorageEx in C#, and all I've got to show for it is an AccessViolationException and a headache.

I'm not even sure how I should declare the function. I've seen a zillion websites declare it a zillion different ways. But this is what I currently have:

[DllImport("ole32.dll")]
        public unsafe static extern UInt32 StgCreateStorageEx([MarshalAs(UnmanagedType.LPWStr)] string
           pwcsName, long grfMode, STGFMT stgfmt, uint grfAttrs, IntPtr pStgOptions, IntPtr reserved2, [In] Guid riid, void** ppObjectOpen)

I've seen sites tell me I need to use MarshalAs on the last parameter, and I've tried using IUnknown, Interface, LPStruct, and several others. The only thing that got me was the marshaller griping about data types. I get the feeling that is probably related, but since it only tells me what it doesn't want, I have no idea what it does wants.

On to how I call the function: This started out much simpler. But after getting the memory violations I started reading and finally came to the conclusion that the only way to be sure the GC wasn't screwing me over was to manage the memory myself, thus you have this mess:

IntPtr ptr2ptr2ptr = Marshal.AllocHGlobal(IntPtr.Size);
IntPtr ptr2ptr = Marshal.AllocHGlobal(IntPtr.Size);
IntPtr ptr2data = Marshal.AllocHGlobal(104857600); // pretty sure that's enough for whatever StgCreateStorageEx wants to do.
Marshal.WriteIntPtr(ptr2ptr,ptr2data);
Marshal.WriteIntPtr(ptr2ptr2ptr, ptr2ptr);
Guid IID_IStorage = new Guid("0000000B-0000-0000-C000-000000000046");
UInt32 results;

results = NativeMethods.StgCreateStorageEx(null, NativeMethods.STGM_READWRITE + NativeMethods.STGM_SHARE_EXCLUSIVE,
                  NativeMethods.STGFMT.STGFMT_STORAGE, 0, IntPtr.Zero, IntPtr.Zero, IID_IStorage, (void**)Marshal.ReadIntPtr(ptr2ptr2ptr).ToPointer());

Anybody got any ideas on how to straighten this mess out and make it actually work?

By the way, the ultimate goal here is to be able to drag a folder from a TreeView into Windows Explorer and have it write that folder and all of its contents to wherever it dropped. The catch is the TreeView represents a file system stored entirely in a database. The only way I could find to do that was to pass an IStorage, hence the need for a call to StgCreateStorageEx.

+1  A: 

From StgCreateStorage's signature and documentation, I don't think you're supposed to be creating a buffer like that for it, you just need a reference to a pointer so that StgCreateStorage can set the value to the created object.

(void**) can be interperated as ref object where object == (void*)

I think the key problem here that is causing the access violation is that riid is REFID which is a typedef for (IID*) and thus should be ref Guid, the

pinvoke should be able to handle this using:


        [DllImport("ole32.dll")]
        public static extern UInt32 StgCreateStorageEx(
            [MarshalAs(UnmanagedType.LPWStr), In] string pwcsName,
            int grfMode,
            int stgfmt,
            uint grfAttrs,
            [In] IntPtr pStgOptions,
            [In] IntPtr reserved2,
            [In] ref Guid riid,
            [MarshalAs(UnmanagedType.IUnknown), Out] out object ppObjectOpen);
KeeperOfTheSoul
So I just declare "object ppObjectOpen;" and then pass that in as the last parameter? I still get the access violation doing it this way. I'll post the modified code if I can ever figure out how to get out of this tiny comment box.
I fixed the method signature, grfMode should have been an int not a long.
KeeperOfTheSoul
Feel free to replace stgfmt and grfmode with enums, just ensure the enum's type is int.
KeeperOfTheSoul
That got it! When I changed grfMode to an int, everything worked! Thank you thank you thank you thank you!