software-on-demand-ita.com

Giuseppe Pischedda

Software Engineer

software-on-demand-ita.com

Giuseppe Pischedda

Software Engineer

OLE Drag&Drop for Mere Mortals

Part 2.

OLE Drag&Drop in plain Win32 SDK.

In questa seconda parte vedremo come realizzare un'applicazione Win32 che supporti Drag&Drop source e target il cui risultato finale sarà quello mostrato nel video e nelle immagini qui sotto:

Win32 Drag&Drop demo running. Problems? Watch it on YouTube


drag-and-drop drag-and-drop drag-and-drop drag-and-drop drag-and-drop




Creeremo una semplice applicazione di tipo "dialog" volutamente in plain Windows SDK style la quale, per poter essere sia drag&drop source che target, dovrà implementare le interfacce:


Nel processo OLE drag&drop (schema qui sotto) coinvolgeremo altre interfacce COM che per però son gestite dalla shell di windows e pertanto non sarà necessario implementarle.

Schema generale OLE Drag&Drop:


drag-and-drop
Per un'introduzione al trasferimento dei dati tramite OLE potete dare un'occhiata alla parte 1 di questo tutorial.


Nello schema generale (qui sopra) l'applicazione A trasferisce tramite OLE all'applicazione B un oggetto "data" tramite un puntatore all'interfaccia COM IDataObject.
L'applicazione B riceve l'oggetto "data" attraverso un altro puntatore all'interfaccia IDataObject.
Il sistema operativo tramite COM con un terzo puntatore all'interfaccia IDataObject si occupa di sincronizzare i 2 puntatori a IDataObject di A e B e comunicare a queste "l'esito" del Drag&Drop.

Con "data object" si intende un qualunque contenuto (immagini, stringhe di testo, elenco di nomi di files e cosi' via).

Il contenuto dell'oggetto "data" può avere un formato predefinito (se vuole scambiare dati in formato standard con le altre applicazioni) o personalizzato (privato), od entrambi; nel nostro caso useremo 4 formati predefiniti :

  • CF_TEXT
  • CF_UNICODETEXT
  • CF_BITMAP
  • CF_HDROP

Sempre osservando lo schema generale ci rendiamo subito conto che la nostra applicazione di esempio, apparentemente, differisce da esso dato che nel nostro progetto non è prevista nessuna applicazione B (drop target). In realtà nel nostro caso sarà la stessa applicazione a ricoprire il ruolo di A (drag&drop source) e B (drag&drop target) a seconda dei casi, il che significa che la stessa potrà trasferire e ricevere dati sia con copie in esecuzione di se stessa che con qualsiasi altra applicazione che supporti OLE drag&drop come quelle di Microsoft Office ( Excel, Word etc.) nonchè altre apps come Paintbrush (tramite il clipboard) etc...


Schema dell'applicazione di esempio e classi coinvolte

drag-and-drop

Ho voluto impiegare le classi della libreria ATL per semplificare alcuni passaggi relativi soprattutto al caricamento dei vari formati di file immagine, ma non solo, anche il tipo di puntatore CComPtr che è un puntatore sicuro che si elimina dalla memoria da solo quando esce dallo scope senza esere costretti chiamare "delete" (o meglio Release() in questo caso) è piuttosto utile.

CAtlList è una lista concatenata già pronta all'uso che conterrà l'elenco dei file quando faremo Drag&Drop da Windows Explorer verso la nostra applicazione e CString è un'ottima classe per la gestione delle stringhe (è in pratica quella che si utilizza anche in ambito MFC).

Nello schema ho evidenziato la classe CStaticImage, creata da me ad hoc, che eredita la classe ATL CImage.

Questa classe è un' utility che raggruppa alcune funzionalità che non sono gestite direttamente dalla classe CImage, ma soprattutto è capace di clonare l'immagine contenuta in un oggetto CImage e/o di restituirne il relativo HBITMAP.
Quest'ultimo è l'handle relativo all'immagine correntemente caricata in un oggetto di tipo CImage. Il Picture Control che useremo anch'esso visualizza un'immagine in memoria puntando al suo handle.
Fin qui tutto facile, la classe CImage tramite la funzione Detach() restituisce l'handle dell'immagine in memoria per cui basterà assegnare quest'ultimo al controllo picture di Win32 semplicemente con la funzione SendMessage(...)

CImage image;
//..........
//..........
HBITMAP hBm = (HBITMAP)image;
SendMessage(hwnd,STM_SETIMAGE,(WPARAM)(NULL),(LPARAM)(hBm));

Da questo momento però hBm si è "appropriato" dell'handle dell'immagine caricata in image e non potremo riassegnarlo o copiarlo.
La soluzione più ovvia sarebbe quella di creare una nuova variabile di tipo CImage e assegnarla a image:

CImage image2 = image;

Questo sarebbe catastrofico, perchè image2 punterebbe esattamente all'area di memoria di image, quindi chiamando:
o image.Destroy() (wrong!! dealloca anche image2)
o image2.Destroy() (wrong!!!... image è già deallocata)

nel migliore dei casi eliminiamo l'oggetto CImage e la nostra immagine non sarà più disponibile.
Nel peggiore invece cercheremmo di eliminare 2 volte l'oggetto dalla stessa area di memoria, producendo così un bell' errore di access violation.

La classe CStaticImage invece può creare un clone dell'immagine caricata in memoria da un oggetto CImage, ma in un'area di memoria differente, e restituirne l'HBITMAP. In questo modo potremo copiare e/o riassegnare liberamente copie dell'immagine originale.
Questa caratteristica ci tornerà molto utile al momento opportuno.

Cominciamo a scrivere un pò di codice


Per iniziare creiamo un nuovo progetto Visual C++ Win32 in Visual Studio 2017.

La nostra applicazione è "dialog based" e sfortunatamente tra i progetti di VC++ in Visual Studio 2017 non esiste nessun template di questo tipo, per cui scegliamo il classico wizard per la creazione di applicazioni Win32 per Windows Desktop → Windows Desktop Wizard e la "trasformiamo" in un'applicazione dialog box.

Chi non sapesse come fare può scaricare il template per Visual Studio 2017, creato da me, (e che peraltro io stesso userò per l'applicazione di esempio), vedi immagine qui sotto.

VC++ Project Template



Dato che la nostra applicazione usa qualche classe ATL (CComPtr, CImage, CAtlList e CString), non dimentichiamoci di aggiungere i relativi includes.


Implementazione delle interfacce



I metodi dell'interfaccia IDropTarget consentono innanzitutto di rendere la nostra applicazione reattiva all'operazione OLE drag&drop.
Tuttavia il sistema operativo non manderà i messaggi necessari finchè non sarà informato che (tra le altre) la nostra applicazione è un drag&drop target.
Per far si che il sistema "veda" la nostra applicazione come target dovremo implementare l'interfaccia IDropTarget in una nostra classe e quindi registrare la stessa applicazione quale, appunto, drop target.
A questo scopo, al momento di inizializzare la nostra app, eseguiremo la registrazione con la funzione:

           
HRESULT RegisterDragDrop(
  _In_ HWND         hwnd,
  _In_ LPDROPTARGET pDropTarget
);    

i cui argomenti sono:

  • hwnd = l'HANDLE della nostra applicazione
  • pDropTarget = il puntatore all'interfaccia COM IDropTarget

Prima di chiudere la nostra applicazione dovremo revocare a questa il "diritto" di essere drag&drop target tramite la funzione:
 HRESULT RevokeDragDrop(
  _In_ HWND hwnd
);           
            

nella quale hwnd è sempre l'handle della nostra applicazione.

IDropTarget, come tutte le interfacce COM, discende da IUnknown, per cui dovremo implementare anche quest'ultima.

Tra i membri privati della classe che implementa IDropTarget aggiungiamo un puntatore (CComPtr) all'interfaccia IDropTargetHelper; questa è un'interfaccia tra quelle gestite dalla Shell di Windows, per cui non dovremo curarci di implementarla, ma soltanto di istanziarla tramite la funzione CoCreateInstance(...), ed è responsabile di dare il feedback all'utente che esegue il drag&drop mostrando un'immagine durante l'operazione di trascinamento (per esempio la tipica miniatura quando si esegue il drag&drop da Windows Explorer).

Quattro metodi delle interfacce IDropTarget e IDropTargetHelper sono identici:
  • DragEnter (determina se il drop può essere accettato e se si l'effetto del drop : copy, move,link)
  • DragLeave (rimuove il feedback e rilascia l'oggetto "IDataObject")
  • DragOver (fornisce il feedback e comunica l'effetto del drop alla funzione DoDragDrop la quale a sua volta comunica all'applicazione drop source l'effetto del drop)
  • Drop (riceve l'oggetto IDataObject, rimuove il feedback per l'utente e rilascia l'oggetto IDataObject)
più un quinto
  • Show
che è invece esclusivo dell'interfaccia IDropTargetHelper e che serve per mostrare o nascondere l'immagine durante il drag&drop.

Per usufruire del supporto fornito dall'interfaccia IDropTargetHelper, chiameremo semplicemente, tramite il puntatore ad essa, per ogni metodo di IDropTarget, il metodo corrispondente di IDropTargetHelper, esempio:

HRESULT CDropTarget::DragLeave()
{
	m_pDTH->DragLeave();

	return S_OK;
}

Aggiungeremo inoltre un membro private di tipo CAtlList<CString> che popoleremo con i nomi dei file quando l'utente farà drag&drop da Windows Explorer.

Una nota per chi legge:

dato che stiamo scrivendo un'applicazione che ha che fare con OLE (e con COM), darò per scontato che conosciate COM, almeno le basi, per cui non mi soffermerò su cosa sia IUnknown, HRESULT, QueryInterface(), AddRef(), Release() etc., o ancora, perchè è necessario scrivere nella nostra classe un metodo come CreateInstance() (vedi sorgente) e la sua funzione.
Coloro che sono un pò confusi su OLE e COM dovrebbero, prima di continuare, approfondire i due argomenti.



Implementiamo l'interfaccia IDropTarget


Aggiungiamo un file header al progetto Visual Studio. Io gli darò il nome della classe che conterrà: CDropTarget.h.

Dichiariamo la classe CDropTarget che, com' è d'obbligo, deve discendere dall'interfaccia IDropTarget:

       
/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#pragma once
#include "stdafx.h"


class CDropTarget : public IDropTarget 
{

public:
	 HRESULT static CreateInstance(IDropTarget **pp);

protected:
	
	/*
	** IUnknown **
	*/
	HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, _Out_ void   **ppvObject);
	ULONG   STDMETHODCALLTYPE AddRef();
	ULONG   STDMETHODCALLTYPE Release();
	

private:

	/*
	** IDropTarget ** 
	*/
	HRESULT STDMETHODCALLTYPE DragEnter(
		_In_      IDataObject *pDataObj,
		_In_      DWORD       grfKeyState,
		_In_      POINTL      pt,
		_Inout_ DWORD       *pdwEffect
	);

	
	 HRESULT STDMETHODCALLTYPE DragLeave();
	 	

	 HRESULT STDMETHODCALLTYPE DragOver(
		 _In_      DWORD  grfKeyState,
		 _In_      POINTL pt,
		 _Inout_ DWORD  *pdwEffect
	 );

	 
	 HRESULT STDMETHODCALLTYPE Drop(
		 _In_      IDataObject *pDataObj,
		 _In_      DWORD       grfKeyState,
		 _In_      POINTL      pt,
		 _Inout_   DWORD       *pdwEffect
	 );

	 

private:
	
	UINT m_refCount;
	
	CDropTarget();
	virtual 
		~CDropTarget();
	
	CComPtr m_pDTH;
	HWND m_hwnd;

	CAtlList m_szFileList;

};

            
     

Implementiamo la classe CDropTarget nel file CDropTarget.cpp:



/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#include "stdafx.h"
#include "CDropTarget.h"



extern HWND g_hwnd_Dlg;
extern HBITMAP g_Hbm;

HRESULT CDropTarget::CreateInstance(IDropTarget ** pDropTarget)
{
	HRESULT hr = E_OUTOFMEMORY;
	
	if (pDropTarget != NULL) 
	{
	
		CDropTarget *pCDropTarget = new CDropTarget();
		
		hr = pCDropTarget->QueryInterface(IID_IDropTarget, reinterpret_cast(pDropTarget));
				
		if (SUCCEEDED(hr))
		return S_OK;
	}

	return hr;

}

//IUnknown
HRESULT CDropTarget::QueryInterface(REFIID riid, void ** ppvObject)
{
	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDropTarget))
		*ppvObject = static_cast(this);
	else
	{
		*ppvObject = NULL;
		return E_NOINTERFACE;
	}

	AddRef();

	return S_OK;
}

ULONG CDropTarget::AddRef()
{
	return InterlockedIncrement(&m_refCount);
}

ULONG CDropTarget::Release()
{
	ULONG refCount = InterlockedDecrement(&m_refCount);
	if (refCount == 0)
	{
		delete this;
		return 0;
	}

	return refCount;
}

//IDropTarget
HRESULT CDropTarget::DragEnter(IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
	if (m_pDTH) 
	{
		POINT pts = { pt.x, pt.y };
		
		m_pDTH->DragEnter(m_hwnd, pDataObj, &pts, *pdwEffect);
	}

	return S_OK;
}

HRESULT CDropTarget::DragLeave()
{
	m_pDTH->DragLeave();

	return S_OK;
}

HRESULT CDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
	if (m_pDTH) 
	{
		POINT pts = { pt.x, pt.y };
		
		m_pDTH->DragOver(&pts, *pdwEffect);
	}

	return S_OK;
}

HRESULT CDropTarget::Drop(IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect)
{
	if (m_pDTH) 
	{
		POINT ptx = { pt.x, pt.y };
		
		m_pDTH->Drop(pDataObj, &ptx, *pdwEffect);
	}

		
	
	FORMATETC  txt_formatetc;
    txt_formatetc.cfFormat = CF_UNICODETEXT;
	txt_formatetc.ptd = NULL;
	txt_formatetc.dwAspect = DVASPECT_CONTENT;
	txt_formatetc.lindex = -1;
	txt_formatetc.tymed = TYMED_HGLOBAL;
	

	FORMATETC  dropfiles_formatetc;
	dropfiles_formatetc.cfFormat = CF_HDROP;
	dropfiles_formatetc.ptd = NULL;
	dropfiles_formatetc.dwAspect = DVASPECT_CONTENT;
	dropfiles_formatetc.lindex = -1;
	dropfiles_formatetc.tymed = TYMED_HGLOBAL;


	FORMATETC  bitmap_formatetc;
	bitmap_formatetc.cfFormat = CF_BITMAP;
	bitmap_formatetc.ptd = NULL;
	bitmap_formatetc.dwAspect = DVASPECT_CONTENT;
	bitmap_formatetc.lindex = -1;
	bitmap_formatetc.tymed = TYMED_GDI;

	

	STGMEDIUM medium;

	
	HRESULT hr = E_OUTOFMEMORY;

	// check if formatetc is valid for text
	hr = pDataObj->QueryGetData(&txt_formatetc);
	
	if (SUCCEEDED(hr)) //formatetc is valid for text
	{
		hr = pDataObj->GetData(&txt_formatetc, &medium);

		if (SUCCEEDED(hr)) 

		{
			
			LPWSTR pData = (LPWSTR) ::GlobalLock(medium.hGlobal);

			

			SendMessage(g_hwnd_Dlg, DLG_MSG_SET_TEXT_FROM_CLIPBOARD, (WPARAM)(NULL), (LPARAM)(pData));
			
			
		}

		GlobalUnlock(medium.hGlobal);
		ReleaseStgMedium(&medium);


	}

 
	// check if formatetc is valid for HDROP
	hr = pDataObj->QueryGetData(&dropfiles_formatetc);
  
	if (SUCCEEDED(hr)) // formatetc is valid for HDDROP
	{
		
		m_szFileList.RemoveAll();
		SendMessage(g_hwnd_Dlg, DLG_MSG_CLEAR_DROPPEDFILES_LIST, (WPARAM)(NULL), (LPARAM)(NULL));
		
		hr = pDataObj->GetData(&dropfiles_formatetc, &medium);

		if (SUCCEEDED(hr)) 
		{

			HDROP p = static_cast(GlobalLock(medium.hGlobal));


			int num = DragQueryFile(p, 0xFFFFFFFF, NULL, 0);
			for (int iFile = 0; iFile < num; iFile++) {
				int size = DragQueryFile(p, iFile, NULL, 0);

				TCHAR * buf = new TCHAR[size + 1];


				DragQueryFile(p, iFile, buf, size + 1);


				m_szFileList.AddTail(CString(buf));

				delete[] buf;
			}


			SendMessage(g_hwnd_Dlg, DLG_MSG_GET_DROPPEDFILES_FROM_CLIPBOARD, (WPARAM)(NULL), (LPARAM)(&m_szFileList));

			GlobalUnlock(medium.hGlobal);
			ReleaseStgMedium(&medium);

		}
	}


	// check if formatetc is valid for images
	hr = pDataObj->QueryGetData(&bitmap_formatetc);

	if (SUCCEEDED(hr)) // formatetc is valid for images
	{
		
		

		hr = pDataObj->GetData(&bitmap_formatetc, &medium);

		if (SUCCEEDED(hr)) 
		{

			
			if (medium.hBitmap != NULL)
			{

				SendMessage(g_hwnd_Dlg, WM_UPDATE_GUI, (WPARAM)(NULL), (LPARAM)medium.hBitmap);
				
			}
			
			GlobalUnlock(medium.hBitmap);
			ReleaseStgMedium(&medium);

			
		}



	}

		
	if ((grfKeyState & MK_CONTROL) == MK_CONTROL)
	{
		*pdwEffect = DROPEFFECT_COPY;
	}
	else
		if ((grfKeyState & MK_ALT) == MK_ALT)
		{
			*pdwEffect = DROPEFFECT_MOVE;
		}
		else

	
		m_pDTH.p->Release();
	

	return S_OK;


}


//Default ctor
CDropTarget::CDropTarget()
{
	m_refCount = 0;

	CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_INPROC_SERVER,
		IID_IDropTargetHelper, (LPVOID*)&m_pDTH);
		

}


CDropTarget::~CDropTarget()
{

}



 

Aggiungiamo un nuovo file header, che chiamerò CDropSource.h, al progetto Visual Studio nel quale dichiariamo la classe CDropSource che discende da IDropSource e, come al solito, da IUnknown.
I metodi di questa interfaccia sono:

  • GiveFeedback(..) (dà il feedback all'utente che esegue il drag&drop)
  • QueryContinueDrag(..) (determina se l'operazione drag&drop in corso deve continuare o meno)


Questi metodi non vengono chiamati direttamente dalle applicazioni, ma piuttosto accade che quando un oggetto "IDataObject" inizia l'operazione drag&drop questo chiama la funzione DoDragDrop(...) la quale chiama i membri di IDropSource.

Implementiamo l'interfaccia IDropSource

Dichiariamo la classe CDropSource

            
/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#pragma once
#include "stdafx.h"


class CDropSource : public IDropSource
{

public:

	static HRESULT STDMETHODCALLTYPE CreateInstance(IDropSource **pDropSource);

	
	/*
	**  IUnknown **
	*/    
	HRESULT STDMETHODCALLTYPE QueryInterface (_In_ REFIID riid, _Out_ void   **ppvObject);
	ULONG   STDMETHODCALLTYPE AddRef();
	ULONG   STDMETHODCALLTYPE Release();

	
	/*
	** IDropSource **
	*/
	HRESULT STDMETHODCALLTYPE GiveFeedback(
		_In_ DWORD dwEffect
	);


	HRESULT STDMETHODCALLTYPE QueryContinueDrag(
		_In_ BOOL  fEscapePressed,
		_In_ DWORD grfKeyState
	);



	
private:

	//Private ctor and descrutor
	CDropSource();
	
	virtual  
	~CDropSource();


 UINT m_refCount;
 HWND m_hwnd;

 

};
          

Implementiamo la classe CDropSource




/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#include "stdafx.h"
#include "CDropSource.h"

HRESULT CDropSource::CreateInstance(IDropSource **pDropSource)
{

	HRESULT hr = E_OUTOFMEMORY;


	if (pDropSource != NULL) 
	{
		
		CDropSource *pCDropSrc = new CDropSource();
		
		hr = pCDropSrc->QueryInterface(IID_IDropSource, reinterpret_cast(pDropSource));
		
		if (SUCCEEDED(hr))
			return S_OK;

		
	}

	return hr;


}

//IUnknown
HRESULT CDropSource::QueryInterface(REFIID riid, void ** ppvObject)
{
	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDropSource))
	{
		*ppvObject = static_cast(this);
	}
	
	else
	{
		*ppvObject = NULL;
		return E_NOINTERFACE;
	}

	AddRef();

	return S_OK;
}

ULONG CDropSource::AddRef()
{
	return InterlockedIncrement(&m_refCount);
}

ULONG CDropSource::Release()
{
	ULONG refCount = InterlockedDecrement(&m_refCount);
	if (refCount == 0)
	{
		delete this;
		return 0;
	}

	return refCount;
}


//IDropSource
HRESULT CDropSource::GiveFeedback(DWORD dwEffect)
{
	
	
	return DRAGDROP_S_USEDEFAULTCURSORS;
}

HRESULT CDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
{
	if (fEscapePressed)
		return DRAGDROP_S_CANCEL;    
	
	if (!(grfKeyState & MK_LBUTTON))
		return DRAGDROP_S_DROP;         
	
	return S_OK;
}




// private Ctor and destructor
CDropSource::CDropSource()
{
	m_refCount = 0;
		
}

CDropSource::~CDropSource()
{
	
}


E' l'interfaccia che consente a COM di gestire un "data object" quando le applicazioni eseguono il trasferimento dei dati via OLE. E' fondamentale però sapere che IDataObject non è un qualcosa di esclusiva pertinenza dell'argomento drag&drop, ma piuttosto è il fulcro di un meccanismo generale alla base dello scambio di dati tra applicazioni via OLE (Uniform Data Transfer).

Schema trasferimento dati via IDataObject:

Osservando lo schema è evidente che in realtà, sebbene ogni applicazione implementi un oggetto IDataObject, nessuna di queste comunica direttamente con il data object delle altre ma piuttosto è COM che si interpone e sovraintende a tale comunicazione.

Per consentire a COM di capire cosa sia esattamente il contenuto di data object quest'ultimo deve contenere il formato dei dati in transito (popolando la struttura FORMATETC) ed il mezzo col quale si intende trasferirli (popolando la struttura STGMEDIUM).

Le applicazioni (e COM) dovranno chiamare i vari metodi di IDataObject in relazione all'operazione che intendono eseguire, per esempio per ricevere i dati un'applicazione dovrà chiamare il metodo GetData(...), ovvero per il contrario SetData(...).

Nota:
osservando l'implementazione della mia classe CDataObject (più sotto) noterete che il metodo SetData(...) restituisce come hresult E_NOTIMPL.
In questo caso, quel metodo non è stato implementato perchè al momento di creare il nostro data object ci avvarremo dell'aiuto della shell di windows alla quale delegheremo tale incombenza (quando al momento opportuno, chiameremo l'API SHDoDragDrop(...)).

