Start browser from Windows service - c#

I am a little new to the Microsoft world. I have read this answer and many other links and I'm aware that after Vista, it is not possible for windows service to interact with desktop.
However, I have kind of an emergency where I have to find a workaround quick. I need my windows service to somehow open a browser with a URL (any kind of an ugly hack will do for now).
The answer states :
you need to write a separate agent application, which will be automatically started on user login, and with which your service will communicate. Then the agent can start the browser or do whatever else you need.
Can someone please explain to me how I could do this in a simple way? How would that service talk to this 'agent'? What is this 'agent' exactly?
Any links or suggestions would be highly appreciated.
EDIT: Currently. my service tries to run the following code: System.Diagnostics.Process.Start("www.google.com");
which, I found out later, does not work

Yes, from a service you can run code on the desktop... The matter is on which desktop? because there may be various users logged at the same time (for example in a Windows Server there may be various users logged remotely).
If use Process.Start from a service as in your example:
System.Diagnostics.Process.Start("www.google.com");
The program will not appear in the desktop of any user, because the service is not associated with any of the desktops of the users.
The solution is to detect which user is logged locally and then execute the program as that user (and under the privileges of that user). For that I want to link you to another of my answers where I demonstrate how to invoke a program from a service. In that answer I present the code needed for this technique.
Note: That answer was about an screen capture, still the technique I describe is to call a program in the session of an user, and that method works from a service. So it applies to your problem.
You can use this method to execute the desired browser directly. As an alternative you can use it to invoke your service executable, where you can use System.Environment.UserInteractive to detect if the executable is not running as a service or not. If you pass the url as execution argument to your service executable then you can open it with the default browser using Process.Start just like your example above.
Note: The code was tested on Windows 7.

There is no "quick way" to do this, I'm afraid. (And since the service can't interact with the user's desktop, even if it could launch the browser the user couldn't see it anyway.)
The "agent application" you've mentioned is a totally separate application which runs under the user's desktop on login. It would have to use some method of inter-process communication (named pipes, TCP/IP, etc.) to communicate with the service.
The reason that services don't interact with the desktop is for reasons of security. There's no "quick workaround" available, and there shouldn't be one. There's also no "simple way" to circumvent that change (and again, there shouldn't be - if you can bypass security in a simple way, it's not much security).

