How to allow launcher application to be pinned to the taskbar - c#

We have a simple launcher.cmd script that sets up some prerequisites (network shares, configuration files etc.), depending on command-line arguments, and then starts a third-party application.
It also launches a custom splash screen (Windows Forms application), but that doesn't have any functionality beyond the splash screen. The external application also contains a plugin that we have written.
Imagine something like this:
set appEnvironment=%1
MyCustomSplashScreen\MyCustomSplashScreen.exe %appEnvironment%
net use X: /delete /yes
net use X: \\someserver\%appEnvironment%
copy Configuration\%appEnvironment%.config ExternalApp\ExternalApp.MyPlugin.config
start ExternalApp\ExternalApp.exe
It is invoked like this:
launcher.cmd Production
The challenge we are facing is that some users pin ExternalApp.exe (the actual application, after launcher.cmd and the splash screen have terminated) to the taskbar. The taskbar shortcut then launches ExternalApp.exe directly and not our launcher script - so all kinds of strange things can happen.
As we have control over the plugin, it would be possible to move some of the logic directly into ExternalApp.exe, but that wouldn't solve the issue of losing the environment parameter. The best solution I can come up with would be different ways of making sure the application can only be launched via the launcher script, essentially making it useless for the user to pin the application to the taskbar.
However, I have thought about being a little more creative. I am planning to do the following:
Move the launcher.cmd logic into MyCustomSplashScreen.exe
In MyCustomSplashScreen.exe, start ExternalApp.exe and make it a docked child window (cf. Docking Window inside another Window ).
Instead of using a parameter, create copies (or links) of MyCustomSplashScreen.exe that reflect the environment, e.g. Launch_Production.exe, Launch_Staging.exe etc.
The consequence would be that only MyCustomSplashScreen would appear on the taskbar and be pinnable. Pinning the application would result in the application specific to that environment (e.g. Launch_Staging.exe) being pinned, just what the user expects.
I am quite confident that this would work. But maybe there is a simpler solution? What I'm looking for is some way to make only my launcher application on the taskbar and not the application it launches.
I found similar questions here and here where it was suggested to manipulate the pinning process itself. Maybe that's a better solution? I'm just not sure if my plugin has enough control over ExternalApp.exe to implement this, so would need to test it.

The solution from Control path to pinned exe in Windows taskbar and start menu works just fine.
I put something like this into my plugin and it does exactly what I want:
private static void SetTaskbarRelaunchCommand(string environment)
{
// WARNING, once RelaunchCommand has been set it can't be changed for any given appID.
// Workaround: delete all links here related to our app.
// %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\ImplicitAppShortcuts
// %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar
// Source: https://stackoverflow.com/a/28388958/33236
const string appID = "MyAppID";
string path = $#"C:\Launcher.exe {environment}";
IntPtr windowHandle = Process.GetCurrentProcess().MainWindowHandle;
var propGuid = new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}");
var id = new PropertyKey(propGuid, 5); // System.AppUserModel.ID
var relaunchCommand = new PropertyKey(propGuid, 2); // System.AppUserModel.RelaunchCommand
var relaunchDisplayNameResource = new PropertyKey(propGuid, 4); // System.AppUserModel.RelaunchDisplayNameResource
WindowProperties.SetWindowProperty(windowHandle, id, appID);
WindowProperties.SetWindowProperty(windowHandle, relaunchCommand, path);
WindowProperties.SetWindowProperty(windowHandle, relaunchDisplayNameResource, $"My App {environment}");
}
It seems like the original version of the Windows API CodePack published by Microsoft in 2009 is no longer officially available. Instead there are a dozen inofficial NuGet packages that (claim to) contain the original library, as well as some where bugs have been fixed.
I ended up using this NuGet package as it seems to be actively maintained.
Of course I am in the fortunate situation that I could manipulate the behavior of the external application through my plugin. Without this, there probably is no other solution than running the application as a docked child window.
UPDATE: It seems like there is another way to achieve thisn when you cannot control the application you're launching. You simply set the System.AppUserModel.Id for the launched application's window. More details here.

