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.
Related
As part of some work I need to get done for Windows 10, I have written a code in C# that essentially detects every minute whether a PC is in screen saver mode or not, and it writes to a table in MySQL the relevant status ("PC in use" if the screen saver is off, "available PC" if the screen saver is on).
I did this using (full link if required - https://www.codeproject.com/Articles/17067/Controlling-The-Screen-Saver-With-C):
// Returns TRUE if the screen saver is actually running
public static bool GetScreenSaverRunning( )
{
bool isRunning = false;
SystemParametersInfo( SPI_GETSCREENSAVERRUNNING, 0,
ref isRunning, 0 );
return isRunning;
}
The code works flawlessly in console application mode (I made a loop to test it out over a minute with a check up on screen save status every 10 seconds), this means in MySQL the status was set correctly every time, depending on the screen save status at the moment of the check up.
The problem occurs when I use this code for a windows service. The service is installed correctly, the log on tab is set on Local System (I also tried with the logged in user instead, same results) and I allow the service to interact with the desktop, just in case, but the difference here is that no matter if the PC enters screen save or not, it always returns false on GetScreenSaverRunning(), thus setting the status of the PC in MySQL as "PC in use", even if the screen saver is on at the moment of check up.
I get the sense that the problem isn't in the code itself, since it works without any issues as a console application, but perhaps something behind the scenes. I tried to search here and on many other websites, haven't found anything related to such a problem.
Does anyone have any idea at all what might be the issue? Any help and/or suggestions will be greatly appreciated!
Thank you in advance.
(I could post the code if required, but it is pretty much straight forward and the main part of it, controlling the screen save detection, is taken from the website mentioned above, afterwards it's a simple if (GetScreenSaverRunning() == true) )
Ever since Vista, Services are barred from a Interactive Session. Even if they run under the same rights, they do not get a interactive Session. I would guess that is getting in the way here.
While you can overwrite this behavior in the Service settings, this is not adviseable for new code. Consider making this a Background Task started by the Task Sheduler instead.
Because the windows service runs in different session then the windows logon. You can't interact with the desktop related services unless you run the windows service in win logon session. There used to be an option in Windows service manager where you can set the properties to "Interact with desktop session" but I don't think that ever worked.
There's a work around to run the windows service using the win logo session.
See this helper class that can get the current logged on user session and interact with the desktop services. https://github.com/murrayju/CreateProcessAsUser/blob/master/ProcessExtensions/ProcessExtensions.cs
What I'm trying to do is to, from a Web Service (WCF), give a remote computer (the Web Service Consumer) the instruction to open its default Web Browser (be it Internet Explorer, Firefox, Chrome, etc.), navigate to a certain web page and keep monitoring the events of that browser so that I can capture a certain value from the Document Title at a certain point, and do stuff with it.
I'm already able to send the command to open Internet Explorer and navigate to a URL, from the Web Service to the remote computer (my consumer), but I don't like the approach since I can't monitor the Document.Title property for changes nor access its value at any given time. Here is an example:
using System.Diagnostics;
public void DoIt();
{
Process batchProcess = new Process();
batchProcess.StartInfo.FileName = "iexplore.exe";
batchProcess.StartInfo.Arguments = "http://whatever.com";
batchProcess.Start();
}
This opens up Internet Explorer on the remote machine and navigates to the Url I give it, but I can't keep watch for the Browser's Events or Properties Values....
Can somebody help? ;-)
I don't think you can access information in one application (the web browser) from another (the WCF client) like that, and it's certainly not possible to do it without knowing what the user's default browser is.
You might have more luck using a WebBrowser control (WPF or Windows Forms), which embeds Internet Explorer's engine into the application and allows you access to the document title.
Background
The company I work on is developing a kiosk-like application for tablets running Windows 8 Pro (on desktop mode). The user shouldn't be able to access anything that isn't the application itself: charms will be disabled, the taskbar will be hidden behind the application, etc.
This also means the user shouldn't be able to change network settings, leaving the responsability to keep the device always connected to us. Up to now, I had success using the Mobile Broadband API to assure the device is connected whenever there's a mobile network available. It'll detect disconnect events and try to connect again.
The Problem
Although the user shouldn't be able to do it, I'm considering the case where the user follows this steps:
User opens right-side charm,
clicks on Settings,
clicks on Network,
clicks on More PC Settings,
clicks on Wireless, and
disables the mobile broadband device.
I would like to be able to revert this programmatically and enable it again.
The Attempts
I have tried some different ways to force 3G being reenabled. Most of them give me the same result: they supposedly enable the device without errors, but I still cannot use it. Enable-NetAdapter in Powershell doesn't throw errors, and the Enable method of Win32_NetworkAdapter appears to work, but no dice.
I thought maybe the method IMbnRadio::SetSoftwareRadioState could be what I'm after, but I can't get to it when the device is disabled. The method IMbnInterfaceManager::GetInterfaces throws a COMException claiming the element could not be found (HRESULT = 0x80070490).
MbnInterfaceManager mbnInterfaceManager = new MbnInterfaceManager();
IMbnInterfaceManager interfaceManager = (IMbnInterfaceManager)mbnInterfaceManager;
// The following line throws a COMException:
IMbnInterface[] interfaces = (IMbnInterface[])interfaceManager.GetInterfaces();
mobileInterface = interfaces[0];
mobileRadio = (IMbnRadio)mobileInterface;
uint requestId;
mobileRadio.SetSoftwareRadioState(MBN_RADIO.MBN_RADIO_ON, out requestId);
Is there a way to override user preferences set on "More PC Settings?"
I found a sketchy way to solve this. Keep in mind this is undocumented, wrong, shameless and immoral, and will probably break eventually. The client is aware of this, but prefers to keep the access to the OS limited.
The setting in case is saved in the Registry. At least in the computers I've checked, it's stored in HKLM\SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}\0022 in a key named RadioOff.
The Airplane Mode setting is also stored in the Registry, but in a different place. It's at HKLM\SYSTEM\CurrentControlSet\Control\RadioManagement\SystemRadioState in a key named (Default).
After changing these keys and rebooting, everything seems to work fine. I'll repeat though: you really shouldn't be doing this, especially the Airplane Mode thing.
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.
I want that the WebBrowser control to use IE9. IE9 is installed on the computer, but the WebBrowser control is still using IE8.
I verified with http://www.whatbrowser.org/en/. I try to make some changes to the registry (found a solution here) but is not working.
I think it is the user agent string that is being passed to the site. It is misidentifying it as IE8 as it might not be meeting the requirements in their logic to match as IE9. I can see the same thing happen on my box as well. You could specify the user agent string to use if you want. Add this to your project
In your using statements add ...
using System.Runtime.InteropServices;
Within your form class add ....
[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 void ChangeUserAgent(String Agent)
{
UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, Agent, Agent.Length, 0);
}
Then just call it somewhere in your code ... maybe the constructor, or the form_load event.
ChangeUserAgent("Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
Browsers lie about their "user agent" to give web sites a break. You're running 9, you cannot have 8 and 9 installed at the same time unless you used the beta version. See this blog post for details about the user agent string.
If you want to make sure then look at the DLL version that gets loaded. Project + Properties, Debug, tick "Unmanaged code debugging". Start your program, Debug + Break All. Debug + Windows + Modules and locate ieframe.dll in the list. The version number column should tell you. I'm getting "8.00.7600.16385 (win7_rtm.090713-1255)", the Win7 release version. I don't have IE9 installed yet.
Use this in the HTML head:
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
Otherwise:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION\yourexename.exe - REG_DWORD = 9000
(decimal)
You can try to add registry value that informs your WebBrowser control witch version of IE you would like to run for your application.
I had similar problem - more here
It seems it might be your page detection script. Try this site (http://www.whatismybrowser.com/). I know other sites gave me the wrong information, but this site correctly identified the browser as the version of IE that was installed on my machine.