Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Windows Hook...wtf

This thread is locked; no one can reply to it. rss feed Print
Windows Hook...wtf
blargmob
Member #8,356
February 2007
avatar

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 :'(

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Erin Maus
Member #7,537
July 2006
avatar

You must keep a reference to the hook delegate in some class or the sort. If you don't, then it will be collected.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

blargmob
Member #8,356
February 2007
avatar

Example? Sorry, still confused ???

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Thomas Harte
Member #33
April 2000
avatar

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.

Erin Maus
Member #7,537
July 2006
avatar

#SelectExpand
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.

---
ItsyRealm, a quirky 2D/3D RPG where you fight, skill, and explore in a medieval world with horrors unimaginable.
they / she

blargmob
Member #8,356
February 2007
avatar

Yes,I forgot to mention that I'm using .NET and C#. Anyway:

Just do what it says and keep a pointer to your delegate somewhere in your managed code

???

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Kibiz0r
Member #6,203
September 2005
avatar

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
avatar

Well upon browing CodeProject.com, I found a User Component for handling global user actions ;D

Everything is working perfectly now.

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Kibiz0r
Member #6,203
September 2005
avatar

I get the feeling none of us got through to you.

blargmob
Member #8,356
February 2007
avatar

You are correct :-[

I have a hard time grasping new programming ideas because I've gone so long just doing my own thing.

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Thomas Harte
Member #33
April 2000
avatar

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:

OBJ *ptr = (OBJ *)malloc(sizeof(OBJ));
// use ptr for stuff
free(ptr);

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.

bamccaig
Member #7,536
July 2006
avatar

...and later call destroy_bitmap to make sure that the memory is returned to the system.

FTFY.

...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...

blargmob
Member #8,356
February 2007
avatar

@Harte:

Aha! Much easier to understand when you compare it with Allegro examples! Thanx you. :)

---
"No amount of prayer would have produced the computers you use to spread your nonsense." Arthur Kalliokoski

Go to: