Groups | Blog | Home
all groups > dotnet interop > april 2004 >

dotnet interop : IDataObject Marshalling


montu17 NO[at]SPAM yahoo.com
4/14/2004 10:21:28 PM
Resending:

Hi,

I am writing a .Net wrapper for the MS object picker, but I am
running into a marshalling problem.

I am having trouble retrieving data returned after selecting users and
clicking OK. When I call the dataObj.GetData (...) it returns a handle
inside STGMEDIUM that is invalid. The GetLastWin32Error returns an
Error Code 6. (ERROR_INVALID_HANDLE)

Please see code below.

Ill appreciate any input on this issue.

Thanks in advance,
Abhinav

/**********************************************
This is code to extract the data
***********************************************/
private string ProcessSelectedObjects (IDataObject dataObj)
{
STGMEDIUM stg = new STGMEDIUM ();
stg.tymed = (uint) TYMED.TYMED_HGLOBAL;
stg.pUnkForRelease = null;
stg.hGlobal = IntPtr.Zero;

FORMATETC fetc = new FORMATETC ();
fetc.cfFormat = PInvoke.RegisterClipboardFormat
(CLIPBOARD_FORMAT.CFSTR_DSOP_DS_SELECTION_LIST);
fetc.ptd = 0;
fetc.dwAspect = (uint) DVASPECT.DVASPECT_CONTENT;
fetc.lindex = -1;
fetc.tymed = (uint) TYMED.TYMED_HGLOBAL;

int hr = dataObj.GetData (ref fetc, ref stg);

if (hr != HRESULT.S_OK)
{
//error
}

int error = 0;

IntPtr ptr = PInvoke.GlobalLock(stg.hGlobal);

if (ptr == IntPtr.Zero)
{
error = Marshal.GetLastWin32Error ();
}

PInvoke.GlobalUnlock(ptr);
}