I wasn't successful in setting the RelaunchCommand and the RelaunchDisplayNameResource on the newest Windows 10 update. But i have found another possibility to set that the Launcher is pinned to the taskbar: Pinning to the taskbar a "chained process"

Related

Toast notifications missing application icon when launched from service

Question:
Why does my unpackaged C# application show its icon when I launch it but not when my Windows service launches it? How can I make my app's toasts always show the app's icon?
Details:
I have a C++ Windows Service that launches a C# Win32 application for toast functionality, since toasts cannot be launched directly from a service to a user. It is an absolute requirement that the service launches the toast app. To my frustration, however, the app's icon (i.e. the icon shown on the .exe in Explorer) refuses to show only when launched by my service. Here is an example of what I see when my service launches the app (Note the three squares. This is the Windows 10 default icon):
When I manually launch the app (i.e. click it), this is what I see instead:
The only difference between the above two screenshots is the launch method. The most succinct way I can describe my issue is that I want the launch method (launched from a service) that yields the first screenshot to yield the second screenshot instead.
I can provide the code snippet I used to generate these toasts, although I doubt its usefulness for finding a solution:
var notifier = ToastNotificationManagerCompat.CreateToastNotifier();
var xml = new Windows.Data.Xml.Dom.XmlDocument();
xml.LoadXml("<toast><visual><binding template=\"ToastGeneric\"><text>Foo</text<text>Bar</text></binding></visual></toast>");
var notif = new Windows.UI.Notifications.ToastNotification(xml);
notifier.Show(notif);
The most useful code sample I believe I can provide is the code that the service uses to launch the app:
void SpawnToastApp()
{
constexpr int nProcFlags = DETACHED_PROCESS | NORMAL_PRIORITY_CLASS;
constexpr wchar_t* wcsDesktop = L"WinSta0\\Default";
constexpr wchar_t* wcsToastApp = L"ToastApp\\Toast App (WIP).exe";
HANDLE hUser = NULL;
STARTUPINFOW si{ 0 };
wchar_t wcsCmdLine[MAX_PATH]{ 0 };
_snwprintf_s(wcsCmdLine, _TRUNCATE, L"\"%S\\%s\" %lu", _strInstallDir, wcsToastApp, GetCurrentProcessId());
_sessionCanToast = WTSQueryUserToken(_sessionId, &hUser);
if (_sessionCanToast)
{
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.lpDesktop = wcsDesktop;
_sessionCanToast = CreateProcessAsUserW(hUser, NULL, wcsCmdLine, NULL, NULL, FALSE,
nProcFlags, NULL, NULL,
&si, &_toastHandlerProcessInformation);
}
if(!_sessionCanToast)
{
/// Log it
}
if (hUser) { CloseHandle(hUser); };
}
I include the C++ code because I believe that I have narrowed the problem down to the launch method but am unsure the specific cause beyond that.
Additional Information:
These screenshots utilize Windows.UI.Notifications.ToastNotifications created from raw XML, but I have also tried using the Microsoft.Toolkit.Uwp.Notifications NuGet Package as recommended by Microsoft to the same effect.
I believe this project is a Windows Form App.
I am not using any sort of package--no APPX, MSIX, or sparse package. This is meant to be a lightweight app whose sole function is for toasts. While using a package isn't out of the question, suffice it to say that the number of hurdles and implementation issues make packaging this app undesirable. Indeed, the only reason I would want to package this app is for the icon in the upper left-hand corner of it, which it evidently does already, just not in the way I desire.
Similar to but NOT a duplicate of:
Change toast notification icon in wpf
I have already done this. My issue pertains to the icon's inconsistency rather than the lack of it entirely.
Why is app icon missing for toast notifications in action center on desktop?
I am using a Release build of my app
Cannot override notification app logo for Windows 10/11 VSTO app
Using AppOverrideLogo gets my icon to show under all circumstances, but it's more like a picture in the body of the toast rather than the small icon in the upper left-hand corner of the toast. Essentially, it's not the style I want.
EDIT 1:
I followed a sparse packaging guide found here to more or less the same result, the main difference being that now no icon not shows up at all anywhere. I used the asset generator in Visual Studio and then used the MSIX unpackaging tool to inspect the contents of the sparse package and confirmed it contained the generated assets. I had to comment out the reference to the splash screen because the app failed to register with that line included in the manifest.
EDIT 2:
I have decided to proceed with this app as if I am not having this issue, and so I used Visual Studio's Performance Profiler to analyze my app's resources. The Performance Profiler launched my toast app, and the toasts had the correct icon, so at this point I am 100% certain it has something to do with my service's launch method. Unfortunately, I am no closer to understanding why the icon does not show only when launched from my service.

