Hello all,
I'm trying to implement drag and drop in my application. code1101 suggested I look into RegisterDragDrop and IDropTarget. Of course, everything I can find is in C++.

Anyways, RegisterDragDrop accepts a pointer to an IDropTarget interface. IDropTarget is defined in the oleidl.h file (with DevC++) as:



EXTERN_C const IID IID_IDropTarget;
#undef INTERFACE
#define INTERFACE IDropTarget
DECLARE_INTERFACE_(IDropTarget,IUnknown)
{
STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD(DragEnter)(THIS_ LPDATAOBJECT,DWORD,POINTL,PDWORD) PURE;
STDMETHOD(DragOver)(THIS_ DWORD,POINTL,PDWORD) PURE;
STDMETHOD(DragLeave)(THIS) PURE;
STDMETHOD(Drop)(THIS_ LPDATAOBJECT,DWORD,POINTL,PDWORD) PURE;
};


Now from what I can tell IDropTarget is just an array of 7 long pointers to functions that we define.

So I want to know if the equivalent in ASM would be:



;protos to match the interfaces of each function

MyQueryInterface PROTO :DWORD,:DWORD
AddRef PROTO
Release PROTO
DragEnter PROTO :DWORD,:DWORD,:DWORD,:DWORD
DragOver PROTO :DWORD,:DWORD,:DWORD
DragLeave PROTO
Drop PROTO :DWORD,:DWORD,:DWORD,:DWORD

;an array of the functions
IDropTarget dd offset MyQueryInterface
dd offset AddRef
dd offset Release
dd offset DragEnter
dd offset DragOver
dd offset DragLeave
dd offset Drop

;calling RegisterDragDrop with a pointer to IDropTarget
invoke RegisterDragDrop,hwnd,addr IDropTarget


If anyone can confirm or clarify I would greatly appreciate it.

--Chorus
Posted on 2002-08-11 17:29:04 by chorus
Hi chorus !

i haven't test this function RegisterDragAndDrop but if you only want to drag files to your application use this :

( WM_DROPFILES)
invoke DragQueryFile,wParam,0,ADDR buffer1,128

But if you want another thing, i can't help you, sorry !

