Get system default menu popup alignment/direction - winapi

I'd like to find out what the default menu popup alignment/direction is. When you use the main menu in an application or use a context popup menu it opens up to the right, centered or to the left - depending on the Windows version, some custom registry settings or left/right-hand options.
There must be a way to get that information using some Windows API (instead of building a complex function for different Windows versions / preferences / registry modifications etc.).
I need this to apply that information to some custom elements in my application that I want to open into the same direction as all other menus on that system. This is a Delphi application, but if it's an API call then I'd be happy about a solution in a language of your choice.

Thanks to the comments of Raymond Chen and Jonathan Potter here's a function that does the trick:
function PopupMenusRightAligned(): Boolean;
var
res: Integer;
begin
SystemParametersInfo(SPI_GetMenuDropAlignment, 0, #res, 0);
Result := res=0;
end;
API documentation: https://msdn.microsoft.com/ms724947

Related

Dynamically insert number on Delphi app's icon

I'm on Delphi 10.4.
I'm looking for a way to dynamically insert a number on the app's icon on the taskbar, so the user can know about how many tasks the apps has done so far. This would be dynamically, as soon as the app does a new task, it will increase the icon's number.
Something like the image below.
Is this possible ?
I don't have any code to post here because i don't have any idea how to do this.
You might not be aware of the TTaskbar taskbar-configuration component and its OverlayIcon property.
Example:
with a very straightforward implementation:
procedure TForm1.btnInfoClick(Sender: TObject);
var
io: TIcon;
begin
io := TIcon.Create;
try
io.Handle := LoadIcon(0, IDI_INFORMATION);
Taskbar1.OverlayIcon := io
finally
io.Free;
end;
end;
In your case, you can either create icons 1.png, 2.png, ... non-programmatically and use those, or you can create icons programmatically (create a CreateOverlayIcon(ANumber: Integer): TIcon function).
I should warn you, however, that the TTaskbar component used to be (very) buggy. Therefore I would not use that one; instead, I would use the ITaskbarList3::SetOverlayIcon API directly.
In any case, my suggestion is to split your problem into two parts:
Create overlay icons.
Use the Windows 7 taskbar overlay icon feature to display these on top of your original icon.

How to ungroup popup window from the application on the taskbar?

Is it possible to instruct the shell taskbar to exclude a certain hwnd popup window from the main application's button "group"?
I have a "stopwatch" popup window. On my machine, with taskbar button combining disabled, the windows appears exactly as i'd like it: a separate item on the taskbar:
But if the user uses (the default, and most corporations prevent users from altering their personal preferences), the separate window is not visible:
Now, i am using ITaskbarList3.SetOverlayIcon to specify an overlay icon for my popup window:
list: ITaskbarList3;
list := CoTaskbarList3.Create;
list.SetOverlayIcon(windowHandle, ico, '');
so Windows will at least do me the favour of picking the most recent overlay icon, and applying it to the combined visual group - which is nice.
But i'd still prefer to have this separate action in a separate item on the taskbar.
One horrible workaround would be to ship another executable with my application; one just to fool the grouper into putting this other hwnd in its own group. But that's not something i want to do.
Why would i think i'm allowed to do this?
I was surprised to learn that you are allowed to prevent the user from pinning an application to the taskbar.
And while the MSDN page on shell programming doesn't give an example specifically for what i want, that doesn't mean it's not out there. It just might mean that it's all poorly documented.
In my case, the "operator" has a stopwatch. When they start the stopwatch, i pop out the form of the application group by giving it a different User Model Application ID:
SetWindowAppModelUserID('Contoso.Frobber.Stopwatch');
and immediately the form pops out:
When the user stops (or pauses) the stopwatch, i let the window coalesce back into the application:
SetWindowAppModelUserID('');
Caveats
You don't actually set the AppModelUserID to an empty string; that is not a valid application identifer. Instead you set it to VT_EMPTY. My helpful wrapper function checks for '', and converts it into a VT_EMPTY value.
Another warning comes from the SDK:
A window's properties must be removed before the window is closed. If this is not done, the resources used by those properties are not returned to the system. A property is removed by setting it to the PROPVARIANT type VT_EMPTY.
This means that care needs to be taken to remove a custom property applied to an HWND before i destroy it. A somewhat daunting task; one that i will almost certainly screw up.
The Code
function TFormEx.SetWindowAppModelUserID(const AppModelUserID: WideString): HRESULT;
var
ps: IPropertyStore;
value: OleVariant;
const
PKEY_AppUserModel_ID: TPropertyKey = ( fmtid: '{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}'; pid: 5);
begin
Result := SHGetPropertyStoreForWindow(Self.Handle, IPropertyStore, {out}ps);
if Failed(Result) then Exit;
if (ps = nil) then
begin
Result := E_FAIL;
Exit;
end;
if AppModelUserID <> '' then
begin
value := AppModelUserID;
Result := ps.SetValue(PKEY_AppUserModel_ID, PROPVARIANT(value));
end
else
begin
{
A window's properties must be removed before the window is closed.
If this is not done, the resources used by those properties are not returned to the system.
A property is removed by setting it to the PROPVARIANT type VT_EMPTY.
}
value := Unassigned; //the VT_EMPTY type
Result := ps.SetValue(PKEY_AppUserModel_ID, PROPVARIANT(value));
end;
end;
Bonus Reading
What if my application is really two applications bundled into a single file, and I want them collected into two groups on the taskbar in Windows 7?
How do I prevent users from pinning my program to the taskbar?
SHGetPropertyStoreForWindow function
Note: Any code released into public domain. No attribution required.

Win32 custom message box

I want to make a custom message box. What I want to customize is the button's text.
MessageBoxW(
NULL,
L"Target folder already exists. Do you want to overwrite the folder?",
L"No title",
MB_YESNOCANCEL | MB_ICONQUESTION
);
I'd like to just change the buttons text to Overwrite, Skip, Cancel.
What's the most simple way?
I have to make this as having same look and feel with Windows default messagebox.
As said by others, a typical way is to create a dialog resource and have a completely independent dialog, which GUI you need to design in the way that it looks like standard dialog (to meet your request for feel and look). If you want to accept text messages, you might probably need to add code which resizes the window appropriately.
Still, there is another option for those who feel like diving into advanced things. While MessageBox API does not offer much for fint tuning, you still have SetWindowsHookEx in your hands. Having registgered the hook, you can intercept standard MessageBox window procedure and subclass it in the way you like.
Typical things include:
changing button text
adding more controls
adding timed automatic close
Hooking standard window can do all of those.
UPD. Hey, I realized I have some code with SetWindowsHookEx to share: http://alax.info/blog/127
You could create an own dialog. Or you could use a window hook as described in this article.
An archived version of the article can be found on web.archive.com.
Make a dialog resource (with a GUI editor, or by hand) and call DialogBox on it. There's no way to alter MessageBox behaviour, other than what's supported by its arguments.
That said, your message box can very well use stock Yes/No options.
The task dialog functionality introduced in Vista does exactly what you want and follows the prevailing system theme. However, if you have to support XP, then this will be of little comfort to you.
I know this question is old, but I just stumbled upon it now.
I would like to expand the other answers in regards to using a TaskDialog instead of a MessageBox. Here's a concise example of using a TaskDialog to do precisely what was asked; change the button's texts:
const TASKDIALOG_BUTTON buttons[] = { {IDYES, L"Overwrite"}, {IDNO, L"Skip"}, {IDCANCEL, L"Cancel"} };
TASKDIALOGCONFIG taskDialogConfig = {
.cbSize = sizeof(TASKDIALOGCONFIG),
.pszMainIcon = TD_WARNING_ICON, // TaskDialog does not support a question icon; see below
.pButtons = buttons,
.cButtons = ARRAYSIZE(buttons),
.pszWindowTitle = L"No title",
.pszContent = L"Target folder already exists. Do you want to overwrite the folder?"
};
TaskDialogIndirect(&taskDialogConfig, NULL, NULL, NULL);
Some noteworthy things:
You need to use TaskDialogIndirect, not the basic TaskDialog function
when not specifying a parent window, the icon specified in pszMainIcon is displayed in the title bar as well
There is no equivalent to the MessageBox's MB_ICONQUESTION, quoting a quote from this forumpost: Don't use the question mark icon to ask questions. Again, use the question mark icon only for Help entry points. There is no need to ask questions using the question mark icon anyway—it's sufficient to present a main instruction as a question.
checking which button was selected would have to be done by passing a pointer to an int as the second argument of TaskDialogIndirect and checking its value on return (the documentation should be pretty clear)
Here is a small open source library that allows you to customize Message Boxes. Developed by Hans Ditrich.
I have successfully used it in another POC that allows embedding a custom icon in such MessageBox that can be called even from a Console application.
I should also point to the Task Dialog. Here is an example of using it:
int nButtonPressed = 0;
TaskDialog(NULL, hInst,
MAKEINTRESOURCE(IDS_APPLICATION_TITLE),
MAKEINTRESOURCE(IDS_DOSOMETHING),
MAKEINTRESOURCE(IDS_SOMECONTENT),
TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON,
TD_WARNING_ICON,
&nButtonPressed);
if (IDOK == nButtonPressed)
{
// OK button pressed
}
else if (IDCANCEL == nButtonPressed)
{
// Cancel pressed
}

Delphi - How to prevent Forms/MsgBoxes to move under prior form?

Many times after the Windows 98 era we have experienced that some dialogs lose their Z-Order and move back to the prior form.
For example:
Dialog1.ShowModal;
Dialog1.OnClickButton() : ShowMessage('anything');
When MessageBox appears, it sometimes doesn't have focus and is moved under Dialog1.
The users are confused about it, they say: My application froze!!!
But if they use Alt+Tab to move to another app and back, the focus returns to the MessageBox and it will be the foreground window.
We have experienced this with ShowMessage, MessageBox, normal forms, and also QuickReport forms.
Does anyone know about this? Is it a Windows bug? How can you prevent it? How to catch this?
Thanks for your help:
dd
I really said that AFTER Win98, so all OSs (Win7 also) are affected by this problem.
We used Delphi 6 Prof, so the properties are not working with Default forms.
Somebody said that message dialogs are controllable with MessageBox + MB_APPLMODAL.
This is good news, but we have many old forms and components, third party tools.
So it is hard work to make a completely new application with substitution of the forms.
But we will try doing this.
I think the answer is this is a half application problem and half a Windows problem. If Windows sometimes handles this, and sometimes doesn't - that seems to be a Windows bug.
But if we can force good modal window making then it is a programming bug.
Can somebody explain to me what is the meaning of the WS_POPUP flag?
Does it have some side effect or not?
Thanks:
dd
That's what the PopupMode and PopupParent properties are for.
E.g., you can do:
Dialog1.PopupMode := pmExplicit;
Dialog1.PopupParent := self;
Dialog1.ShowModal;
This tells Windows the correct Z-order.
For old versions of delphi (prior to Delphi 2007), on forms OTHER than your main form:
interface
TMyForm = Class(TForm)
protected
procedure CreateParams(var Para: TCreateParams); override;
end;
...
implementation
...
procedure TMyForm.CreateParams(var Para: TCreateParams);
begin
inherited;
Para.Style := Para.Style or WS_POPUP;
{ WinXP Window manager requires this for proper Z-Ordering }
// Para.WndParent:=GetActiveWindow;
Para.WndParent := Application.MainForm.Handle;
end;
For message boxes include MB_TOPMOST in your flags:
Application.MessageBox(PChar(amessage), PChar(atitle), otherflags or MB_TOPMOST);
I looked at this page and the FAQ for half an hour and still can't find how to post a comment, so forgive me for this breach of protocol.
First of all I'd like to make clear that the poster, IMHO, is not using Windows 98. He writes "after Windows 98 era" which I understand means he is having this problem with Windows versions after 98.
As I am having this problem too (CB2009), I'd like to emphasize the poster's question "Is it Windows bug?", which I have not seen answered. If it's a Delphi/Builder bug, maybe there is a way to avoid it? I can't see how intercepting all potential dialogs is a workable solution, nor avoid using fsStayOnTop. I have a settings form that needs to stay on top of my main form, but the settings form can and will popup dialogs that under certain conditions will disappear under the settings form.
It would be very helpful if I would understand where the support of z-order goes wrong, as it may offer a clue on how to avoid it.
A trick I've used recently was to apply these two lines of code during the creation of each form:
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopWindow);
Handle is the handle of the form (Form1.Handle). The WS_EX_APPWINDOW part makes each window appear on the task bar, remove it if you don't want that additional effect.
For my main form I use this line:
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
WS_EX_TOPMOST);
I also use this function to help build my custom dialogs (I created a new function for each style of dialog - error, confirmation, etc.):
function CustomDlg(const AMessage : string; const ADlgType: TMsgDlgType;
const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn) : TForm;
begin
Result := CreateMessageDialog(AMessage, ADlgType, AButtons, ADefaultButton);
with Result do
begin
SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopwindow);
FormStyle := fsStayOnTop;
BringToFront;
end;
end;
The FormStyle := fsStayOnTop; part is optional, of course, but I use it to make sure my confirmation and error dialogs are always visible to the user.
It seems like a bit of work but the net effect is that I no longer have to worry about forms accidentally hiding behind other forms.