How can I launch another app in full screen using LaunchUriForResultAsync?

I would like to use the LaunchUriForResultAsync to launch my other app and get some results from it. But I want to launch the other app in full screen and if that's not possible maybe just launch it in a bigger size.
When I launch another app using LaunchUriForResultAsync, the modal app launches in 500x500 size. I would need something bigger than that because I need to create a data entry app that requires more space which multiple applications can launch. I tried to set the DesiredRemainingView and PreferredLaunchWindowingMode to FullScreen. It didn't make my other app launch in full screen.
Is there a way I can use LaunchUriForResultAsync to launch my other app in full screen?
If the device is in Tablet Mode, you can use ViewSizePreference.SizeNone and the app will be launched in full screen:
var options = new Windows.System.LauncherOptions();
options.DesiredRemainingView = Windows.UI.ViewManagement.ViewSizePreference.UseNone;
await Windows.System.Launcher.LaunchUriAsync(uri, options);
I think part of the confusion here is that the size preference defines the space your current app desires, not the size you want for the launched app. Because the target app can have its own preferences.
This solution does not work if Tablet Mode is disabled, then you would have to go with a solution as Rob Caplan suggested.
It is not possible to control the size of the window launched for results. In current versions of Windows this size is hard coded to 500x500. Providing a way to allow an app to control this is being investigated for future a future update.
Since you control both apps you could write a custom system to manage the communication rather than use LaunchUriForResultAsync. AppA and AppB can each define their own protocols (appa: and appb:). AppA can LaunchUriAsync appb: to do whatever it needs, then AppB can LaunchUriAsync appa: to deliver the results. Pass AppA's return URL as an argument to appb: and this can be called from multiple apps.
The trickiest part may be to manage the modality. It's easy to disable AppA's page while it's waiting, but harder to link AppB's window to AppA's after the initial launch. Off the top of my head I'd just relaunch appb: and make sure appb: can track and handle multiple launches sanely.
This is more or less what LaunchUriForResultAsync does for you.

c# How to programmatically disable Windows 8 specific features

This question is asked alot, but I couldnt find working method / way to do it - except for a third party application.
I am pretty sure, or atleast I am being very hopeful that solution for this problem does exist.
As the title says, I want to disable window 8 gestures just like every third app is doing (SkipMetroSuite, ClassicShellMenu or w/e).
I need it to be built in in my app because I cant install anything on the compter my app is dedicated to but my app itself...
Is there a way to do it in C#?
EDIT:
I personally asked the developer of Classic Shell Menu how his programs works, here is the answer:
The principle is to inject a message hook in the thread of window with
class “ApplicationManager_DesktopShellWindow”, then listen for mouse
messages sent to windows with class “EdgeUiInputWndClass”, and hide
those windows. When my program exists it reshows all windows that it
has hidden.
He also mentioned I can find the solution here:
Classic Shell src
But there's one problem, the solution is in c++ and I have no Idea how to port it to c# so I would appreciate your help.
The solution is in ClassicStartMenuDLL.cpp which is in ClassicStartMenuDLL Solution.
The first step to what you want to do is to disable Metro mode (the start screen tiles).
You can achieve this via a registry edit, which you can do programmatically.
The entry of interest is the following:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\RPEnabled
You need to set this to 0
Next, you want to disable the 'hot corners'. This is also a registry edit which can be done programmatically.
The entry of interest is the following:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\ImmersiveShell
Create a new key called EdgeUI, and under thay key create the following DWORD entries:
DisableTLcorner
DisableTRcorner (Windows 8.1+)
DisableCharmsHint
Set both values to 1
Since these are both HKCU settings (i.e. current user), then a simple log-off is all that is required for them to take effect.
Alternatively you can kill the explorer process, though it is not recommended.
If it is not working for you, try to test it with a ready-made registry file first, since you might be doing something wrong -> Disable Charms & Switcher

