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

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.

Related

Crash after the second RichEdit initialization in x64

According to Using Rich Edit Controls I use RichEdit in such way:
MyControl::OnCreate()
{
handle = LoadLibrary(_T("Riched20.dll"));
}
MyControl::OnDestroy()
{
FreeLibrary(handle);
}
It works fine for win32 but recently I’ve built x64 configuration and now my control fails after the page reload.
I’ve noticed that if do this:
MyControl::OnCreate()
{
handle = LoadLibrary(_T("Riched20.dll"));
FreeLibrary(handle);
handle = LoadLibrary(_T("Riched20.dll"));
}
everything works fine.
I don't wish to put this code into production, so is there any suggestions about better solution/workaround?
Since the reported fault module is Richedit20.dll_unloaded it means you are unloading the DLL while code from it is still in use.
For example, if you still have a richedit window open when you (completely) free the DLL, you can see crashes like that as soon as anything triggers a call to the control's window-proc. This is because the control's window-proc was inside the unloaded DLL code.
It should be safe to call LoadLibrary and FreeLibrary multiple times (so long as the calls balance out), so I doubt that is the problem. It may just be triggering the problem. Also, the problem was there in 32-bit builds; you just got lucky and never triggered it.
OnDestroy is the wrong place to call FreeLibrary. There are several window messages which get sent to a window after WM_DESTROY (e.g. WM_NCDESTROY).
Child windows also still exist when OnDestroy is called. If the richedits are children of your control (rather than the control itself) then moving the FreeLibrary into OnNcDestroy may save you. (Child windows are destroyed by the time WM_NCDESTROY is called.) I'd still say it's not a good place to free the library, however.
So you definitely want to move your FreeLibrary call. I would move both it and the LoadLibrary completely out of the control itself. It's not normal to have controls which load/free libraries whenever an instance of them is created. Instead, have some static init/uninit code somewhere which loads the libraries you need once and for all and frees them when the application is shutting down.
(If your app only rarely uses the control then it might make sense to load/free the library only when windows using the control are active. That situation is rare, though. Usually you're better off just leaving the DLL loaded.)

Recreate windows' own tray icon window in another place

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.

Win32 Dialog closing on startup

I recently asked a question here about a dialog problem I had, but i discovered that the problem lies in such a different area i intentionally thought that I will rephrase my question here.
The problem is that I've been working on a DirectX10 game engine. When the engine inits there should pop up a dialog box which gives the user the possibility to choose settings. To develop this i created a separate solution (visual studio 2008) where i made the dialog resource and the proc function. I tested it there with a small winmain function and it works perfect.
Then I copied the resource and all the code to my main engine and fired it up. The only thing that happens is that the dialog pops up very shortly and it immediately closes without even waiting for user input. Also the MessageBox function does the same thing. I went through every step, but I'm really stuck.
Could any of you please look at the code for me and see if you can find what the *bleep* is going on?
There are three different folders in the zip. VKRenderer is the rending part of the engine where the VKD3D10 is a directX10 implementation of the in VKRenderer defined RenderDevice. In the VKRenderer solution also all the exported functions get exported from VKD3D10. VKD3D10 builds to a dll and VKRenderer to a .lib. Then there is StartupTest which uses the builds to try and start up the engine. The SettingsDialogTest is where i created the settings dialog and all the resources. There you can also find the working version of the settings dialog.
The ZIP file can be downloaded here. (The engine is still under development, so be kind with you comments :P ) THANKS A TON IN ADVANCE
pass = HELP
At a guess, try changing:
INT_PTR i = DialogBoxParam(hinst, L"IDD_SETTINGS", hwnd, DlgProcWrapper,(LPARAM) this );
to
INT_PTR i = DialogBoxParam(hinst, MAKEINTRESOURCE( IDD_SETTINGS ), hwnd, DlgProcWrapper,(LPARAM) this );

Subscribe to Vista Events in .NET (e.g. Window Opened)

I am trying to build my own little toolbox for Vista.
One of the features is a "window placeing tool" which places the windows at saved position. Another tool I could imagine are extensions to firefox or thunderbird...
For these tools to work, I need them to be able to capture "Events" in Vista.
To give you a concrete example:
Explorer Opened New Window
User started Firefox
Mouse moved
For the mouse case, there are some examples for C#.
I also know about the directory watcher, nice little helper.
Want I now need is the "new window opened event"
Any idea how to monitor this, without iterating the current window list every 5 seconds (I already know how to get Windows using the DLLImports, and getting Processes using managed code. But I have no Event when the explorer process opens a new windows)
Thanks for your help,
Chris
What you're talking about doing is not simple by any stretch.
You're going to need to register a hook, and you're going to have to build a callback procedure that gets called within another process's execution context -- this is not going to be .NET code (probably C instead), and will have to be in a DLL. That callback procedure will get called every time a certain class of events happens. It will examine the events it receives and filter out the ones you're interested, then send your application the notifications you want (probably via PostMessage). You'll then tap in to your application's main message loop to intercept those messages, and from there you can fire a .NET Event, or whatever you want.
Writing hook callbacks is tricky stuff because the code gets run within another process, not your own, and the memory management and concurrency issues take a bit of forethought. For that same reason, it's not going to be done in C#. Ideally, though, this callback code will be very small and very fast, since it's going to get called so often.
Also note that while perfectly "legal" in Win32, these system hooks have an immense amount of power and are commonly used by malware to change the way your system works. For that reason, you may run afoul of antivirus software if you attempt to do this sort of thing on a customer's computer.
Also note that the far-reaching effects of system hooks also means that simple programming mistakes can take down your whole system, which you will probably discover for yourself at some point while debugging; so save everything before you hit "run".
Good luck!
EDIT
Did a bit more search to see if there's any way to write the hook proc in C#, and came up with this:
How to set a Windows hook in Visual C# .NET
This is almost what you're looking for, but not quite. Hook procedures can either be global (which means that they run on every application) or thread (only runs within your application). The document states that:
Global hooks are not supported in the .NET Framework
Except for the
WH_KEYBOARD_LL low-level hook and the
WH_MOUSE_LL low-level hook, you cannot
implement global hooks in the
Microsoft .NET Framework. To install a
global hook, a hook must have a native
DLL export to inject itself in another
process that requires a valid,
consistent function to call into. This
behavior requires a DLL export. The
.NET Framework does not support DLL
exports. Managed code has no concept
of a consistent value for a function
pointer because these function
pointers are proxies that are built
dynamically.
Which means, again, to monitor things that go on outside your application's view, you need to set a global hook, which can't be written in .NET.
I have exactly the same issue as this, and I think I have a workable solution. Initially I looked into a similar option to the one mentioned by 'tylerl'. In my case however, instead of using 'SetWindowsHookEx', I attempted to use the similar function 'RegisterShellHookWindows'.
Unfortunately, this only succeeded in providing me with notifications of when a subset of windows are created/destroyed. The only windows which it provided notifications for are those shown on the taskbar.
Since I didn't fancy hacking into other processes, or writing the native code which would be required for SetWindowHookEx, I tried digging into the .NET automation APIs introduced in .NET 4.0, and I think this has the answer to your problem (at least as far as detecting when windows are opened / closed).
Here's a code snippet for using this API to detect windows being opened/closed:
using System.Windows.Automation;
private void StartMonitoringForWindowEvents()
{
Task.Factory.StartNew(() =>
{
AutomationEventHandler windowOpenedHandler = new AutomationEventHandler(OnWindowOpened);
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Descendants, windowOpenedHandler);
});
}
private void OnWindowOpened(object source, AutomationEventArgs eventArgs)
{
try
{
AutomationElement sourceElement = (AutomationElement)source;
string message = string.Format(
"Automation.WindowOpened PID: {0}, Handle: {1}, Name:{2}",
sourceElement.Current.ProcessId,
sourceElement.Current.NativeWindowHandle,
sourceElement.Current.Name);
Debug.WriteLine(message);
// for each created window, register to watch for it being closed
RegisterForClosedWindowEvent(sourceElement);
}
catch
{
}
}
private void RegisterForClosedWindowEvent(AutomationElement element)
{
try
{
string elementName = element.Current.Name;
int processId = element.Current.ProcessId;
int nativeHandle = element.Current.NativeWindowHandle;
AutomationEventHandler windowClosedHandler = new AutomationEventHandler(
(ignoreSource, ignoreArgs) => OnWindowClosed(nativeHandle, processId, elementName));
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowClosedEvent, element,
TreeScope.Element, windowClosedHandler);
}
catch
{
}
}
private void OnWindowClosed(int nativeHandle, int processId, string elementName)
{
string message = string.Format(
"Automation.WindowClosed PID: {0}, Handle: {1}, Name:{2}",
processId,
nativeHandle,
elementName);
Debug.WriteLine(message);
}
You will need to add a reference to the assemblies 'UIAutomationClient' and 'UIAutomationClientTypes'.
Here's a link to the MSDN documentation (you'll probably particularly want to take a look at the information on events):
http://msdn.microsoft.com/en-us/library/ms747327.aspx
Important implementation Notes:
1.) Notice that in the sample, I used a task factory to register for reception of the automation events. It's particularly important to avoid using the UI thread when registering for automation events or generally interacting with the automation APIs. Doing so can (and usually quickly does) result in a deadlock. Therefore, I use the task factory to ensure registration is done via the thread pool.
This also means, that the events will be received on the thread pool... So, if you need to perform any UI updates, you will have to marshal these across to the UI thread.
2.) You'll also note, that I capture any needed information on the element which may be closed, at the time of registration (using a closure). This is because, once the element is closed, we will no longer have access to this information - since the element has been destroyed.
Phil
The answer is not C# (or .Net) specific. You'll need to call SetWindowsHookEx( WH_CBT, ... ). This will allows to know when a window is created, destroyed, moved, sized, etc. You'll also need to get the relevant information from the window to identify if its one you need to do something about. Perhaps GetClassInfo, GetWindowLong, and GetWindowText.
The problem with the SetWindowsHookEx is that in order to get events from every window you need to have a separate win32 dll with the function in question exported. Although you might have success with the procedure outlined here.
To expand upon Joel Lucsy's answer, you need to use the Win32 API. However, there's a nice library, the Managed Windows API, that provides an object-oriented wrapper over common APIs. It may have what you need.

