sub Exit_Click {

this works, pretty much like the engine stops if you hold the brake, put in any gear and release the clutch. The way it was designed to be is to return -1, which tells Win32::GUI to exit the message loop and return from the Win32::GUI::Dialog() call back to your script. I say this because it implies that Dialog() returns back to your script whenever any of your event subs happens to return something that looks like a negative number.
You have exec() (or, system() by now, or backtick or fork or whatever) as the last line of your event. If this returns a negative number, your launcher app will again stop. Also, since your script is still alive, it executes everything after the Dialog() call, which you might not expect if you are used to killing your app with exit from out of an event sub.