Sempre tramite la shell di windows (che lo farà per noi con la funzione SHCreateStdEnumFmtEtc(...)) creeremo la lista dei formati supportati dal nostro data object.

Dichiariamo la classe CDataObject che discende da IDataObject ( e da IUnknown)


/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#pragma once
#include "stdafx.h"


class CDataObject : public IDataObject
{


public:

	static HRESULT STDMETHODCALLTYPE CreateInstance(IDataObject **pDataObject);



	/*
	** IUnknown **
	*/
	HRESULT STDMETHODCALLTYPE QueryInterface(_In_ REFIID riid, _Out_ void   **ppvObject);
	ULONG   STDMETHODCALLTYPE AddRef();
	ULONG   STDMETHODCALLTYPE Release();



    /*
	** IDataObject **
	*/

	HRESULT STDMETHODCALLTYPE  DAdvise(
		_In_  FORMATETC   *pformatetc,
		_In_  DWORD       advf,
		_In_  IAdviseSink *pAdvSink,
		_Out_ DWORD       *pdwConnection
	);


	HRESULT STDMETHODCALLTYPE  DUnadvise(
		_In_ DWORD dwConnection
	);


	HRESULT STDMETHODCALLTYPE EnumDAdvise(
		_Out_ IEnumSTATDATA **ppenumAdvise
	);