C# how to run applications on the Desktop?

Is it possible to run applications on the Windows Desktop? I mean... that it can only be seen in the system tray, and it should be able to run alongside the desktop.
I have no idea how to code it, please help me. I'm kind of new to these things, I am supposed to create something like a "Stardock Fence".
I have seen some examples, but they seem buggy, any strong alternative I could use?
Process.Start() can be used to start a windows application/console app from another win application. There are parameters that allow you to optionally hide the UI as well.
You want your application's windows to be always-on-bottom. In other words, your UI will always appear to be beneath any other open window and just above the Desktop's icons.
To accomplish that, see these related questions.
Once you have a window always on the bottom of the z-order, you'll probably want to remove the non-client window chrome (titlebar/min/max/close buttons) so that your UI can look like a more integrated part of the Desktop. There's plenty of examples around; Googling is left as an exercise for the reader.
Not entirely sure what you mean by background. I expect you mean a Windows Service which is a project type in visual studio, or you might (less likely) mean running a background thread.
Actually, if you want it in the system-tray, you don't want it entirely in the background.
If it was to be fully "in the background", then your best bet is to have it as a service.
System tray icons need a window, but you can just make it non-visible and non-taskbar and that's fine.
A common combo is a service that does the actual heavy-lifting, and a hidden-window application with a systray icon that reports on the service's status (possibly making that same window visible when further interaction is needed).
You want to run your application in the background? Is it on a windows machine? If so then you want to look into running your application as a windows service. Here's an msdn link:
Introduction to Windows Services
There's examples in the article I think - if not it's a good starting point. You can configure services to start automatically on startup of the machine etc. Your application will then run in the background.
Basically you craete your application as normal and then host it in a windows service rather than say a console app or a winforms app.

Prevent Process 'A' from spawning Process 'B' which then shows up on top of what should be the "TopMost" Process 'C'