use shellexecute using pinvoke
http://www.pinvoke.net/default.aspx/shell32.shellexecute
C# Signature:
public enum ShowCommands : int
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_FORCEMINIMIZE = 11,
SW_MAX = 11
}
[DllImport("shell32.dll")]
static extern IntPtr ShellExecute(
IntPtr hwnd,
string lpOperation,
string lpFile,
string lpParameters,
string lpDirectory,
ShowCommands nShowCmd);
// Asks default mail client to send an email to the specified address.
ShellExecute( IntPtr.Zero, "open", "mailto:support#microsoft.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );
// Asks default browser to visit the specified site.
ShellExecute( IntPtr.Zero, "open", "http://channel9.msdn.com", "", "", ShowCommands.SW_SHOWNOACTIVATE );
// Opens default HTML editing app to allow for edit of specified file
ShellExecute( IntPtr.Zero, "edit", #"c:\file.html", "", "", ShowCommands.SW_SHOWNOACTIVATE );
//Modified by Aljaz: Replaced the last zero in these calls with 4 otherwise it wouldn't show anything
// 0 stands for SW_HIDE contant, which means execute but don't show the window which is probably not
// what we want.

If you need something urgent - I'd try to put message into MSMQ from the service, and write some simple client that is waiting for queue messages, and proceed them.
Update:
Actually, if you need something very quick - you can even write to some file, and write simple desktop (winforms?) application to read this file in pool (each 5 sec?), and open browser when required.

Related

Windows 10 Feedback Task for my App

Similar to this question which invokes the Windows 10 store to allow a user to write a review or rate an app, I'd also like to be able to invoke the Windows 10 Feedback app and allow users to provide feedback there.
I cannot seem to find much information on:
How this works in general. Can any old app use this service? (I
notice it just kind of shows whatever apps I have running)
How to invoke the Windows Feedback app with my package id
In short - not that I can see.
Other apps are invoked via protocol activation. I haven't seen this documented for the feedback app though so I have to err on the side of 'we haven't made this available yet' (I'm still checking though)
Here's an overall guide to the process http://blog.jerrynixon.com/2012/10/walkthrough-using-windows-8-custom.html?m=1
When I look in the registry under HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Protocol I see (shortened a tad)
[HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Protocol\PackageId\Microsoft.WindowsFeedback...\ActivatableClassId\App.AppX7eaybq6p4x7d4jgd6w6jk7r5dg6yhmbf.mca\CustomProperties]
"Name"="windows-feedback"
So - give that a try via launching windows-feedback
If I do Windows Key-R (run): windows-feedback://
it works fine so this should work:
var uri = new Uri(#"windows-feedback://");
var success = await Windows.System.Launcher.LaunchUriAsync(uri);
if (success)
{
// URI launched
}
else
{
// URI launch failed
}
Update
I've done some searching and it seems the magic parameter there is
windows-feedback:?contextid=522
That launches the NFL feedback for example. This is a predetermined number - I'm not sure how one gets on this list though.

How can I launch an Internet Explorer process as another user, a la /netonly?

I'm trying to launch Internet Explorer as another user from our WPF app, so that when our users visit the (internal) website, they silently authenticate via Integrated Windows Authentication.
We do not want to launch iexplore.exe as the other user because of weird deployment/environment issues that occur when you launch a process for the first time on a computer and it attempts to set up IE7/8 for the first time. Though, if you have a solution for how to mute every IE installer on every machine, I'd love to hear it.
Back to my intended question. I'm able to get the exact IE impersonating behavior I want* from the command prompt using runas (thanks to https://serverfault.com/questions/70376/runas-domain-account-still-asks-for-password):
c:\> runas /noprofile /netonly /user:MyDomain\MyUser iexplore.exe
*note: I can't use runas for our WPF app for many reasons, but the end result is what I want.
Anyway, I'd like the C# equivalent code that does a runas /noprofile /netonly iexplore.exe.
I am halfway there with P/Invoke on CreateProcessWithLogonW. This is what I have:
uint LOGON_NETCREDENTIALS_ONLY = 2;
var lpStartupInfo = new CreateProcessWithLogonW_PInvoke.STARTUPINFO();
CreateProcessWithLogonW_PInvoke.PROCESS_INFORMATION processInformation;
CreateProcessWithLogonW_PInvoke.CreateProcessWithLogonW(
userName,
domain,
pw,
LOGON_NETCREDENTIALS_ONLY,
null,
commandLine,
0,
null,
null,
ref lpStartupInfo,
out processInformation);
This successfully launches Internet Explorer, but does not seem to impersonate the user at all. I am able to impersonate the user via the runas command, so I'm 98% sure the failure to authenticate isn't an IE/zone/password/IIS setting, it's just something I'm not doing right in my call to CreateProcessWithLogonW().
One thing I've noticed is that the runas /netonly command only works if I add the /noprofile switch, which is something that is stumping me. I have no idea how to set the equivalent of this switch via P/Invoke in C#.
Help is appreciated with either solution (solving the "IE runs a wizard when I launch it the first time", or finding the weirdo P/Invoke setting I'm missing).
Okay, I was very very close. The magic fix is adding -noframemerging to the iexplore.exe call, which...honestly I'm not sure what it does, it uses the phrase "process frame" which is awesome and perhaps means something to you.
In any case, this appears to be resolved.
var arguments = "-noframemerging " + url;
var pathToIExploreExe = GetFullPathToIExploreExe();
var commandLine = string.Format("\"{0}\" {1}", pathToIExploreExe, arguments);
uint LOGON_NETCREDENTIALS_ONLY = 2;
var lpStartupInfo = new CreateProcessWithLogonW_PInvoke.STARTUPINFO();
CreateProcessWithLogonW_PInvoke.PROCESS_INFORMATION processInformation;
CreateProcessWithLogonW_PInvoke.CreateProcessWithLogonW(
userName,
domain,
pw,
LOGON_NETCREDENTIALS_ONLY,
null,
commandLine,
0,
null,
null,
ref lpStartupInfo,
out processInformation);
CreateProcessWithLogonW requires that the specified user account must be allowed to log on interactively. Could it be a problem? Try CreateProcessAsUser function if that works.

create interactive elevated process from windows service and show to logged-on user

I have a service that spawns a WPF application process when a user logs on.
But for some reason the WPF application gets killed about 10 minutes after it has been created? The termination is immediate with no traces found in the Event Log nor are any normal close/exit events called in the WPF application.
In fact, when the termination occurs, Windows 7 seems to hang for a second, the mouse becoming unresponsive and then acting out the mouse gestures after a short delay (when it normalizes, but now lacking the created process).
The When
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
CanHandleSessionChangeEvent = true;
}
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLogon
&& changeDescription.SessionId > 0)
{
ApplicationLoader.PROCESS_INFORMATION procInfo;
ApplicationLoader.StartProcessAndBypassUAC(#"myapp.exe", out procInfo);
}
base.OnSessionChange(changeDescription);
}
}
Process Creation As Per Pero Matic Code
// ...
bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
null, // file to execute
applicationName, // command line
ref sa, // pointer to process SECURITY_ATTRIBUTES
ref sa, // pointer to thread SECURITY_ATTRIBUTES
false, // handles are not inheritable
dwCreationFlags, // creation flags
IntPtr.Zero, // pointer to new environment block
null, // name of current directory
ref si, // pointer to STARTUPINFO structure
out procInfo // receives information about new process
);
the termination does not seem to happen if i target notepad.exe, however?
tested it with a vanilla\empty WPF application (.NET 4), and that crashed as well
Process Creation with Administrative Privileges and No Prompt
It seems that the issue is trying to duplicate the administrative SYSTEM token from winlogon.exe (but which is running in session 1+), because if you duplicate the specific user token instead (e.g. from explorer.exe) then the crashes are no more!
this is confirmed with the same vanilla/empty WPF application, and with running Marcel Roma code here - note that he uses explorer.exe instead of winlogon.exe
although using explorer.exe gets rid of the termination I lose the administrative privileges with that, which does not work for me
any ideas how to get it to work with the winlogon process token?
or is it possible to adjust the exlorer.exe token to make the duplicate elevated? im guessing somehow using TokenElevation and SetTokenInformation or AdjustTokenPrivileges
or could it be that Windows 7 has been patched to disallow such process impersonation?
alternatively, is there any way to get the specific user token with administrative privileges (rather than the owner being SYSTEM), but again, without password knowledge/prompts (excluding CreateProcessWithLogonW)
is this maybe to do with garbage collection somehow?
Well I'm just suggesting you a work around:
Why you don't put your core functionalities in a windows service, and then use the wpf app as a frontend ? So that if the user kill it, it doesn't stop the service. Then the service can regularly check that the wpf front end is started, and if needed restart it.
I think it'll be a more "trusted" design that the one you're trying to do, which could let the antivirus think you're a bad software and block you.
And to protect the windows service there is another question here: Protecting a Windows Service from untrusted users
I don't think you can (and definitly should not be able) to do this. Your best bet is to create an application that doesn't need elevated privileges and then use IPC to talk back to your service which then performs administrative tasks on the users behalf.

Changing the user agent using urlmon.dll

While looking for a way to change the user agent string for the webBrowser control, I found this nifty method:
[DllImport("urlmon.dll", CharSet = CharSet.Ansi)]
private static extern int UrlMkSetSessionOption(int dwOption, string pBuffer, int dwBufferLength, int dwReserved);
const int URLMON_OPTION_USERAGENT = 0x10000001;
public static void ChangeUserAgent(string Agent)
{
UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, Agent, Agent.Length, 0);
}
Basically, I needed a way to change the user agent until I want to change it again.
The usual:
webBrowser1.Navigate ("http://www.whatsmyuseragent.com", "_self" , null, "User-Agent: Luke's Web Browser");
Only works for one request.
However, I keep reading everywhere that the first method only works once per session. In my case, it works as many times as I want it to. So my guess is that this is related to the instance of Internet Explorer on the computer?
So my questions are:
What version does the end user need to have installed on their computer for this method to work as intended? IE. change as much as I want.
Since this is related to the Internet Explorer installed on the computer, does changing the user agent in my application effect the browser?
If the user has Internet Explorer open, will this method still work?
Thanks!
We use the "UrlMkSetSessionOption" function quite a bit. We have a "custom web browser shell" which is really just an IE user control embedded into a full screen WinForms program. We change the user agent to identify to our web server that this is our "custom" browser shell. But to answer your specific questions:
We've used this with both IE8 on XP and IE9 on Win7. I think it is version independent, but we always use the latest version.
As far as we can tell, changing this setting only affects IE running in the process that invoked the method. So if a user launches IE from the desktop, the user agent is unchanged. If you restart the program, the user agent is unchanged.
It works with and without standalone IE instances running. The user agent for those standalone instances remain unchanged.

