How to marshal unmanaged memory for IQueryInfo

  • Thread starter Thread starter Alan Bahm
  • Start date Start date
A

Alan Bahm

Hi all,

I'm trying to write an InfoTip extension for my filetype in explorer
(running on WinXP & W2K). The interface IQueryInfo supports this
feature (with some help from IPersistFile), through the function call
GetInfoTip. GetInfoTip requires that you return a string for the
InfoTip that is allocated off of CoTaskMem, so that the calling
process (Explorer) can deallocate it.

I'm running into some conceptual trouble with implementing this in the
managed world.

I've used the following interface definition:


[ComImport(),
Guid("00021500-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IQueryInfo
{
int GetInfoTip(
[In] int dwFlags,
[Out, MarshalAs(UnmanagedType.BStr)] out string ppwszTip);
int GetInfoFlags([Out] int pdwFlags);
}


I started by naively returning a string directly:


public int GetInfoTip(int dwFlags, out string ppwszTip)
{
Debug.WriteLine("ShellSupport.GetInfoTip(" + dwFlags + ")");
ppwszTip = "Hello world.";
return 0;
}


and while the infotip shows up briefly, the Explorer promptly crashes
(I assume because it is trying to de-allocate the string.)
So I _read_ the documentation and realized I probably should be doing
this instead:


public int GetInfoTip(int dwFlags, out string ppwszTip)
{
Debug.WriteLine("ShellSupport.GetInfoTip(" + dwFlags + ")");
ppwszTip = Marshal.StringToCoTaskMemAuto("Hello world.");
return 0;
}


but of course, ppwszTip is a *string*, not an *IntPtr*, so this won't
compile. If I change the second argument in the interface to


[Out, MarshalAs(UnmanagedType.BStr)] out IntPtr ppwszTip


then my function isn't entered at all (the COM signature is too
different?)

All told, I'm conceptually stuck, trying to figure out how to return a
..NET System.String that points to unmanaged memory. I don't think
that can be done, but I'm certain that a C# info tip _can_ be written.
What am I missing?

Any help appreciated,
Alan Bahm
(e-mail address removed)
 
Alan,
int GetInfoTip(
[In] int dwFlags,
[Out, MarshalAs(UnmanagedType.BStr)] out string ppwszTip);
int GetInfoFlags([Out] int pdwFlags);
}

The return types should be void, unless you really want to explicitly
return the HRESULT, in which case you should add the [PreserveSig]
attribute to the methods.

The string parameter should be an LPWSTR, not a BSTR, so use
UnmanagedType.LPWStr instead.

pdwFlags should be an out parameter. Note that out == [Out] ref, so
using only the [Out] attribute isn't enough.

and while the infotip shows up briefly, the Explorer promptly crashes
(I assume because it is trying to de-allocate the string.)

More likely because your method signatures are incorrect.

If I change the second argument in the interface to


[Out, MarshalAs(UnmanagedType.BStr)] out IntPtr ppwszTip


then my function isn't entered at all (the COM signature is too
different?)

That should work too if you remove the MarshalAs attribute. But the
runtime returns out strings in CoTaskMemAlloc'ed memory by default so
using a string should be easier.



Mattias
 
Mattias,

Thank you very much! I didn't realize that the interop takes care of
the string allocation.
You were right - I went back over the signatures, and I got it working
after changing the int return value to void.

Best regards,
Alan
 
Back
Top