	HRESULT STDMETHODCALLTYPE EnumFormatEtc(
		_In_  DWORD          dwDirection,
		_Out_ IEnumFORMATETC **ppenumFormatEtc
	);


	HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(
		_In_  FORMATETC *pformatectIn,
		_Out_ FORMATETC *pformatetcOut
	);


	HRESULT STDMETHODCALLTYPE GetData(
		_In_  FORMATETC *pformatetcIn,
		_Out_ STGMEDIUM *pmedium
	);


	HRESULT STDMETHODCALLTYPE QueryGetData(
		_In_ FORMATETC *pformatetc
	);

	HRESULT STDMETHODCALLTYPE GetDataHere(
		_In_      FORMATETC *pformatetc,
		_Inout_ STGMEDIUM *pmedium
	);


	HRESULT  STDMETHODCALLTYPE SetData(
		_In_ FORMATETC *pformatetc,
		_In_ STGMEDIUM *pmedium,
		_In_ BOOL      fRelease
	);


	
	
	//Set text function for this sample
	void SetText(CString szText);

private:

	//Private ctor and destructor
    CDataObject();
	
	virtual  
	~CDataObject();


	//Other private members
	UINT m_refCount;
	CString m_szText;



};

Implementiamo l'interfaccia IDataObject


/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#include "stdafx.h"
#include "CDataObject.h"




extern CStaticImage *cImage;
extern HBITMAP g_Hbm;
extern CString g_szText;



//Class activator
HRESULT CDataObject::CreateInstance(IDataObject ** pDataObject)
{
	HRESULT hr = E_OUTOFMEMORY;

	if (pDataObject != NULL) 
	{
		
		CDataObject *pCDataObj = new CDataObject();
		
		hr = pCDataObj->QueryInterface(IID_IDataObject, reinterpret_cast(pDataObject));
		
		if (SUCCEEDED(hr))
		return S_OK;
		
		
	}

	return hr;
}


//IUnknown implementation
HRESULT CDataObject::QueryInterface(REFIID riid, void ** ppvObject)
{
	
	if (IsEqualIID(riid, IID_IUnknown) || 
	    IsEqualIID(riid, IID_IDataObject))
		{

			*ppvObject = static_cast(this);

		}
	else
	return E_NOINTERFACE;
	

	AddRef();

	return S_OK;

}

ULONG CDataObject::AddRef()
{
	return InterlockedIncrement(&m_refCount);
}

ULONG CDataObject::Release()
{
	ULONG refCount = InterlockedDecrement(&m_refCount);
	if (refCount == 0)
	{
		delete this;
		return 0;
	}

	return refCount;
}




//IDataObject implementation

HRESULT CDataObject::DAdvise(FORMATETC * pformatetc, DWORD advf, IAdviseSink * pAdvSink, DWORD * pdwConnection)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::DUnadvise(DWORD dwConnection)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::EnumDAdvise(IEnumSTATDATA ** ppenumAdvise)
{
	return E_NOTIMPL;
}

