Recreate windows' own tray icon window in another place - winapi

I have an application like WindowBlinds that recreates window look at all. But i'll now stuk with tray window.
I disassemble Shell_NotifyIcon() func and found it uses then Shell_NotifyIconW() func then just by ordinal (from shellw.dll) function 215 to create,modify and remove icon's.
In this funcition then I found i can find WndProc by hwnd (in disassembled code i am getting hwnd by FindWindowW(L"Shell_TrayWnd", 0);) but how?
Anybody work with this?
Please help.

Explorer just gets a WM_COPYDATA message IIRC, so you could subclass (you need to be in the same process) and catch that message, or interact with the tray icon list with toolbar messages, see the open source TraySaver app.
You could also take a look at some of the open source alternative shells, they have a lot of this undocumented stuff figured out. I linked to a couple of them in a somewhat related answer.

Related

How to use WinAPI , prevent/cancel a window closing?

I want to prevent users from closing a window by Alt + F4 or by clicking the close button.
How to achieve this?
I guess the windows API can do it, but I don't have any experience, and I can't find a specific solution.
Of course, it's good to be able to implement it,don't have to use a specific API.
Background: it is very difficult to find the last place in Word after closing it for a few days. After word2013, word2013 brought with it a way to return to the previous reading position, but that thing is very unstable and often can't be saved. When word is closed, I want to stop closing and pop up a notice to remind me to add a bookmark before exiting.
EDIT: This won't work, as it turned out. At least the message hook won't work because the message is posted and not sent, and about the CBT hook I'm not sure either, and I can't test it at the moment to give an evidence-based statement. The solution is probably to subclass the window but this is also non-trivial and I can't explain it properly and with working examples right now. I can't delete this answer though because it already has a comment. See here for more info. So take it with a grain of salt. I'm turning the answer to community wiki, feel free to edit it and fix/improve the solution!
EDIT2: Seems even subclassing won't be enough because Word is doing things its own way.
You need a windows hook. Either a CBT hook or a getmessage hook will do.
You have to create a DLL for this to work. The hook handler must be located in the DLL. It must have the same bitness as Word (probably 64 Bit). Then you call SetWindowsHookEx to install a global hook.
In the hook, you will have to check whether the current action is a window-closing attempt (in a CBT hook you would check for a HCBT_SYSCOMMAND of SC_CLOSE, in a getmessage hook you would check for a WM_CLOSE message), and whether it is about a Word window (for example using the window class - not sure if it has a recognizable class, you'd have to check - or the process' executable file name which you can get using GetModuleFileName since you will run inside Word's process) and prevent the action (by returning 1 from a CBT hook or returning 0 from a getmessage hook - to allow, call CallNextHookEx).

How to disable copy/paste commands in the Windows edit control context menu?

How can I disable those 3 standard cut/copy/paste commands in the context menu of the native Windows OS edit control?
I also need to disable the equivalent clipboard-related commands like CTRL+C/CTRL+V.
Is there a special edit control style or anything else we can use to disable all copy/paste operations with one easy setting?
Typically, when a control displays a popup menu, a WM_INITPOPUPMENU message is generated which "allows an application to modify the menu before it is displayed, without changing the entire menu."
Unfortunately, a standard Win32 Edit control does not generate that message for its default popup menu, as confirmed in a November 2000 article of MSDN Magazine (the link on MSDN itself is dead, but this link is from the Internet Archive):
MSDN Magazine, November 2000, C++ Q&A:
Q: Why isn't a WM_INITMENUPOPUP message generated when you right-click an edit control?
A: I can't tell you why there isn't one, but I can confirm it's true ... edit controls don't send WM_INITMENUPOPUP. The edit control must be calling TrackPopupMenu with a null HWND handle and/or TPM_NONOTIFY, which tells the menu not to send notifications. It's possible (and again I'm only guessing) that the authors were trying to improve performance by reducing message traffic ... In any case, suppose you want to add your own menu items to the edit control context menu. How do you do it? Alas, you have no choice but to reinvent the wheel
So the only option available is to subclass the edit control and handle the WM_CONTEXTMENU message instead, creating and displaying your own custom popup menu as needed. Which means you have to manually duplicate the functionality of any standard menu items that you want to appear in your custom menu.
Update: there is a way to access and modify the edit control's standard popup menu after all (I just tested it and it worked). TecMan provided a link to a VBForums discussion that talks about it, however it gets a few details wrong. I got the correct details from a PureBasic forum discussion.
The correct approach is as follows:
subclass the edit control to intercept the WM_CONTEXTMENU message. Either SetWindowSubClass() or SetWindowLongPtr(GWL_WNDPROC) can be used, though the first is preferred.
when the WM_CONTEXTMENU message is received, call SetWindowsHookEx() to install a thread-local hook (use 0 for the hMod parameter and GetCurrentThreadId() for the dwThreadId parameter). Either a WH_CBT or WH_CALLWNDPROC hook can be used. Then dispatch WM_CONTENTMENU to the default message handler via DefSubclassProc() or CallWindowProc() to invoke the standard popup menu.
inside the hook procedure, when a HCBT_CREATEWND (WH_CBT hook) or WM_CREATE (WH_CALLWNDPROC hook) notification is received, pass the provided HWND to GetClassName(). If the class name is #32768 (the standard window class name for menus, as documented on MSDN), post (very important!) a custom window message using PostMessage(), specifying the menu window's HWND in the message's WPARAM or LPARAM parameter, to any HWND that you control, such as your main window, or even the edit control itself (since it is already subclassed). You will need the menu's HWND in the next step. You can optionally now uninstall the hook at this time, or wait for DefSubclassProc()/CallWindowProc() to exit (it will exit after the menu has been dismissed). You need to use PostMessage() because the menu window has not created its HMENU yet at this time. PostMessage() delays the next step until after the HMENU is ready.
when the custom window message is received, send a MN_GETMENU message via SendMessage() to the menu's HWND that you obtained from the hook. You now have the menu's HMENU and can do whatever you want with it.
to disable the Cut, Copy, and Paste menu items, call EnableMenuItem(). Their menu item identifiers are the same values as the WM_CUT, WM_COPY and WM_PASTE messages, respectively (this is not documented by Microsoft, but is consistent across Windows versions).
Update: I just found a much simpler solution (which also worked when I tested it).
subclass the edit control to intercept WM_CONTEXTMENU, as described above.
when the message is received, call SetWinEventHook() to install a thread-local event hook (set the hmodWinEventProc parameter to 0, the idProcess parameter to GetCurrentProcessId(), the idThread parameter to GetCurrentThreadId(), and the dwFlags parameter to 0 - not WINEVENT_INCONTEXT!). Set the eventMin and eventMax parameters both to EVENT_SYSTEM_MENUPOPUPSTART so that it is the only event you receive. Then dispatch the message to the default handler to invoke the popup menu.
when your event callback is called, the menu has already been fully initialized, so you can send the MN_GETMENU message to the provided HWND, which will be the menu's window (the callback's idObject parameter will be OBJID_CLIENT and the idChild parameter will be 0).
manipulate the HMENU as needed.
unhook the event hook when done using it, as described above.
As you can see here, this does work.
Before modifying the menu:
After disabling the menu items:
Even deleting the menu items:
You could leave the options visible but lock the clipboard from usage.
If this solution suits you all you need to do is make a program that opens the clipboard by calling OpenClipboard(NULL). In order to release the clipboard call CloseClipboard().
One approach (similar to hypmir's idea but not quite as intrusive) is to simply overwrite the clipboard with "DATA REMOVED BY TecMan" whenever it is updated. You could do this as a registered clipboard viewer.
Open the clipboard, clear all formats, add CF_TEXT with the notice, close it.
I would use a short delay (maybe a timer callback) so that you make your update AFTER the first update has been processed by any other registered clipboard viewers on the system.
Your mileage may vary.
Abusing the clipboard like this is never a good idea.
I found one interesting idea of how to get the handle of the edit control's context menu on vbforums.com:
http://www.vbforums.com/showthread.php?776385-RESOLVED-Modify-right-click-context-menu-in-standard-controls
It demonstrates how to add custom context menu items to the standard OS context menu. I think, this idea can be used to modify the menu too. Theoretically I need to enumerate the menu items and disable the items related to the copy/paste commands. The question is how to know whether a menu item is related to copy/paste? Getting the menu item text is a bad idea ;)
Another problem of that code is that it is based on some Windows features that are not documented. I've checked the solution, it still works in Windows 10, but who knows how the edit control context menu may be changed in the future updates of the OS...

