Click here to Skip to main content
15,891,184 members
Articles / Programming Languages / C++

Drag and Drop on a Tray Icon

Rate me:
Please Sign up or sign in to vote.
2.80/5 (4 votes)
6 Aug 2007CPOL2 min read 53.9K   885   23  
How to detect that an item was dropped on a specific tray icon
//*********************************************************************************
//
// Title: Terminal Services Drag Drop
//
//*********************************************************************************
#define _UNICODE

#include "DDHook.h"
#include "ProcessData.h"

#include <Windowsx.h>	// MapWindowRect
#include <commctrl.h>

struct TRAYDATA
{
	HWND hwnd;				
	UINT uID;				
	UINT uCallbackMessage;	
	DWORD Reserved[2];		
	HICON hIcon;				
};

#define	DLL_EXPORT extern "C" __declspec(dllexport)

// Shared DATA
#pragma data_seg ( "SHAREDDATA" )

	// this is the total number of processes this dll is currently attached to
	int				iInstanceCount		= 0;
	HWND			g_hWnd				= 0;

#pragma data_seg ()

#pragma comment(linker, "/section:SHAREDDATA,rws")

bool			bHooked        = false;
HHOOK			hhook	       = 0;//msgs
HINSTANCE		hInst	       = 0;

BOOL APIENTRY DllMain( HINSTANCE hinstDLL, DWORD  ul_reason_for_call, LPVOID lpReserved )
{
    switch (ul_reason_for_call)
	{
		case DLL_PROCESS_ATTACH:
			{
				// remember our instance handle
				//OutputDebugString("DDHook dll loaded...");
				hInst = hinstDLL;
				++iInstanceCount;

				TCHAR szMsg[256] = {0};
				//wsprintf(szMsg,L"Tray Rectangle: %d-%d %d-%d",R.left,R.right,R.top,R.bottom);
				wsprintfW(szMsg,L"DDHook dll loaded...,iInstanceCount: %d", iInstanceCount);
				OutputDebugStringW(szMsg);

				 break;
			}
		case DLL_THREAD_ATTACH:
				break;
		case DLL_THREAD_DETACH:
			break;
		case DLL_PROCESS_DETACH:
			{
				//OutputDebugString("DDHook dll unloaded...");
				--iInstanceCount;
				
				
				TCHAR szMsg[256] = {0};
				//wsprintf(szMsg,L"Tray Rectangle: %d-%d %d-%d",R.left,R.right,R.top,R.bottom);
				wsprintfW(szMsg,L"DDHook dll unloaded...,iInstanceCount: %d", iInstanceCount);
				OutputDebugStringW(szMsg);

				//CloseVirtualChannel();
			}
			break;
    }

    return TRUE;
}

void QueryDragDropFileNames(HDROP hDrop)
{
	// this could be used later to actually drop stuff directly onto windows in the RDP session
	POINT pt = {0};
	if (DragQueryPoint(hDrop,&pt))
	{
		TCHAR szText1[MAX_PATH] = {0};
		wsprintfW(szText1,L"Dropped at pos: x[%d] y[%d]", pt.x,pt.y);
		OutputDebugStringW(szText1);
	}
	else
	{
		OutputDebugStringW(L"Failed to run DragQueryPoint()");
	}

	UINT cFiles = DragQueryFile(hDrop, 0xFFFFFFFF/*(UINT)-1*/, NULL, 0);	
	
	TCHAR szText[MAX_PATH] = {0};
	wsprintfW(szText,L"Drag/Drop %d files/directories:", cFiles);
	OutputDebugStringW(szText);

	for( UINT indx = 0; indx < cFiles; indx++ )
	{
		TCHAR szFile[MAX_PATH] = {0};
		UINT cChars = DragQueryFileW(hDrop, indx, szFile, sizeof(szFile));
		if (cChars > 0)
		{
			OutputDebugStringW(szFile);
		}
	}
}