Bye !
Posted on 2002-08-11 18:25:14 by kylekiller
Thanks kylekiller, but I'm specifically trying to drag and drop urls from WebBrowsers, which can't be handled through WM_DROPFILES (quite unfortunately for me :()


--Chorus
Posted on 2002-08-11 18:29:26 by chorus
chorus,

You've got a pretty good definition for the interface there. However, the array of function pointers (which is usually called the virtual table of function pointers, or just vtable for short) needs a bit of indirection to work.

You need to also define a dword pointer variable, inside this store the address of your function table. You pass the address of that extra pointer to RegisterDragDrop.

That may seem round about, but the interface abstraction assumes you have a C++ like object holding the function table reference. Hence you need the extra pointer variable to trick the interface calling code into seeing an object.

Objects always get called in a little two-step dance, given the object pointer (here our dword pointer variable), get its contents, and executre the instruction pointed to by the contents of that address. Thus, to invoke an object's method in masm:



mov eax, pObject
mov eax, [eax]
call [eax + method offset]


Note the , they are quite importaint and subtle here.
Posted on 2002-08-11 22:49:41 by Ernie
You possibly should add the THIS parameter to your protos, if you want to use "invoke".
Posted on 2002-08-11 22:52:43 by japheth
Thank you very much guys! That clarifies things a lot!

I've downloaded an example in C++ and have been stepping through the disassembly with the C++ source for reference and trying to see how it all works. It's a mess, you can barely make sense of anything from it.

I have one other question though.

The C++ source uses the AddRef and Release methods of IUnknown to increment and decrement a reference counter that is kept in the IDropTarget structure. When Release decrements this counter to zero it does delete this (because it was allocated by new)/ What I want to do, however, is just declare the structure in .data and not even have to worry about HeapAlloc and HeapFree for it (i.e., new and delete). Is this ok? And if so, do I even have to bother inc/dec reference count?

Thanks again,

--Chorus

PS here is the code snippet for reference sake:



case WM_INITDIALOG:
{
HRESULT hr;
pDrpTgt = new CDemoDropTarget();
// The IDropTarget interface is marshaled by OLE using MSHLFLAGS_TABLEWEAK,
// OLE does not keep a strong lock on IDropTarget. This will result
// in the interface being released after the first drop. To prevent this
// from happening we keep a strong lock on IDropTarget by calling
// CoLockObjectExternal.
if (pDrpTgt)
{
hr = CoLockObjectExternal(pDrpTgt, TRUE, TRUE);

if (hr == S_OK)
{
hr = RegisterDragDrop(hWnd, pDrpTgt);

if (hr != S_OK)
{
MessageBox(hWnd,"Unable to Register as OLE Object","",MB_OK);
}
}
else
{
MessageBox(hWnd,"OLE Lock Object Error","",MB_OK);
}
}
Posted on 2002-08-12 07:20:10 by chorus
For those who are interested, I have put together an application that accepts drag and drop through the OLE interface. I tried commenting as best as I could.

It checks to see if the file being dropped is a text file, and if so will read in a small buffer from the text file and send it to an edit control when the file is dropped.

Still not quite sure how to accept URLs from Internet Explorer though

--Chorus
Posted on 2002-08-12 14:24:40 by chorus
Yes, as you see, ignoring what AddRef and Release tell you to do isn't very harmful. One way to think of this is your app itself is the object containing the interface, and as long as the app is running the object exists.

On the other hand, you may have been lucky here with your handeling of QueryInterface. QI is the way clients of your object/interface learn what it is capable of doing. Since you blindly return a legit pointer and an S_OK, you are declaring your object literally capable of doing anything.

Here it worked for you, possibly cause windows didn't check if you implemented anything else. This migfht become a huge problem implimenting some other interface, so just kreep it in mind if you keep exploring COM interfaces.

Those points are minor. I very much enjoy the way you got COM to work seemingly without very much fuss at all.

Very nice work.
Posted on 2002-08-12 21:55:26 by Ernie
Thank you very much. There's still a lot I think I have to do to get URLs however. There isn't a lot of documentation on this subject.

Heh.. forgot to make QueryInterface more robust :) To busy trying to get the DragEnter and Drop methods to work. I'll check on that.

I have another question though. I figured most of this stuff out by tracing a simple C++ program through OllyDebug. One thing I noticed is that in the C++ program, this doesn't seem to point to the beginning of the IDropTarget structure but to the byte just after the vtable portion of IDropTarget.

In my program this points to the beginning of the IDROPTARGET structure. I tested my program to make sure it was ok by displaying the refcount variable in the edit box, and it incremented as I thought it would.

Maybe the C++ program is doing something else I can't find yet, I dunno.

Thanks again,

--Chorus
Posted on 2002-08-12 22:17:53 by chorus
OOP implementation usually works something like this:

When the object is created, a chunk of memory is allocated (via New or some command). With C++, the vtable is placed inside the chunk, along with the dword pointer to the vtable.

That's like you passing back lpDropTarget, thats the THIS in your code. The method code can take THIS, and by knowing the binary layout it used to create and initialize the object transform THIS into a pointer to any element it chooses.

THIS is essentially a pointer to the chunk of memory, and the compiler knows how to use it.

That said, since you are building the object, you are free to arrange how THIS works. When I build objects (with CoLib), I leave the vtable static in the .data area, so each object instance need not contain it. This is cmpletely different from C++ (which I believe builds the vtable at runtime to allow for inheritance).

So you don't have to copy how C++ does it, you can devise your own method, as long as you are consistant.
Posted on 2002-08-12 22:45:16 by Ernie
The way to get a URL is to use the EnumFormatEtc to get a pointer to allow you to walk through the supported data formats, using Next. Each call to the Next function will return one of the supported data types of the drop source. When you find the type you are interested in you, then call GetData with that type. You can also use the same method to give the correct cursor feedback in the drop target DragEnter routine.

Try this app which I found very useful for checking what data types are available:

http://home.inreach.com/mdunn/code/ClipSpy/clipspy.html

Hope this is useful

Nick
Posted on 2002-08-13 03:49:05 by Nick
Nick, thank you very much! I'm looking into your method now.

In the meantime however, I have found another solution for my problem. Here's how I do it:

--Create a string "UniformResourceLocator"
--Call RegisterClipboardFormat on that string. This returns the clipboard format uint that is registered already by Internet Explorer for the same name. I save this as CF_URL. (I'm assuming IE has already registered the name...dunno if that's truly correct).
--in the FORMATETC struc, instead of asking for CF_HDROP I ask for CF_URL
--Call GetData
--Call GlobalLock on medium.hGlobal to get a pointer to the data
--The data block is exactly the null terminated URL
--Copy it :)
--GlobalUnlock
--continue as before

Attached is the program that accepts URLs dragged from IE (and only URLs at the moment). Haven't tested it with Netscape or Opera or anything yet. If anybody tries it with one of these browsers can you let me know if it works?

PS Still haven't fixed up QueryInterface... that'll be my next thing

--Chorus
Posted on 2002-08-13 13:00:23 by chorus
Well... it's getting URL's alright, but not waiting for me to actually drop it.

Also, can only asccept the URL direct from MSIE. If I drop it first on my desktop as a shortcut, it doesn't paste. And it no longer accepts text files.

I'm sure you know all this. Nice to see your work progressing.
Posted on 2002-08-13 17:56:32 by Ernie
heh... I guess I was too excited about getting the URL to show up... I posted maybe a bit too early.


Here's a new version:
--accepts text files and reads a small buffer of text
--accepts URLs from browser; waits until dropped
--shows proper icon when dragging a URL over

Doesn't yet accept shortcuts from desktop yet, as desktop converts them to .URL files. Should be easy enough to implement.

--Chorus
Posted on 2002-08-13 18:38:58 by chorus
Ernie,
I have a quick question. I went back to go implement the QueryInterface function so as to be proper about everything, only to discover that the procedure never gets called! I tested this first by returning E_NOINTERFACE all the time and that didn't change anything (prog still accepts text and URLs). Then I inserted a MessageBox call and got nothing.

Do you have any idea why this is happening? I understood that QueryInterface got called with RegisterDragDrop?? And either way, it seems to work regardless...

Thanks for any input,

--Chorus
Posted on 2002-08-14 12:27:29 by chorus