I'm trying to use System.AppDomain.CurentDomain.ApplicationExit += new System.EventHandler(SomeFunction); to call the function SomeFunction when the application closes. In this case Unity 3D. But it doesn't work. I have no idea why. It is an editor script and is not an instance (static). What do you think I'm doing wrong?
There is no OnApplicationQuit method for Windows. According to the official documentation:
On Windows Store Apps and Windows Phone 8.1 there is no application quit event. Consider using OnApplicationFocus event when focusStatus equals false.
Therefore, instead of checking whether or not the application closed, you'll want to check whether or not the application obtained or lost focus.
In C#:
void OnApplicationFocus(bool focus)
{
if (!focus)
{
//Do something
}
}
The documentation does not explicitly mention it, but it can be assumed OnApplicationQuit is intended for Android only as iOS also does not support it.
You could use
function OnApplicationQuit() {
//put script in here
}
So that before the application quits it calls that function and does what ever is in the { and }
Related
I have an app that has a few windows defined as a windows group in the structure conforming to App in the main scene:
WindowGroup("StandingsView") {
StandingsView()
.environmentObject(appServices)
}
.handlesExternalEvents(matching: Set(arrayLiteral: "StandingsView"))
The appServices take some time to be configured, so I do not want to automatically restore the windows at start. I create the windows upon user selections being valid, the services being fully configured, and the user pressing a 'start' SwiftUI button:
if let standingsURL = URL(string: "raceStratLiteApp://StandingsView") {
NSWorkspace.shared.open(standingsURL)
}
I've tried closing the windows in the appDelegate's applicationShouldTerminate().
I've also tried setting the isRestorable to false in applicationShouldTerminate:
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
for window in NSApplication.shared.windows {
window.isRestorable = false
}
return .terminateNow
}
Are there any other methods to not restore a window? or better yet, to be able to programmatically restore it with its previous size, etc but launch only on user direction to 'start'
TIA
The code solution as #Asperi suggests in the later comment:
func applicationDidFinishLaunching(_ notification: Notification) {
UserDefaults.standard.register(defaults: ["NSQuitAlwaysKeepsWindows" : false])
}
From the tip provide by #Asperi, writing the following to defaults will cease the writing of the window states:
$ defaults write <bundleid> NSQuitAlwaysKeepsWindows -bool false
So this is not a code change to the app, but rather an environment config the would be done on install.
I also deleted the savedState directory located at ~/Library/Saved Application State/<bundleid>.savedState for archive builds and at ~/Library/Containers/<App Name>/Data/Library/Saved Application State/<bundleid>.savedState for debug builds. Am not sure if that mattered but once doing these steps it solved the problem. Thanks #Asperi
I have a small computer lab for students to use fairly unsupervised, with a printer attached on the network. I am trying to implement a simple scripting additions alert dialog with all the rules about the printer that I need to pop up when they select print from any number of different applications.
I am trying to attach the script directly to the printer itself in the User/Library/Printer directory, (xxx.xxx.xxx.xxx.app) so any browser, or pdf viewer, etc. will get the message displayed when they try to run the printer.
I have tried using automator with applescript, I have tried renaming the printer and calling the applescript the name of the printer, so far no good.
What am I missing?
In this answer I will show how to create a JavaScript for Automation (JXA) applet that listens for app-launch and screensaver-stop notifications and then displays an alert when it receives one, thereby producing the desired outcome described in the question. I also describe how this approach can be adapted to trigger an AppleScript script, which would produce the specific behavior described in the title of the question.
Instructions
Open the Script Editor app and create a new document
From the pop-up near the top-left of the window, select JavaScript instead of AppleScript
Paste in the code provided below
Save the script as an applet by changing the 'File Format' to 'Application' in the save panel and enabling the 'Stay open after run handler' option.
Run the applet by choosing 'Run Application' from the 'Script' menu
Launch an app and notice an alert
Start and then stop the screensaver and notice an alert
Code
var me = Application.currentApplication(); me.includeStandardAdditions = true
ObjC.import('Cocoa')
ObjC.registerSubclass({
name: 'MainController',
methods: {
'appDidLaunch:': {
types: ['void', ['id']],
implementation: function(notification) {
var appName = notification.userInfo.objectForKey('NSApplicationName').js
me.activate()
me.displayAlert(`Hello, ${appName}!`, {message: 'Nice to meet you.'})
Application(appName).activate()
}
},
'screensaverDidStop:': {
types: ['void', ['id']],
implementation: function(notification) {
me.activate()
me.displayAlert('Goodbye, screensaver!', {message: 'It was nice knowing you.'})
}
}
}
})
var controller = $.MainController.new
$.NSWorkspace.sharedWorkspace.notificationCenter.addObserverSelectorNameObject(controller, 'appDidLaunch:', $.NSWorkspaceDidLaunchApplicationNotification, undefined)
$.NSDistributedNotificationCenter.defaultCenter.addObserverSelectorNameObject(controller, 'screensaverDidStop:', 'com.apple.screensaver.didstop', undefined)
Discussion
First, the applet code creates a new class named 'MainController', which implements two methods, 'appDidLaunch:' and 'screensaverDidStop:'. These methods are implemented to use the 'display alert' functionality from Standard Additions.
Next, the applet code instantiates an object of this class, and registers that instance as on observer of the notifications that are posted when apps are launched, and when the screensaver stops.
The applet continues to run after the JXA code executes, and when the events occur, the JXA functions are invoked.
Next Steps
If you want to run an AppleScript script from JXA, you can refer to the answer to this question.
If you want to make it harder to quit the applet accidentally, you can make the applet a 'UI Element' by setting the LSUIElement key to 'true' in the applet's Info.plist.
Finally, you might want to add the applet to the user's Login Items so that it starts automatically after a reboot.
Is there any way to change the firefox shift hotkey that makes firefox start in safe mode? I've set up some unit tests using Selenium and PHPUnit, but if I'm working on the machine while the tests are running then I frequently find I'm pressing shift as I type (holding shift as I select blocks of code is another big offender). This causes the test to fail (and time out) even if you click past the safe mode prompt that pops up.
Is there a way to disable this hot key, or change the key to something that I'd use less often?
I've also met with this problem and didn't find a solution. It seems that it is still an open issue: Mozilla Forums thread, Bug 653410, Bug 644175 and so on. As a workaround you can install firefox 3.6 as this feature was implemented since firefox 4, but probably this will not suite you.
Mozilla finally added an environment variable to control this behavior. Unfortunately, configuring this environment variable in a way that applies to the overall graphical system, rather than merely a bash session, is a bit difficult. This used to be done via /etc/launchd.conf, but macOS dropped support for this in v10.10. Fortunately, systemctl offers a .plist file system which can define run programs and define system-wide environment variables at boot, so I published this working .plist file, with instructions for installing and removing it:
https://github.com/mcandre/dotfiles/blob/master/setenv.MOZ_DISABLE_SAFE_MODE_KEY.plist
This is awesome for me, because I like to launch my web browser from anywhere in the GUI with Control+Alt+G via QuickSilver, which of course includes the Alt modifier that Firefox tends to interpret as signaling safe mode.
Until Bug 653410 is fixed, the best workaround I can come up with is to detect when safe mode is launched and handle it in the best way fit for your particular purposes. This may mean killing the Firefox process and launching again, or it may mean warning the user, or both. When Firefox is launched into safe mode, it writes "LastVersion=Safe Mode" to its compatibility.ini file in its profile directory. An example C# function to watch for this is given below.
FileSystemWatcher safeModeWatcher;
private void watchSafeMode()
{
string profiles = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Mozilla", "Firefox", "Profiles");
string defaultProfile = Directory.GetDirectories(profiles, "*default*")[0];
safeModeWatcher = new FileSystemWatcher(defaultProfile, "compatibility.ini");
safeModeWatcher.NotifyFilter = NotifyFilters.LastWrite;
safeModeWatcher.Changed += delegate(object s, FileSystemEventArgs e)
{
if (File.ReadAllText(e.FullPath).Contains("LastVersion=Safe Mode"))
{
// safe mode!
System.Diagnostics.Trace.WriteLine("safe mode detected!");
// TODO kill Firefox and launch again, or whatever makes sense for you
}
};
safeModeWatcher.EnableRaisingEvents = true;
// ...
// TODO Dispose safeModeWatcher when done
}
I've made a wxPerl application which presents just a simple frame which only contains a wxMenuBar, wxPanel, wxTextCtrl and a wxStaticBitmap. The development and deployment platform is Windows XP and beyond.
The image is added to the form like this:
my $logoData = Wx::Bitmap->new(App::Resource::Images::getLogoPath(), wxBITMAP_TYPE_BMP);
my $logo = Wx::StaticBitmap->new($self, -1, $logoData);
I've had no problems displaying the image. I've made an installer with Inno Setup that adds a icon to the users' desktop. If the application gets started using that shortcut the window doesn't draw my wxStaticBitmap. Only when the application loses focus and some other window is being moved over it, only then will my wxStaticBitmap be drawn.
When starting the application from the start menu, quick start, or directly after compiling it with wxpar, or just with the perl interperter displays my wxStaticBitmap fine.
The only thing I've found is calling Refresh() and Update() on my wxFrame. After creating this wxFrame I call Show(1) and right after that Refresh() and Update(). But so far have had no luck with it.
wxStaticBitmap is derived from wxWindow, so it has both Update and UpdateWindowUI methods. What happens when you call one of those on $logo immediately after creating it?
Edit: I just tried it, and the Update* methods don't help. However, what does force it to repaint is to call SetBitmap after creating the object. Here's what I did:
my $bmp = Wx::Bitmap->new("./testcard.bmp", wxBITMAP_TYPE_BMP);
my $logo = Wx::StaticBitmap->new($frame, wxID_ANY, $bmp);
$logo->SetBitmap($bmp);
$frame is a Wx::Frame, and I just put a button in a sizer and the above code in its event handler callback.
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.