I have a windows form application which needs to be the TopMost. I've set my form to be the TopMost and my application works as I'd like it to except for in one case.
There is a 3rd party application (referred to as player.exe) that displays SWF movie files on a portion of the screen that popup on top of my application.
Using Process Monitor I determined that player.exe application calls
flash.exe <PositionX> <PositionY> <Width> <Height> <MovieFile>
in my case:
flash.exe 901 96 379 261 somemovie.swf
Since flash.exe is being spawned in a new process after my form has been set to the TopMost it is appearing on top of my application.
First thing I did was make my application minimize the player.exe main application window hoping that this would prevent the Flash from appearing also. But, unfortunately it doesn't... even with the window minimized whenever the flash movie starts it shows up at the pixel location (901,96). I then tried creating a timer to keep setting the form.TopMost property to true every 10ms. This sort of works but you still see a very quick blip of the swf file.
Is there some type of Windows API call which can be used to temporarily prevent player.exe from spawning child processes which are visible? I admit it sounds a little far fetched. But, curious if anyone else has had a similar problem.
Addendum:
This addendum is to provide a reply to some of the suggestions layed out in Mathew's post below.
For the emergency situation described in the comments, I would look at possible solutions along these lines:
1) How does the third party application normally get started and
stopped? Am I permitted to close it
the same way? If it is a service, the
Service Control Manager can stop it.
If it is a regular application,
sending an escape keystroke (with
SendInput() perhaps) or WM_CLOSE
message to its main window may work.
Easiest way to close the app is to CTRL-ALT-DEL, then kill process. -OR-
The proper way is to Hold ESC while clicking the left mouse button... then input your username and password, navigate some menu's to stop the player.
There is no PAUSE command... believe it or not.
I don't think using WM_CLOSE will help since minimizing the application doesn't. Would that kill the process also? If not, how do you reopen it.
2) If I can't close it nicely, am I permitted to kill it? If so,
TerminateProcess() should work.
I can't kill the process for two reasons. 1) Upon relaunch you need to supply username/password credentials... There may be a way to get around this since it doesn't prompt when the machine is rebooted but... 2) Whenever I kill the process in task manager it doesn't die gracefully and asks if you want to send an error report.
3) If I absolutely have to leave the other process running, I would try
to see if I can programmatically
invoke fast user switching to take me
to a different session (in which there
will be no competing topmost windows).
I don't know where in the API to start
with this one. (Peter Ruderman
suggests SwitchDesktop() for this
purpose in his answer.)
I got really excited by this idea... I found this article on CodeProject which provides a lot of the API Wrapper methods. I stopped implementing it because I think that in order for desktop's to work you must have explorer.exe running (which I do not).
EDIT2: On second thought... maybe explorer.exe isn't needed. I'll give it a try and report back.
Edit3: Was unable to get the code in that article working. Will have to put this on hold for a moment.
Answer Summary
As one might have expected, there is no simple answer to this problem. The best solution would be to problematically switch to a different desktop when you need to guarantee nothing will appear over it. I was unable to find a simple C# implementation of desktop switching that worked and I had a looming doubt that I would just be opening a whole new set of worms once it was implemented. Therefore, I decided not to implement the desktop switching. I did find a C++ Implementation that works well. Please post working C# virtual desktop implementations for others.
Setting the TopMost property (or adding the WS_EX_TOPMOST style to a window) does not make it unique in the system. Any number of topmost windows may be created by any number of applications; the only guarantee is that all topmost windows will be drawn 'above' all non-topmost windows. If there are two or more topmost windows, the Z-order still applies. From your description, I suspect that flash.exe is also creating a topmost window.
Aside from periodically forcing your window to the top of the Z-order, I think there is little you can do. Be warned, however, that this approach is dangerous: if two or more windows are simultaneously trying to force themselves to the top of the Z-order, the result will be a flickering mess that the user will likely have to use the task manager to escape.
I recommend that your program not attempt to meddle with other processes on the computer (unless that is its explicit purpose, e.g. a task manager clone). The computer belongs to the user, and he may not value your program more highly than all others.
Addendum:
For the emergency situation described in the comments, I would look at possible solutions along these lines:
How does the third party application normally get started and stopped? Am I permitted to close it the same way? If it is a service, the Service Control Manager can stop it. If it is a regular application, sending an escape keystroke (with SendInput() perhaps) or WM_CLOSE message to its main window may work.
If I can't close it nicely, am I permitted to kill it? If so, TerminateProcess() should work.
If I absolutely have to leave the other process running, I would try to see if I can programmatically invoke fast user switching to take me to a different session (in which there will be no competing topmost windows). I don't know where in the API to start with this one. (Peter Ruderman suggests SwitchDesktop() for this purpose in his answer.)
You can use the Process class to start flash.exe directly - and use an appropriate ProcessStartInfo settings to show the window in a hidden state - or with a WindowStyle of hidden or minimized.
You could also consider using the SetWindowsHookEx API to intercept the process start API calls, and when the process is flash.exe run some code to restore you window to top-most status.
Matthew's answer is excellent, but I suspect you may be asking the wrong question. Why does your application need to be topmost? If you're trying to create a kiosk or some such, then topmost is not the way to go.
Edit: After reading your response to Matthew's comment, I'd suggest creating a new desktop and switching to it before displaying your alert. (See CreateDesktop and SwitchDesktop in MSDN.)

Categories

Resources