Internet Explorer crashes when MSXML2::IXMLDOMDocumentPtr -> Release() is called

I am creating a shell extension in C++ (ATL 9) using Visual Studio 2008. The Shell Extension creates a global MSXML2::IXMLDOMDocumentPtr object m_XmlDoc in the module class. This m_XmlDoc is then used in the extension by all classes to read xml document.
The problem that I am facing is with Internet explorer. When the Shell Extension is active and I open/close internet explorer, I get a debug dialog and IE crashes. The error message says "Unhandled exception at 0x6aac30f1 in iexplore.exe: 0xC0000005: Access violation reading location 0x03050970."
When I click "break" on the message window, It takes me to the "Release" method of COM Smart Pointer and the error seems to be on
m_pInterface->Release();
This call was made from Module's destructor and also the value of m_pInterface is not NULL. I think maybe internet explorer is using the XML DOM and the call to Release creates some problem in it.
MSXML2::IXMLDOMDocumentPtr m_XmlDoc;
In _AtlModule.Init() method
::CoInitialize(NULL);
m_XmlDoc.CreateInstance(MSXML2::CLSID_DOMDocument40);
dllMain code:
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
::CoInitialize(NULL);
if (dwReason == DLL_PROCESS_ATTACH)
{
_AtlModule.Init();
CreateImageLists();
::DisableThreadLibraryCalls(hInstance);
}
hInstance;
return _AtlModule.DllMain(dwReason, lpReserved);
}
the use of DisableThreadLibraryCalls is discouraged, you seen it?
There are at least two problems with your code as posted:
You're calling CoInitialize in DllMain.
You're creating a COM object in DllMain.
It wouldn't surprise me if you're doing something in CreateImageLists() which you also shouldn't be doing in DllMain.
Also, the reason that your crash was "fixed" by not using the smart pointer is because now you're not actually releasing the object anymore. Your code is broken, and not releasing the reference isn't a valid way to fix anything.
I would suggest that you read, and then re-read, the documentation for DllMain paying particular attention to the things you should never do within your implementation of the function. As you'll see right up front:
Warning There are serious limits on what you can do in a DLL entry point. To provide more complex initialization, create an initialization routine for the DLL. You can require applications to call the initialization routine before calling any other routines in the DLL.
I suspect once you read it, and fix your code to create the COM object at a valid time, and release it at a valid time, your shell extension will stop crashing.
The problem was because of the COM Smart Pointer used for XmlDomDocument. I changed it to a normal pointer and it is working fine even in Vista.
This problem has a different behaviour in XP and Vista. In XP, I was getting an unhandled exception when I closed Internet Explorer. In Vista, I was not able to browse the virtual drive.

Resources