Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / January 2008

Tip: Looking for answers? Try searching our database.

Hooking IDataObject.GetData in .Net

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Roßert G. Schaffrath - 19 Jan 2008 15:23 GMT
Sorry for the cross-posting.  I am having a hard time trying to classify
exactly what group this question would apply to.

I had posted an earlier message to
microsoft.public.dotnet.framework.windowsforms trying to find a way to
access the lindex member of the FORMATETC structure for a
CFSTR_FILECONTENTS callback request under C#.  After some experimenting
I was able to come up with the following code snippet that receives the
callback with a filled in FORMATETC and allows me to save the lindex
value.  Unfortunately, I am now stumped at how to forward the callback
to the .Net framework copy of IDataObject.GetData so that the request
can be processed by the framework and ultimately forwarded to my
override of DataObject.GetData.  The location that I would want to
forward the request is marked as "TODO:".  Can this be done or do have I
have fully implement my own IDataObject.GetData routine?

    internal class DataObjectEx : DataObject,
System.Runtime.InteropServices.ComTypes.IDataObject
    {
        private Int32 m_lindex;

        void
System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref
System.Runtime.InteropServices.ComTypes.FORMATETC formatetc, out
System.Runtime.InteropServices.ComTypes.STGMEDIUM medium)
        {
            if (formatetc.cfFormat ==
DataFormats.GetFormat(NativeMethods.CFSTR_FILECONTENTS).Id)
                m_lindex = formatetc.lindex;
            // TODO: Forward request on to .Net framework
IDataObject.GetData
        }

        public override object GetData(string format, bool autoConvert)
        {
            // Handle requests for certain formats and then...
            return base.GetData(format, autoConvert);
        }
    }
Nicholas Paldino [.NET/C# MVP] - 20 Jan 2008 22:13 GMT
Robert,

   You won't be able to do this, I'm afraid.  The reason for this is
because of the C# language.  Because the interface was explicitly
implemented, you can't cast yourself to that interface to call the base
implementation.

   Also, the implementers of the interface did not create another method
which is virtual which you could override which provides the actual
implementation.  Because of this, you don't have access to the base
implementation either.

   I can't say if it was an intentional move or not, but you don't have
access to the base implementation here.

   You can still get around this though.  Instead of deriving from
DataObject, you should create a new class which encapsulates it, and then
forward the calls on the implementations of IDataObject (both versions, the
one from the System.Windows.Forms namespace, and the one from the
System.Runtime.InteropServices.ComTypes namespace) to the instance you
create internally.  You can cast that as appropriate to get the "base"
implementation, after (or before) you have done your processing.

Signature

         - Nicholas Paldino [.NET/C# MVP]
         - mvp@spam.guard.caspershouse.com

> Sorry for the cross-posting.  I am having a hard time trying to classify
> exactly what group this question would apply to.
[quoted text clipped - 35 lines]
>         }
>     }
Roßert G. Schaffrath - 21 Jan 2008 01:27 GMT
Thanks for insight.  I'll try the encapsulation approach and see how
that goes.  Working in C# is great but there are times you get to 99% of
what you want to do and then run into a brick wall.  An override would
have been nice but as you point out, whether or not it was intentionally
omitted, it does not exist and I have to cope as best I can.

Thanx!

> Robert,
>
[quoted text clipped - 18 lines]
> instance you create internally.  You can cast that as appropriate to get
> the "base" implementation, after (or before) you have done your processing.
Roßert G. Schaffrath - 23 Jan 2008 12:36 GMT
I tried working this out with encapsulation but I was not having much
success.  For fun, I decided to take apart the IDataObject.GetData
method in the .NET Framework using Lutz Roeder's .NET Reflector and see
what was going on under the hood.  It turns out the method is not
terribly complicated and I was able to lift it and add it to my code.
The heavy work is being done by the IDataObject.GetDataHere method that
I did not replace so the actual solution is trivial.

The net (no pun intended) result follows:

        private static TYMED[] ALLOWED_TYMEDS =
(TYMED[])Enum.GetValues(typeof(TYMED));
        private Int32 m_lindex;

        [SecurityPermission(SecurityAction.Demand, Flags =
SecurityPermissionFlag.UnmanagedCode)]
        void
