Drag-and-Drop is great, if you have some place suitable to drop. Making such a place need not be expensive.
Introduction
Microsoft Window's File Manager is a convenient enough tool for some file operations; however, when programming, it often falls short. One limitation is its lack of a convenient method to start a file browser, text editor, or hex editor with a file whose association isn't set appropriately. How many times have you double clicked on a readme.doc file and started up MS Word instead of Notepad, or tried to view a .c file by double clicking in the file manager and instead started up the whole IDE, or received the message 'No application is associated with this file'. The File Manager does provide the ability to drag files from the file manager onto an application, but this only works if the application can accept dragged files and is already running.
My solution to File Manager's inconvieniences is DragTo, a drag and drop utility. DragTo is a very small utility that allows you to drop files onto the DragTo icon and start your favorite program to operate on those files. You can configure DragTo at compile time (by modifying the resource compiler script) to start any program that accepts files as command-line arguments. DragTo has low memory requirements and has a small desktop footprint, an important factor with today's cluttered desktops. The version I use the most is DragToNotepad, and I will use this as the example application.
DragToNotepad is distinctive because it always appears as an icon. Any file you drop on the icon opens its own Notepad. If multiple files are dropped on DragToNotepad, a new Notepad starts for each file. For unsophisticated users, DragToNotepad might be improved by having it check that the input file is a text file; however, the purpose of the program is to overcome File Manager's file associations, not to introduce new limitations.
DragToNotepad's purpose imposes certain implementation constraints. Since it is only used as a place to drop files, a full DragToNotepad window might be confusing; therefore I prevent it from being maximized or restored to normal window size. The lack of a standard window means there is no way to access the menu, so DragToNotepad must provide some other way to access the functions that would normally be found on the menu bar. Specifically, DragToNotepad must provide a way to exit the program and must provide access to the About dialog box. In this case, I utilize DragToNotepad's system menu. (That's the menu that pops up when you single click on the upper-left-hand corner of a window, or single click on an iconized program.)
The Program
DragToNotepad is a simple application, and while it does not behave like a typical Windows program, the code is quite standard. Like most Windows programs, DragToNotepad has to register a window class, create a window, and process Windows message in a Windows procedure. In addition, DragToNotepad must keep the window minimized no matter what the user does, add menu items to the system menu and process their messages, and, of course, process files dropped from the File Manager.
The DragToNotepad source includes .h, .c, .def, and .rc files. dragto.c (Listing 1) provides all the code for the program. Because DragTo and its variants were designed for easy code reuse, dragto.c and dragto.h (Listing 2) do not contain hard-coded strings for the application name (that is, the title that appears beneath the DragToNotepad icon), Windows class name, or name of the program to be executed. dragto.rc (Listing 3) contains all the information needed to change DragTo from DragToNotepad into DragToImageEdit or DragToHexEdit, specifically, the window class name, the application name (i.e., Notepad), the icon to be displayed, and the About dialog box. dragto.def (Listing 4) provides Windows-specific information to the linker. It can be left unchanged, but contains the name of the application and a comment on what the program does. The application name specified by dragto.def is not the name the user sees, but names the DragTo executable itself.
In WinMain (Listing 1) , DragToNotepad loads a WndClass structure and registers the window class much like other Windows programs. One important difference is that DragTo does not know its window class name or application name until run time. DragToNotepad calls LoadString to get the window class name and other implementation-specific strings it will need. It then stores them into global variables. (These strings are added to the executable by the resource compiler, which I will discuss briefly later.) There are a couple of things worth noting about the way the program fills in the WndClass structure. First, many items, including style and menu, get initialized to zero or NULL since they don't pertain to a minimized window. Second, the class name is loaded from the variable szClassName, which was loaded earlier by LoadString.
After the WndClass structure is loaded, DragToNotepad registers the window class, and then creates the window by calling CreateWindow. A couple of lines down DragToNotepad informs Windows that it can accept files, by calling DragAcceptFiles. Next DragToNotepad modifies the system menu. Before modifying a menu, DragToNotepad must know its handle, so it calls GetSystemMenu and stores the returned handle in hmenu for future use.
Adding menu items to the system menu is easy; DragToNotepad calls the AppendMenu function. Like most Windows API calls, the first parameter is a handle, in this case hmenu. The second parameter specifies the type of menu item, which is MF_STRING for a normal menu item and MF_SEPARATOR for a separation line. The third parameter to AppendMenu is the menu ID, which should be a unique unsigned integer. The program will use this ID later to determine which menu item is pressed. Since it is convienent to use a symbolic name for the ID, IDM_ABOUT is #defined in DragTo.h. AppendMenu's last parameter holds the string to be displayed in the menu.
Besides adding menu items, a program can remove items from a menu with DeleteMenu. The first five system menu items ('restore', 'move,' 'size,' 'minimize,' and 'maximize') all modify the window state and do not pertain to a window which is always an icon. It's possible to remove most of these items, but this can be tricky and should be approached carefully. When I attempted to remove the 'move' system menu item, the system menu disappeared completely, so while the other four items are removed, 'move' remains.
Now that the system menu is initialized, DragToNotepad can display the window. DragToNotepad calls ShowWindow with SW_MINIMIZED to start the program as an icon.
Responding to Windows Messages
Like other Windows programs, DragToNotepad includes a window procedure (MainWndProc) to process Windows message.The first message typically added to a window procedure is WM_DESTROY. Processing this message allows an application to clean up before quitting. When DragToNotepad receives WM_DESTROY it calls PostQuitMessage. The other messages that DragToNotepad processes are more application specific. It will process WM_SYSCOMMAND to process the window's system menu, WM_QUERYOPEN to keep the window minimized, and WM_DROPFILES to process the files that are dragged from the file manager.
DragToNotepad responds to system menu items by processing WM_SYSCOMMAND messages. It does so in the same manner that most applications process WM_COMMAND messages to implement a standard menu bar. wParam holds the menu ID. If the user has chosen 'About,' DragToNotepad displays the About box, otherwise, it passes the message on to the default window procedure so it can handle any of the other options. If the message is not sent on to the default window procedure, the system menu will not work.
DragToNotepad's design specifies that it should always remain an icon. Windows sends a WM_QUERYOPEN message whenever it wants a window to change to full-size from an icon. DragToNotepad bypasses the default processing that would restore the window; in effect, it eats the message and returns zero to tell Windows not to open the window.
Using Dropped Files
The above takes care of DragToNotepad's general behavior. Now it's time to implement the guts of the application processing files dragged from the file manager. DragToNotepad already informed Windows that it wants to accept dragged files; now it must process WM_DROPFILES messages in order to do something with the files. In a WM_DROPFILES message, wparam contains a handle to a non-documented data structure that holds the names of the dropped files. To use this data, DragToNotepad must use the Drag-Drop API functions. It has already used DragAccept in WinMain to inform Windows that it can accept dropped files. DragToNotepad uses two Drag-Drop functions to process WM_DROPFILES: DragQuery and DragFinish. DragQuery retrieves data from the internal data structure and DragFinish releases the data structure. (The one Drag-Drop function DragToNotepad does not use is DragQueryPoint. It returns the mouse position where the files were dropped.)
Function On_DropFiles (Listing 1) contains all the code to process the dropped files. This function is called in response to the WM_DROPFILES message. I store the file information handle from wparam into hFilesInfo. This is not really necessary, but it prevents having to typecast wparam to HANDLE in the rest of the Drag-Drop functions. On_DropFiles retrieves the number of files dropped by calling DragQueryFiles with the second parameter set to -1. It later uses DragQueryFiles to get the names of the dropped files.
The for loop executes Notepad.exe or some other command for each file dropped. I could have hard-coded the string and path into the program, but to make the program reusable, I made DragTo get the path/name from a stringtable. Recall that DragToNotepad retrieved the command name in WinMain by calling LoadString. The command name ("Notepad.exe") was stored in szCommandName. This will be the program called by DragToNotepad to operate on the files dropped. For every file dropped, DragTo retrieves the file name with DragQueryFiles. It then calls ShellExecute to start Notepad. ShellExecute conveniently takes the command name as one parameter and the rest of the command-line options as a second parameter. The last parameter to ShellExecute specifies the window style of the notepad being created; SW_SHOW opens the Notepad as a normal window. The other parameters are set to NULL to specify default action.
Since ShellExecute is called each time through the for loop, DragToNotepad creates a new instance of Notepad for each file. There is an alternative for applications that accept multiple files. The loop could be used to assemble a string of filenames separated by whitespace (file1 file2 file3), and this list could be sent to the application. Since one of the design objectives of DragTo was to get the most reuse without recompiling the .c file, I have chosen the one application per file approach as it works with more applications.
The final step in processing WM_DRAGFILES is to free the resources allocated by the unseen data structure, by calling DragFinish. This is important, because Windows 3.1 is very susceptible to memory leaks. Whenever a windows resource is allocated, it must later be deallocated. Windows 95 and Windows NT are much more robust, but forgetting to deallocate resources is still a primary source of program errors.
Customizing DragTo with Resource Files
All the executable code for DragToNotepad is contained in dragto.c, but the strings that cause Notepad to be called and the specific window class to be registered are contained in dragto.rc. The .rc stands for resource compiler, and rc.exe (provided with your compiler) binds these resources to the executable. You use the resource files to specify menus, icons, bitmaps, and stringtables to be used in the program. DragToNotepad uses the stringtable to specify windows class names and application names, allowing customization of the program without editing the C files.
The structure of dragto.rc is fairly standard. First come the #include statements, and then the resources. (The order in which the resources are listed doesn't matter.) The stringtable declaration is simply a list of ID numbers and strings. The ID numbers IDS_COMMAND, IDS_NAME, and IDS_CNAME are defined as 1, 2, and 3 respectively in dragto.h with #define statements. When the program calls LoadString, it indexes one of the strings in the string table. (While it is called a StringTable, it is not a simple array. Even if the string ID's were 601, 701, and 801, the performance and memory allocation would be the same.)
The icon resource has special significance in DragToNotepad. Since DragToNotepad will only be seen as an icon, choosing a descriptive icon is important. The icon resource line declares the form, identifier string "ICON," memory use (DISCARDABLE), and icon file name. The identifier string will be used when loading the icon into the windows class by LoadIcon.
The final resource in dragto.rc is the About dialog box. Most people edit dialog boxes with a dialog editor, but they can be edited by hand. When I modify this program, I usually modify the text in the About box by hand, unless I need to change the position of the text with the dialog box.
By isolating all of the changeable data in the .rc file, modifying DragToNotepad into DragToWrite would entail changing just the three strings in the string table to "write.exe," "DT Write," and "DragToWriteClass." Then find a new icon and change the text in the About box. Now recompile. Voila, instant utility.
Always Room for Improvement
There are plenty of improvements that can be made to DragTo. One which I mentioned earier is to accept multiple files and start one application with a file list. Another would be to allow the command name to be changed at run time. This can easily be done with a dialog box and a global string variable. A third improvement would be to allow the user to start the application without a file by double clicking on the icon. Once you've modified DragTo to your satisfaction, you can start using it with simple applications, and working on new applications that come to mind.
Carl Zmola is a consulting computer programmer specializing in Microsoft Windows programs. He has a BA in Physics from the University of Chicago and an MS in Computer Science from the University of Illinois at Chicago. He currently resides in Smithfield Utah. When he is not programming, he can be found fly-fishing. He can be reached by e-mail at zmola@doc.sunrem.com.