HRESULT CDataObject::EnumFormatEtc(DWORD dwDirection, IEnumFORMATETC ** ppenumFormatEtc)
{
	
	HRESULT   hr;
	FORMATETC formatetc[] = {
	
	{ CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL | TYMED_ISTREAM },
	{ CF_UNICODETEXT, NULL, DVASPECT_CONTENT, -1,  TYMED_HGLOBAL | TYMED_ISTREAM },
	{ CF_BITMAP, NULL, DVASPECT_CONTENT, -1, TYMED_GDI }
	
};

	if (dwDirection == DATADIR_GET) {
		hr = SHCreateStdEnumFmtEtc(3, formatetc, ppenumFormatEtc);
		return hr;
	}
	else
		return E_NOTIMPL;
	


	
}

HRESULT CDataObject::GetCanonicalFormatEtc(FORMATETC * pformatectIn, FORMATETC * pformatetcOut)
{
	
	return E_NOTIMPL;
}

HRESULT CDataObject::GetData(FORMATETC * pformatetcIn, STGMEDIUM * pmedium)
{
	
	
	
	
	if(pformatetcIn->cfFormat == CF_TEXT ) {
		HGLOBAL hglobal;
		TCHAR    *p;

				
		hglobal = GlobalAlloc(GHND, m_szText.GetLength() + 1);

		p = (TCHAR *)GlobalLock(hglobal);
		lstrcpy(p, m_szText);
		GlobalUnlock(hglobal);

		pmedium->tymed = TYMED_HGLOBAL;
		pmedium->hGlobal = hglobal;
		pmedium->pUnkForRelease = NULL;

		
	}
	
	else
		if (pformatetcIn->cfFormat == CF_UNICODETEXT) {
			HGLOBAL hglobal;
			LPWSTR    p;

		
			hglobal = GlobalAlloc(GHND, (m_szText.GetLength() + 1) * sizeof(TCHAR));

			p = (LPWSTR)GlobalLock(hglobal);
			lstrcpy(p, m_szText);
			GlobalUnlock(hglobal);

			pmedium->tymed = TYMED_HGLOBAL;
			pmedium->hGlobal = hglobal;
			pmedium->pUnkForRelease = NULL;

			return S_OK;
		}


	else 
		if (pformatetcIn->cfFormat == CF_BITMAP) 
		{
		
			
			if (NULL != cImage)			
				g_Hbm = cImage->Clone();

			
			

			pmedium->hBitmap = g_Hbm;
			pmedium->tymed = TYMED_GDI;
			pmedium->pUnkForRelease = NULL;
			
		
		}
	
	else
		return E_FAIL;
		

		return S_OK;


}

HRESULT CDataObject::QueryGetData(FORMATETC * pformatetc)
{
	if (pformatetc->cfFormat == CF_TEXT   || 
		pformatetc->cfFormat == CF_BITMAP || 
		pformatetc->cfFormat == CF_UNICODETEXT)
		return S_OK;

	return E_NOTIMPL;
}

HRESULT CDataObject::GetDataHere(FORMATETC * pformatetc, STGMEDIUM * pmedium)
{
		
	ULONG cb = (m_szText.GetLength() + 1) * sizeof(TCHAR);

	
	if ((pformatetc->cfFormat == CF_TEXT && pmedium->tymed == TYMED_ISTREAM)||
		(pformatetc->cfFormat == CF_UNICODETEXT && pmedium->tymed == TYMED_ISTREAM))
	{
		ULONG uWritten;
		pmedium->pstm->Write(m_szText, cb, &uWritten);
		uWritten = uWritten;
		
	}	
	else
		return E_FAIL;
		

	return S_OK;
}

HRESULT CDataObject::SetData(FORMATETC * pformatetc, STGMEDIUM * pmedium, BOOL fRelease)
{
	
	SetText(g_szText);

	return E_NOTIMPL;
}

void CDataObject::SetText(CString szText)
{

	m_szText = szText;

}



// default ctor / destructor
CDataObject::CDataObject()
{

	m_refCount = 0;

	m_szText = L"Text from OLE Drag&Drop";
}

CDataObject::~CDataObject()
{
}



Ereditiamo la classe ATL CImage per avere più controllo soprattutto nella creazione di copie di una CImage.
Chiamo questa classe CStaticImage

CStaticImage

Implementiamo la classe CStaticImage


/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#pragma once
#include "stdafx.h"


class CStaticImage : public CImage
{
public:
	
	CStaticImage();
	
	CStaticImage(LPCTSTR fileName);
	
	CStaticImage(HBITMAP hBm);
	
	virtual ~CStaticImage();

	HBITMAP GetSafeHBITMAP();
		
	CStaticImage(const CStaticImage &img);

	int Width();
	int Height();

		
	typedef struct tagSCALE_PERCENT
	{
		int cWidth;
		int cHeight;
	}SCALEPERCENT,*LPSCALEPERCENT;

	
	BOOL ScaleImage(int percent, SCALEPERCENT & scale);

	CStaticImage operator=(CStaticImage &obj);

	HBITMAP GetImageCopy();

	//Make a CImage copy
	HBITMAP  Clone();
	
	CImage Image;
	
	CImage GetClonedImage();
	
	HBITMAP GetClonedSafeHBITMAP();
	
	VOID SetImage(HBITMAP hBm);

	



private:
	CImage m_Image;
	CStaticImage *pImage;

	
	
};




/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#include "stdafx.h"

CStaticImage::CStaticImage()
{

	pImage = nullptr;

	
}

CStaticImage::CStaticImage(LPCTSTR fileName)
{
	
	m_Image.Load(fileName);


}

CStaticImage::CStaticImage(HBITMAP hBm)
{

	CImage tmpImage;
	IStream *pIstream = nullptr;
	HRESULT  hr = CreateStreamOnHGlobal(NULL, TRUE, &pIstream);

	tmpImage.Attach(hBm);

	hr = tmpImage.Save(pIstream, Gdiplus::ImageFormatBMP);


	m_Image.Load(pIstream);

	pIstream->Release();

}

CStaticImage::~CStaticImage()
{

	m_Image.Destroy();
	delete pImage;
	

}

HBITMAP CStaticImage::GetSafeHBITMAP()
{
	
	return static_cast(m_Image);
}





CStaticImage::CStaticImage(const CStaticImage &img)
{
	
	
	pImage = new CStaticImage();
	pImage->m_Image = img.m_Image;
		
	
}

int CStaticImage::Width()
{
	return m_Image.GetWidth();
}

int CStaticImage::Height()
{
	return m_Image.GetHeight();
}


BOOL CStaticImage::ScaleImage(int percent, SCALEPERCENT &scale)
{
	scale.cWidth = percent * Width() / 100;
	scale.cHeight = percent * Height() / 100;
	return TRUE;
}



CStaticImage CStaticImage::operator=(CStaticImage & obj)
{
	
	m_Image = obj.m_Image;
    return *this;
		
}