System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref
System.Runtime.InteropServices.ComTypes.FORMATETC formatetc, out
System.Runtime.InteropServices.ComTypes.STGMEDIUM medium)
        {
            if (formatetc.cfFormat ==
(Int16)DataFormats.GetFormat(NativeMethods.CFSTR_FILECONTENTS).Id)
                m_lindex = formatetc.lindex;

            medium = new
System.Runtime.InteropServices.ComTypes.STGMEDIUM();
            if (this.GetTymedUseable(formatetc.tymed))
            {
                if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) !=
TYMED.TYMED_NULL)
                {
                    medium.tymed = TYMED.TYMED_HGLOBAL;
                    medium.unionmember =
NativeMethods.GlobalAlloc(NativeMethods.GHND |
NativeMethods.GMEM_DDESHARE, 1);
                    if (medium.unionmember == IntPtr.Zero)
                    {
                        throw new OutOfMemoryException();
                    }
                    try
                    {

((System.Runtime.InteropServices.ComTypes.IDataObject)this).GetDataHere(ref
formatetc, ref medium);
                        return;
                    }
                    catch
                    {
                        NativeMethods.GlobalFree(new
HandleRef((STGMEDIUM)medium, medium.unionmember));
                        medium.unionmember = IntPtr.Zero;
                        throw;
                    }
                }
                medium.tymed = formatetc.tymed;

((System.Runtime.InteropServices.ComTypes.IDataObject)this).GetDataHere(ref
formatetc, ref medium);
            }
            else
            {
                Marshal.ThrowExceptionForHR(NativeMethods.DV_E_TYMED);
            }
        }

        private Boolean GetTymedUseable(TYMED tymed)
        {
            for (Int32 i = 0; i < ALLOWED_TYMEDS.Length; i++)
            {
                if ((tymed & ALLOWED_TYMEDS[i]) != TYMED.TYMED_NULL)
                {
                    return true;
                }
            }
            return false;
        }

So far my code has been working like a charm.  It is nice to have a
working solution after several weeks of hacking at it.

Robert

> Robert,
>
[quoted text clipped - 18 lines]
> instance you create internally.  You can cast that as appropriate to get
> the "base" implementation, after (or before) you have done your processing.
Roßert G. Schaffrath - 23 Jan 2008 12:37 GMT
I tried working this out with encapsulation but I was not having much
success.  For fun, I decided to take apart the IDataObject.GetData
method in the .NET Framework using Lutz Roeder's .NET Reflector and see
what was going on under the hood.  It turns out the method is not
terribly complicated and I was able to lift it and add it to my code.
The heavy work is being done by the IDataObject.GetDataHere method that
I did not replace so the actual solution is trivial.

The net (no pun intended) result follows:

        private static TYMED[] ALLOWED_TYMEDS =
(TYMED[])Enum.GetValues(typeof(TYMED));
        private Int32 m_lindex;

        [SecurityPermission(SecurityAction.Demand, Flags =
SecurityPermissionFlag.UnmanagedCode)]
        void
System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref
System.Runtime.InteropServices.ComTypes.FORMATETC formatetc, out
System.Runtime.InteropServices.ComTypes.STGMEDIUM medium)
        {
            if (formatetc.cfFormat ==
(Int16)DataFormats.GetFormat(NativeMethods.CFSTR_FILECONTENTS).Id)
                m_lindex = formatetc.lindex;

            medium = new
System.Runtime.InteropServices.ComTypes.STGMEDIUM();
            if (this.GetTymedUseable(formatetc.tymed))
            {
                if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) !=
TYMED.TYMED_NULL)
                {
                    medium.tymed = TYMED.TYMED_HGLOBAL;
                    medium.unionmember =
NativeMethods.GlobalAlloc(NativeMethods.GHND |
NativeMethods.GMEM_DDESHARE, 1);
                    if (medium.unionmember == IntPtr.Zero)
                    {
                        throw new OutOfMemoryException();
                    }
                    try
                    {

((System.Runtime.InteropServices.ComTypes.IDataObject)this).GetDataHere(ref
formatetc, ref medium);
                        return;
                    }
                    catch
                    {
                        NativeMethods.GlobalFree(new
HandleRef((STGMEDIUM)medium, medium.unionmember));
                        medium.unionmember = IntPtr.Zero;
                        throw;
                    }
                }
                medium.tymed = formatetc.tymed;

((System.Runtime.InteropServices.ComTypes.IDataObject)this).GetDataHere(ref
formatetc, ref medium);
            }
            else
            {
                Marshal.ThrowExceptionForHR(NativeMethods.DV_E_TYMED);
            }
        }

        private Boolean GetTymedUseable(TYMED tymed)
        {
            for (Int32 i = 0; i < ALLOWED_TYMEDS.Length; i++)
            {
                if ((tymed & ALLOWED_TYMEDS[i]) != TYMED.TYMED_NULL)
                {
                    return true;
                }
            }
            return false;
        }

So far my code has been working like a charm.  It is nice to have a
working solution after several weeks of hacking at it.

Robert

> Robert,
>
[quoted text clipped - 18 lines]
> instance you create internally.  You can cast that as appropriate to get
> the "base" implementation, after (or before) you have done your processing.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.