How to manage undo for OSX Services? - cocoa

Services are triggered in the context of an application. For example one service may richly format the text in paste board and paste it on the application that requested it.
Is it possible for the service to get reference to its parent's NSUndoManager and modify its undo history?

Services are triggered in the context of an application, conceptually, but they are not actually executed in that application's process.
Undo-ability is the responsibility of the target application. If a service is pasting something into an app, it's going to end up sending a -paste: message up the responder chain of the target app's front-most window. If an "endogenous" paste operation in the target app is undoable, I would expect the "exogenous" paste operation to also be undoable. Similarly, if the app doesn't support undo for paste operations then I wouldn't expect a service-sourced paste to be undoable.
Services are isolated from the target application by the pasteboard, as described here. They will not be able to get the NSUndoManager from the application (or any other object) -- think of what a huge, gaping security hole that would be! It is probably possible to send an AppleEvent to an application from a system service, but that's about it.

Related

Plugin Development - Embedding Custom Framework to XPC

I have recently created a custom framework that is planned to be re-used for multiple projects. The catch is, this is for a plugin, and knowing that we can't simply embed the framework within the plugin's bundle, due to symbol collisions and what-not, I'm thinking of simply embedding it with the plugin's XPC. On a side-note, this framework will be used to launch custom interfaces, such as a view controller, views, and use some delegates slapped inside it that the plugin will have to take ownership (which I am hoping). Which brings me to my question: is it possible for a different process to take ownership of objects instantiated in the XPC? I am quite new to using frameworks, so I've spent hours trying to jerry-rig stuff together in XCode based on tutorials I've found online, sadly to no avail.
A framework is a bundle of code and resources that can be used, and reused, by multiple applications. It can be embedded within your application, be part of the operating system (the entirety of Cocoa is actually a collection of frameworks), or dynamically located and programmatically loaded at runtime. Once loaded, the framework's code, classes, and resources appear to the application as if they had been compiled directly into the host app. The key is that the code executes directly, in your process's memory space.
XPC is an inter-process communications facility. It allows one process to send and receive messages with a different process. It cannot be used to communicate with itself.
You cannot "take ownership" of an object using XPC. All XPC messages serialize ("archive" in Cocoa-speak) any object and de-serialize that object on the receiving end. The second process now how a replica of the original object; it is not a reference to the original object and is constrained to the boundaries of its process.
If your second process needs to display something, you have (basically) three options:
(1) Make the second process its own application. The second process can be a full-fledged Cocoa app with windows and so forth. You can make it an "accessory" app, so it does not have a menubar or appear in the dock. See LSUIPresentationMode Info.plist property and/or NSApplication.activationPolicy.
(2) The advanced technique is to use an IOSurface. An IOSurface is, essentially, a method by which a second process (your XPC Service) can draw directly into a window of your application. Again, the drawing objects still exist—and are completely isolated in—the second process; but what they draw will appear in your application as if they were local view objects. (This is how Safari works; every browser page is rendered by an isolated background process drawing into a surface.)
(3) Use a poor-man's IOSurface: send your data to the second process, have it render the results into something (pixel array, TIFF, PNG, ...) that can be serialized and drawn by the host app, then use XPC to send that rendered image back to the host app for display.
Daemons and Services Programming Guide
IOSurface

Is a serviced component shared between user sessions on a terminal server, or is one process started for each user session?