HWND FindSysPagerWindow()
{
	HWND hWnd = FindWindowEx(GetDesktopWindow(), 0, "Shell_TrayWnd", NULL);
	if(hWnd)
	{
		hWnd = FindWindowEx(hWnd,NULL,"TrayNotifyWnd", NULL);
		if(hWnd)
		{
			hWnd = FindWindowEx(hWnd,NULL,"SysPager", NULL);
		}
	}
	return hWnd;
}

HWND FindTrayToolbarWindow()
{
	HWND hWnd = FindWindow("Shell_TrayWnd", NULL);
	if(hWnd)
	{
		hWnd = FindWindowEx(hWnd,NULL,"TrayNotifyWnd", NULL);
		if(hWnd)
		{
			hWnd = FindWindowEx(hWnd,NULL,"SysPager", NULL);
			if(hWnd)
			{				
				hWnd = FindWindowEx(hWnd, NULL,"ToolbarWindow32", NULL);
			}
		}
	}
	return hWnd;
}

BOOL FileDroppedAtIcon(const HWND a_hWndOwner, const int a_iButtonID, const PPOINT pPoint)
{
    HWND hWndTray = FindTrayToolbarWindow();

    //now we have to get an ID of the parent process for system tray
    DWORD dwTrayProcessID = -1;
    GetWindowThreadProcessId(hWndTray, &dwTrayProcessID);

    //here we get a handle to tray application process
    HANDLE hTrayProc = OpenProcess(PROCESS_ALL_ACCESS, 0, dwTrayProcessID);
 
    //now we check how many buttons is there - should be more than 0
    int iButtonsCount = SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);

    //We want to get data from another process - it's not possible 
    //to just send messages like TB_GETBUTTON with a locally
    //allocated buffer for return data. Pointer to locally allocated 
    //data has no usefull meaning in a context of another
    //process (since Win95) - so we need 
    //to allocate some memory inside Tray process.
    //We allocate sizeof(TBBUTTON) bytes of memory - 
    //because TBBUTTON is the biggest structure we will fetch. 
    //But this buffer will be also used to get smaller 
    //pieces of data like RECT structures.
    LPVOID lpData = VirtualAllocEx(hTrayProc, NULL, 
                    sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);

    BOOL bIconFound = FALSE;

    for(int iButton=0; iButton<iButtonsCount; iButton++)
    {
        //first let's read TBUTTON information 
        //about each button in a task bar of tray

        DWORD dwBytesRead = -1;
        TBBUTTON buttonData;
        SendMessage(hWndTray, TB_GETBUTTON, iButton, (LPARAM)lpData);

        //we filled lpData with details of iButton icon of toolbar 
        //- now let's copy this data from tray application
        //back to our process
        ReadProcessMemory(hTrayProc, lpData, &buttonData, sizeof(TBBUTTON), &dwBytesRead);

        //let's read extra data of each button: 
        //there will be a HWND of the window that 
        //created an icon and icon ID
        DWORD dwExtraData[2] = { 0,0 };
        ReadProcessMemory(hTrayProc, (LPVOID)buttonData.dwData,dwExtraData, sizeof(dwExtraData), &dwBytesRead);

        HWND hWndOfIconOwner = (HWND) dwExtraData[0];
        int  iIconId         = (int)  dwExtraData[1];

        if(hWndOfIconOwner != a_hWndOwner || iIconId != a_iButtonID)
        {
            continue;
        }

        //we found our icon - in WinXP it could be hidden - let's check it:
        if( buttonData.fsState & TBSTATE_HIDDEN )
        {
            break;
        }

        //now just ask a tool bar of rectangle of our icon
        RECT rcPosition = {0,0};
        SendMessage(hWndTray, TB_GETITEMRECT, iButton, (LPARAM)lpData);
       // ReadProcessMemory(hTrayProc, lpData, lprcPosition/*&rcPosition*/, sizeof(RECT), &dwBytesRead);
		ReadProcessMemory(hTrayProc, lpData, &rcPosition, sizeof(RECT), &dwBytesRead);

		MapWindowRect(hWndTray,NULL,&rcPosition);

		if (pPoint->x > rcPosition.left && pPoint->x < rcPosition.right &&
			pPoint->y > rcPosition.top && pPoint->y < rcPosition.bottom)
		{
			OutputDebugString("Stuff dropped at our icon...!!!");	

			bIconFound = TRUE;
			break;
		}

        break;
    }

    VirtualFreeEx(hTrayProc, lpData, NULL, MEM_RELEASE);
    CloseHandle(hTrayProc);

    return bIconFound;
}

