|
Windows Hook...wtf |
blargmob
Member #8,356
February 2007
|
Howdy, I'm writing a program that records user actions such as keypreses, mouse position, mouse clicks and then plays it back. I've got the mouse to fully work, but something is wrong with the keypress recording. I've set a low-level key hook to capture any key presses outside of the form. Now when I implement this hook in a test project, it works fine. However, when I implement it in this project, it fails and throws this exception as soon as I hit a key: A callback was made on a garbage collected delegate of type 'AutoMagic!Utilities.globalKeyboardHook+keyboardHookProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
What? This hook works fine in other small, test, applications I wrote! Maybe some file stream or something is interfering? Help --- |
Erin Maus
Member #7,537
July 2006
|
You must keep a reference to the hook delegate in some class or the sort. If you don't, then it will be collected. --- |
blargmob
Member #8,356
February 2007
|
Example? Sorry, still confused --- |
Thomas Harte
Member #33
April 2000
|
Based purely on my reading of the error message, you're writing a .NET-type managed application that interacts with old, unmanaged code. You're creating a managed object to act as a delegate for unmanaged code. The managed code appears to use a garbage collector and will (ideally automatically deallocate anything to which you do not currently have a pointer. The managed-to-unmanaged bridge can't interact with the garbage collector, so the warning means that you're passing a pointer to the unmanaged code, then not keeping a copy with the effect that the managed runtime may subsequently deallocate that object. Just do what it says and keep a pointer to your delegate somewhere in your managed code, then ceremoniously throw it away when whatever you're doing with the unmanaged code is finished. [My site] [Tetrominoes] |
Erin Maus
Member #7,537
July 2006
|
1public class Hook
2{
3 HookFunc func;
4
5 public Hook()
6 {
7 func += TheHook;
8 SetKeyboardHookEx(func); /* Or whatever that function is called */
9 }
10
11 TheHook(IntPtr something, int whatever, bool coolness)
12 {
13 /* Do stuff here */
14
15 if (GetKey(something) == Key.Esc)
16 {
17 /* Set flag for the while loop to tell it to stop */
18 }
19 }
20}
21
22static void Main(string[] arguments)
23{
24 Hook hook = new Hook();
25
26 while (hook.IsRunning) { }
27}
If you don't keep the delegate around, it will be garbage collected, since you're dealing with unmanaged code. --- |
blargmob
Member #8,356
February 2007
|
Yes,I forgot to mention that I'm using .NET and C#. Anyway: Thomas Harte said: Just do what it says and keep a pointer to your delegate somewhere in your managed code
--- |
Kibiz0r
Member #6,203
September 2005
|
With garbage collection, things get deallocated when there are no references to them. .NET can't see into unmanaged code, so it doesn't know that unmanaged code is still referencing the delegate, so it destroys it. The solution is to make a reference to it, yourself, for as long as the delegate is in use by the unmanaged code, so it doesn't get destroyed. --- |
blargmob
Member #8,356
February 2007
|
Well upon browing CodeProject.com, I found a User Component for handling global user actions Everything is working perfectly now. --- |
Kibiz0r
Member #6,203
September 2005
|
I get the feeling none of us got through to you. --- |
blargmob
Member #8,356
February 2007
|
You are correct I have a hard time grasping new programming ideas because I've gone so long just doing my own thing. --- |
Thomas Harte
Member #33
April 2000
|
blargmob said: You are correct I'll take one more shot... In unmanaged code, you are responsible for explicitly allocating and deallocating objects. So, in C style, you might see: This pattern appears prominently in Allegro, it being a pure-C library. For example, you must explicitly call create_bitmap to get hold of a valid BITMAP * in the first place, and later call release_bitmap to make sure that the memory is returned to the system. In managed code, there is garbage collection. That means that you don't explicitly allocate or deallocate memory. I'm not up on .NET semantics because I don't use it, but the point is that you explicitly instantiate objects so as to set their working parameters (which will look a lot like create_bitmap) but subsequently you don't need to worry about deallocating the memory (ie, there is nothing equivalent to release_bitmap). The .NET runtime deals with that for you — it uses a bunch of techniques that you don't need to care about to determine when your program is in such a state that it can never access a particular object again. At some point after that, it will deallocate the object. Any attempt to access a deallocated object has an undefined result (at best, you'll read the old values because the memory hasn't been reused, at worst your program will crash or be forcibly terminated by the OS). You should never attempt to access a deallocated object. The various techniques that .NET uses for garbage collection can be applied only within managed code. Once you pass a pointer to something in managed code to some unmanaged code, it has no way to figure out when the unmanaged code is finished with the object. All it can do is to continue to apply the rules for the managed area. That means that any object that originates in the managed code will be deallocated when the managed code no longer retains a pointer to it. If the unmanaged code tries to access the object after it has been deallocated, the result will be undefined. You are creating an object in managed code, passing it to unmanaged code, then putting yourself in a position whereby it may be automatically deallocated. If you keep a pointer to that object in your managed code — even if you don't use it — then the managed runtime will spot that you want to keep the object in memory and will not deallocate it, so everything will operate correctly. It's a tiny bit messy, but it's an unavoidable problem in the transition from unmanaged to managed code. The only way to avoid having to worry about things like this is to use managed code exclusively. [My site] [Tetrominoes] |
bamccaig
Member #7,536
July 2006
|
Thomas Harte said: ...and later call destroy_bitmap to make sure that the memory is returned to the system. FTFY. Thomas Harte said: ...you don't need to worry about deallocating the memory (ie, there is nothing equivalent to free). FTFY. destroy_bitmap is essentially a destructor, with the exception that it is also responsible for releasing its own memory, whereas in managed code the garbage collector is responsible for that. .NET does have an IDisposable interface (System.IDisposable) for objects that have unmanaged resources. Before going out of scope, your code is liable to IDisposable::Dispose() of all IDisposable instances. C# (and now Visual Basic *shudder*) support a using statement to automate this process to make it less error prone. In C#, this... { MyDisposable o = new MyDisposable(args); try { o.DoWork(); } finally { o.Dispose(); } } ...becomes this... using(MyDisposable o = new MyDisposable(args)) { o.DoWork(); } ...which, as I understand, would be converted to the above by the compiler... -- acc.js | al4anim - Allegro 4 Animation library | Allegro 5 VS/NuGet Guide | Allegro.cc Mockup | Allegro.cc <code> Tag | Allegro 4 Timer Example (w/ Semaphores) | Allegro 5 "Winpkg" (MSVC readme) | Bambot | Blog | C++ STL Container Flowchart | Castopulence Software | Check Return Values | Derail? | Is This A Discussion? Flow Chart | Filesystem Hierarchy Standard | Clean Code Talks - Global State and Singletons | How To Use Header Files | GNU/Linux (Debian, Fedora, Gentoo) | rot (rot13, rot47, rotN) | Streaming |
blargmob
Member #8,356
February 2007
|
@Harte: Aha! Much easier to understand when you compare it with Allegro examples! Thanx you. --- |
|