Is it possible to create a GetOpenFileName dialog in a fullscreen app? - windows

I have a fullscreen application wrtten in C++ and would like to open a dialog window so the user can select a file to be opened without the app breaking out of fullscreen mode.
On Windows, to run in fullscreen mode, I call ChangeDisplaySettings(&settings, CDS_FULLSCREEN). (Technically, I am using SDL, but this is the call it uses.)
To open the file dialog, I use the following code:
HWND hWnd = NULL;
SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if( SDL_GetWMInfo(&wmInfo) ) {
hWnd = wmInfo.window; // Note: This is sucessful, and hWnd != NULL
}
OPENFILENAMEW ofn;
wchar_t fileName[MAX_PATH] = L"";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = fileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST;
if( GetOpenFileNameW( &ofn ) ) {
DoSomethingWith( fileName );
}
When run, hWnd is not NULL, but when this dialog is created, it shifts the window focus to the dialog, which breaks out of the fullscreen app, similar to alt-tabbing to another window while in fullscreen. Once the file is selected and the Open File dialog closes, I have to manually switch back to the fullscreen app.
Is it possible to do what I want using existing Windows dialogs, or do I have to write my own in-app file browsing system or run in windowed mode only?

Sure... you just have to pass the HWND of the full screen window as the parent of the Open File common dialog (it is the hwndOwner parameter in the OPENFILENAME structure that is passed to GetOpenFileName).

Related

Clipboard data won't get pasted

I'm tryng to load into and paste data from the clipboard like this:
int main() {
Sleep(3000);
char buf[] = "Hello33";
HWND hwnd = GetActiveWindow();
if (OpenClipboard(hwnd)) {
EmptyClipboard();
HGLOBAL hClipboardData;
hClipboardData = GlobalAlloc(GMEM_DDESHARE, BUFLEN);
char * pchData;
pchData = (char*) GlobalLock(hClipboardData);
strcpy(pchData, LPCSTR(buf));
GlobalUnlock(hClipboardData);
SetClipboardData(CF_TEXT, hClipboardData);
CloseClipboard();
SendMessage(hwnd, WM_PASTE, 0, 0);
}
}
Starting the program, then open text editor, text editor is the top window and no text is pasted. If I do the paste command Ctrl-V I got Hello33 into the text area.
GetActiveWindow returns the main window not the child window
you should try GetFocus();
but even this will not work, be cause it is in an other thread that belong to other process.
to resolve that, call AttachThreadInput
finally i suggest to drop all of this, and try FindWindow instead and send message directly to the Edit Box, by using SendDlgItemMessage

How to simulate selection change in the "file type" ComboBox in GetSaveFileName dialog from a separate process?

I'm writing a custom module to work with a proprietary software. (That software has been discontinued and I do not have its source code.) My module will run as a separate process. Its goal is to automate an operation via this proprietary software. To do that I need to be able to simulate saving the output from this software. I can bring up its Save As dialog via simulating a toolbar button click:
I then attempt to change the "Save as type" combo box to the file type required, add file path to save to, and simulate the click on the Save button. I came up with the following code to do that:
//All I have to work with are the following window handles:
//HWND hWndFileName = file name window handle
//HWND hWndSaveAsType = save as type window handle
//HWND hWndSaveBtn = Save button handle
DWORD dwComboIDSaveAsType = ::GetWindowLong(hWndSaveAsType, GWL_ID);
HWND hParWnd = ::GetParent(hWndSaveAsType);
//Select index for file type
int nSaveAsIndex = 3;
::SendMessage(hWndSaveAsType, CB_SETCURSEL, nSaveAsIndex, 0);
::SendMessage(hParWnd, WM_COMMAND,
(dwComboIDSaveAsType & 0xffff) | ((((DWORD)CBN_SELCHANGE) << 16) & 0xFFFF0000),
(LPARAM)hWndSaveAsType);
//Set path to save
::SendMessage(hWndFileName, WM_SETTEXT, NULL,
(LPARAM)L"C:\\Test\\test file");
//Simulate Save button click
::SendMessage(hWndSaveBtn, BM_CLICK, 0, 0);
Interestingly, the code above achieves the goal (by visually change the "Save as type" combo box) but when the file is saved, it still has the old, or originally selected type, i.e. "Quick Report file (.QRP)".
Any idea how to fix this?
I think I got it. Evidently I needed to send CBN_SELENDOK to the parent also. So it should be this:
//All I have to work with are the following window handles:
//HWND hWndFileName = file name window handle
//HWND hWndSaveAsType = save as type window handle
//HWND hWndSaveBtn = Save button handle
DWORD dwComboIDSaveAsType = ::GetWindowLong(hWndSaveAsType, GWL_ID);
HWND hParWnd = ::GetParent(hWndSaveAsType);
//Select index for file type
int nSaveAsIndex = 3;
::SendMessage(hWndSaveAsType, CB_SETCURSEL, nSaveAsIndex, 0);
::SendMessage(hParWnd, WM_COMMAND,
MAKEWPARAM(dwComboIDSaveAsType, CBN_SELENDOK),
(LPARAM)hWndSaveAsType);
::SendMessage(hParWnd, WM_COMMAND,
MAKEWPARAM(dwComboIDSaveAsType, CBN_SELCHANGE),
(LPARAM)hWndSaveAsType);
//Set path to save
::SendMessage(hWndFileName, WM_SETTEXT, NULL,
(LPARAM)L"C:\\Test\\test file");
//Simulate Save button click
::SendMessage(hWndSaveBtn, BM_CLICK, 0, 0);