Drag and drop files/text to tray icon (C#, WindowsForms)

I'm trying allow drag and drop to the tray icon on my application.
I know it's impossible to do this with the higher level C# WindowsForms API, since NotifyIcon doesn't support drag events.
So with a little help of a more windows-experienced friend I set out to try this via Win32 API. The idea was registering a hook in the tray window handler (after setting DragAcceptFiles(hWnd,TRUE); on the "SysPager" window handler).
The hooking and dropping part is working from the tray to the DLL.
LRESULT CALLBACK myHookProc (int code, WPARAM wParam, LPARAM lParam){
if (code == HC_ACTION)
{
PMSG msg = (PMSG) lParam;
switch(msg->message){
case WM_DROPFILES:
::MessageBox(NULL, L"Dropped files!", L"Test", MB_OK);
// call my app's registered hook
break;
}
return CallNextHookEx(oldHookProc, code, wParam, lParam);
}
As expected I get the message box popping up.
The problem is that I now need to call a function on my C# (WindowsForms) application to notify of this event. Here's where I ran into brick wall.
When I register the callback from my application in the DLL, I store it; but when myHookProc is called, it's value is NULL.
Turns out I was misunderstanding how DLLs work; there isn't a shared instance between my application and the tray area (they're copied or each has its own "instance" if you could call it that) so I can't use any static variables or anything like that to hold the callback reference back to my application.
Spent a couple of hours investigating this and the only solution seems to be shared memory (tried the #pragma data_seg I ran into in some forum, but to no avail), but it starts to feel too much of an overkill for such a "simple" use case.
So the million dollar questions are:
Is it really necessary to offload the hooking to a DLL?
Do I really need to resort to shared memory to accomplish this?
(Bonus question) WM_DROPFILES only works for files; how can I get a drop event fired for text?
Please keep in mind this is my first shot with .NET, C# and Win32 (less than a week); detailed answers explaining why - rather than just stating - will be greatly appreciated!
Thanks.
Yes, you really need to do those things because the window is owned by another process. Global hooks require a DLL that can be injected. Full D+D support requires RegisterDragDrop and COM code. Icky COM code.
And no, you really should not do this because somebody else might have already had the same idea as you. And got his program shipped first. The appcompat team at MSFT must have a nightmare with it. Careful with Raymond Chen, he's got a Bad Temper.

Getting screenshot of Child Window

If I have a handle to a window, how do I take a screenshot of any new child windows when they show up? Right now I have code that takes a screenshot every .1 seconds of a windows form. When I click on a drop down list box the subsequent screenshots do not include it. Using spy++ I can see that a new child window was created but not sure how to make sure it is included in my screenshots. Does anybody have any code that might include child windows?
Thanks in advance,
Bob
Yes, the dropdown of a ComboBox is a special window, a LISTBOX. .NET doesn't provide a built-in way to get the handle for it, you can P/Invoke SendMessage and send the CB_GETCOMBOBOXINFO message. COMBOBOXINFO.hwndList contains the handle.
Note that there are other controls that behave that way, DateTimePicker for example. Also note that the window can extend beyond the bounds of your form.
The code in this thread should be helpful to get the P/Invoke right.

how to get the icon of a java application?

I've got a code that lists the running application on a win32 box, and then displays theirs icons.
So far so good, I get the hwnd of the app, then call for GetClassLong(hwnd,GCL_HICONSM), and everything's fine.
But the case of a java apps is a pain to deal with, as the process answering to my calls is javaw.exe, and not the shiny-pimpy java application, who's got a so beautiful icon...
I gave a shot at GetWindowThreadProcessId also, but alas, it's the PID of javaw that's returned...
There's a way to do this though, as the task manager (alt+tab) displays the good icon.
I answer to my own question, thanks to PhiLho who put me on the right track: an article from Codeproject with the right algorithm to get a window icon (wether it's java or not):
//first, try:
SendMessageTimeout(WM_GETICON)
//if no icon found, try
GetClassLong(GCL_HICONSM)
//if still no icon, try
SendMessageTimeout(WM_WM_QUERYDRAGICON)
//if still no icon, you're doomed, return an error, or a void icon
For some reason a java app answers to the first call, but not to the others, which seems to be handled by javaw.exe.
Thanks again PhiLho.
Mmm, it can be done, because Process Viewer has a Show Applications button which does that (even if the main view shows the Java's icon). Alas this freeware isn't open source, so it won't tell its secret... :-(
Sysinternals' ProcMon doesn't do that, alas.
I will dig a bit more... :-)
[EDIT] Both a MS KB article and a Code Project article recommend using WM_QUERYDRAGICON if GCL_HICON fails...

Resources