How can I make a systray (notification area) icon receive WM_MOUSEWHEEL messages?

I want to extend an existing application I made to make it set mixer volume by wheel-scrolling over it's notification area icon.
As far as I know, the notification area doesn't receive any WM_MOUSEWHEEL messages, but still I found an application that does exactly what I want to achieve (http://www.actualsolution.com/power_mixer).
Using WinspectorSpy I've noticed some strange messages the application's form receives: 0x000003d0 and 0x000003d1, but I found no references about them.
Does anyone have any idea on how I could achieve the desired functionality?
If you want to capture mouse/keyboard events outside of your application you will need Low-level Hooks.
A nice beginners article about installing a mouse hook in Delphi is How to Hook the Mouse to Catch Events Outside of your application on About.com written by Zarko Gajic.
The user which starts your application will need administrative rights to install a hook.
After you capture the message you should determine if it's above your icon in the notification bar (which can be difficult because there is no exact api to get your position on the bar) and than process the scroll event.
I explained about the mouse hooking, and mentioned it could be difficult to locate your exact icon. I did found the following article about how to locate a tray icon.
CTrayIconPosition - where is my tray icon? by Irek Zielinski. I think if you try to understand how it works you can turn it around and use it to check if your mouse is currently positioned above your icon.
But you should first check if the mouse is even in the tray area. I found some old code of mine (2005) which locates the correct region.
var
hwndTaskBar, hwndTrayWnd, hwndTrayToolBar : HWND;
rTrayToolBar : tRect;
begin
hwndTaskBar := FindWindowEx (0, 0, 'Shell_TrayWnd', nil);
hwndTrayWnd := FindWindowEx (hwndTaskBar , 0, 'TrayNotifyWnd',nil);
hwndTrayToolBar := FindWindowEx(hwndTrayWnd, 0, 'ToolbarWindow32',nil);
Windows.GetClientRect(hwndTrayToolBar, rTrayToolBar);
end
Using this piece of code and the knowledge from the mentioned article I think you could implement the functionality that you wanted.
Not sure if this will solve the problem but it might be worth a try as a starting point. You could create a top level transparent window that you then position over the top of the taskbar icon. That top level window will receive mouse notifications when the mouse is over it. You can then process them as required. How to find the screen location of the taskbar icon is something I do not know and so that might be an issue.
Id don't know if this will help you, but in Delphi the standard Message library states:
WM_MOUSEWHEEL = $020A;
It would also be helpful if you let as know which language you are using.
Just thought I'd point out that Power Mixer is capturing scroll wheel events on the whole taskbar, while the mouse middle click operates just on the sys tray icon.

Resources