Icon added to notification tray disappears on mouse over

I want my application to have an icon in the notification area in Windows 7. I used Shell_NotifyIcon to add the icon. The icon appears, but when I bring the mouse pointer over the icon, the icon disappears. The application is running the whole time. The icon isn't hidden, it just disappears.
Shell_NotifyIcon returns a non-zero value, which means it succeeds.
Here's the relevant code:
static const int ID_TRAYICON = 300;
static const int MSG_TRAYICON = WM_USER + 1;
NOTIFYICONDATA nid;
void InitTrayIconData()
{
memset(&nid, 0, sizeof(NOTIFYICONDATA));
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hwnd;
nid.uID = ID_TRAYICON;
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
nid.uCallbackMessage = MSG_TRAYICON;
nid.hIcon = LoadIcon(nullptr, IDI_APPLICATION);
//nid.uVersion = NOTIFYICON_VERSION_4;
lstrcpy(nid.szTip, TEXT("Data Aggregator in-dev version"));
}
Then while processing the WM_CREATE message:
InitTrayIconData();
Shell_NotifyIcon(NIM_ADD, &nid);
And while processing WM_DESTROY:
Shell_NotifyIcon(NIM_DELETE, &nid);
I've also noticed that for some reason the MSG_TRAYICON message is never called.
I figured it out. When I called InitTrayIconData() in WM_CREATE, the global hwnd hadn't been assigned the value returned from CreateWindowEx yet (the WM_CREATE message wasn't sent after the CreateWindowEx call, but during it, which I didn't know). So the line,
nid.hWnd = hwnd;
just equated nid.hWnd to nullptr (which is what I had initialized hwnd to).
I fixed the problem by passing the hwnd argument in WndProc to the InitTrayIconData(), so it would use that hwnd instead of the global hwnd.
This happens when the system can't communicate with the application that owns the notification icon.
Normally this is because the process has terminated abnormally. In your case you state that the process is running the whole time. Thus I can only conclude that the window handle associated with the notification icon has been destroyed, or is not responding to messages correctly. That diagnosis also tallies with your observation that you do not receive MSG_TRAYICON.

Increasing the size of console output display

Can we change/increase the size of console output to view large size of data in console application at once?
There seem to be different ways to Rome:
This should be the recommended way I would think, cause the name says it all: GetConsoleWindow as is demonstrated here.
A quick hack might be the windows API function SendInput. If you simulate Alt+Enter, you could probably fullscreen the active window.
Here are some propositions using API calls from user32.dll
Check out the SetConsoleScreenBufferInfoEx API. It takes a CONSOLE_SCREEN_BUFFER_INFOEX as input and that has a dwSize member which contains the size of the console screen buffer, in character columns and rows.
MSDN for SetConsoleScreenBufferInfoEx Function: http://msdn.microsoft.com/en-us/library/ms686039(v=VS.85).aspx
I once used a small hack that is first setting the console's output buffer and then trying to find the console window and resize it. It worked well on XP, I never tested it on newer Windows versions.
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
SMALL_RECT sr;
sr.Top = 0;
sr.Left = 0;
sr.Bottom = 10;
sr.Right = 79;
SetConsoleWindowInfo(h, TRUE, &sr);
TCHAR title[512];
GetConsoleTitle(title, 512);
HWND hWnd = FindWindow(NULL, title);
if(hWnd != NULL) {
HWND hWndInsertAfter = NULL;
UINT nFlags = SWP_NOSIZE | SWP_NOZORDER;
#if 0 // Don't move?
nFlags |= SWP_NOMOVE;
#endif
SetWindowPos(hWnd, hWndInsertAfter , 40, 350, 0, 0, nFlags);
SetForegroundWindow(hWnd);
}
If you are using the command prompt window, right click it's label on the task bar and click the Properties option.

Determining if a Window Has a Taskbar Button