I have some .NET code in a COM+/Enterprise Services serviced component. I communicate with this component from a WPF application and also from a legacy VBA application.
This arrangement works well when only one user is logged on to a machine. The component starts in its own process when either the .NET or the legacy application instantiates one of its COM objects.
The system also works for the first user to try to run it on a terminal server installation. However, when another user logs on, he/she is unable to use the application. I had hoped that each session would run in isolation, and that one host process would run per session. Am I wrong in this expectation?
In Component Services on the Activation tab my application is configured to run as a "Server application". On the Identity tab, "Interactive user" is selected. On the Security tab, "Enforce access checks for this application" is unchecked.
There isn't session isolation as you describe, instead process ownership limits what you have access to.
Your conclusion seems correct & you will need to determine a suitable mechanism to exchange data with the service.
I used WCF to create a service with a net named pipe listener https://learn.microsoft.com/en-us/dotnet/framework/wcf/index
The idea of using proxies to make rpc calls is attractive, but I found the proxy definitions and stubs to link it all together quite clumsy to use.
If you have events that may be triggered at either end then keeping client/service in sync becomes problematic.
AIUI you cannot invoke a rpc method that ends up invoking an rpc back at the originating end, although that could be a named pipe limitation.
If I was doing this again I would use a socket server in the service & the websocket protocol for biderectional data transfer, even though you might need to implement some thread handling to avoid the listener thread blocking whilst servicing requests.
Hard to find anything authoritative on this. For standard COM you can set the identity to 'Launching user'. The same is not available for COM+.
According to this archived post,
A COM+ application can be configured to run under the logged in account, or
a specified account. Under the application properties, see the Identity tab.
...
Once set however, it remains under that account until the application shuts
down, so you can't have multiple users using the same COM+ application under
different IDs.
That seems to match what is said in this knowledge base article too.
My conclusion is, I should probably accept that my component must run once per machine rather than once per session. It will need to be modified to accommodate this. Since it needs to start new processes in individual sessions, it will have to run as a Windows service under the Local System account (giving due attention to the security implications).

Are Windows-GUI calls (creating visible windows, etc.) allowed in a Windows-Service?

