/* DDCLIENT.C -- Illustrates 3.0 and 3.1 Drag and Drop protocols Copyright (c) Dave Maxey 1992 From Chapter 6 of "Undocumented Windows" (Addison-Wesley 1992) by Andrew Schulman, Dave Maxey and Matt Pietrek Build using: WINIOBC DDCLIENT (for Borland C++ v3.00) WINIOMS DDCLIENT (for Microsoft C/SDK) DDCLIENT.C is slightly changed from the way it appears in the book because the DRAGOBJ_ constants (from DRAGDROP.H) have been split out. The DDCLIENT.C in the text incorrectly assumed that DRAGOBJ_PROGRAM, for example, included the DRAGOBJ_EXTERNAL flag. */ #include #include #include #include "wmhandlr.h" #include "winio.h" #include "dragdrop.h" #ifndef __BORLANDC__ #define MK_FP(a,b) ((void far *)(((unsigned long)(a) << 16) | (b))) #endif BOOL tInClientArea = FALSE; // We also get documented drag and drop in 3.1 // Here we don't use the documented API, but rather manipulate the // undocumented DROPINFO structure directly. long my_dropfiles(HWND hwnd, WORD wMsg, WORD wParam, DWORD lParam) { LPDROPINFO lpDropInfo; int i = 0; LPSTR lpszFile; printf("WM_DROPFILES received\n"); lpDropInfo = (LPDROPINFO) GlobalLock(wParam); lpszFile = (LPSTR) &lpDropInfo->chBuffer; while (*lpszFile) { i++; printf("%02d File name : %Fs\n", i, lpszFile); lpszFile = (LPSTR) ((DWORD) lpszFile + lstrlen(lpszFile) + 1); } GlobalUnlock(wParam); // Replicates the functionality of DragFinish! GlobalFree(wParam); return 1; } // Handles WM_DROPOBJECT - Sent when user releases left button // inside our client area. long drop_handler(HWND h, WORD wMsg, WORD wParam, DWORD lParam) { LPOFSTRUCT lpofstruct; LPDRAGINFO lpDragInfo; LPSTR lpTail, lpHead, lpFileName; WORD wSourceDS; int i = 0; char szPath[120]; char *szFile; BOOL bPath; static const char *szObjType[] = {"executable", "data/text file", "directory", "multiple files/dirs"}; // Reset ready for next drag/drop tInClientArea = FALSE; lpDragInfo = (LPDRAGINFO) lParam; // Get source app (File Manager, presumably) DS wSourceDS = (GetWindowWord(lpDragInfo->hwndSource, GWW_HINSTANCE) & 0xfffc) | 1; // Use wFlags field (without top bit, and decremented to // zero-base) as index to get object type string. printf("Drop in progress...\n" "------------------------------\n" "Object type : %s\n", szObjType[(lpDragInfo->wFlags - 1) & 3]); // hOfstruct only non-NULL when a single file, and // extension has association if (lpDragInfo->hOfstruct) { lpofstruct = (LPOFSTRUCT) MK_FP(lpDragInfo->hOfstruct, 0); printf("01 File name : %Fs\n\t(Extension has association)\n" "------------------------------\n" "Drop completed successfully\n\n", lpofstruct->szPathName); return DRAG_FILE; } // This returns a list of files and complete directory paths // by forming a far pointer into the source application's near // heap and parsing to space characters. If a list item has no // path, we assume that the source app is or emulates FileMan, // and use the source window title to obtain the directory. // This technique is not only a hack, but it only works in v3.0! // In 3.1 we can use the documented API. if ((GetWindowText(lpDragInfo->hwndSource, (LPSTR) szPath, sizeof(szPath))) || (GetWindowText(GetParent(lpDragInfo->hwndSource), (LPSTR) szPath, sizeof(szPath)))) { szFile = szPath + strlen(szPath); while (szFile && (*szFile != '\\')) szFile--; ++szFile; } else szFile = szPath; lpTail = (LPSTR) MK_FP(wSourceDS, (WORD) (lpDragInfo->szList)); do { // Separate out next 'token', record whether it contains a path lpHead = lpTail; bPath = FALSE; while (*lpTail != ' ') if (*lpTail++ == '\\') bPath = TRUE; *lpTail = 0; // if list item has no path, use szPath. if (! bPath) { lstrcpy((LPSTR) szFile, lpHead); lpFileName = (LPSTR) &szPath; } else lpFileName = lpHead; printf("%02d %s : %Fs\n", ++i, bPath ? "Directory" : "File name", lpFileName); *lpTail++ = ' '; } while (*lpTail); printf("-----------------------------\n" "Drop completed successfully\n\n"); return DRAG_FILE; } // Handles WM_QUERYDROPOBJECT - Sent whenever the object being // dragged moves over our window. long query_handler(HWND h, WORD w, WORD wParam, DWORD lParam) { LPDRAGINFO lpDragInfo = (LPDRAGINFO) lParam; // Dragged icon has reached client area (wParam == 0). // Since lParam is a DRAGINFOPTR, we can decide whether we // want to accept on the basis of file-type, and whether or // not 'associated'. This code is only here to illustrate // the point, and is a pointless and redundant test, // and accepts all object types. if ((wParam == 0) && ((lpDragInfo->wFlags == DRAGOBJ_PROGRAM | DRAGOBJ_EXTERNAL) || (lpDragInfo->wFlags == DRAGOBJ_DATA | DRAGOBJ_EXTERNAL) || (lpDragInfo->wFlags == DRAGOBJ_DIRECTORY | DRAGOBJ_EXTERNAL) || (lpDragInfo->wFlags == DRAGOBJ_MULTIPLE | DRAGOBJ_EXTERNAL) || (lpDragInfo->hOfstruct != NULL))) return 1; else return 0; } // Handles WM_DRAGSELECT - Sent whenever the object being // dragged moves into or out of our client area. We don't actually // need to handle this one. long select_handler(HWND h, WORD w1, WORD w2, DWORD l) { printf("Drag %s\n", (tInClientArea ^= 1) ? "in progress" : "suspended"); return 1; } // Handles WM_DRAGMOVE - Sent whenever the object being // dragged is dragged after we have signaled acceptance of it. // We don't actually need to handle this one, either. long move_handler(HWND h, WORD w1, WORD w2, DWORD l) { return 1; } int main() { winio_settitle(__hMainWnd, "Drag'n'Drop Client"); winio_about("DDCLIENT" "\nIllustrates 3.0 and 3.1 Drag and Drop protocols" "\n\nFrom Chapter 6 of" "\n\"Undocumented Windows\" (Addison-Wesley, 1992)" "\nby Andrew Schulman, David Maxey and Matt Pietrek" ); wmhandler_set(__hMainWnd, WM_DROPOBJECT, (WMHANDLER) drop_handler); wmhandler_set(__hMainWnd, WM_QUERYDROPOBJECT, (WMHANDLER) query_handler); wmhandler_set(__hMainWnd, WM_DRAGSELECT, (WMHANDLER) select_handler); wmhandler_set(__hMainWnd, WM_DRAGMOVE, (WMHANDLER) move_handler); wmhandler_set(__hMainWnd, WM_DROPFILES, (WMHANDLER) my_dropfiles); printf( "This program lists any files dragged over and\n" "dropped on it from the accompanying DDSERVER\n" "program, or from File Manager, or any other\n" "program that uses the undocumented 3.0 or\n" "partially documented 3.1 protocol.\n\n" "Waiting for drag'n'drop messages...\n\n"); return 0; }