CreateProcessAsUser doesn't draw the GUI

I have a windows service running under "SYSTEM" account that checks if a specific application is running for each logged in user. If the application is not running, the service starts it (under corresponding user name).
I'm trying to accomplish my goal using CreateProcessAsUser(). The service does start the application under corresponding user name, but the GUI is not drawn. (Yes, I'm making sure that "Allow service to interact with desktop" check box is enabled).
System: XP SP3, language: C#
Here is some code that might be of interest:
PROCESS_INFORMATION processInfo = new PROCESS_INFORMATION();
startInfo.cb = Marshal.SizeOf(startInfo);
startInfo.lpDesktop = "winsta0\\default";
bResult = Win32.CreateProcessAsUser(hToken, null, strCommand, IntPtr.Zero, IntPtr.Zero, false, 0, IntPtr.Zero, null, ref startInfo, out processInfo);
As far as I understand, setting startInfo.lpDesktop = "winsta0\default"; should have used the desktop of corresponding user.
Even contrary to what is stated here: http://support.microsoft.com/kb/165194, I tried setting lpDesktop to null, or not setting it at all, both giving the same result: process was started in the name of expected user and I could see a part of window's title bar. The "invisible" window intercepts mouse click events, handles them as expected. It just doesn't draw itself.
Is anyone familiar with such a problem and knows what am I doing wrong?
MSDN has a sample of how to create a process as another user setting explicit permissions on the window station and desktop objects:
CreateProcessAsUser() windowstations and desktops
You can port the code to C# using P/Invoke or you could use a C++/CLI assembly.
However, be aware that your scenario is not supported and likely to break with Vista's (and Windows 7's) Session-0 isolation (download the whitepaper on the right).

Categories

Resources