HBITMAP CStaticImage::GetImageCopy()
{
	return pImage->m_Image;
}

//Make a CImage copy
HBITMAP CStaticImage::Clone()
{
	
	//Make a CImage copy

	if (!Image.IsNull())
	{
		Image.Detach();
		Image.Destroy();
	}

	HBITMAP hBm = static_cast(m_Image);

	if (hBm != NULL)
	{
		DIBSECTION dbs;
		
		::GetObject(hBm, sizeof(DIBSECTION), &dbs);
		
		dbs.dsBmih.biCompression = BI_RGB;

		HDC hdc = ::GetDC(NULL);
		
		HBITMAP hbm_ddb = ::CreateDIBitmap(hdc, &dbs.dsBmih, CBM_INIT, 
			                                   dbs.dsBm.bmBits, (BITMAPINFO*)&dbs.dsBmih, 
			                                   DIB_RGB_COLORS);
		
		::ReleaseDC(NULL, hdc);

		return hbm_ddb;

	}

	DeleteObject(hBm);

	return hBm;
		
}

CImage CStaticImage::GetClonedImage()
{
	return Image;
}

HBITMAP CStaticImage::GetClonedSafeHBITMAP()
{
	return static_cast(Image);
}

VOID CStaticImage::SetImage(HBITMAP hBm)
{
	
	if (!m_Image.IsNull())
	{
		m_Image.Detach();
		m_Image.Destroy();
	}

	CImage tmpImage;
	IStream *pIstream = nullptr;
	HRESULT  hr = CreateStreamOnHGlobal(NULL, TRUE, &pIstream);

	tmpImage.Attach(hBm);

	hr = tmpImage.Save(pIstream, Gdiplus::ImageFormatBMP);

	
	m_Image.Load(pIstream);

	pIstream->Release();
	tmpImage.Detach();
	tmpImage.Destroy();

}



L'applicazione



Nota:
se state utilizzando il template project di Visual Studio 2017 di cui ho detto sopra, è già presente una dialog nelle risorse.

Viceversa, aggiungiamo al progetto Visual Studio una resource di tipo Dialog e, sulla stessa, dalla toolbox aggiungiamo 2 static controls, 1 picture control un checkbox control ed un edit control.
La GUI dovrebbe essere come quella nella figura qui sotto:

drag-and-drop

Main Dialog

Main window and AboutBox dialog procedure declaration:


                /* ** ** ** ** ** ** ** ** ** ** **

                Win32 OLE Drag&Drop demo

                Copyright 2018 Giuseppe Pischedda
                All rights reserved

                https://www.software-on-demand-ita.com

                ** ** ** ** ** ** ** ** ** ** ** **/
                #pragma once

                #include "stdafx.h"
                #include "resource.h"

                /*
                Dialog based app function forward declaration
                */

                /*
                Main dlg function
                */
                INT_PTR CALLBACK MainDialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);


                /*
                Aboutbox dlg function
                */
                INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);

                

Nota:
Nell'applicazione di esempio ho intenzionalmente utilizzato delle variabili globali per il passaggio dei dati tra i moduli affinchè non fossero nascoste in classi o strutture per cercare di semplificare la lettura del sorgente.
Così com'è intenzionale, per il medesimo motivo, la scrittura dell'applicazione in plain Win32 SDK style.


