Reputation: 1006
First, to clarify, I am not asking how to drag-and-drop a file onto an exe's icon. I want to know how to handle drag and drop onto an already running win32 console application. I'm also not asking how to handle drag and drop inside of WinMain based applications through the Windows message pump. I want to do this inside of a program with the entry point int main() that doesn't have a WndProc (yet) or anything.
That said, I'm wondering if my goal is achievable (and hoping that it is).
I have a server application that is running within a console window. Due to a large codebase and a lot of weird coupling, it is an 'output only' console for all intensive purposes. Within it though, I can still handle things like key presses, as I have an update loop ticking. I'd like to be able to drag and drop files full of commands (which use a custom syntax) onto my running application and have it process them.
Is this possible to do? I was thinking that potentially I could get a pointer to the HWND of the console (which hopefully is a thing?), and then maybe subclass that window to use a custom WndProc to listen for the WM_DROPFILES message.
I've never really tried to set up handling of windows messages in an int main() program instead of a WinMain program, but I'm hoping it's somehow possible.
Any help would be greatly appreciated! Weird solutions are fine.
Upvotes: 7
Views: 6161
Reputation: 1
After spending about two days’ worth of researching about this subject, I was not able to find a single reliable resource describing the console window’s message loop. I found some unreliable ones (they were AI generated) describing vaguely that you could tap into the console window’s message loop, but then precise information on how to do this is lacking everywhere. Anyway, I finally gave up completely when the following code was not able to detect windows messages directed to the console window. I must give credit to Sz about the insularity of the "CSRSS" process.
/*
This is the final work which shows whether it is possible
or not to track messages sent to the command window. All
tests have shown that this is not possible.
15-04-2024
*/
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam);
char textString[] = "Click this area to create a Console Window";
HWND hConsoleWindow = (HWND)1; // needs to be global
HANDLE ohandle, ihandle; // needs to be global
LPDWORD lpThreadId; // needs to be global (!)
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
HWND hWnd;
MSG lpMsg;
WNDCLASS wndclss;
TCHAR szBuffer[2056];
if(!hPrevInst)
{
wndclss.lpszClassName = "Console Message Test";
wndclss.hInstance = hInst;
wndclss.lpfnWndProc = WndProc;
wndclss.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclss.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclss.lpszMenuName = NULL;
wndclss.hbrBackground = GetStockObject(WHITE_BRUSH);
wndclss.style = 0;
wndclss.cbClsExtra = 0;
wndclss.cbWndExtra = 0;
if(!RegisterClass(&wndclss)) return FALSE;
}
hWnd = CreateWindow("Console Message Test", "Console Message Test",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 200, NULL,
NULL, hInst, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&lpMsg, NULL, 0, 0)) // This is the message loop
{
TranslateMessage(&lpMsg);
//hConsoleWindow = FindWindow("ConsoleWindowClass", NULL); // activate this line facultatively, as a test. Might have an effect if no other conole window is open.
/* the above line will terminate the program if no console is present. All this means is that a NULL was returned. */
if(lpMsg.hwnd == hConsoleWindow) return( lpMsg.wParam);
/* (the above line) if any message reaches the console window through this loop, the program closes! */
//if(lpMsg.message == WM_DROPFILES) return( lpMsg.wParam); // activate this line facultatively (as a test)
/* the above line will make the program close if a file is dropped into the white area */
DispatchMessage(&lpMsg);
}
return(lpMsg.wParam);
}
DWORD WINAPI ConsoleThread(LPVOID Param)
{
DWORD dwCount, dwMsgLen;
TCHAR szBuffer[2056];
for(;;){
ReadConsole(ihandle, szBuffer, sizeof(szBuffer) - 2, &dwCount, NULL);
WriteConsole(ohandle, "You typed: ", 11, &dwMsgLen, NULL);
WriteConsole(ohandle, szBuffer, dwCount, &dwMsgLen, NULL);
}
}
// Main Window Procedure
LRESULT CALLBACK WndProc(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT pstruct;
static HANDLE hThread = NULL;
switch(messg)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &pstruct);
TextOut(hdc, 0, 0, textString, (sizeof(textString) - 1));
EndPaint(hWnd, &pstruct);
break;
case WM_LBUTTONDOWN:
AllocConsole();
hConsoleWindow = FindWindow("ConsoleWindowClass", NULL);
if(hConsoleWindow) Beep(1000, 250); // high note when console detected
//DragAcceptFiles(hConsoleWindow, TRUE); // this line seems to have no effect
DragAcceptFiles(hWnd,TRUE); /* this line makes the white area accept dragged
objects once it is clicked */
if(hThread == NULL) {
ohandle = GetStdHandle(STD_OUTPUT_HANDLE);
ihandle = GetStdHandle(STD_INPUT_HANDLE);
hThread = CreateThread(NULL, 0, ConsoleThread, NULL, 0, lpThreadId);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_DROPFILES:
Beep(500, 250); // low note when file drop detected
break;
default:
return(DefWindowProc(hWnd, messg, wParam, lParam));
}
return(0);
}
PS I take the OP to refer to XP/Windows Server 2003 era Windows OSes’ command line. Later Windows OSes are different (i.e. multiple items dropped are accepted by default in a console program.)
Upvotes: -1
Reputation: 13134
#include <vector>
#include <string>
#include <iostream>
#include <conio.h>
int main()
{
std::cout << "Please drop files and press [Enter] when done ...\n";
std::vector< std::string > files;
for( int ch = _getch(); ch != '\r'; ch = _getch() ) {
std::string file_name;
if( ch == '\"' ) { // path containing spaces. read til next '"' ...
while( ( ch = _getch() ) != '\"' )
file_name += ch;
} else { // path not containing spaces. read as long as chars are coming rapidly.
file_name += ch;
while( _kbhit() )
file_name += _getch();
}
files.push_back( file_name );
}
std::cout << "You dropped these files:\n";
for( auto & i : files )
std::cout << i << '\n';
}
Upvotes: 2
Reputation: 595295
AFAIK, a console window does not support drag&drop by default. You can always create your own separate popup window with its own message loop so the user has something to drag items onto.
To use drag&drop on the console window itself, try using GetConsoleWindow()
to get the console HWND, then either:
subclass the HWND using SetWindowLong/Ptr()
or SetWindowSubClass()
, then register the HWND using DragAcceptFiles()
to start receiving WM_DROPFILES
messages. Be sure to call DragAcceptFiles()
again to stop receiving the messages and then unhook your subclass before exiting the app.
implement the IDropTarget
interface and then register the HWND using RegisterDragDrop()
to start receiving notifications. Be sure to call RevokeDragDrop()
before exiting the app.
WM_DROPFILES
is easier to code for, but IDropTarget
is more flexible as it handles virtual items as well as physical files.
Upvotes: 7