I am looking for a way to check if a given window has a taskbar button. That is, given a handle to a window, I need a TRUE if the window is in the taskbar, and FALSE otherwise.
Conversely, I am wondering if there is a way to get a handle to the window that belongs to a given taskbar button, which I suppose would require a way to enumerate through the taskbar buttons.
(The first former is the part that I need, and the latter part is optional.)
Thanks a lot.
Windows uses heuristics to decide whether or not to give a taskbar button to a window, and sometimes there is a delay before it can decide, so doing this 100% accurately is going to be quite hard. Here's a rough start on the rules. There are modern style flags that make it easy to know, but when those styles are missing the taskbar is reduced to guessing.
First off, you will need both of the the window style flags.
LONG Style = GetWindowLong(hwnd, GWL_STYLE);
LONG ExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
Now the rules, there are three rules that are certain.
if ExStyle & WS_EX_APPWINDOW, then TASKBAR
if ExStyle & WS_EX_TOOLWINDOW, then NOT_TASKBAR
if Style & WS_CHILD then NOT_TASKBAR
The rest are guesses:
Style & WS_OVERLAPPED suggests TASKBAR
Style & WS_POPUP suggests NOT_TASKBAR especially if GetParent() != NULL
ExStyle & WS_EX_OVERLAPPEDWINDOW suggests TASKBAR
ExStyle & WS_EX_CLIENTEDGE suggests NOT_TASKBAR
ExStyle & WS_EX_DLGMODALFRAME suggests NOT_TASKBAR
I'm sure that there are other rules for guessing, and in fact that the guessing rules have changed from version to version of Windows.
Toplevel window
WS_EX_APPWINDOW -> taskbar, no matter the other styles!
OWNER must be NULL (GetWindow(window, GW_OWNER))
no: WS_EX_NOACTIVATE or WS_EX_TOOLWINDOW:
order is important.
second question: in windows xp/vista it was possible to get into the process of the taskbar and get all window ID´s:
void EnumTasklistWindows()
{
int b2 = 0;
TBBUTTON tbButton;
DWORD dwProcessId = 0, dwThreadId = 0;
HWND hDesktop =::GetDesktopWindow();
HWND hTray =::FindWindowEx(hDesktop, 0, ("Shell_TrayWnd"), NULL);
HWND hReBar =::FindWindowEx(hTray, 0, ("ReBarWindow32"), NULL);
HWND hTask =::FindWindowEx(hReBar, 0, ("MSTaskSwWClass"), NULL);
HWND hToolbar =::FindWindowEx(hTask, 0, ("ToolbarWindow32"), NULL);
LRESULT count =::SendMessage(hToolbar, TB_BUTTONCOUNT, 0, 0);
dwThreadId = GetWindowThreadProcessId(hToolbar, &dwProcessId);
shared_ptr<void> hProcess (OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId), CloseHandle);
if (NULL == hProcess.get())
{
return;
}
memset(&tbButton, 0, sizeof(TBBUTTON));
for (int i = 0; i < count; i++)
{
memset(&tbButton, 0, sizeof(TBBUTTON));
shared_ptr<void> lpRemoteBuffer (
VirtualAllocEx(hProcess.get(), NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE),
bind<BOOL>(VirtualFreeEx, hProcess.get(), _1, 0, MEM_RELEASE));
if (NULL == lpRemoteBuffer.get())
{
return;
}
SendMessage(hToolbar, TB_GETBUTTON, i, (LPARAM) lpRemoteBuffer.get());
b2 = ReadProcessMemory(hProcess.get(), lpRemoteBuffer.get(),
(LPVOID) & tbButton, sizeof(TBBUTTON), NULL);
if (0 == b2)
{
continue;
}
BYTE localBuffer[0x1000];
BYTE *pLocalBuffer = localBuffer;
DWORD_PTR ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
pLocalBuffer = localBuffer;
ipLocalBuffer = (DWORD_PTR) pLocalBuffer;
DWORD_PTR lpRemoteData = (DWORD_PTR) tbButton.dwData;
ReadProcessMemory(hProcess.get(), (LPVOID) lpRemoteData, (LPVOID) ipLocalBuffer,
sizeof(DWORD_PTR), NULL);
HWND windowHandle;
memcpy(&windowHandle, (void *) ipLocalBuffer, 4);
if (windowHandle != NULL)
{
trace ("adding button: %x\n", windowHandle);
}
}
}
this not possible with windows 7 anymore. so you need to loop over all toplevel windows.
This MSDN article has some good information about when and why the Shell decides to create a taskbar button for a window:
The Shell creates a button on the taskbar whenever an application creates a window that isn't owned. To ensure that the window button is placed on the taskbar, create an unowned window with the WS_EX_APPWINDOW extended style. To prevent the window button from being placed on the taskbar, create the unowned window with the WS_EX_TOOLWINDOW extended style. As an alternative, you can create a hidden window and make this hidden window the owner of your visible window.

Resources