Support text and file drag and drop with the IDROPTARGET interface
vcbear
About Windows enclosure extension programming, drag and drop is a relatively simple type, and there are many articles that introduce this technique on the Internet. Most of them introduce the use of the ColedropTarget implementation, I think it is usually very good, but I am used to in some program modules, completely don't use MFC, such as pure SDK programming, and in ATL, MFC is Quite income. So ColedgedropTarget is not perfect in this sense.
After the article and code of MSDN and www.codeproject.com, I found that drag and drop actually mainly used the interface method of the iDropTarget, which is very simple, may wish to face the original IDROPTARGET to implement their own drag and drop classes. .
As a learning note, there is such a text to throw bricks:
IDropTarget is a pure virtual interface that allows the client to support drag and drop. No function on the interface in advance, but allows users to take over the results of the drag and drop by implementing the interface function. The IDROPTARGET interface has the following member functions:
Basic COM member function
QueryInterface addref transase
Take over the member function of the drag and drop event:
Dragenter Dragover Dragleave Drop
That is to say, the entity of the above 7 functions is to be implemented in the client program.
When the system is detected, the corresponding function of the IDROPTARGET interface implemented in the customer program will be called in the appropriate time, and check the logo returned in these functions to determine the appearance of the mouse and drag and drop.
Implement an IDROPTARGET interface to create a class class for iDroptarget for this:
Class CDROPTARGETEX: PUBLIC IDROPTARGET
The IDROPTARGET interface is defined in oleidl.h, for a purely interface.
Declare the seven functions included in the interface in CDROPTARGETEX, the original shape is:
HRESULT STDMETHODCALLTYPE QueryInterface (REFIID iid, void ** ppvObject); ULONG STDMETHODCALLTYPE AddRef (void); ULONG STDMETHODCALLTYPE Release (void); HRESULT STDMETHODCALLTYPE DragOver (DWORD grfKeyState,
PoinTL PT,
DWORD * PDWEFFECT);
HRESULT stdmethodcalltype Dragenter (iDataObject * pdataobject,
DWORD GRFKEYSTATE, POINTL PT,
DWORD * PDWEFFECT);
HRESULT stdmethodCallType Dragleave (Void); HRESULT stdmethodCallType Drop (iDataObject * pdataobj,
DWORD GRFKEYSTATE,
PoinTL PT,
DWORD __RPC_FAR * PDWEFFECT);
(In order to achieve the addRef count, there is a Ulong TB_REFCount member variable is necessary. Queryinterface, addref, realase this three functions is the most basic, please refer to:
Before explaining the specific implementation of other functions of iDroptarget, it is necessary to introduce a function that you may never call but do exist: doDragDrop function. This function is called when the data of a data source is called, it is responsible
Detecting the target window supports drag and drop, discovers the IDROPTARGET interface of the target window to track the status of the mouse and keyboard, and determine its DrageEnter, Dragmove, DROP, or DRAGLEAVE interface according to the status decision to obtain the return value of the client program, according to these values and users. Interface and data sources interact. It can be said that doDragDrop controls the entire process of dragging and drop, we have to do, just to take over the event, take over and get the corresponding information, and interact with DodragDrop. Understanding this helps us understand why 4 functions in the zone can achieve the effect of dragging, because the system has done a lot for us.
Another very important API is registerdragdrop, the original shape of this function is like this:
Winoleapi registerdragdrop (
HWND HWND,
IDROPTARGET * PDROPTARGET
);
No need to be scared by WinoleApi, this is a macro:
#define stdapi extern_c HRESULT stdapicalLLTYPE
That is to show a standard WIN API function, returning a value of HRESULT.
The function of the function registerdragdrop is to tell the system: a window (HWND parameter specified) can accept drag and drop, the interface to take over the drag and drop is PDROPTARGET.
Remember to call Oleinitialize initialize the OLE environment before calling RegisterDragDrop.
A function is designed in class CDROPTARGETEX
Bool CDROPTARGETEX :: DragDropRegister (HWND HWND, DWORD AcceptKeyState = | mk_lbutton) {
IF (! iswindow (hwnd) RETURN FALSE;
HRESULT S = :: registerdragdrop (hwnd, this); if (successted (s)) {m_htargetwnd = hwnd; m_acceptKeyState = AcceptKeyState; Return true;} else {return false;}}}
In this function calls RegisterDragDrop, incoming this pointer, indicating that this class has implemented an idroptarget. The trailer is dragged from this class. In addition, it is defined to drag and drop the mouse and keyboard characteristics. For this class, I hope that the default only the left mouse button is dragged, so the default AcceptKeyState value is mk_lbutton. Related keyboard mouse constants also have several MK_SHIFT, MK_ALT, MK_RBOTTON, MK_MBUTTON, MK_BOTTON, etc. I think this few constants can understand it from the literal. These constants can be combined with the "bit and" operation.
The Drag and drop associated interface function (4) of the IDROPTARGET is discussed below, which is dominated by text and files.
Dragenter
When you use a mouse to select a file or a piece of text, move the mouse to a window that can accept drag and drop (have been called over the registerDragDrop), Dragenter will be called the first time. Take a look at its original shape:
HRESULT DRAGENTER (iDataObject * pdataobject,
DWORD GRFKEYSTATE,
PoinTL PT,
DWORD * PDWEFFECT)
PDataObject is an iDataObject interface instance that passes from the original data dragged, contains some related methods of the data object, which can be obtained by this interface.
GrfKeyState is the status of the current keyboard and mouse when Dragenter is called, containing the keyboard mouse state constant described above. Pt indicates the point where the mouse is located. It is a reference coordinate throughout the screen.
PDWEFFECT is a DWORD pointer provided by DodragDrop, and the client returns a specific state via this pointer to DodragDrop. Effective status includes:
DROPEFFECT_NONE = 0 indicates that this window cannot accept drag and drop.
DROPEFFECT_MOVE = 1 Indicates the result of dragging will be deleted
DROPEFFECT_COPY = 2 indicates that drag and drop will cause replication of source objects.
DROPEFFECT_LINK = 4 indicates that the drag and drop source creates a connection to your own
DROPEFFECT_SCROLL = 0x80000000 indicates that the drag and drop target window is or will roll. This sign can be used in several other sections
For drag and drop objects, generally just use dropeffect_none and dropeffect_copy.
What should I do in Dragenter? It is mainly to tell the drag and drop has entered the window area and determine whether to support a specific type of drag and drop.
First, to determine the status of the keyboard. When I call DragDropRegister, I passed an AcceptKeyState and saved it in the M_AcceptKeyState member variable, and now I can take it with the GrfKeyState you get here:
IF (GrfKeyState! = m_acceptkeystate) {* pdWeffect = dropeffect_none; return;
If the status of the keyboard and the mouse is different from what I expect, then returning DROPEFFECT_NONE in PDWeffect means that it is not accepted.
Then, it is judged that there is data I am interested in the IDATAOBJECT object.
Here you will introduce two key structures formatetc and stdmedium
FormateTC is a key structure for OLE data exchange, a description of a format for some kind of device, data, and related media.
It is defined as
Typedef struct tagformatetc {
ClipFormat CFFORMAT;
DVTARGETDEVICE * PTD;
DWORD DWASPECT;
Long LinDex;
DWORD TYMED;
} Formatetc, * lpformatetc;
Here we are most interested in cfformat and typed data. Cfformat is a standard "Pastepad" data type such as CF_Text. TYMED indicates the media that is attached to the data, such as memory, disk file, storage object, and more. Other members can see MSDN.
A typical FormateTC structural variable is defined as follows:
Formatetc cfmt = {(clipformat) CF_Text, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
IDataObject provides a getData interface to get the data contained in its act, such as:
STGMEDIUM STGMEDIUM; RET = PDataObject-> getData (& CFMT, & STGMEDIUM);
GetData is incorporated into CFMT to point out the data interested and will return in the STGMEDIUM structure.
STGMEDIUM is defined as follows 1
typedef struct tagSTGMEDIUM {DWORD tymed; [switch_type (DWORD), switch_is ((DWORD) tymed)] union {[case (TYMED_GDI)] HBITMAP hBitmap; [case (TYMED_MFPICT)] HMETAFILEPICT hMetaFilePict; [case (TYMED_ENHMF)] HENHMETAFILE hEnhMetaFile; [case (TYMED_HGLOBAL)] hGLOBAL hGlobal; [case (TYMED_FILE)] LPWSTR lpszFileName; [case (TYMED_ISTREAM)] IStream * pstm; [case (TYMED_ISTORAGE)] IStorage * pstg; [default];}; [unique] IUnknown * pUnkForRelease (} STGMEDIUM; TYPEDEF STGMEDIUM * LPSTGMEDIUM; It seems quite complicated, in fact, is the combination of a series of handles or data object interfaces, using one of them according to the specific type of data. Tymed and formatetc, pointing out the carrier type of data (unfortunately it does not point out specific standard types such as CF_Text or other). As for PunkForRelease, it is an interface specified by the source data to pass to the ReleaseSTGMedium function. If it is not null, the ReleaseSTGMedium function uses this interface to release the data. If you are null, the ReleaseSTGMedium function uses the default iUnknown interface. This object pointer should be NULL for routine drag and drop.
Get a handle or data object interface, which is also equivalent to the data of dragging and drop.
Define a specific FormateTC structure instance passed to the getData of iDataObject, you can directly ask and get a specific data. If we are very determined for the data we want, this is a more efficient method. But if we expect to be able to adapt to drag and drop, we can take the scenarios that enumerate all the data types included in iDataObject. This is to use the IenumFormatetc interface.
The IEnumFormatetc interface is obtained from the iDataObject interface:
IEnumformatetc * penumfmt = null; ret = pdataObject-> enumformatetc (datadir_get, & penumfmt);
If the acquisition is successful, you can enumerate all data formats through the next method of the IEnumFormateTC interface:
Penumfmt-> reset (); hResult ret = s_ok while (ret! = s_ok)
{
Ret = penumfmt-> next (1, & cfmt, & fetched);
IF (cfmt.cfformat == cf_text || cfmt.cfformat == cf_hdrop) {IF (getDragdata (pDataObject, cfmt) ENTERRESULT = true;}}
The first parameter represents the number of formatetc structure data, and the CFMT is a FormateTC pointer, pointing to a data buffer, used to return formatetc data. , Fetched is the number of formatetc data obtained after NEXT call. Generally acquired one time until NEXT returns to S_OK.
We can get a CFMT call IDATAOBJECT-> getData method, but in general, a data object contains more than one, and generally there are some custom data types (see: registerclipboardformat, if To implement DRAG / DROP source data, this function is useful), we are not interested in this because it only requires dragging of text and files, for this, only handed data for cfformat as cf_text and cdroP: getDragdata is CDROPTARGETEX One member function of the class:
///
// Get The DragData from IDataObject, save in HANDEL BOOL CDropTargetEx :: GetDragData (IDataObject * pDataObject, FORMATETC cFmt) {HRESULT ret = S_OK; STGMEDIUM stgMedium; ret = pDataObject-> GetData (& cFmt, & stgMedium); // GetData (CF_TEXT !, & stgMedium); if (FAILED (ret)) {return FALSE;} if (stgMedium.pUnkForRelease = NULL) {return FALSE;} /// switch (stgMedium.tymed) {case TYMED_HGLOBAL: {LPDRAGDATA pData = new DRAGDATA; pData-> cfFormat = cFmt.cfFormat; memcpy (& pData-> stgMedium, & stgMedium, sizeof (STGMEDIUM)); m_Array.push_back (pData); return true; break;} default: // type not supported, so return error {: : ReleaseSTGMEDIUM (& STGMEDIUM);} Break;} Return False;
In this member function, according to CFMT, call the iDataObject-> GetData function to obtain data (for CF_Text and CF_HROP, the media carrier TyMed of the data is Hglobal type).
In the specific implementation, I defined a structure:
Typedef struct _dragdata {
Int cfformat;
STGMEDIUM STGMEDIUM;
} Dragdata, * lpdragdata;
Record the STGMEDIUM and data types (such as CF_Text, recorded in cfformat) in DragData. And use a vector array to save this structure in an array. For STGMEDIUM data we want, we immediately call the ReleaseSTGMedium function to release, levese to cause memory leakage.
In this way, the work of Dragenter is basically completed, and finally needs to return to DodragDrop to return the corresponding state: If we get the desired data, give * pdweffect to DROPEFFECT_COPY, otherwise, it is DROPEFFECT_NONE;
If you support drag and drop, the mouse shape will become an endless icon, otherwise, it is an icon that refuses to sense.
Dragover
After the mouse drag the object enters the window, you will move within the window range, then DodragDrop calls the iDropTarget's Dragover interface. Its prototype is: HRESULT DRAGOVER (DWORD GRFKEYSTATE POINTL PT,
DWORD * PDWEFFECT
)
The implementation of this interface method can be simpler: as long as the status of the keyboard and the mouse is determined according to the requirements of GrfKeyState, it is determined whether the point supports drag and drop according to the PT of the mouse point (such as limiting the drag and drop area on the window). Part of the words), then assigning DROPEFFECT_COPY or DROPEFFECT_NONE. Of course, you can do something you like, such as printing the mouse coordinate to the screen. However, for performance and security, it is recommended not to do delayed operation.
Dragleave:
This method has no incoming parameters, quite simple.
When the dragable mouse leaves the window area, this method will be called, you can write some code for cleaning memory here. In the CDROPTARGETEX class, since the data structure is in the Dragenter, it is added to a pointer array, so I must clean this data here, and the stdmedium in this structure calls ReleaseSTGMEDIUM then delete this structure.
In addition, if desired, you can notify the user that the mouse pointer has left the drag and drop area.
Drop
If the mouse does not leave the window, it is released in the window, then the "put" of the drag and drop time occurs at this time, the DROP method of the IDROPTARGET interface is called. It is
HRESULT DROP (iDataObject * PDataObject, DWORD GRFKEYSTATE, POINTL PT, DWORD * PDWEFFECT)
Some information suggests here to call pDataObject-> getData methods to get data, in the CDROPTARGETEX class, the data is actually got in Dragenter. The reason why this is what I want to get data at the beginning, and it is judged from itself whether it supports drag and drop, not when "put". Did it legal data.
Since the data has been obtained, then I can extract STGMEDIUM data from the pointer array of saved data, and process according to the specific format of the data (finally remember RELEASTGMEDIUM for STGMEDIUM)
For CF_Text type data, STGMEDIUM's member hglobal is included in a global memory data. Getting these data is:
TCHAR * PBUFF = NULL; PBUFF = (LPSTR) Globalock (htext); GlobalUnlock (hText);
Then get a pointer PBUFF pointing to memory data. In my example, it is generally a "/ 0" text string. This will implement the drag and drop of text.
For data of the CF_HDROP type, the STGMEDIUM member HGlobal is a handle of an HDROP type. Through this handle, you can get a list of files drag and drop. Such as:
Bool cdroptargetex :: processdrop (hdrop hdrop) {uint ifiles, ICH = 0; tchar buffer [max_path] = "; MEMSET (& iFiles, 0xFF, Sizeof (ifiles));
INT count = :: DragQueryFile (HDROP, IFILES, BUFFER, 0); // Get The Drag _files Number.
IF (count)
For (int i = 0; i } :: Dragfinish (HDROP); Return True; } The Buffer obtained is the file name drag and drop. If you drag and drop multiple files, you can get the file name of these files in the FOR cycle. This enables the drag and drop of the file. CDROPTARGETEX class is very simple: Define a CDROPTARGETEX instance: cdroptargetex droptarget; Drag and drop the window handle after the window is created: DROPTARGET.DRAGDROPREGISTER (HWND); or DROPTARGET.DRAGDROPREGISTER (HWND, MK_CONTROL | MK_LBUTTON); Indicates that the left mouse button is pressed and pressing the drag and drop of the CTRL button; For the result of obtaining drag and drop, I am using the callback function: Parled original TypeDef void (_stdcall * Dropcallback) (LPCSTR BUFFER, INT TYPE); Define a function DROPCALLBACK in an appropriate place (such as a window implementation CPP) Void _stdcall DropCallback (LPCSTR BUFFER, INT TYPE) And add its address to the DROPTARGET instance: DROPTARGET.SETCALLBACK (DROPCALLBACK); In this way, drag and drop the text to the customer window, the callback function will be called, the parameter buffer is the text of the drag, format is CF_Text. When dragging and dropping files, all callback functions are called for each drag and drop, the parameter buffer is the full path name, format is CF_HDROP. The example of the DropCallback code is: Void_stdcall Dropcallback (LPCSTR BUFFER, INT FORMAT) {Switch (Format) {Case CF_Text: {setWindowText (HEDIT, BUFFER); Break;} Case CF_HDrop: {Tchar BUF [2048] = ""; Sprintf (buf, "file: <% s> IS DRAG AND DROP TO this Windows, Open it? ", buffer; if (HmainWnd, BUF," Question ", MB_YESNO) == iDyes) {shellexecute (0," open ", buffer," ",", Sw_show);}} DEFAULT: Break; } Summary: Use the idroptarget to implement universal drag, as long as the 7 interfaces are implemented, and the resulting IDATAOBJECT calls the correct format (FormateTC) to call the correct GetData, return DROPEFFECT to determine the features and results of the drag and drop, and process drag and drop The result can be. The small problem to pay attention is: To call Oleinitialize instead of COINITIALIZE or COINTILALIZEEX to initially, the registerdragdrop will not succeed, and the return error is E_OUTOFMEMORY - the memory is not enough, and cannot be performed. Calling ReleaseSTGMedium releases the data in STGMedium instead of calling the Hglobal member to call CloseHandle. Drag and drop operation is related to data exchange of two processes, it will block both processes until the drag is completed, so, in taking over In the interface method, do not perform too much time-consuming operations. This example is quite simple, but also simplifies, for example, cancels the vector, the HGLOBAL handle will be stored as a member variable, or all the operations of the acquired data are all in the DROP method. For drag and drop files, there is a simpler method: responding to WM_DROPFILES messages. The step is: Call DROPACCEPFILES for the customer window so that the window can accept file drag and drop. Responding to the WM_DROPFILES message, its WPARAM is the HDROP handle call DropQueryFiles to get the drag and drop file list and end drag, see the code about ProcessDrop on ProcessDrop. For a comprehensive elaboration of drag and drop, see MSDN-> PlatformSDK Document-> User Interface Services-> Windows Shell About "Transferring Shell Objects with Drag-And-Drop and The Clipboard" chapter. The Windows Shell system provides a lot of interfaces that allow users to use and expand these interfaces, which is very convenient to develop and use rich shell services. It is indeed a very smart design. Accounts and code