Use the idroptarget interface to simultaneously support text and file drag and drop

Source: Internet
Author: User
Tags ole

For Windows Shell extension programming, drag-and-drop is a relatively simple one. You can find many articles about this technique on the Internet. Most of them are implemented by coledroptarget of MFC. I think it is good to use coledroptarget, but I am used to using some program modules instead of MFC, such as pure SDK programming, when using ATL, MFC is quite cumbersome. So coledroptarget is not perfect in this sense.

I have referenced msdn and related articles and code www.codeproject.com (By Thomas Blenkers) Later, I found that drag-and-drop mainly uses the idroptarget interface method, which is very simple. You may wish to implement your own drag-and-drop class directly in the face of the original idroptarget.

As a study note, there is such a text to give a reference:

Idroptarget

It is a pure Virtual Interface left by the system for customer programs that support drag-and-drop. Instead of implementing any functions of the interface, you can implement interface functions to take over the drag-and-drop results. The idroptarget interface has the following member functions:

  • Basic

Com member functions

QueryInterface

Addref

Release

  • Take over the drag-and-drop event member function:

  • Dragenter

    Dragover

    Dragleave

    Drop

    That is to say, to achieve the above in the customer Program

    The entity of the Seven functions.

    The system will call

    The corresponding function of the idroptarget interface, checks the flag returned by the user in these functions, and determines the appearance of the mouse and the drag-and-drop results.

     

     

    Implementation

    Idroptarget Interface
    To this end, create a class whose base class is idroptarget:

    Class cdroptargetex: Public idroptarget

    The idroptarget interface is defined in oleidl. h as a pure virtual interface.

    In cdroptargetex, declare the Seven functions contained in the interface in sequence. The original form 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 );

    (To implement the addref count, there is anotherUlong tb_refcountMember variables are required.QueryInterface, addref, releaseThe implementation of these three functions is the most basic in the com knowledge. See the example below)

    Explain

    Before implementing other functions of idroptarget, it is necessary to introduce a function that you may never directly call but does exist:DodragdropFunction. This function is called when data from a data source is dragged.

    • Check whether the target window supports drag-and-drop and find the target window

    Idroptarget Interface

  • Tracks the mouse and keyboard status at any time, and calls it based on the status.
  • Drageenter, dragmove, drop or dragleave Interface

  • Obtain the return values of the client program from these interfaces and interact with the user interface and data sources.

  • It can be said that

    Dodragdrop controls the entire drag-and-drop process. All we need to do is take over the events in this process and get the relevant information, andDodragdropInteraction. Knowing this helps us understand why the drag-and-drop effect can be achieved through four functions in a partition interface, because the system has already done a lot for us.

    Another important

    API isRegisterdragdropThe original form of this function is as follows:

    Winoleapi registerdragdrop (

    Hwnd,

    Idroptarget * pdroptarget

    );

    No need to be

    Winoleapi is scared. This is a macro:

    # Define stdapi extern_c hresult stdapicalltype

    That is, a standard

    The win API function returns the value of an hresult.

    Function

    RegisterdragdropThe function is to tell the system that a window (specified by the hwnd parameter) can accept drag and drop. The interface for taking over the drag and drop operation is pdroptarget.

    Remember to call

    Before registerdragdrop, you must callOleinitializeInitialize the OLE environment.

    In

    A function is designed in cdroptargetex.

    Bool cdroptargetex: dragdropregister (hwnd,

    DWORD acceptkeystate = | mk_lbutton)

    {

    If (! Iswindow (hwnd) return false;

    Hresult S =: registerdragdrop (hwnd, this );

    If (succeeded (s ))

    {

    M_htargetwnd = hwnd;

    M_acceptkeystate = acceptkeystate;

    Return true;

    }

    Else {return false ;}

    }

    Call this function

    Registerdragdrop: This pointer is passed in, indicating that this class implements idroptarget. This class takes over the drag-and-drop event. In addition, the drag-and-drop mouse and keyboard feature constant are defined. For this class, I want to accept only the drag-and-drop of the left mouse button by default. Therefore, the default acceptkeystate value isMk_lbutton. The keyboard and mouse constants are alsoMk_shift, mk_alt, mk_rbotton, mk_mbutton, mk_bottonWait a few times. I think the constants can be understood literally. These constants can be combined with bitwise operations.

    The following details

    The drag-and-drop interface functions of idroptarget (4). Here, the drag-and-drop objects are mainly text and files.

    • Dragenter

    • When you select a file or a piece of text with the mouse, move the mouse over a certain acceptable drag and drop (you have already called

      In the registerdragdrop window, dragenter will be called immediately. Let's take a look at its prototype:

      Hresult dragenter (idataobject * pdataobject,

      DWORD grfkeystate,

      Pointl PT,

      DWORD * pdweffect)


      Pdataobject is an idataobject interface instance passed in from the drag-and-drop original data. It contains some related methods of data objects and can be used to obtain data.


      Grfkeystate indicates the current keyboard and mouse status when dragenter is called, including the keyboard and mouse status constants described above.


      PT indicates the point where the mouse is located. The whole screen serves as the reference coordinate.


      Pdweffect is a DWORD pointer provided by dodragdrop. The client program returns a specific status to dodragdrop. Valid statuses include:

      Dropeffect_none = 0 indicates that drag and drop are not allowed in this window.

      Dropeffect_move = 1 indicates that the drag-and-drop result will delete the source object.

      Dropeffect_copy = 2 indicates that drag and drop will cause source object replication.

      Dropeffect_link = 4 indicates that the drag-and-drop source object creates a connection to itself.

      Dropeffect_scroll = 0x80000000 indicates that the target window is being dragged or will be rolled. This flag can be used with several others

      For drag-and-drop objectsDropeffect_noneAndDropeffect_copyYou can.

      What should I do in dragenter? It mainly informs you that the drag and drop operation has entered the window area and determines whether a specific type of Drag and Drop operation is supported.

      First, you must judge the keyboard status. When dragdropregister is calledAcceptkeystateAnd save it inM_acceptkeystateIn the member variable, you can now compare it with the grfkeystate obtained here:

      If (grfkeystate! = M_acceptkeystate)

      {

      * Pdweffect = dropeffect_none;

      Return s_ OK;

      }

      If the keyboard and mouse are in different States than I expected

      Returned in pdweffectDropeffect_noneIndicates that drag-and-drop is not accepted.

      Then

      To check whether the idataobject object is of interest to me.

      Here we will introduce two key structures.

      Formatetc and stdmedium

      Formatetc is a key structure of OLE data exchange. It describes the format of a certain device, data, and related media.

      It is defined

      Typedef struct tagformatetc

      {

      Clipformat cfformat;

      Dvtargetdevice * PTD;

      DWORD dwaspect;

      Long lindex;

      DWORD tymed;

      }

      Formatetc, * lpformatetc;

      What we are most interested in here is

      CfformatAndTymedTwo data types.CfformatIs a standard "Clipboard" data type, suchCf_textAnd so on. Tymed indicates the media attached to the data, such as memory, disk files, and storage objects. For other members, see msdn.

      A typical

      The formatetc structure variables are defined as follows:

      Formatetc cfmt = {(clipformat) cf_text, null, dvaspect_content,-1, tymed_hglobal };

      Idataobject provides a getdata interface to obtain data contained in its instance, such:

      Stgmedium;

      Ret = pdataobject-> getdata (& cfmt, & stgmedium );

      Getdata is passed into cfmt to indicate the data of interest andStgmediumStructure.


      Stgmedium is defined as follows: 1

      Typedef struct tagstgmedium

      {

      DWORD tymed;

      [Switch_type (DWORD), switch_is (DWORD) tymed)]

      Union {

      [Case (tymed_gdi)] hbitmap;

      [Case (tymed_mfpict)] hmetafilepict;

      [Case (tymed_enhmf)] henhmetafile;

      [Case (tymed_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 looks complicated. In fact, it is mainly the combination of a series of handles or data object interfaces. You can use one of them based on the specific data type.

      In the same way as in formatetc, tymed indicates the carrier type of the data (unfortunately, it cannot indicate the specific standard type such as cf_text or others ). As for punkforrelease, the interface specified by the source data is used to pass to the releasestgmedium function. If it is not null, The releasestgmedium function uses this interface to release data. If it is null, The releasestgmedium function uses the default iunknown interface. For conventional drag and drop operations, the object pointer should be null.

      A handle or data object interface is also equivalent to a drag-and-drop data.

      Define a specific

      FormatetcThe getdata structure instance that is passed to idataobject can directly query and obtain a specific data. If we are very specific about the data we want, this is a more efficient method. However, if we want to be able to adapt to the drag and drop objects, we can use a solution to enumerate all data types contained in idataobject. This is necessaryIenumformatetcInterface.

      The ienumformatetc interface is obtained from the idataobject interface:

      Ienumformatetc * penumfmt = NULL;

      Ret = pdataobject-> enumformatetc (datadir_get, & penumfmt );

      If it is obtained successfully, you can use

      The next method of the ienumformatetc interface to enumerate all data formats:

      Penumfmt-> Reset ();

      Hresult ret = s_ OK

      While (Ret! = S_ OK)

      {

      RET

      =Penumfmt-> next (1, & cfmt, & fetched );

      If (succeeded (RET ))

      If (cfmt. cfformat = cf_text

      | Cfmt. cfformat = cf_hdrop)

      {

      If (getdragdata (pdataobject, cfmt ))

      Enterresult = true;

      }

      }

      The first parameter indicates

      Number of formatetc structure data. cfmt is a formatetc pointer pointing to a data buffer to return formatetc data ., Fetched is the number of formatetc data after the next call. Generally, get one at a time until the return value of next is not s_ OK.

      We can obtain

      Cfmt calls the idataobject-> getdata method, but generally, a data object contains more than one type of data, and generally has some custom data types (for more information about custom data types, see:RegisterclipboardformatIf you want to implement the drag/drop source data by yourself, this function is useful.) We are not interested in this, because here we only need to process text and file dragging. Therefore, only data with cfformat cf_text and cf_hrop is processed:

      Getdragdata is a member function of the cdroptargetex class:


      //////////////////////////////////////// ///////////

      // Get the dragdata from idataobject, save in Handel

      Bool cdroptargetex: getdragdata (idataobject * pdataobject, formatetc cfmt)

      {

      Hresult ret = s_ OK;

      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

      Cfmt: Call the idataobject-> getdata function to obtain data (for cf_text and cf_hrop, the data media carrier tymed isHglobalType ).

      In actual implementation, I defined a structure:

      Typedef struct _ dragdata

      {

      Int cfformat;

      Stgmedium;

      } Dragdata, * lpdragdata;

       

      Set

      StgmediumAnd Data Types (such as cf_text, recorded in cfformat) are recorded in dragdata. A vector array is used to save the structure in the array. For the stgmedium data that we don't want, we immediately call the releasestgmedium function to release it to avoid Memory leakage.

      In this way,

      The dragenter operation is basically complete. The last thing we need to do is to return the corresponding status to dodragdrop: if we get the desired data, assign * pdweffectDropeffect_copyOtherwiseDropeffect_none;

      If drag-and-drop is supported, the mouse shape turns into an acceptable icon. Otherwise, it is a rejecting icon.

      • Dragover

      • When you drag the object to the window, it will move within the window range.

        Dodragdrop calls the dragover interface of idroptarget. Its prototype is:

        Hresult dragover (

        DWORD grfkeystate

        Pointl PT,

        DWORD * pdweffect

        )

        The implementation of this interface method is much simpler:

        Grfkeystate determines whether the keyboard and mouse statuses meet the requirements. Based on the mouse points passed in by PT, it determines whether drag and drop is supported (for example, if the drag and drop area is limited to a part of the window), and then* PdweffectAssignedDropeffect_copyOrDropeffect_none.Of course, you can also do things you like, such as printing the mouse coordinates to the screen. However, for the sake of performance and security, we recommend that you do not perform operations with significant latency.

        • Dragleave:

        • This method does not include parameters, which is quite simple.

          When the dragged mouse leaves the window area, this method will be called. Here you can write some code to clean up the memory. In

          In the cdroptargetex class, because some new data structures are added to a pointer array in dragenter, I must clean up the data here, call releasestgmedium in this structure and delete this structure.

          In addition, if necessary, you can be notified that the mouse pointer has left the drag-and-drop area.

          • Drop
          • If the mouse does not exit the window, but the button is released in the window, the drag-and-drop time is put,

            The Drop Method of the idroptarget interface is called. Its prototype is

            Hresult drop (

            Idataobject * pdataobject,

            DWORD grfkeystate,

            Pointl PT,

            DWORD * pdweffect

            )

            It is recommended that you call the API only here for some information.

            Pdataobject-> getdataMethod to obtain data. In the cdroptargetex class, the data is already inDragenter. The reason for doing so is that I want to get data from the beginning and determine whether to support drag-and-drop, rather than determining whether the data is legal when it is "put.

            Since the data has been obtained, I can extract it from the pointer array that stores the data.

            StgmediumAnd process the data according to the specific format of the data.Releasestgmedium)

            For

            Cf_text data. hglobal, a member of stgmedium, contains global memory data. To obtain the data, follow these steps:

            Tchar * pbuff = NULL;

            Pbuff = (lpstr) globallock (htext );

            Globalunlock (htext );

            Then a pointer pbuff pointing to the memory data is obtained. In my example

            A text string ending with "/0. In this way, the text is dragged and dropped.

            For

            For cf_hdrop data, the hglobal member of stgmedium is a hdrop type handle. You can use this handle to obtain a list of drag-and-drop files. For example:

            Bool cdroptargetex: processdrop (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 <count; I ++)

            {

            If (: dragqueryfile (hdrop, I, buffer, sizeof (buffer )))

            {

            // Got the filename in buffer

            }

            }

            : Dragfinish (hdrop );

            Return true;

            }

            Obtained

            Buffer is the drag-and-drop file name. If you drag and drop multiple files, you can obtain the file names of these files in sequence in the for loop. In this way, you can drag and drop files.

             


            The cdroptargetex class is very easy to use:

            Define a cdroptargetex instance in the related files of the customer window:Cdroptargetex droptarget;

            After the window is created, drag and drop the window handle for registration:


            Droptarget. dragdropregister (hwnd );

            Or


            Droptarget. dragdropregister (hwnd, mk_control | mk_lbutton );

            Indicates to press and hold the left mouse button

            The ctrl key is valid;

            For obtaining the drag-and-drop results, I use the callback function method:

            Callback prototype

            Typedef void (_ stdcall * dropcallback) (lpcstr buffer, int type );

            In a proper place (such as window implementation

            CPP) definition function dropcallback:

            Void _ stdcall dropcallback (lpcstr buffer, int type)

            And assign the address

            Droptarget instance:

            Droptarget. setcallback (dropcallback );

            In this way, drag and drop the text to the client window, And the callback function will be called.

            BufferIs the drag-and-drop text,FormatCf_text. When a drag-and-drop file is used, a callback function is called for each drag-and-drop file.BufferIs the full path name of the file,FormatIsCf_hdrop.

            Sample

            Dropcallback code:

            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 (MessageBox (hmainwnd, Buf, "Question", mb_yesno) = idyes)

            {

            ShellExecute (0, "open", buffer, "", "", sw_show );

            }

            }

            Default:

            Break;

            }

            }

             


            Conclusion: Use

            Idroptarget implements universal drag-and-drop. You only need to implement seven of its interfaces, and call the correct getdata to obtain data in the correct format (formatetc) for the idataobject, and returnDropeffectDetermine the drag-and-drop features and results, and process the drag-and-drop results.

            Note the following:

            • To callOleinitializeInstead of coinitialize or coinitializeex. OtherwiseRegisterdragdropThe operation will not succeed. The returned error is e_outofmemory-insufficient memory.

            • Call releasestgmedium to release the data in stgmedium, instead of directly calling closehandle for its hglobal member.

            • The drag-and-drop operation is related to the data exchange between two processes. Both processes are blocked until the drag-and-drop operation is complete. Therefore, do not perform time-consuming operations in the interface method that takes over the drag-and-drop operation.

            This example is quite simple and can be simplified, such as canceling

            Vector, stores the obtained hglobal handle as a member variable, or stores all the operations to obtain data in the drop method.

            There is also a simpler method for drag-and-drop files: Response

            Wm_dropfilesMessage. Steps:

            • Call customer window

            DropaccepfilesSo that the window can accept the drag and drop of files.

          • Response
          • Wm_dropfiles message, whose wparam is the hdrop handle

          • Call this handle
          • Dropqueryfiles: Get the drag-and-drop file list and end the drag-and-drop operation. For more information, seeProcessdropCode

            For more information about drag and drop, see

            Msdn-> platformsdk document-> User Interface services-> Windows ShellAbout"Transferring Shell objects with drag-and-drop and the clipboard. The Windows Shell system provides many interfaces for you to use and expand these interfaces. It is indeed a clever design to conveniently develop and use a wide range of shell services.

             

            Contact Us

            The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

            If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

            A Free Trial That Lets You Build Big!

            Start building with 50+ products and up to 12 months usage for Elastic Compute Service

            • Sales Support

              1 on 1 presale consultation

            • After-Sales Support

              24/7 Technical Support 6 Free Tickets per Quarter Faster Response

            • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.