Dopo aver dichiarato le window procedures per la main dialog e aboutbox dialog provvediamo a dichiarare le variabili globali:

  • HWND g_hwnd_Dlg; (global handle della main dialog)
  • CStaticImage *cImage (puntatore alla classe CStaticImage)
  • CAtlList<CString>DroppedFiles; (lista concatenata di CStrings)
  • HBITMAP g_Hbm; (HBITMAP dell'immagine correntemente caricata)
  • CString g_szText; (stringa di testo)
  • IDropSource *pDs; (puntatore a IDropSource)
  • IDataObject *pDo; (puntatore a IDataObject)
  • IDropTarget *pDt; (puntatore a IDropTarget)

Al momento di inizializzare la nostra applicazione (messaggio WM_INITDIALOG) è necessario inizializzare anche COM con la funzione:

  • OleInitialize()

La nostra applicazione è drag&drop target quindi istanziamo la classe CDropTarget e puntiamo ad essa:

  • CDropTarget::CreateInstance(&pDt);

  • quindi la registriamo quale target con:

  • RegisterDragDrop(hDlg, pDt);


Visto che è anche un'applicazione Dra&Drop source istanziamo le classi CDropSource e CDataObject:

  • CDropSource::CreateInstance(&pDs);
  • CDataObject::CreateInstance(&pDo);

Tralascio, dato che è abbastanza intuitivo, ciò che avviene negli altri messaggi tranne WM_LBUTTONDOWN ( ovvero quando l'utente fa click col pulsante sinistro del mouse) perchè è quello il momento in cui inizia l'operazione del Drag&Drop.

Appena l'utente inizia l'operazione Drag&Drop dobbiamo popolare il data object. Noi deleghiamo alla shell di windows (che conosce tutti i formati che abbiamo implementato nella classe CDataObject) questo compito chiamando la funzione:

  • SHCreateDataObject(NULL, NULL, NULL, pDo, IID_IDataObject, reinterpret_cast(&pDo));

Ora che abbiamo un data object valido lo passiamo ad OLE chiamando la funzione:

  • OleSetClipboard(pDo);

A questo punto possiamo avviare il "protocollo" che sincronizza i vari data object delle diverse applicazioni e COM per il trasferimento dei dati via OLE;
noi delegheremo quest'altro onere ancora una volta alla shell di windows chiamando la funzione:

  • SHDoDragDrop(hDlg, pDo, pDs, DROPEFFECT_COPY, &dwEffect);



Main Dialog implementation


/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
#include "stdafx.h"
#include "MainDialogFunc.h"

/*
** Global variables declaration **
*/

HWND g_hwnd_Dlg;
CStaticImage *cImage = nullptr;
CAtlList DroppedFiles;
HBITMAP g_Hbm = nullptr;
CString g_szText;


/*Drag&Drop Source and IDataObject*/
IDropSource *pDs;
IDataObject *pDo;


/*Drag&Drop Target*/
IDropTarget *pDt;

/* ** ** ** ** ** ** */

/*
** Dialog window procedure **
*/
INT_PTR CALLBACK MainDialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	
	HBRUSH hbrBkgnd = nullptr;

	switch (uMsg)
	{

	case WM_NCCREATE:
	{

		if ((BOOL)(((LPCREATESTRUCT)lParam)->lpCreateParams))
			EnableNonClientDpiScaling(hDlg);

		return DefWindowProc(hDlg, uMsg, wParam, lParam);

	}


	case WM_INITDIALOG:

	{
		
		OleInitialize(NULL);
		
		
		UINT uDpi = 96;

		// Determine the DPI to use, accoridng to the DPI awareness mode
		DPI_AWARENESS dpiAwareness = GetAwarenessFromDpiAwarenessContext(GetThreadDpiAwarenessContext());
		switch (dpiAwareness)
		{
			// Scale the window to the system DPI
		case DPI_AWARENESS_SYSTEM_AWARE:
			uDpi = GetDpiForSystem();
			break;

			// Scale the window to the monitor DPI
		case DPI_AWARENESS_PER_MONITOR_AWARE:
			uDpi = GetDpiForWindow(hDlg);
			break;

		case DPI_AWARENESS_UNAWARE:

			break;
		}

		/*
		*** Set font for DPI ***
		*/
		
		LOGFONT lfText = {};
		SystemParametersInfoForDpi(SPI_GETICONTITLELOGFONT, sizeof(lfText), &lfText, FALSE, uDpi);
		HFONT hFontNew = CreateFontIndirect(&lfText);
		SendMessage(hDlg, WM_SETFONT, (WPARAM)hFontNew, MAKELPARAM(TRUE, 0));
		
		
		g_hwnd_Dlg = hDlg;
		Button_SetCheck(GetDlgItem(hDlg, IDC_CHECK1),TRUE);
		
		/*
		*** DRAG AND DROP TARGET *** 
		*/
		HRESULT hr = E_OUTOFMEMORY;

		hr = CDropTarget::CreateInstance(&pDt);
		hr = RegisterDragDrop(hDlg, pDt);
		hr = pDt->Release();


		/*
		*** DRAG AND DROP SOURCE ***
		*/
		hr = CDropSource::CreateInstance(&pDs);
		
		
		/*
		*** IDataObject ***
		*/
		hr = CDataObject::CreateInstance(&pDo);

		
	}
	break;

	
	case WM_CTLCOLORDLG:

		/*
		*** Dlg background color ***
		*/
	
	return (INT_PTR)GetStockObject(WHITE_BRUSH);
		break;

		
		
	case WM_CTLCOLORSTATIC:
	{

		/*
		** Set controls background **
		*/
		HWND hwndCtrl = (HWND)lParam;
		HDC hdcStatic = (HDC)wParam;
		SetTextColor(hdcStatic, RGB(0, 0, 0));
		SetBkColor(hdcStatic, RGB(255, 255, 255));

		if (hwndCtrl == GetDlgItem(hDlg, IDC_STATIC1) || 
			hwndCtrl == GetDlgItem(hDlg, IDC_STATIC2) ||
			hwndCtrl == GetDlgItem(hDlg, IDC_CHECK1))
		
		{

			if (hbrBkgnd == NULL)
			{
				hbrBkgnd = CreateSolidBrush(RGB(255, 255, 255));
			}
			return (INT_PTR)hbrBkgnd;
		}
		
	

	}
	break;
	
	
	
	case WM_PAINT:
	{

		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hDlg, &ps);
		
		// TODO: Add any drawing code that uses hdc here...
		
		EndPaint(hDlg, &ps);
	}
	break;

	



	case WM_LOADIMAGE:
	{
	
		ShowWindow(GetDlgItem(hDlg, IDC_IMAGE), TRUE);
		UpdateWindow(GetDlgItem(hDlg, IDC_IMAGE));

		if (NULL != cImage)
			delete cImage;
		

		cImage = new CStaticImage(L"d:\\com_dragdrop.png");
				
				
		/*
		*** Create CImage Copy
		*/
		/*
		HBITMAP hh = cImage->Clone();

		SendMessage(GetDlgItem(hDlg, IDC_IMAGE), STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)(HBITMAP)hh);

		DeleteObject(hh);
		*/


		SendMessage(hDlg, WM_UPDATE_GUI, (WPARAM)NULL, (LPARAM)NULL);
		
		
		return TRUE;
		

	}
		break;



	case DLG_MSG_CLEAR_DROPPEDFILES_LIST:
	{
		DroppedFiles.RemoveAll();

		break;
	}

	case DLG_MSG_SET_TEXT_FROM_CLIPBOARD:
	{

		CString szText = CString((LPWSTR)lParam);

		Edit_SetText(GetDlgItem(hDlg, IDC_EDIT1), szText.GetBuffer());

		szText.ReleaseBuffer();

		break;
	}



	case DLG_MSG_GET_DROPPEDFILES_FROM_CLIPBOARD:
	{


		DroppedFiles.AddTailList((CAtlList*)(lParam));

		int count = DroppedFiles.GetCount();
		CString szText;

		POSITION pos = DroppedFiles.GetHeadPosition();


		TCHAR buff[MAX_PATH] = { 0 };
		Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), buff, MAX_PATH);

		CString fileName = DroppedFiles.GetAt(pos);
		CString extension = PathFindExtension(DroppedFiles.GetAt(pos));
		if (extension.Compare(L".png")  == 0 ||
			extension.Compare(L".jpg")  == 0 ||
			extension.Compare(L".jpeg") == 0 ||
			extension.Compare(L".bmp")  == 0)

		{
			
			if(NULL != cImage)
			delete cImage;
			
			cImage = new CStaticImage(fileName);
			SendMessage(hDlg, WM_UPDATE_GUI, NULL, NULL);
			
		}
	
		
		szText = buff;

		
		do {
						
			szText += DroppedFiles.GetNext(pos);
			szText += L"\r\n";
			
		} while (pos != NULL);

		Edit_SetText(GetDlgItem(hDlg, IDC_EDIT1), szText);

		break;
	}





		case WM_UPDATE_GUI:
		{
			

						
			
			ShowWindow(GetDlgItem(hDlg, IDC_IMAGE), SW_SHOW);
			UpdateWindow(GetDlgItem(hDlg, IDC_IMAGE));

			
			
			g_Hbm = reinterpret_cast(lParam);

				
				
				
				if (g_Hbm != NULL)
				{

					if (NULL != cImage)
						delete cImage;


					cImage = new CStaticImage(g_Hbm);
				
			    }
			
						
	
					RECT rct;
					GetClientRect(GetDlgItem(hDlg, IDC_STATIC1),&rct);

					int maxWidth = rct.right - rct.left;
					int maxHeight = rct.bottom - rct.top;
					
					CStaticImage::SCALEPERCENT scale;

					
					if (cImage->Width() > (maxWidth) || cImage->Height() > (maxHeight))
					{
						
						cImage->ScaleImage(60, scale);

					}

					else
						cImage->ScaleImage(100, scale);
					

					
					int width = scale.cWidth;
					int height = scale.cHeight;

					GetClientRect(hDlg, &rct);
					int cX = rct.right + rct.left;
					int cY = rct.bottom - rct.top;

					SendMessage(GetDlgItem(hDlg, IDC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)cImage->GetSafeHBITMAP());

					MoveWindow(GetDlgItem(hDlg, IDC_IMAGE), (cX / 2) - (width / 2),  50 , width, height, TRUE);
										
		
			
		}

			break;
		

	case WM_SIZE:
	{


		SendMessage(hDlg, WM_UPDATE_GUI, (WPARAM)NULL, (LPARAM)NULL);


	}
		break;




	case WM_LBUTTONDOWN:
	{

	
		if (NULL == cImage)
			return TRUE;
		
		HRESULT hr = E_FAIL;

		BOOL checked = Button_GetCheck(GetDlgItem(hDlg, IDC_CHECK1));
		
		if (checked)
		{
			TCHAR szString[MAX_PATH];
			Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), szString, MAX_PATH);
			g_szText = szString;
			pDo->SetData(NULL, NULL, NULL);
		}

		hr = SHCreateDataObject(NULL, NULL, NULL, pDo, IID_IDataObject, reinterpret_cast(&pDo));

		hr = OleSetClipboard(pDo);

	 	DWORD dwEffect;
		
		//Set CDataSource pointer pDs to NULL if you want to use the Shell DropSource class
		hr = SHDoDragDrop(hDlg, pDo, pDs /*NULL*/, DROPEFFECT_COPY, &dwEffect);

	}

	break;

	case WM_COMMAND:
	{
		int wmId = LOWORD(wParam);
		
		switch (wmId)
		{
		
			/*
			*** Button commands
			*/

	
		case IDCANCEL:
			SendMessage(hDlg, WM_CLOSE, 0, 0);
			return TRUE;

		case IDOK:
		{
			
			SendMessage(hDlg, WM_COMMAND, (WPARAM)IDM_IMAGE_LOAD, (LPARAM)(NULL));
			
			return TRUE;
			
		}

		/****************************************/

		/*
		*** Menu commands
		*/

		case IDM_EXIT:
			DestroyWindow(hDlg);
			break;


		case IDM_ABOUT:
			DialogBox(NULL, MAKEINTRESOURCE(IDD_ABOUTBOX), hDlg, About);
			break;


		case IDM_IMAGE_LOAD:
		{
			OPENFILENAMEW ofn;
			TCHAR szFileName[MAX_PATH] = L"";

			ZeroMemory(&ofn, sizeof(ofn));

			ofn.lStructSize = sizeof(ofn);
			ofn.hwndOwner = hDlg;
			ofn.lpstrFilter = L"Images (*.png;*.jpg;*.jpeg;*.bmp)\0*.png;*.jpg;*.jpeg;*.bmp\0PNG files (*.png)\0*.png\0JPG files (*.jpg,*jpeg)\0*.jpg\0BMP files (*.bmp)\0*.bmp\0\0";
			ofn.lpstrFile = szFileName;
			ofn.nMaxFile = MAX_PATH;
			ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;


			if (GetOpenFileName(&ofn))
			{
				CString fileName = ofn.lpstrFile;
				CString extension = PathFindExtension(fileName);
                			
				ShowWindow(GetDlgItem(hDlg, IDC_IMAGE), TRUE);
				UpdateWindow(GetDlgItem(hDlg, IDC_IMAGE));

				if (NULL != cImage)
					delete cImage;


				cImage = new CStaticImage(fileName);

			   SendMessage(hDlg, WM_UPDATE_GUI, (WPARAM)(NULL), (LPARAM)(NULL));

			

			}
		}
			
		break;




		case IDM_IMAGE_CLEAR:
		{

			SendMessage(GetDlgItem(hDlg, IDC_IMAGE), STM_SETIMAGE, (WPARAM)IMAGE_BITMAP,(LPARAM)(NULL));
		}
		break;


		case IDM_CLIPBOARD_CLEAR:
		{
		
			OpenClipboard(hDlg);
			EmptyClipboard();
			CloseClipboard();

		
		
		}
			break;




		case IDM_IMAGE_COPYTOCLIPBOARD:
		{

			if (NULL == cImage)
				break;

			HBITMAP hBm = cImage->Clone();

			if (NULL != hBm)
			{
				OpenClipboard(NULL);
				EmptyClipboard();
				SetClipboardData(CF_BITMAP, hBm);
				CloseClipboard();
			}
			DeleteObject(hBm);
		}
		break;

		/************************/	
		

		default:
			return DefWindowProc(hDlg, uMsg, wParam, lParam);
		}
	}
	break;


	case WM_CLOSE:
		
	{
		DestroyWindow(hDlg);
	}
	return TRUE;

	case WM_DESTROY:
		OleUninitialize();
		delete cImage;
		RevokeDragDrop(hDlg);
		DeleteObject(hbrBkgnd);
		PostQuitMessage(0);
		return TRUE;
	}

	return FALSE;
}




