I have the following snippet of code here that im trying to build to automatically change the proxy settings:
public class ProxyManager
{
public static bool UnsetProxy()
{
return SetProxy(null);
}
public static bool SetProxy(string Ip,int Port)
{
return SetProxy(Ip + ":" + Port.ToString());
}
public static bool SetProxy(string ProxyAddress)
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
if (ProxyAddress == null)
{
registry.SetValue("ProxyEnable", 0);
}
else
{
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", ProxyAddress.ToString());
}
//Force the update!
registry.Clase();
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
return true;
}
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;
}
But for some reason the proxy settings are not being set, I know the method is being executed correctly as I insert an event into the Event Manger after the method is called and that is visible.
For some reason though the proxy settings are not, I'me calling the function like so:
EventManager.WriteEntry("Proxy Settings Enabled");
ProxyManager.SetProxy("10.222.62.65:8080");
My application is a windows service and is running under the authority of the Local System Account which has full privileges.
I suspect that it might be a combination of the fact that you're using the code Registry.CurrentUser and that it's running under the Local System Account.
The combination of those two snippets of your question makes me think that you might be changing the settings for the wrong user account? I'd suggest trying to run the service under your account and see if that makes any difference (assuming that this is possible due to UAC etc).
i wrote a similar program for disabling network adapters and changing proxy. It is at tognet.codeplex.com. I have experienced that // Force the update code somehow does not wan to refresh proxy settings on a windows 7 box. If i restart IE and look at the proxy settings again only then it shows the correct state of the proxy.
The reason is that you are changing the registry branch of CURRENT_USER, so there are actually two different branches - for your own user, and for Local System. And when you are running as Windows Service, you are changing the other branch. So actually you set the values, bot for a totally defferent user.
So what you need - is to get SID of your user, and then save it somewhere, so your service could use it, and access the correct branch (the one that is owned by your user). The code below tested on Windows 10.
public static RegistryKey? GetCurrentUserKey()
{
var sidString = GetSidFromLocalMachine();
if (string.IsNullOrWhiteSpace(sidString))
{
sidString = WindowsIdentity.GetCurrent().User?.ToString();
}
if (string.IsNullOrWhiteSpace(sidString))
return null;
RegistryKey resultKey = Registry.Users.OpenSubKey(sidString + "\\", true);
return resultKey;
}
public static string GetSidFromLocalMachine()
{
var settingsKey = Registry.LocalMachine.OpenSubKey(regKeyInternetSettings, true);
if (settingsKey != null)
return settingsKey.GetValue(regSid).ToString();
return string.Empty;
}
public static bool SaveSidToLocalMachine(string sid)
{
if (string.IsNullOrWhiteSpace(sid))
return false;
var settingsKey = Registry.LocalMachine.OpenSubKey(regKeyInternetSettings, true);
if (settingsKey == null)
return false;
settingsKey.SetValue("SID", sid);
settingsKey.Close();
return true;
}
You need to call SaveSidToLocalMachine before running the service, or set it manually. Then any time you need to load any registry key from your service, just call
var key = GetCurrentUserKey()?.OpenSubKey(regKeyInternetSettings, true);
key.SetValue("ProxyEnable", 1);
key.Close();
And don't forget to refresh:
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
Related
I just discovered that the reason my code for detecting and changing key values wasn't working, was because it WAS working - just on a different user. Because my winform app will need elevated access for some operations, I have the following in my manifest per many guides saying this is necessary:
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Meanwhile, I have code for detecting and changing a value in the registry as follows:
using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64))
using (var F1key = hklm.OpenSubKey(#"SOFTWARE\Classes\TypeLib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win64"))
{
// EGADS! It's active!
if (F1key != null)
{
fckF1Status.Text = "F1 Help is on. Turning off";
F1key.SetValue("", "", RegistryValueKind.String);
}
else
{
fckF1Status.Text = "F1 Help is off. Turning on";
F1key.SetValue("", "c:\windows\helppane.exe", RegistryValueKind.String);
}
}
The problem is that the changes only show up when I'm looking at regedit as an admin and it appears to be loading it into the "current user" branch of the admin and not the logged in user. How can I make sure registry changes to Registry.CurrentUser are for the logged in user and not the admin/elevated account?
The answer was that HKCU is just an alias and you need to work with Registry.Users instead. To do that, you have to determine who the current user actually is which was done like so (from How to get current windows username from windows service in multiuser environment using .NET):
private string loggedInUser;
private SecurityIdentifier loggedInSID;
private string loggedInSIDStr;
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);
private enum WtsInfoClass
{
WTSUserName = 5,
WTSDomainName = 7,
}
private static string GetUsername(int sessionId, bool prependDomain = true)
{
IntPtr buffer;
int strLen;
string username = "SYSTEM";
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
if (prependDomain)
{
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
WTSFreeMemory(buffer);
}
}
}
return username;
}
private void YOUR_CONSTRUCTOR(object sender, EventArgs e)
{
loggedInUser = GetUsername(Process.GetCurrentProcess().SessionId);
NTAccount f = new NTAccount(loggedInUser);
loggedInSID = (SecurityIdentifier)f.Translate(typeof(SecurityIdentifier));
loggedInSIDStr = loggedInSID.ToString();
}
The only modificaations I made from the linked answer was to to set some variables in my class and assign them in the constructor. Later, when I wanted to set the values for the user, I used this:
using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Registry64))
{
using (hklm.CreateSubKey(loggedInSIDStr + #"\SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win32")) { };
using (hklm.CreateSubKey(loggedInSIDStr + #"\SOFTWARE\Classes\Typelib\{8cec5860-07a1-11d9-b15e-000d56bfe6ee}\1.0\0\win64")) { };
}
This seems to do the job. You still won't see the changes in HKCU if you opened Regedit as admin because it will point to the admin's users branch of the registry, but it will show up in the proper user's branch.
I'm trying to enable the SeDebugPrivilege for my process by using the AdjustTokenPrivileges function, it succeeds but no privileges are enabled and the last error code is ERROR_NOT_ALL_ASSIGNED.
I read the documentation of the function and looked for examples both here and on other sites to understand if I was doing something wrong but I couldn't find any problems.
I checked if my user account has this privilege and it does (the Administrator group does have it and the account is part of the group).
The application is running elevated.
This is the method that I use to enable the privilege:
public static bool EnableRequiredPrivilegesForCurrentProcess()
{
IntPtr TokenHandle = GetCurrentProcessTokenForWriting();
Win32Structures.LUID_AND_ATTRIBUTES[] PrivilegesLUIDs = new Win32Structures.LUID_AND_ATTRIBUTES[1];
if (Win32SecurityFunctions.LookupPrivilegeValue(null, Win32Constants.SE_DEBUG_NAME, out Win32Structures.LUID DebugPrivilegeLUID))
{
Win32Structures.LUID_AND_ATTRIBUTES Attribute = new()
{
Luid = DebugPrivilegeLUID,
Attributes = (uint)Win32Enumerations.PrivilegeLUIDAttributes.SE_PRIVILEGE_ENABLED //0x00000002
};
PrivilegesLUIDs[0] = Attribute;
}
else
{
return false;
}
int StructureSize = Marshal.SizeOf(typeof(Win32Structures.LUID_AND_ATTRIBUTES));
IntPtr LuidAttributesArrayPointer = Marshal.AllocHGlobal(StructureSize * PrivilegesLUIDs.Length);
Marshal.StructureToPtr(PrivilegesLUIDs[0], LuidAttributesArrayPointer, false);
Win32Structures.TOKEN_PRIVILEGES NewPrivileges = new()
{
PrivilegeCount = 1,
Privileges = LuidAttributesArrayPointer
};
if (Win32TokenFunctions.AdjustTokenPrivileges(TokenHandle, false, ref NewPrivileges, 0, IntPtr.Zero, out _))
{
Marshal.DestroyStructure(LuidAttributesArrayPointer, typeof(Win32Structures.LUID_AND_ATTRIBUTES));
Marshal.FreeHGlobal(LuidAttributesArrayPointer);
return true;
}
else
{
Win32OtherFunctions.CloseHandle(TokenHandle);
Marshal.DestroyStructure(LuidAttributesArrayPointer, typeof(Win32Structures.LUID_AND_ATTRIBUTES));
Marshal.FreeHGlobal(LuidAttributesArrayPointer);
return false;
}
}
This is the GetCurrentProcessTokenForWriting() method referenced in the first line:
private static IntPtr GetCurrentProcessTokenForWriting()
{
if (Win32TokenFunctions.OpenProcessToken(Win32OtherFunctions.GetCurrentProcess(), Win32Enumerations.TokenAccessRights.TOKEN_WRITE, out IntPtr TokenHandle))
{
return TokenHandle;
}
else
{
return IntPtr.Zero;
}
}
My application gathers a wide array of info from all processes in the system by calling various functions which require different access rights on the process handle, I require the SeDebugPrivilege to be able to open an handle to a process with PROCESS_ALL_ACCESS access right (if possible).
I am writing an add-on for Internet Explorer 11.
I have already added guid variables:
[ComVisible(true), Guid("86524891-49EB-4F46-BAE3-C5545B81A671"), ClassInterface(ClassInterfaceType.None)]
And already added references to MSHTML and SHDocVw
And already write ComRegister function, unregister function
[ComRegisterFunction]
public static void RegisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);
if (registryKey == null)
{
registryKey = Registry.LocalMachine.CreateSubKey(BHOKEYNAME);
}
string guid = type.GUID.ToString("B");
RegistryKey ourKey = registryKey.OpenSubKey(guid);
if (ourKey == null)
{
ourKey = registryKey.CreateSubKey(guid);
}
ourKey.SetValue("Alright", 1);
registryKey.Close();
ourKey.Close();
}
[ComUnregisterFunction]
public static void UnregisterBHO(Type type)
{
RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(BHOKEYNAME, true);
string guid = type.GUID.ToString("B");
if (registryKey != null)
{
registryKey.DeleteSubKey(guid, false);
}
}
And I've already set / get site functions:
public int SetSite([MarshalAs(UnmanagedType.IUnknown)] object site)
{
if (site != null)
{
webBrowser = (SHDocVw.WebBrowser)site;
webBrowser.DocumentComplete += new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
webBrowser.BeforeNavigate2 += new DWebBrowserEvents2_BeforeNavigate2EventHandler(this.OnBeforeNavigate2);
webBrowser.NavigateComplete2 += WebBrowser_NavigateComplete2;
}
else
{
webBrowser.DocumentComplete -= new DWebBrowserEvents2_DocumentCompleteEventHandler(this.OnDocumentComplete);
webBrowser.BeforeNavigate2 -= new DWebBrowserEvents2_BeforeNavigate2EventHandler(this.OnBeforeNavigate2);
webBrowser.NavigateComplete2 -= WebBrowser_NavigateComplete2;
webBrowser = null;
}
return 0;
}
private void WebBrowser_NavigateComplete2(object pDisp, ref object URL)
{
}
public int GetSite(ref Guid guid, out IntPtr ppvSite)
{
IntPtr punk = Marshal.GetIUnknownForObject(webBrowser);
int hr = Marshal.QueryInterface(punk, ref guid, out ppvSite);
Marshal.Release(punk);
return hr;
}
Then I added a setup project:
Setup project propery İmage
So when I setup the project, I can't see my "add-on" in Internet Explorer. Any suggestions?
If you believe it's a registration issue then you should show the definition of BHOKEYNAME. It's also unlikely that the Site functions are related to deployment because they are called by the browser when your BHO is correctly hooked up.
It's unclear what other "wiring" you may or may not have put in your setup project. Windows Installer will not just call those register functions. You have to arrange for that to happen somehow - when you're in development mode (and not deployment mode) that's what RegAsm.exe will do, but that is tricky from a setup project.
The other thing that seems to be missing is the COM part. If your Dll needs registering for COM, there appears to be nothing in your setup project that will do any COM registration.
So I believe there are at least two things that need doing:
It appears that your Dll needs COM registration, therefore you should go to the properties window for the Dll in the File System view and choose a vsdr COM setting.
If you need BHO registry entries creating then why not just use the Registry view in the setup project? All the functionality for creating (and automatically uninstallng) registry entries is in there, and you don't need any code at all to get that done.
I am attempting to write an application that automatically changes the proxy server based upon what network connection is active. In this application, the user can also manually click a server in the notifyIcon context menu and call the function to switch the server.
My problem is the following: The application changes the proxy server the first time the function is called, but will not work after that. I have put debug statements in to make sure the correct proxy server is being passed to that function (and it is indeed correct), but the registry entries never get changed after the first time. What am I doing wrong?
Here is my Proxy Class:
class Proxy
{
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;
static bool settingsReturn, refreshReturn;
public void SetProxy(ProxyList proxy)
{
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", proxy.server + ":" + proxy.port);
registry.Close();
// These lines implement the Interface in the beginning of program
// They cause the OS to refresh the settings, causing IP to realy update
settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
}
And here is how I call the function from the context menu:
void Form1_Click(object sender, EventArgs e)
{
Proxy proxyServer = new Proxy();
ToolStripMenuItem item = (ToolStripMenuItem)sender;
proxyServer.SetProxy(XML.proxy[(int)item.Tag]);
proxyServer = null;
notifyIcon1.BalloonTipText = XML.proxy[(int)item.Tag].name + " is now your Active Proxy";
notifyIcon1.ShowBalloonTip(1);
}
From Microsoft's Knowledge Base: How to programmatically query and set proxy settings under Internet Explorer
Note INTERNET_OPTION_PER_CONNECTION_OPTION causes the settings to be changed on a system-wide basis when a NULL handle is used. To correctly reflect global proxy settings, you must call the InternetSetOption function with the INTERNET_OPTION_REFRESH option flag.
This is from MSDN
INTERNET_OPTION_PER_CONNECTION_OPTION
75
Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies a list of options for a particular connection. This is used by InternetQueryOption and InternetSetOption. This option is only valid in Internet Explorer 5 and later.
Read about the INTERNET_PER_CONN_OPTION_LIST structure.
Note: the value for the INTERNET_PER_CONN_PROXY_SERVER flag is 2.
RegistryKey class is disposable. Maybe it is being cached or something, try disposing it in addition of closing it.
i think you'd better do a plug-in for your browser instead of changing the registry directly.
I have a class that modifies the ProxyEnable and ProxyServer registry keys of Internet Settings.
If I use this class in a Console App and run it the value are changed and I can see the changes using regedit.
Now the part that doesn't work is I am trying to use this class in an Installer class so that the registry keys are changed during installation of my application.
Here is the code for the class:
public class ProxySettings {
[DllImport("wininet.dll")]
public static extern bool InternetSetOption(IntPtr hInternet, int dwOption,
IntPtr lpBuffer, int dwBufferLength);
public const int INTERNET_OPTION_SETTINGS_CHANGED = 39;
public const int INTERNET_OPTION_REFRESH = 37;
static bool settingsReturn, refreshReturn;
public void EnableProxy(string proxy) {
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", proxy);
settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED,
IntPtr.Zero, 0);
refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
public void DisableProxy() {
RegistryKey registry = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true);
registry.SetValue("ProxyEnable", 0);
registry.DeleteValue("ProxyServer", false);
settingsReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED,
IntPtr.Zero, 0);
refreshReturn = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
}
}
Here is my Installer class:
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer {
public WindowsServiceInstaller() {
ServiceProcessInstaller serviceProcessInstaller =
new ServiceProcessInstaller();
ServiceInstaller serviceInstaller = new ServiceInstaller();
//# Service Account Information
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
//# Service Information
serviceInstaller.DisplayName = "Client Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
//# This must be identical to the WindowsService.ServiceBase name
//# set in the constructor of WindowsService.cs
serviceInstaller.ServiceName = "Client Service";
this.Installers.Add(serviceProcessInstaller);
this.Installers.Add(serviceInstaller);
}
public override void Commit(System.Collections.IDictionary savedState) {
base.Commit(savedState);
ServiceController controller = new ServiceController("Client Service");
if(controller.Status != ServiceControllerStatus.Running) {
controller.Start();
}
ProxySettings proxy = new ProxySettings();
proxy.EnableProxy("127.0.0.1:8889");
}
public override void Uninstall(System.Collections.IDictionary savedState) {
base.Uninstall(savedState);
ProxySettings proxy = new ProxySettings();
proxy.DisableProxy();
}
}
In the override of Commit I am starting the service which works fine but for some reason I don't see the changes to the registry.
Just too make sure the class was initializing and firing I added
System.Diagnostics.Process.Start("http://www.microsoft.com");
to both the EnableProxy and DisableProxy methods. During the install the website opened and also during uninstall but the registry remained unchanged.
What am I doing wrong? Why will it work running as a Console App but not in an Installer?
I can't say for sure why it works in the Console app and not the installer, but I notice that you're neither disposing nor directly flushing the registry key.
Try replacing the code in your EnableProxy with this (and similarly with DisableProxy) and see what happens:
using (RegistryKey registry = Registry.CurrentUser.OpenSubKey(
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true))
{
registry.SetValue("ProxyEnable", 1);
registry.SetValue("ProxyServer", proxy);
}
(This is one of many reasons why you should always dispose an IDisposable)
Okay, I have figured out what was going on.
I had the installer set to install for Everyone. I tried installing and selecting Just Me and it worked.
I guess that makes sense since I was setting a registry key for the current user but was installing for everyone. Does this sound right?
Anyway, I now have some more testing to do to see if the software will function correctly if installed for Just Me. The application is a Windows Service.