LRESULT CALLBACK GetMsgProc(UINT nCode, WPARAM wParam, LPARAM lParam)
{
	if (nCode == HC_ACTION) 
	{
		MSG* msg = (MSG*)lParam;
		HWND hWnd = msg->hwnd;

		if (msg->message == WM_DROPFILES)
		{
			OutputDebugString("WM_DROPFILES received"); 

			TCHAR szText1[MAX_PATH] = {0};
			wsprintfW(szText1,L"nCode: [%d] wParam: [%d] lParam: [%d]",nCode,wParam,lParam);
			OutputDebugStringW(szText1);

			RECT R_DROP = {0};
			GetWindowRect( msg->hwnd, &R_DROP );

			TCHAR szMsg[256] = {0};
			wsprintfW(szMsg,L"DropRectangle: %d-%d %d-%d",R_DROP.left,R_DROP.right,R_DROP.top,R_DROP.bottom);
			OutputDebugStringW(szMsg);

			wsprintfW(szMsg,L"DropPoint: x:%d y:%d",msg->pt.x,msg->pt.y);
			OutputDebugStringW(szMsg);

			wsprintfW(szMsg,L"nCode: [%d] wParam: [%d] lParam: [%d]",msg->time,msg->wParam,msg->lParam);
			OutputDebugStringW(szMsg);

			HWND hSysPager = FindSysPagerWindow();
			if (hSysPager != NULL)
			{				
				RECT R2 = {0};
				GetWindowRect( hSysPager, &R2 );

				// check if the drop area is the same as the tray area...
				if (R_DROP.left == R2.left &&
					R_DROP.right == R2.right &&
					R_DROP.bottom == R2.bottom &&
					R_DROP.top == R2.top)
				{
					HWND hToolbarWindow32 = FindTrayToolbarWindow();
					if (hToolbarWindow32)
					{
						DWORD dwTrayPid = 0;
						GetWindowThreadProcessId(hToolbarWindow32, &dwTrayPid);

						int count = (int)SendMessage(hToolbarWindow32, TB_BUTTONCOUNT, 0, 0);
						CProcessData<TBBUTTON> data(dwTrayPid);
						TBBUTTON tb = {0};
						TRAYDATA tray = {0};

						for(int i=0; i<count; i++)
						{		
							SendMessage(hToolbarWindow32, TB_GETBUTTON, i, (LPARAM)data.GetData());		
							data.ReadData(&tb);			
							data.ReadData<TRAYDATA>(&tray,(LPCVOID)tb.dwData);

							if (tray.hwnd == g_hWnd && FileDroppedAtIcon(g_hWnd,i,&msg->pt))
							{
								SendMessage(g_hWnd,msg->message,msg->wParam,msg->lParam);
							}
						}
					}
				}
			}
		}
	}
	else
	{
		//return CallNextHookEx(hhook, nCode, wParam, lParam);
	}

	return CallNextHookEx(hhook, nCode, wParam, lParam);
}

DLL_EXPORT void SetMsgHook(HWND hWnd)
{
	if (!bHooked)
	{
		hhook	= SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, hInst, (DWORD)NULL);
		bHooked	= true;	

		g_hWnd  = hWnd;
	}
}

DLL_EXPORT	void RemoveMsgHook(void)
{
	if (bHooked) 
	{
		UnhookWindowsHookEx(hhook);	
		bHooked = false;

		g_hWnd  = NULL;
	}
}

DLL_EXPORT int GetInstanceCount()
{
	return iInstanceCount;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) a large company
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions