仿酷狗音樂播放器開發日誌二十七 用ole為表單增加檔案拖動功能(附源碼),酷狗ole
轉載請說明原出處,謝謝~~
中秋到了,出去玩了幾天。今天把仿酷狗程式做了收尾,已經開發完成了,下一篇部落格把完結的情況說一下。在這篇部落格裡說一下使用OLE為表單增加檔案拖拽的功能。使用播放器,我更喜歡直接拖動音樂檔案添加到軟體裡,所以做這個功能很重要。做OLE拖拽之前學習了兩篇文章:
http://www.codeproject.com/Articles/840/How-to-Implement-Drag-and-Drop-Between-Your-Progra%E3%80%91
http://blog.csdn.net/liu4584945/article/details/6205341
先來看一下原酷狗裡的檔案拖動功能:
可以看到,我拖動音樂檔案到軟體裡,進去音樂列表的範圍內就顯示可複製的表徵圖,不在範圍則顯示不可拖拽的表徵圖。
讓軟體支援檔案拖拽有兩種方法:OLE拖放和檔案管理工具拖放。第一種方法通過處理表單的WM_DROPFILES訊息,表單就可以收到拖放進來的檔案名稱。OLE拖放允許你拖放可同時被儲存在剪貼簿上的任何資料,並且更加細緻的控制拖放過程。第一個是比較簡單的也是我之前一直使用的方法,下面相關函數的介紹:
UINT DragQueryFile(HDROP hDrop, UINT iFile, LPTSTR lpszFile, UINT cch)
本函數用來取得拖放的檔案名稱。其中,hDrop是一個指向含有被拖放的檔案名稱的結構體的控制代碼;iFiles是要查詢的檔案序號,因為一次可能同時拖動很多個檔案;lpszFiles是出口緩衝區指標,儲存iFiles指定序號的檔案的路徑名,cch指定該緩衝區的大小。有兩點值得注意,第一,如果我們在調用該函數的時候,指定iFile為0xFFFFFFFF,則DragQueryFile將忽略lpszFile和cch參數,返回本次拖放操作的檔案數目;第二,如果指定lpszFile為NULL,則函數將返回實際所需的緩衝區長度。
BOOL DragQueryPoint(HDROP hDrop, LPPOINT lppt);
本函數用來擷取,當拖放操作進行中時,滑鼠指標的位置。第二個參數lppt是一個指向POINT結構體的指標,用來儲存檔案放下時,滑鼠指標的位置。視窗可以調用該函數以查詢檔案是否落在自己的視窗矩形中。
void DragFinish(HDROP hDrop);
當拖放操作處理完畢後需調用該函數釋放系統分配來傳輸檔案名稱的記憶體。
使用這個方法時,在表單初始化完成後調用函數調用DragAcceptFiles(m_hWnd,TRUE),讓表單可以接收WM_DROPFILES訊息。然後在Duilib的表單類中重寫HandleCustomMessage函數,去處理WM_DROPFILES訊息,代碼如下:
else if(uMsg == WM_DROPFILES){HDROP hDrop = (HDROP)wParam;TCHAR szFilePathName[_MAX_PATH] = {0};UINT nNumOfFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); //得到檔案個數for (UINT nIndex=0 ; nIndex< nNumOfFiles; ++nIndex){DragQueryFile(hDrop, nIndex, szFilePathName, _MAX_PATH); //得到檔案名稱//擷取了檔案名稱,開始處理}DragFinish(hDrop);}
這樣就處理完了,處理WM_DROPFILES訊息的方法簡單,但是效果比較差,無法動態擷取檔案在表單上的座標,樣式也難看一些,拖動時的表徵圖僅僅是一個加號而不是原檔案的表徵圖樣式。適用於做一些要求簡單的檔案拖動效果。
接下來說一下OLE檔案拖動:
OLE檔案拖動屬於Windows的外殼擴充編程。我在網上查了一些資料,都是關於MFC下OLE拖放的。最後找到了部落格開頭起到的檔案是介紹win32拖放的。我參考了兩篇文章的代碼,最終封裝為一個DropTargetEx類。但是這樣做了之後的確是可以達到拖放效果,但是發現拖放時的表徵圖還僅僅是一個加號,而不像我部落格開頭貼的原酷狗的圖片,是對應的檔案的表徵圖。查閱資料後瞭解需要使用IDropTargetHelper介面,讓系統輔助來處理訊息,就可以達到漂亮的拖拽效果,具體代碼我都寫在類裡面了。大家可以根據自己的需求來修改。
這裡先看一下最終的效果:
這個類可以用於win32工程和duilib工程裡,使用方法為,在duilib的表單類中聲明一個拖放類的對象:
CDropTargetExm_DropTarget;//使表單支援拖放操作
然後在Notify函數的訊息裡寫入下面的代碼來註冊拖放表單:
<span style="font-size:14px;">m_DropTarget.DragDropRegister(m_hWnd);m_DropTarget.SetHDropCallBack(OnDropFiles);</span>
這裡需要寫一個回呼函數,來通知主表單檔案被拖動,回呼函數的圓形如下,其中CFrameWnd為你的表單類:
typedef void (*DROPCALLBACK)(CFrameWnd*, HDROP);
回呼函數的具體寫法和WM_DROPFILES訊息處理的方法類似,需要把回呼函數聲明為表單類的友元。這樣就增加了拖動功能。CDropFileEx類的代碼如下:
#ifndef DROP_TARGET_EX_H#define DROP_TARGET_EX_H#include "OleIdl.h"#include "ShObjIdl.h"typedef struct _DRAGDATA{int cfFormat;STGMEDIUM stgMedium;}DRAGDATA,*LPDRAGDATA;typedef void (*DROPCALLBACK)(CFrameWnd*, HDROP);class CDropTargetEx : public IDropTarget{public:CDropTargetEx(CFrameWnd *pMainWnd);virtual ~CDropTargetEx();BOOL DragDropRegister(HWND hWnd,DWORD AcceptKeyState = MK_LBUTTON);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);BOOL GetDragData(IDataObject *pDataObject,FORMATETC cFmt);void SetHDropCallBack(DROPCALLBACK pFun);void ProcessDrop(LPDRAGDATA pDropData/*HDROP hDrop*/);//枚舉資料格式的函數,我這裡並沒有用到,但是將來可能會用,函數目前只枚舉了HDROP類型BOOL EnumDragData(IDataObject *pDataObject);private:CFrameWnd *m_pMainWnd;CDuiRectm_rcList;ULONGtb_RefCount;HWNDm_hTargetWnd;DWORDm_AcceptKeyState;boolm_bUseDnDHelper;IDropTargetHelper* m_piDropHelper;DROPCALLBACKm_pDropCallBack;vector<LPDRAGDATA> m_Array;};#endif//DROP_TARGET_EX_H
#include "duilib.h"CDropTargetEx::CDropTargetEx(CFrameWnd *pMainWnd):m_pMainWnd(pMainWnd),tb_RefCount(0),m_hTargetWnd(0),m_AcceptKeyState(0),m_piDropHelper(NULL),m_bUseDnDHelper(false),m_pDropCallBack(NULL){// Create an instance of the shell DnD helper object.if ( SUCCEEDED( CoCreateInstance ( CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,IID_IDropTargetHelper, (void**) &m_piDropHelper ) )){m_bUseDnDHelper = true;}}CDropTargetEx::~CDropTargetEx(){if ( NULL != m_piDropHelper )m_piDropHelper->Release();}BOOL CDropTargetEx::DragDropRegister(HWND hWnd,DWORD AcceptKeyState){if(!IsWindow(hWnd))return false;HRESULT s = ::RegisterDragDrop (hWnd,this);if(SUCCEEDED(s)){ m_hTargetWnd = hWnd;m_AcceptKeyState = AcceptKeyState; if (m_pMainWnd->GetLeftListPos(m_rcList))return true;return false;}else { return false; }}HRESULT STDMETHODCALLTYPE CDropTargetEx::QueryInterface(REFIID iid, void ** ppvObject){*ppvObject = NULL;if (iid == IID_IDropTarget)*ppvObject = static_cast<IDropTarget*>(this);if( *ppvObject != NULL )AddRef();return *ppvObject == NULL ? E_NOINTERFACE : S_OK;}ULONG STDMETHODCALLTYPE CDropTargetEx::AddRef(void){InterlockedIncrement(&tb_RefCount); return tb_RefCount;}ULONG STDMETHODCALLTYPE CDropTargetEx::Release(void){ULONG ulRefCount = InterlockedDecrement(&tb_RefCount);return ulRefCount; }HRESULT STDMETHODCALLTYPE CDropTargetEx::DragOver(DWORD grfKeyState,POINTL pt,DWORD *pdwEffect){ScreenToClient(m_hTargetWnd, (LPPOINT)&pt);if( grfKeyState != m_AcceptKeyState || pt.x < m_rcList.left || pt.x > m_rcList.right || pt.y < m_rcList.top || pt.y > m_rcList.bottom){*pdwEffect = DROPEFFECT_NONE;}else{*pdwEffect = DROPEFFECT_COPY ;}if ( m_bUseDnDHelper ){m_piDropHelper->DragOver((LPPOINT)&pt, *pdwEffect);}return S_OK;}HRESULT STDMETHODCALLTYPE CDropTargetEx::DragEnter(IDataObject * pDataObject,DWORD grfKeyState, POINTL pt,DWORD * pdwEffect){if( grfKeyState != m_AcceptKeyState ){*pdwEffect = DROPEFFECT_NONE;return S_OK;}//我這裡只關心CE_HDROP類型,如果需要,可以調用EnumDragData函數來枚舉所有類型FORMATETC cFmt = {(CLIPFORMAT) CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};GetDragData(pDataObject, cFmt);*pdwEffect = DROPEFFECT_COPY;if ( m_bUseDnDHelper ){m_piDropHelper->DragEnter ( m_hTargetWnd, pDataObject, (LPPOINT)&pt, *pdwEffect );}return S_OK;}HRESULT STDMETHODCALLTYPE CDropTargetEx::DragLeave(void){int temp = m_Array.size();for(UINT i = 0;i < m_Array.size(); i++){LPDRAGDATA pData = m_Array[i];::ReleaseStgMedium(&pData->stgMedium);delete pData;m_Array.clear();}if ( m_bUseDnDHelper ){m_piDropHelper->DragLeave();}return S_OK;}HRESULT STDMETHODCALLTYPE CDropTargetEx::Drop(IDataObject *pDataObj,DWORD grfKeyState,POINTL pt,DWORD __RPC_FAR *pdwEffect){int temp = m_Array.size();for(UINT i = 0; i < m_Array.size(); i++){LPDRAGDATA pData = m_Array[i];//我這裡只擷取了HDROP類型的資料,所以直接開始處理ProcessDrop(pData);::ReleaseStgMedium(&pData->stgMedium);delete pData;m_Array.clear();}if ( m_bUseDnDHelper ){m_piDropHelper->Drop ( pDataObj, (LPPOINT)&pt, *pdwEffect );}return S_OK;}BOOL CDropTargetEx::EnumDragData(IDataObject *pDataObject){IEnumFORMATETC *pEnumFmt = NULL;//如果擷取成功,則可以通過IEnumFORMATETC介面的Next方法,來枚舉所有的資料格式:HRESULT ret = pDataObject->EnumFormatEtc (DATADIR_GET,&pEnumFmt);pEnumFmt->Reset ();HRESULT Ret = S_OK;FORMATETC cFmt = {0};ULONG Fetched = 0;while(Ret != S_OK){Ret = pEnumFmt->Next(1,&cFmt,&Fetched);if(SUCCEEDED(ret)){if( cFmt.cfFormat == CF_HDROP){if(GetDragData(pDataObject,cFmt))return TRUE;}}else{return FALSE;}}return TRUE;}BOOL CDropTargetEx::GetDragData(IDataObject *pDataObject,FORMATETC cFmt){HRESULT ret=S_OK;STGMEDIUM stgMedium;ret = pDataObject->GetData(&cFmt, &stgMedium);if (FAILED(ret))return FALSE;if (stgMedium.pUnkForRelease != NULL)return FALSE;switch (stgMedium.tymed){//可以擴充這塊代碼,配合EnumDragData函數來儲存更多類型的資料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;}void CDropTargetEx::SetHDropCallBack(DROPCALLBACK pFun){if (pFun != NULL){m_pDropCallBack = pFun;}}void CDropTargetEx::ProcessDrop(LPDRAGDATA pDropData/*HDROP hDrop*/){switch(pDropData->cfFormat){case CF_TEXT:{//m_pTextCallBack((HDROP)pDropData->stgMedium.hGlobal);break;}case CF_HDROP:{m_pDropCallBack(m_pMainWnd, (HDROP)pDropData->stgMedium.hGlobal);break;}default:break;}}
總結:
CDropTargetEx類的為:點擊開啟連結
目前只是根據我的需求編寫 CDropTargetEx類,實際上還可以擴充來完成更多功能。
Redrain 2014.9.9
怎把酷狗音樂播放器添加到QQ空間日誌裡
樓上的說得不對,本來我也不會的,不過酷狗有教程,樓主可以去看下,應該就可以明白了,如何找到教程?首先先上傳一首歌曲到你的音樂硬碟,然後開啟個人中心,在【我的音樂硬碟】裡點擊【我的音樂檔案】,點擊你上傳的歌曲的進階設定,這時就有【把歌曲帖到Blog或BBS 怎麼貼?】的字樣,點擊怎麼貼,就有告訴你如何貼的方法了,如果還是不能明白,用百度hi跟我線上聊
怎把酷狗音樂播放器添加到QQ空間日誌裡 添加別的播放器也行 不是要添加歌是日誌裡的播放器
我也要做音樂把音樂添加到日誌裡。 的說得不對,本來我也不會的,不過酷狗有教程,樓主可以去看下,應該就可以明白了,如何找到教程?首先先