How to check for installed browsers using C# for newbies - c#

I am building an app and its a simple one, all I want it to do is display os information in plain english and the architecture as well as check for installed browsers and then I'll add the ability for it to delete cookies and what not.
What Im stuck on is the browser detection part. Can anyone point me to some decent tutorials or how tos? Thanks.
Edit: OK I managed to finally scratch out some working code using the snippet provided by hcb below and the comments from the others (thanks everyone). So far it is doing exactly what I want so I thought id share what I have for those trying to do the same thing:
RegistryKey browserKeys;
browserKeys = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\WOW6432Node\Clients\StartMenuInternet");
if (browserKeys == null)
{
browserKeys = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Clients\StartMenuInternet");
}
string[] browserNames = browserKeys.GetSubKeyNames();
foreach (string browser in browserNames)
{
using (RegistryKey tempKey = browserKeys.OpenSubKey(browser))
{
foreach (string keyName in tempKey.GetValueNames())
{
if (tempKey.GetValue(keyName).ToString() == "Internet Explorer")
{
internetExplorerButton.Enabled = true;
internetExplorerButton.BackgroundImage = Properties.Resources.iExplorer;
if (internetExplorerButton.Enabled == true)
{
Label ieLabel = new Label();
ieLabel.Text = "Found!";
explorerLable.Text = ieLabel.Text;
}
}
To my extreme annoyance, I noticed that Google want to install their browser in the Local App Data. I managed to work this out writing the code again separately and checking:
Registry.CurrentUser.OpenSubKey(#"SOFTWARE\Google\Update\Clients");
Edit2: Checking CurrentUser for Chrome seems to work fine for a few friends so it must be OK.

Like this:
RegistryKey browserKeys;
//on 64bit the browsers are in a different location
browserKeys = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\WOW6432Node\Clients\StartMenuInternet");
if (browserKeys == null)
browserKeys = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Clients\StartMenuInternet");
string[] browserNames = browserKeys.GetSubKeyNames();

Related

CEFSharp app displays blank screen and then responds on it's own after a minute

I've been working on a app which uses CEFSharp (version 83.4.20) to load my companies VOIP platform (engage.ringcentral.com). The source is up on Github https://github.com/dylanlangston/EngageRC
The app has been working for about a month now and after putting it into production I've received reports of an odd issue. People are seeing a blank screen and unable to interact with the webpage (
Example of issue ). After a few minutes the issue seems to resolve itself and their able to interact with the webpage again. I haven't been able to reproduce the issue myself.
I thought this might be a rendering issue. So far I've tried to adjust the following in my app:
Remove the lines below.
settings.CefCommandLineArgs.Add("disable-gpu");
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
Replace them with.
settings.CefCommandLineArgs.Add("disable-gpu-compositing");
Unfortunately this hasn't resolved the issues and I'm unsure what I'm missing.
The relevant code for CEF Initialization is located in https://github.com/dylanlangston/EngageRC/blob/master/Windows/MainWindow.Designer.cs under the InitializeChromium method. See Below
//
// Chrome
//
private CefSharp.WinForms.ChromiumWebBrowser chromeBrowser;
public ChromiumWebBrowser InitializeChromium(string URL, string Name)
{
// Check if already Initialized
if (Cef.IsInitialized == false)
{
CefSettings settings = new CefSettings();
// Enable Logging if debugging or console window
if (!this.debug || !this.console)
{
settings.LogSeverity = LogSeverity.Disable;
}
else
{
settings.LogSeverity = LogSeverity.Verbose;
}
// Enable Microphone settings
settings.CefCommandLineArgs.Add("enable-media-stream", "1");
// Set Custom Browser Paths
settings.CefCommandLineArgs.Add("disable-gpu-shader-disk-cache", "1");
// Disable GPU to fix rendering issues on some machines.
settings.CefCommandLineArgs.Add("disable-gpu");
// Disable CORS protection (cross site scripting) which the engage.ringcentral.com site doesn't seem to like/respect, disabled as the website doesn't seem to improve with this turned on.
//settings.CefCommandLineArgs.Add("disable-web-security");
// Enable session Cookie persistence, disabled as it's unneeded.
//settings.PersistSessionCookies = true;
// Custom Browser paths
settings.BrowserSubprocessPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\CefSharp.BrowserSubprocess.exe");
settings.LocalesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\locales\");
settings.ResourcesDirPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\");
// Check if Resources folder is writable. If it isn't then write to application data.
DirectoryInfo di = new DirectoryInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\"));
if ((di.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
{
settings.RootCachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\cache");
settings.CachePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\cache");
settings.LogFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), #"EngageRC\CEFSharp\debug.log");
}
else
{
settings.RootCachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\cache");
settings.CachePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\cache");
settings.LogFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\CEFSharp\debug.log");
}
// Initialize cef with the provided settings or add new tab
Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: null);
}
// Create a browser component
chromeBrowser = new ChromiumWebBrowser(URL);
// Set Name
chromeBrowser.Name = Name;
// Adjust size and scaling
this.chromeBrowser.Dock = DockStyle.Fill;
this.chromeBrowser.Width = this.Width;
this.chromeBrowser.Height = this.Height;
// Add control
this.Controls.Add(chromeBrowser);
// Bring to front
this.chromeBrowser.BringToFront();
// Set label to Goodbye.
this.label1.Text = "Goodbye";
// Return control
return chromeBrowser;
}
I wasn't able to find any previous questions with my Google foo so it seems this isn't something other's are seeing their applications. I'm still learning the ropes when it comes to C# so it's 100% possible this is my own fault and something I'm doing wrong.
I was able to get verbose logging from a machine that experienced the issue. I'm mostly seeing the following error. Full logs at https://gist.github.com/dylanlangston/194e389ead437e6c6fe08b2d4746bf43
[0729/083616.491:ERROR:paint_controller.cc(646)] PaintController::FinishCycle() completed
Any ideas or recommendations to help troubleshoot this is appreciated!
Updated: Based on #amaitland's helpful comments it seems this behavior may be caused by using Thread.Sleep on a thread on I'm not supposed to.
After reviewing my code it looks like I have this in two locations.
First I call thread.sleep to wait until the browsers zoom level matches the value I've saved to the windows isolatedstorage. This shouldn't be running except when the application first starts. I don't think this is the problem but I am posting it here anyways to be safe. (https://github.com/dylanlangston/EngageRC/blob/master/CEFSharpHandlers.cs)
public void OnLoadingStateChanged(object sender, LoadingStateChangedEventArgs args)
{
ChromiumWebBrowser chrome = (ChromiumWebBrowser)sender;
if (!args.IsLoading) // Loading finished.
{
// If first load set zoom level to previous level
if (firstLoad < 3)
{
try
{
double zoom = double.Parse(ConfigReader.ReadIsolatedStorage("z"), System.Globalization.CultureInfo.InvariantCulture);
while (args.Browser.GetZoomLevelAsync().Result != zoom) { chrome.SetZoomLevel(zoom); Thread.Sleep(50); }
}
catch { }
firstLoad++;
}
// These JS and CSS modifications built into EngageRC
string hotfixJS = "";
string hotfixCSS = "/* Fix for dropdowns */ ul.dropdown-menu[style=\\\"opacity: 1;\\\"] {display: block !important; } /* End fix for dropdowns */";
// Inject custom javascript and css code on page load
chrome.ExecuteScriptAsync(hotfixJS);
chrome.ExecuteScriptAsync("var engageRCSS = document.getElementById('EngageRCSS'); if (!engageRCSS) { var node = document.createElement('style'); node.setAttribute('id', 'EngageRCSS'); node.innerHTML = \"" + hotfixCSS +"\"; document.body.appendChild(node); }");
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.js"))) { chrome.ExecuteScriptAsync(System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.js"))); }
if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.css"))) { chrome.ExecuteScriptAsync("var customCSS = document.getElementById('customcss'); if (!customCSS) { var node = document.createElement('style'); node.setAttribute('id', 'customcss'); node.innerHTML = \"" + System.IO.File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, #"Resources\Custom.css")).Replace("\n", String.Empty).Replace("\r", String.Empty).Replace("\t", String.Empty).Replace("\"", "\\\"") + "\"; document.body.appendChild(node); }"); }
}
}
I also have it when displaying a notification. This is most likely my problem as this is running in the main application thread... (https://github.com/dylanlangston/EngageRC/blob/master/NotificationSupport.cs)
public void displayNotification(string Title = "EngageRC", string Message = "")
{
// Display notification for 5 seconds
Notify notification = MainWindow.notification;
try
{
if (!IsActive(hWnd)) // Check if application is already active.
{
string config = ConfigReader.GetConfigValue("NotificationsDisabled").ToLower();
if (!(config == "true" || config == "1"))
{
notification.NewNotification(Title, Message, 5000);
}
config = ConfigReader.GetConfigValue("GetFocusOnCallDisabled").ToLower();
if (!(config == "true" || config == "1") && (Message == "You have incoming call"))
{
// Minimize window
ShowWindow(hWnd, 0x02);
// Restore window to previous state.
ShowWindow(hWnd, 0x09);
}
config = ConfigReader.GetConfigValue("GetFocusOnPendingDispositionDisabled").ToLower();
if (!(config == "true" || config == "1") && (Message == "You have a disposition pending"))
{
// Minimize window
ShowWindow(hWnd, 0x02);
// Restore window to previous state.
ShowWindow(hWnd, 0x09);
}
}
}
catch { notification.SetIconVisible(false); }
finally
{
Thread.Sleep(4999);
notification.SetIconVisible(false);
}
}
Based on what I've heard I should make the Notification Form run on a different thread from the main application then? Or am I way off base?
Update:
I was able to move the notification into it's own thread. I'm closing this issue and will have the user's test to see if the blank screen persists.
Thanks for the help again!!
#amaitland's comments were helpful in narrowing this issue down.

Registry Key Mapping

I am building an application in which I need to be able to gather information from the user's local Registry, and then utilize that to perform various tasks. I know where the certain registry key is located, but I can't seem to figure out how to properly extract the data. Here is the one I am trying to extract:
My ideal event would happen as follows: the utility searches for the registry value, determines it and stores it (in a var or something), then a button is displayed to the user to proceed to the next screen (I'm using WinForms). I have already set the button as "invisible" beforehand. See the attached code.
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("HKEY_LOCAL_MACHINE\Software\Wow6432Node\DovetailGames\FSX\10.0"))
{
if (key != null)
{
Object o = key.GetValue("Install_Path");
if (o != null)
{
sc3op2.Visible = true; //Button is "sc3op2"
}
}
}
I guess my main problem is the formatting of the code to extract these values. Any help would be appreciated. Thanks.
Your answer might be here. Apparently, it has something to do with the virtualization of the application settings for 32 and 64-bit platforms. See the updated section If it returns null, set your build architecture to Any CPU. On my 64-bit platform, I am getting null when built using x86 or Any CPU build configuration. But it is returning the value when built using x64.
const string keyName = #"Software\Wow6432Node\DovetailGames\FSX";
var o = Registry.LocalMachine.OpenSubKey(keyName, false);
var value = o?.GetValue("Install_Path", "-");
Console.WriteLine(value);
First you need to remove HKEY_LOCAL_MACHINE.
You need to use # before the string.
take a look Screenshot
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Apple Inc.\Apple Application Support"))
{
if (key != null)
{
Object o = key.GetValue("Installdir");
if (o != null)
{
// do something
}
}
}

How to uninstall application programmatically

I tried this,this to uninstall the application programmatically. I am not getting any error or exception but the application is not uninstalled from my machine. Please see tried code also
public static string GetUninstallCommandFor(string productDisplayName)
{
RegistryKey localMachine = Registry.LocalMachine;
string productsRoot = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products";
RegistryKey products = localMachine.OpenSubKey(productsRoot);
string[] productFolders = products.GetSubKeyNames();
foreach (string p in productFolders)
{
RegistryKey installProperties = products.OpenSubKey(p + #"\InstallProperties");
if (installProperties != null)
{
string displayName = (string)installProperties.GetValue("DisplayName");
if ((displayName != null) && (displayName.Contains(productDisplayName)))
{
string uninstallCommand =(string)installProperties.GetValue("UninstallString");
return uninstallCommand;
}
}
}
return "";
}
Please help me to uninstall the application programmatically using C#.
The above routine will return a string, assuming it found a match that may look like:
MsiExec.exe /X{02DA0248-DB55-44A7-8DC6-DBA573AEEA94}
You need to take that and run it as a process:
System.Diagnostics.Process.Start(uninstallString);
Note that it may not be always msiexec, it can be anything that the program chooses to specify. In case of msiexec, you can append /q parameter to your uninstallString to make it uninstall silently (and it won't show those Repair/Remove dialogs).
Update: If you're using Windows installer 3.0 or above, you can also use /quiet for silent install/uninstall. It's basically same as /qn (if you're on older versions). Source. Thanks #JRO for bringing it up!

open window explorer folder from my results c#

iv made a web forum, as i have lots of folders on my local drive i can now search for any folders i want on webpage.
Now am looking to add a link to the results of the search so it takes me directly to the folder.
My code in c#:
protected void List_Dirs(string searchStr = null)
{
try
{
MainContentLocal.InnerHtml = "";
string[] directoryList = System.IO.Directory.GetDirectories("\\\\myfiles\\Web");
int x = 0;
foreach (string directory in directoryList)
{
if (searchStr != null && searchStr.Length > 1)
{
UserInfo.Text = "Your Search for : <strong>" + SearchPhrase.Text + "</strong> returns ";
if(directoryP.ToLower().Contains(searchStr.ToLower()))
{
MainContentLocal.InnerHtml += directoryP + "<br />";
x++;
}
}
else
{
MainContentLocal.InnerHtml += directoryP + "<br />";
}
if (searchStr != null && searchStr.Length > 1)
{
UserInfo.Text += "<strong>" + x.ToString() + "</strong> results";
UserInfo.CssClass = "userInfo";
}
}
catch(Exception DirectoryListExp)
{
MainContentLocal.InnerHtml = DirectoryListExp.Message;
}
}
When i enter something is search i will get a list of folders like:
Your Search for : project returns 2 results
job234 project234 Awards
job323 project game
now is there any way for me to click the result so i can open a window explore on the webpage
Thanks
You can create links like project234.
string folder = "\\\\myfiles\\Web";
if (string.IsNullOrWhiteSpace(Request["folder"])) {
// Folder clicked
folder = string.Format("{0}{1}", folder, Request["folder"]);
Process.Start(folder);
}
string[] directoryList = System.IO.Directory.GetDirectories(folder);
Then it will open it on the server. So if it really is local, than it will work. If there is no security problem. But I'm not sure. You can also use file:// links (as Ryan Mrachek notes), but browsers are not happy to let you open them.
If your result is a file, you can open that file programmatically through the Process class by invoking Process.Start("C:\\MyResults.txt"). This will open the results in the default text editor. In the same way, you can also open a web page by inserting passing a Url to Process.Start. I hope this is what wanted.
our file urls are malformed. It should be:
file:///c:/folder/
Please refer to The Bizarre and Unhappy Story of File URLs.
This works for me:
link
When you click Link, a new Windows Explorer window is opened to the specified location. But as you point out, this only works from a file:// URL to begin with.
A detailed explanation of what is going on can be found here. Basically this behavior by design for IE since IE6 SP1/SP2 and the only way you can change it is by explicitly disabling certain security policies using registry settings on the local machine.
So if you're an IT admin and you want to deploy this for your internal corporate LAN, this might be possible (though inadvisable). If you're doing this on some generic, public-facing website, it seems impossible.

What's wrong with Registry.GetValue?

I trying to get a registry value:
var value = Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography", "MachineGuid", 0);
In Windows XP all ok, but in Windows 7 returns 0. In HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography using regedit I see MachineGuid, but if I run
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE").OpenSubKey("Microsoft").OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree).GetValueNames();
keys.Length is 0.
What do I do wrong? With other values all ok in both of OS.
The problem is that you probably are compiling the solution as x86, if you compile as x64 you can read the values.
Try the following code compiling as x86 and x64:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("MachineGUID:" + MachineGUID);
Console.ReadKey();
}
public static string MachineGUID
{
get
{
Guid guidMachineGUID;
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography") != null)
{
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid") != null)
{
guidMachineGUID = new Guid(Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid").ToString());
return guidMachineGUID.ToString();
}
}
return null;
}
}
}
You can read more about Accessing an Alternate Registry View.
You can found in here a way of reading values in x86 and x64.
It probably has to do with UAC (User Account Control). The extra layer of protection for Windows Vista and Windows 7.
You'll need to request permissions to the registry.
EDIT:
Your code right now:
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft")
.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree)
.GetValueNames();
Only requests the permissions on the Cryptography subkey, maybe that causes the problem (at least I had that once), so the new code would then be:
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE", RegistryKeyPermissionCheck.ReadSubTree)
.OpenSubKey("Microsoft", RegistryKeyPermissionCheck.ReadSubTree)
.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree)
.GetValueNames();
EDIT2:
I attached the debugger to it, on this code:
var key1 = Registry.LocalMachine.OpenSubKey("SOFTWARE", RegistryKeyPermissionCheck.ReadSubTree);
var key2 = key1.OpenSubKey("Microsoft", RegistryKeyPermissionCheck.ReadSubTree);
var key3 = key2.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
var key4 = key3.GetValueNames();
It turns out, you can read that specific value, at least that's my guess, because all data is correct, until I open key3, there the ValueCount is zero, instead of the expected 1.
I think it's a special value that's protected.
You say you're on 64-bit Windows: is your app 32-bit? If so it's probably being affected by registry redirection and is looking at HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography. You may have to P/Invoke to work around it: http://msdn.microsoft.com/en-us/library/aa384129.aspx.
If you're not an administrator, you only have read permission on HKLM. You need to open the key read-only instead. Not sure how to do that with .NET's Registry class; with the API directly, you use RegOpenKeyEx() with the KEY_READ flag.
EDIT: After checking MSDN, I see that OpenSubKey() does open read only, and returns the contents if it succeeds and nothing if it fails. Since you're chaining multiple OpenSubKey calls, it's most likely one of them that's failing that causes the others to fail. Try breaking them out into separate calls, and checking the intermediate values returned.
Maybe a little late to the party, but, none of the solutions worked for me.
This is how I've solved this issue:
public static Guid GetMachineGuid
{
get
{
var machineGuid = Guid.Empty;
var localMachineX64View = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var cryptographySubKey = localMachineX64View.OpenSubKey(#"SOFTWARE\Microsoft\Cryptography");
if (cryptographySubKey == null) return machineGuid;
var machineGuidValue = (string)cryptographySubKey.GetValue("MachineGuid");
Guid.TryParse(machineGuidValue, out machineGuid);
return machineGuid;
}
}
I solved the problem when i imported Microsoft.Win32 and changed the application-settings to x64 like pedrocgsousa mentioned.

Categories