// Message handler for about box.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;

	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}





"stdafx.h"


/* ** ** ** ** ** ** ** ** ** ** **

Win32 OLE Drag&Drop demo

Copyright 2018 Giuseppe Pischedda
All rights reserved

https://www.software-on-demand-ita.com

** ** ** ** ** ** ** ** ** ** ** **/
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"


#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <Windowsx.h>

// C RunTime Header Files
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>


// TODO: reference additional headers your program requires here
//Common controls
#include <commctrl.h>

//Common dialogs
#include <Commdlg.h>

//OLE
#include <Ole2.h>
#include <OleIdl.h>

//ATL for CComPtr
#include <atlbase.h>
#include <atlstr.h>
#include <atlcoll.h>
#include <atlimage.h>

//Shell
#include <shlobj.h>
#include <shellapi.h>
#include <Shlwapi.h>

//Sal annotation
#include <sal.h>


#include "CStaticImage.h"
#include "CDropTarget.h"
#include "CDropSource.h"
#include "CDataObject.h"



#pragma comment(lib, "ComCtl32.lib")


#pragma comment(linker, \
  "\"/manifestdependency:type='Win32' "\
  "name='Microsoft.Windows.Common-Controls' "\
  "version='6.0.0.0' "\
  "processorArchitecture='*' "\
  "publicKeyToken='6595b64144ccf1df' "\
  "language='*'\"")
  

#define WM_LOADIMAGE WM_APP + 1
#define WM_UPDATE_GUI WM_APP + 2
#define DLG_MSG_CLEAR_DROPPEDFILES_LIST WM_APP + 3
#define DLG_MSG_GET_DROPPEDFILES_FROM_CLIPBOARD WM_APP + 4
#define DLG_MSG_SET_TEXT_FROM_CLIPBOARD WM_APP + 5

Se tutto è andato per il meglio nella scrittura del codice, eseguiamo la nostra applicazione OLE Drag&Drop source e target e potremo trasferire o ricevere dati, da e verso tutte le applicazioni che supportano OLE (compresa la nostra).

Per testare la nostra applicazione provate a fare drag&drop di un gruppo di file da Windows Explorer, un' immagine dal vostro desktop, stringhe di testo e immagini da Microsoft Word, Excel etc.




Processing request, please wait...


OLE Drag&Drop Source

GitHub repository   

OLE Drag&Drop Source





Giuseppe Pischedda 2018


Ehi, se ti va puoi offrirmi un caffè.


Ehi, se ti va puoi offrirmi un caffè.

Informativa estesa art.13 d.lgs. 196/03 ed art. 13 del Regolamento UE 2016/679 del 27 aprile 2016 (privacy)