/*********************************
These are the structures
**********************************/
/* Interfaces */
[
ComImport,
CLSCompliant(false),
Guid("0000010e-0000-0000-C000-000000000046"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IDataObject
{
[PreserveSig ()]
int GetData(ref FORMATETC pFormatEtc, ref STGMEDIUM b);

void GetDataHere(ref FORMATETC pFormatEtc, ref STGMEDIUM b);

[PreserveSig()]
int QueryGetData(IntPtr a);

[PreserveSig()]
int GetCanonicalFormatEtc(IntPtr a, IntPtr b);

[PreserveSig()]
int SetData(IntPtr a, IntPtr b, int c);

[PreserveSig()]
int EnumFormatEtc(uint a, IntPtr b);

[PreserveSig()]
int DAdvise(IntPtr a, uint b, IntPtr c, ref uint d);

[PreserveSig()]
int DUnadvise(uint a);

[PreserveSig()]
int EnumDAdvise(IntPtr a);
}

[
CLSCompliant(false),
StructLayout(LayoutKind.Sequential)
]
public struct FORMATETC
{
public int cfFormat;
public int ptd;
public uint dwAspect;
public int lindex;
public uint tymed;
}

[
CLSCompliant(false),
StructLayout(LayoutKind.Sequential)
]
public struct STGMEDIUM
{
public uint tymed;
public IntPtr hGlobal;
public Object pUnkForRelease;
}

/********************************************
Call to INvokeDialog
********************************************/
public override string InvokeDialog (IntPtr handle)
{
IDsObjectPicker idop = null;
idop = Initialize ();

IDataObject dataObj = null;
idop.InvokeDialog(handle, out dataObj);

//have to process dataObj and return SID string
return ProcessSelectedObjects (dataObj);
Abh Khush
4/15/2004 12:19:56 PM
HI Mattias,

Thanks for the reply.

In the Initialize method I am setting the objectSid which is what seems
to be causing my IDataObject.GetData to fail. If I set
initInfo.cAttributesToFetch = 0 then I am able to retrieve the STGMEDIUM
successfully and am able read the Name and the AdsPath. So it looks like
I am not setting the initInfo.apwzAttributeNames correctly to be
interpreted as WCHAR* array[].

So, the question now is how does one pass an array of strings inside a
structure from .net to COM so that it is interpreted correctly.

BTW, www.dotnetinterop.com stuff is good work.

Thanks,
Abhinav


/***********Snippet of Initialize
// Init DSOP_INIT_INFO

IntPtr refScopeInitInfo = Marshal.AllocHGlobal
(Marshal.SizeOf (typeof (DSOP_SCOPE_INIT_INFO)) * 2);
Marshal.StructureToPtr (scopeInitInfo[0], refScopeInitInfo,
true);
Marshal.StructureToPtr (scopeInitInfo[1],
(IntPtr) ((int) refScopeInitInfo + Marshal.SizeOf
(typeof (DSOP_SCOPE_INIT_INFO))),
true);


string attrName = "objectSid";
IntPtr refAttrName = Marshal.StringToHGlobalUni (attrName);

DSOP_INIT_INFO initInfo = new DSOP_INIT_INFO ();
initInfo.cbSize = (uint) Marshal.SizeOf (initInfo);
initInfo.pwzTargetComputer = "";
initInfo.cDsScopeInfos = 2;
initInfo.aDsScopeInfos = refScopeInitInfo;
initInfo.flOptions =
DSOP_INIT_INFO_FLAGS.DSOP_FLAG_MULTISELECT;
initInfo.cAttributesToFetch = 1;
initInfo.apwzAttributeNames = refAttrName;

idop.Initialize (ref initInfo);
*********End snipped of initialize************/


*** Sent via Developersdex http://www.developersdex.com ***
Mattias Sjögren
4/15/2004 2:14:52 PM

[quoted text, click to view]

Your code looks find to me. I could take it, fill in the missing
pieces and it worked great here. So I'm not sure why it fails for you.

To be fully correct, you should change FORMATETC.cfFormat to a
(u)short and probably tag STGMEDIUM.pUnkForRelease with
[MarshalAs(UnmanagedType.IUnknown)]. But I don't think any of these
will fix your problem.

What does your code in Initialize look like? Are you sure that
IDsObjectPicker.Initialize succeeds? What's the actual value of
stg.hGlobal after the GetData call? Have you tried calling
IDataObject.QueryGetData?



Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
Abhinav Khushraj
4/16/2004 7:02:06 PM
HI Mattias,

Thanks for the reply.

In the Initialize method I am setting the objectSid which
is what seems
to be causing my IDataObject.GetData to fail. If I set
initInfo.cAttributesToFetch = 0 then I am able to
retrieve the STGMEDIUM
successfully and am able read the Name and the AdsPath.
So it looks like
I am not setting the initInfo.apwzAttributeNames
correctly to be
interpreted as WCHAR* array[].

So, the question now is how does one pass an array of
strings inside a
structure from .net to COM so that it is interpreted
correctly.

BTW, www.dotnetinterop.com stuff is good work.

Thanks,
Abhinav

/***********Snippet of Initialize
// Init DSOP_INIT_INFO

IntPtr refScopeInitInfo = Marshal.AllocHGlobal
(Marshal.SizeOf (typeof (DSOP_SCOPE_INIT_INFO)) * 2);
Marshal.StructureToPtr (scopeInitInfo[0],
refScopeInitInfo,
true);
Marshal.StructureToPtr (scopeInitInfo[1],
(IntPtr) ((int) refScopeInitInfo + Marshal.SizeOf
(typeof (DSOP_SCOPE_INIT_INFO))),
true);

string attrName = "objectSid";
IntPtr refAttrName = Marshal.StringToHGlobalUni
(attrName);

DSOP_INIT_INFO initInfo = new DSOP_INIT_INFO ();
initInfo.cbSize = (uint) Marshal.SizeOf (initInfo);
initInfo.pwzTargetComputer = "";
initInfo.cDsScopeInfos = 2;
initInfo.aDsScopeInfos = refScopeInitInfo;
initInfo.flOptions =
DSOP_INIT_INFO_FLAGS.DSOP_FLAG_MULTISELECT;
initInfo.cAttributesToFetch = 1;
initInfo.apwzAttributeNames = refAttrName;

idop.Initialize (ref initInfo);
*********End snipped of initialize************/
AddThis Social Bookmark Button