First off, I know some proper ways of making a truly interactive Windows Service.
The situation is, I do have a tool that does not interact with the user as such. However, it does display non-blocking notifications both via popup windows and via the Windows Notification Area (aka System Tray). It also writes a logfile of the notifications it displays.
This tool is normally spawned by a main user application and as long as the main application is a normal application, these notifications do work as intended.
When this tool is spawned by a Windows Service, no notifications are displayed, naturally. (The Desktop Session for the service isn't visible.) But this would be OK, we have the logfile and these notifications are just - notifications, nothing the user absolutely must see under all circumstances.
The question now becomes: Is a process running in the context of a Service (the Service itself or any process it starts) "allowed" to make Windows API calls that display a visible GUI?
Will most Windows API calls (e.g. creating and showing a window, using Shell_NotifyIcon, etc.) behave the same in the invisible session of the service?
Or would I have to make sure throughout the source code, that no GUI displaying/modifying stuff is called in the context of the service?
And yes, calling ::MessageBox is a bad idea because it will block. But I can handle these calls.
And yes, this could be designed better, but it's what I have at the moment and it would be nice if I hadn't to rip the whole tool apart to make sure no GUI related code is run in the service.
GUI elements from a Windows Service are shown on Session 0. On Windows XP & 2003, users were allowed to log in to Session 0 and interact normally with the windows created by a service, but Microsoft put a knife in the heart of interactive services in Vista (and beyond) by isolating Session 0.
So, to answer your specific questions:
Is a process running in the context of a Service (the Service itself
or any process it starts) "allowed" to make Windows API calls that
display a visible GUI?
Will most Windows API calls (e.g. creating and showing a window, using Shell_NotifyIcon, etc.) behave the same in the invisible session
of the service?
Yes, GUI calls are allowed and should succeed as normal. The only notable exceptions that I know of are those related to tray icons because the process providing the task bar (explorer.exe) is not running in the isolated Session 0.
Or would I have to make sure throughout the source code, that no GUI displaying/modifying stuff is called in the context of the service?
That should not be necessary, though you should proceed cautiously with any GUI interaction from your service. Test thoroughly!
I would like to provide some info wrt. Raymonds Chen's comment to the other answer
You should avoid presenting UI in a service because you may trigger
the UI Detection Service which will switch the user to your service UI
temporarily. – Raymond Chen
I find these good articles:
What is Interactive Services Detection and Why is it Blinking at Me?
Inside Session 0 Isolation and the UI Detection Service, Part1, Part2
Where one can find explanation on what the UI detection service (UI0Detect) is and does and how it's supposed to work.
Interactive Services Detection (the blinking button on the taskbar) is
a mitigation for legacy applications that detects if a service is
trying to interact with the desktop. This is handled by the
Interactive Services Detection (UI0Detect) service.
However, one must note that this only can work if the service that is trying to view a GUI has the flag "Allow service to interact with desktop" set, because only then the service process will be running on WinSta0of Session0 even allowing it to show anything at all.
Alex Ionescu mentions this:
If UI0Detect.exe ...
the SCM has started it at the request of the Window Hook DLL. The
service will proceed ...
The service first does some
validation to make sure it’s running on the correct WinSta0\Default
windowstation and desktop and then notifies the SCM of success or
failure.
So, to come back to Raymond's comment: As far as I can see, as long as a service doesn't tick the type= interact option (see sc.exe), and normally you don't tick this, the UI0Detect service doesn't do anything and there shouldn't be any "danger" of triggering it.
Note: The information above is based on my limited research and tests on only a single Windows 7 PC.

Windows OS level file open event trigger

Colleagues, I have need to run a script/program on certain basic OS level events. In particular when a file in Windows is opened. The open may be read-only or to edit, and may be initiated by a number of means, either from windows explorer (open or ), be selected from a viewing or editing application from the native file chooser, or drag-n-drop into an editing or viewing application.
Further, i need the trigger to "hold" the event from completing the action until the runtime on the program has completed. The event handler program may return a pass state, or fail state. If fail state has been returned, then the event must disallow the initially requested action.
Lastly, I need to add to the file in question a property or attribute that will contain metadata that will be used by the above event trigger handler program to make a determination as to the pass/fail condition that will ultimately determine if the user is permitted to open the file.
Please note that this is NOT a windows event log situation, but one at the OS level file open event.
thanks very much for your help.
Edit
What I had hoped that someone was aware of was an OS level trigger, similar to how you can enable a trigger on an event-log writer occurance in Windows (ie: send an email, run a script when an event hits the event logger).
The application environment I work in, a PLM system, allows for event handling. So, when a user initiates an action, say a checkout of a file from an object, there are 3 available handlers or triggers that can be programmed. The "check" or "pre-action" trigger is a process that fires when the user starts initiates the event, but before the system permits it to be processed. So, one can block or otherwise alter the response of the system programmatically based on some condition, like user context.
I have also enabled startup/shutdown script firing via group policy, so i can delay a server shutdown until a database has closed properly. This was done in Windows server 2003. So, with that hook, i could manage shutdown or startup.
Hence, I had hoped someone might be aware of an OS trigger that would do the same for a file-open operation. This would be deployed on all workstations and desktops.
Again, if there are any ideas, it would be greatly appreciated.
Thanks again.
regards,
j
A couple strategies come to mind. One is to run the app with your own DLL to intercept API calls like WriteFile. Another would be to run the app under the debugger interface.

In Windows, what default event sources are available in the Application Event Log?

Short Version:
Are the event sources "Application" and "Application Error" always included in the Application Event Log? Are they available on new installations of Windows XP, Vista and Windows 7? Would it be really bad to use them instead of creating my own source (an impossibility for me)?
Long Version:
I have a ClickOnce application that is used by users without administrative privileges on their machines.
When I try to write to the Appliction Event Log, I get a security exception. (The Windows event logging infrastructure is trying to create me a new event source, and gets a security violation.)
So I would like to try reusing an existing event source. I have found a only two"generic-sounding" sources in the Application Event Log. Are these always part of a Windows installation, and would make a reasonable choice?
Application
Application Error
I am sure this is frowned upon, as I should distinguish my application using its own event source. But this is for infrequent fatal errors, which should be getting logged elsewhere by my code. I just want a really easy place to find them on a client machine in case it all goes wrong...
When I try to write to the Appliction Event Log, I get a security exception. (The Windows event logging infrastructure is trying to create me a new event source, and gets a security violation.)
I have just answered this here: Using EventLog in ClickOnce application
So I would like to try reusing an existing event source. I have found a only two"generic-sounding" sources in the Application Event Log. Are these always part of a Windows installation, and would make a reasonable choice?
It's really not wise to do this. Existing event sources will be used by either Windows applications, or by third party applications. If any of those are removed, or changed by something like a service pack or patch, your program will crash unless you have implemented exception handling to handle the exception gracefully, but then you wont have any event logging.
Also consider the work you may have to do to port your app to the next version of Windows. I suggest you will be making a rod for your own back.
In the answer I linked to, I suggested the best way to handle the problem, is to install your application using admin privs with the installer creating the source, or by creating a simple app that effectively does the same using the admin role.
The only thing else I can suggest is to always run your application in admin mode.

Resources