I am using the following code to successfully open the requested URL in the system default browser on Windows 8.1:
public static void OpenUrlInDefaultBrowser(string url)
{
var httpKey = Registry.ClassesRoot.OpenSubKey(#"http\shell\open\command");
if (httpKey == null || httpKey.GetValue(string.Empty) == null)
return;
var cmd = httpKey.GetValue(string.Empty) as string;
if (cmd != null)
{
try
{
if (cmd.Length > 0)
{
string[] splitStr;
string fileName;
string args;
if (cmd.Substring(0, 1) == "\"")
{
splitStr = cmd.Split(new[] { "\" " }, StringSplitOptions.None);
fileName = splitStr[0] + "\"";
args = cmd.Substring(splitStr[0].Length + 2);
}
else
{
splitStr = cmd.Split(new[] { " " }, StringSplitOptions.None);
fileName = splitStr[0];
args = cmd.Substring(splitStr[0].Length + 1);
}
System.Diagnostics.Process.Start(fileName, args.Replace("%1", url));
}
}
catch (Exception)
{
// handle exception
}
}
httpKey.Close();
}
However, on my Windows Server 2008 R2 VM, this same code opens Internet Explorer (the default on that machine), but will only load the URL res://iesetup.dll/SoftAdmin.htm. IE is set to Enhanced Security Mode OFF. Chrome works as expected on this machine.
Simply invoking Process.Start(url) also fails to open the requested URL.
When I execute the following from the "Run..." menu, it works as expected: "C:\Program Files\Internet Explorer\iexplore.exe" http://example.com. The same goes for start-process http://example.com in PowerShell.
Did you try simply calling Process.Start("http://your_website_here"); ? It's not like you need to specify browser, if you want to run hyperlinks on default one.
#davidsbro - yes, i wouldn't have made it as answer otherwise :)
#mark - try http://blog.blksthl.com/2012/11/28/how-to-disable-ie-enhanced-security-in-windows-server-2012/ - that has something to do with server security settings and not application.
I wrote a modified version of your above method to start ie with command line options and is a little more detailed in figuring out which process to start.
Also, try changing the Process.Start calls in the code I'm posting to use a specific user and password.
class Program
{
static void Main(string[] args)
{
OpenUrlInBrowser("http://www.stackoverflow.com");
}
public static string GetDefaultBrowserCommand()
{
string command = null;
var httpKey = Registry.ClassesRoot.OpenSubKey(#"http\shell\open\command");
if (httpKey == null)
throw new Exception("No default browser is configured!");
else
{
command = (string)httpKey.GetValue("", "iexplore.exe");
int exeIndex = command.ToLower().IndexOf(".exe");
if (exeIndex > -1)
{
int endOfCommand = command.IndexOf('"', exeIndex);
int startOfCommand = command.LastIndexOf('"', exeIndex - 1);
if (startOfCommand > -1 && endOfCommand > -1)
{
command = command.Substring(startOfCommand + 1, endOfCommand - 1);
return command;
}
else
throw new Exception("Error: Default browser is not set in the registry correctly!");
}
else
throw new Exception("The default browser registry setting does not specify an executable!");
}
}
public static void OpenUrlInBrowser(string url)
{
string browserCommand = GetDefaultBrowserCommand();
FileInfo fi = new FileInfo(browserCommand);
ProcessStartInfo psi = null;
if (!fi.Exists)
Console.WriteLine("The default browser specified in the registry does not physical exist on disk!");
else
{
string commandFileName = Path.GetFileNameWithoutExtension(fi.FullName).ToLower();
switch (commandFileName)
{
case "iexplore":
psi = new ProcessStartInfo(browserCommand, string.Concat("\"", url, "\"", " ", "-extoff -new"));
break;
default:
psi = new ProcessStartInfo(browserCommand, string.Concat("\"", url, "\""));
break;
}
}
psi.UseShellExecute = true; //<- have to set this to make runas work
psi.Verb = "runas"; //<- instructs the process to runas administrator, which will give the user a UAC prompt if UAC is turned on.
Process.Start(psi);
}
}
Related
I'm doing a project that uses python as background script and C# as guy.
My problem is that I can't figure out how to cause my GUI to automatically search for the pythonw.exe file in order to run my python scripts.
Currently I'm using this path:
ProcessStartInfo pythonInfo = new ProcessStartInfo(#"C:\\Users\\Omri\\AppData\\Local\\Programs\\Python\\Python35-32\\pythonw.exe");
but I want it to auto detect the path of pythonw.exe (I need to submit the project and it won't run on others computers unless they change the code itself)
Any suggestions may be helpful.
Inspired by #Shashi Bhushan's answer I made this function for getting the Python path reliably;
private static string GetPythonPath(string requiredVersion = "", string maxVersion = "") {
string[] possiblePythonLocations = new string[3] {
#"HKLM\SOFTWARE\Python\PythonCore\",
#"HKCU\SOFTWARE\Python\PythonCore\",
#"HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\"
};
//Version number, install path
Dictionary<string, string> pythonLocations = new Dictionary<string, string>();
foreach (string possibleLocation in possiblePythonLocations) {
string regKey = possibleLocation.Substring(0, 4), actualPath = possibleLocation.Substring(5);
RegistryKey theKey = (regKey == "HKLM" ? Registry.LocalMachine : Registry.CurrentUser);
RegistryKey theValue = theKey.OpenSubKey(actualPath);
foreach (var v in theValue.GetSubKeyNames()) {
RegistryKey productKey = theValue.OpenSubKey(v);
if (productKey != null) {
try {
string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("ExecutablePath").ToString();
// Comment this in to get (Default) value instead
// string pythonExePath = productKey.OpenSubKey("InstallPath").GetValue("").ToString();
if (pythonExePath != null && pythonExePath != "") {
//Console.WriteLine("Got python version; " + v + " at path; " + pythonExePath);
pythonLocations.Add(v.ToString(), pythonExePath);
}
} catch {
//Install path doesn't exist
}
}
}
}
if (pythonLocations.Count > 0) {
System.Version desiredVersion = new System.Version(requiredVersion == "" ? "0.0.1" : requiredVersion),
maxPVersion = new System.Version(maxVersion == "" ? "999.999.999" : maxVersion);
string highestVersion = "", highestVersionPath = "";
foreach (KeyValuePair<string, string> pVersion in pythonLocations) {
//TODO; if on 64-bit machine, prefer the 64 bit version over 32 and vice versa
int index = pVersion.Key.IndexOf("-"); //For x-32 and x-64 in version numbers
string formattedVersion = index > 0 ? pVersion.Key.Substring(0, index) : pVersion.Key;
System.Version thisVersion = new System.Version(formattedVersion);
int comparison = desiredVersion.CompareTo(thisVersion),
maxComparison = maxPVersion.CompareTo(thisVersion);
if (comparison <= 0) {
//Version is greater or equal
if (maxComparison >= 0) {
desiredVersion = thisVersion;
highestVersion = pVersion.Key;
highestVersionPath = pVersion.Value;
} else {
//Console.WriteLine("Version is too high; " + maxComparison.ToString());
}
} else {
//Console.WriteLine("Version (" + pVersion.Key + ") is not within the spectrum.");
}
}
//Console.WriteLine(highestVersion);
//Console.WriteLine(highestVersionPath);
return highestVersionPath;
}
return "";
}
You can find python installation path by lookup following keys on windows machine.
HKLM\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
HKCU\SOFTWARE\Python\PythonCore\versionnumber\InstallPath
for win64 bit machine
HKLM\SOFTWARE\Wow6432Node\Python\PythonCore\versionnumber\InstallPath
You can refer this post for how to read registry using C#
How to read value of a registry key c#
Find the environment variable name in Windows, for that assembly and use Environment.GetEnvironmentVariable(variableName)
Check out How to add to the pythonpath in windows 7?
An example on how to search for Python within the PATH environment variable:
var entries = Environment.GetEnvironmentVariable("path").Split(';');
string python_location = null;
foreach (string entry in entries)
{
if (entry.ToLower().Contains("python"))
{
var breadcrumbs = entry.Split('\\');
foreach (string breadcrumb in breadcrumbs)
{
if (breadcrumb.ToLower().Contains("python"))
{
python_location += breadcrumb + '\\';
break;
}
python_location += breadcrumb + '\\';
}
break;
}
}
Just change the FileName to "python.exe" if you already set python env path
private void runPython(string cmd, string args)
{
ProcessStartInfo start = new ProcessStartInfo();
start.FileName = "python.exe";
start.Arguments = string.Format("{0} {1}", cmd, args);
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
using (Process process = Process.Start(start))
{
using (StreamReader reader = process.StandardOutput)
{
string result = reader.ReadToEnd();
Console.Write(result);
}
}
}
On my machine, with Python 3.11 installed, I can query it by defining this property:
public string PythonInstallPath
{
get => (string)Microsoft.Win32.Registry.GetValue(
#"HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\3.11\InstallPath",
"ExecutablePath", null);
}
Pythonw.exe is located in the same path, so you can do:
public string PythonWInstallPath
{
get => System.IO.Path.Combine(System.IO.Path.GetDirectoryName(PythonInstallPath),
"pythonw.exe");
}
There is also a way to look it up in the environment, check this out as an alternative.
I use a tool's binaries in a C# project called GraphViz.
The problem is I must include the binaries path as hard-coded and I don't want to do that.
IRenderer renderer = new Renderer("C:\\Program Files (x86)\\Graphviz2.38\\bin"); // todo: remove hardcoded GraphViz path
I want to mimic the linux which command.
Simply passing the binary name (e.g dot) and get the path.
GetBinaryPath("dot"); // return the above path
I'd appreciate any ideas or topics to start searching.
Note
Target OS: Windows
.NET version : 4
If you need to find path given only executable name (and installation directory is in your PATH environment variable)
Option 1:
Using where command with Process class. (test for exit code, parse the output)
Option 2:
You can get environment PATH environment variable, split it by ';' and test for your executable name existence.
First you need to find all directories where windows search for a exectuable file and that is from the environment variable %PATH%.
Then you need to find all extensions (.com, .exe, .bat etc) from %PATHEXT%.
Then you just check them like this:
internal class Program {
private static void Main(string[] args) {
if (args.Length != 1) {
Console.WriteLine("Incorrect usage!");
return;
}
var extensions = GetExecutableExtensions(args[0]);
var paths = GetPaths();
var exeFile = GetFirstExecutableFile(args[0], extensions, paths);
if (exeFile == null) {
Console.WriteLine("No file found!");
}
else {
Console.WriteLine(exeFile);
}
}
private static string GetFirstExecutableFile(string file, string[] extensions, string[] paths) {
foreach (var path in paths) {
var filename = Path.Combine(path, file);
if (extensions.Length == 0) {
if (File.Exists(filename)) {
return filename;
}
}
else {
foreach (var ext in extensions) {
filename = Path.Combine(path, file + ext);
if (File.Exists(filename)) {
return filename;
}
}
}
}
return null;
}
private static string[] GetExecutableExtensions(string file) {
var data = GetCmdOutput("echo %PATHEXT%");
var arr = data.TrimEnd('\n', '\r').Split(new [] {';'}, StringSplitOptions.RemoveEmptyEntries);
//If the command passed in ends with a executable extension then we dont need to test all extensions so set it to emtpy array
foreach (var ext in arr) {
if (file.EndsWith(ext, StringComparison.OrdinalIgnoreCase)) {
return new string[0];
}
}
return arr;
}
private static string[] GetPaths() {
var data = GetCmdOutput("echo %PATH%");
return data.TrimEnd('\n', '\r').Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
}
private static string GetCmdOutput(string cmd) {
using (var proc = new Process {
StartInfo = new ProcessStartInfo {
FileName = "cmd.exe",
Arguments = "/c " + cmd,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
}) {
proc.Start();
return proc.StandardOutput.ReadToEnd();
}
}
}
I was trying to automate server patch installation for my product and I came to know about Wix Toolset.
I was hoping to get the JBoss Version in my installer. The command to get the same is standalone.bat --version from cmd.
So from my installer I created one CustomAction where I tried to run it and get the output.
public static string runRunnableBatch(string path){
Process exploitVersionService = new Process();
string runnableBinPath = path;
exploitVersionService.StartInfo.WorkingDirectory = path + "bin";
exploitVersionService.StartInfo.FileName = path + "bin\\standalone.bat";
exploitVersionService.StartInfo.CreateNoWindow = false;
exploitVersionService.StartInfo.Arguments = string.Format("--version");
exploitVersionService.StartInfo.UseShellExecute = false;
exploitVersionService.StartInfo.RedirectStandardOutput = true;
exploitVersionService.StartInfo.RedirectStandardInput = false;
exploitVersionService.Start();
exploitVersionService.WaitForExit();
// /*
string opt = "";
while (!exploitVersionService.StandardOutput.EndOfStream){
opt += exploitVersionService.StandardOutput.ReadLine();
}
// */
//using (StreamWriter writer = new StreamWriter("D:\\_log.txt"))
//using (StreamReader reader = exploitVersionService.StandardOutput){
// writer.AutoFlush = true;
// for (; ; ){
// string textLine = reader.ReadLine();
// if (textLine == null)
// break;
// writer.WriteLine(textLine);
// }
//}
//StreamReader exploitVersionFeed = exploitVersionService.StandardOutput;
//string output = exploitVersionFeed.ReadToEnd();
return opt;
}
When I was doing that, all I got as output was the first line of the whole output string.
I needed the whole string in my code so that from regular expression I could extract the version.
Also tried with
public static string runRunnableBatch(string path){
string executableBinPath = path + "bin";
string executableBinPath_BatchCmd = "cd " + "\"" + executableBinPath + "\"";
string outputFileName = "TempVerInfoHolder.txt";
string outputFilePath = executableBinPath+#"\TempVerInfoHolder1.txt";
string versionRetriever_BatchCmd = #"standalone.bat --version > " + "\""+outputFilePath+"\"";
string partitionName_BatchCmd = #Strings.Utils.getPartitionFromPath(path);
// Creating command sequence
SortedList<int, string> commandSequence = new SortedList<int, string>();
// ~ d:
commandSequence.Add(1, partitionName_BatchCmd);
// ~ cd %path%
commandSequence.Add(2, executableBinPath_BatchCmd);
// ~ standalone.bat --version > %filename%
commandSequence.Add(3, versionRetriever_BatchCmd);
runCommandFromSequence(commandSequence);
// Run together
return "";
}
private static void runCommandFromSequence(SortedList<int, string> commandSequence){
Process seqCmdExecHost = new Process();
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "cmd.exe";
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.CreateNoWindow = false;
seqCmdExecHost.StartInfo = psi;
seqCmdExecHost.Start();
using (StreamWriter writer = seqCmdExecHost.StandardInput) {
if (writer.BaseStream.CanWrite) {
foreach (int item in commandSequence.Keys){
MessageBox.Show(seqCmdExecHost.Id.ToString());
MessageBox.Show(commandSequence[item]);
writer.WriteLine(commandSequence[item]);
}
}
string opt = "";
while (!seqCmdExecHost.StandardOutput.EndOfStream){
opt += seqCmdExecHost.StandardOutput.ReadLine();
}
MessageBox.Show("Exited? " + seqCmdExecHost.HasExited);
MessageBox.Show("O/P? " + opt);
}
}
I have tried some other way as well. Switching the commented code of the above function was one of them.
Output getting while doing it from code level
Calling "D:\Servers\VA\XYZ\JBoss-7.1.1-Final\bin\standalone.conf.bat
Output while running the same command manually from cmd
Calling D:\Servers\VA\XYZ\JBoss-7.1.1-Final\bin\standalone.conf.bat
======================================================================
JBoss Bootstrap Environment
JBOSS_HOME: D:\Servers\VA\XYZ\JBoss-7.1.1-Final
JAVA: C:\Program Files\Java\jdk1.7.0_67\bin\java
JAVA_OPTS
======================================================================
Listening for transport dt_socket at address: 8787
19:08:08,333 INFO [org.jboss.modules] JBoss Modules version 1.1.1.GA
JBoss AS 7.1.1.Final "Brontes"
Press any key to continue . . .
My observation is, the stream is getting closed once the nested standalone.conf.bat is getting called from standalone.bat.
If any workaround available to get the full output in string/buffer/stream, would be appreciated.
Thanks
What you could do is call the Command line Application instead of calling the batch file
exploitVersionService.StartInfo.WorkingDirectory = path + "bin";
exploitVersionService.StartInfo.FileName = "cmd.exe";
exploitVersionService.StartInfo.CreateNoWindow = false;
exploitVersionService.StartInfo.Arguments = string.Format(" /c \"{0}\" --version",path + "bin\\standalone.bat");
I found one work around to do achieve this.
I created the batch file programmatically and ran it with cmd.
public static void createBatchToGetVersion(string path)
{
CustomLogger.getInstance().debug("Started creating batch file");
BatchOps.executableBinPath = path + "bin";
CustomLogger.getInstance().debug("Ëxecutable bin path: " + BatchOps.executableBinPath);
BatchOps.tempBatchFileName = "JBossVerCmd.bat";
BatchOps.holderFileName = #"JBossVerHolder.txt";
BatchOps.absoluteHolderPath = Strings.Utils.normalize(executableBinPath) + holderFileName;
CustomLogger.getInstance().debug("Normalized absoluteHolderPath: " + BatchOps.absoluteHolderPath);
CustomLogger.getInstance().debug("Checking if JBOSS_HOME entered by user actuallty points to JBOSS");
if (!File.Exists(Strings.Utils.normalize(executableBinPath) + "standalone.bat"))
{
CustomLogger.getInstance().error("standalone.bat not found. JBOSS_HOME Dir is not correctly entered");
throw new CustomExceptions.DirectoryNotAcceptableException("Bad directory is assigned to JBOSS_HOME or JBOSS_HOME structure corrupted");
}
/*
* Batch file formation.
* Contains:
* Start file
* D:
* cd D:\Fusion Server\jboss 7.1.1\bin
* #echo | call standalone.bat --version > sample.txt
* #echo Done
* End file
* #echo is required here because it exits the cmd when completed whithout having the user pressing any key
*/
string changePartition_cmd = Strings.Utils.getPartitionFromPath(path);
string changeDirectory_cmd = #"cd " + BatchOps.executableBinPath;
string getVersion_cmd = #"#echo | call standalone.bat --version > " + holderFileName;
string exitCmd = #"#echo Done";
CustomLogger.getInstance().debug("Command to be written on batch file");
CustomLogger.getInstance().debug("\r\n" + changePartition_cmd + "\r\n" + changeDirectory_cmd + "\r\n" + getVersion_cmd + "\r\n" + exitCmd);
SortedList<int, string> commandSequence = new SortedList<int, string>();
CustomLogger.getInstance().debug("Initializing command sequence.");
commandSequence.Add(1, changePartition_cmd);
commandSequence.Add(2, changeDirectory_cmd);
commandSequence.Add(3, getVersion_cmd);
commandSequence.Add(4, exitCmd);
// Will create one if file never existed and open one delete the content and set the pointer to the begnning
// if already existed
StreamWriter batchFileWriter = null;
try
{
CustomLogger.getInstance().debug("Establishing stream to and from temp batch file");
batchFileWriter = new StreamWriter(tempBatchFileName);
CustomLogger.getInstance().debug("Command sequence ready to be written on temp batch file.");
Perform.writeToStreamFromSequence(batchFileWriter, commandSequence);
CustomLogger.getInstance().debug("Command sequence successfully written");
}
catch (IOException ex)
{
CustomLogger.getInstance().error("Error while writing command sequence.\n" + ex.ToString());
// throw exception to CustomAction
throw new IOException("Error while writing commandSequence");
}
finally
{
// Not required. Stream already closed in writeToStreamFromSequence
}
}
public static void runTempBatchFile()
{
Process seqCmdExecHost = new Process();
ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd", #"/c " + BatchOps.tempBatchFileName);
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
seqCmdExecHost.StartInfo = procStartInfo;
seqCmdExecHost.Start();
seqCmdExecHost.WaitForExit();
seqCmdExecHost.Close();
}
I found the moment i was doing
procStartInfo.UseShellExecute=true;
procStartInfo.RedirectStandardOutput=true
It was giving the first line of output. Dont know why?. If anyone has any idea about it please explain.
Thanks
I'm trying to remove a Silverlight Out Of Browser app programatically passing the arguments to sllauncher in following this post: http://timheuer.com/blog/archive/2010/03/25/using-sllauncher-for-silent-install-silverlight-application.aspx However it won't uninstall the app when given the origin.
It turns out that when you have an automatically updating Out-Of-Browser application, Silverlight stamps each application Uri with a time stamp that can be found in the application's folder in the C:\Users\Trevor\AppData\Local\Microsoft\Silverlight\OutOfBrowser(AppFolderName) metadata file. So to facilitate the removal of our app in preparation for our new one, I implemented the following:
UninstallExisting(GetInstalledAppUri()); // This is how it's called
//This is the two method's implementation
// TODO: Change to your app name.
const string appName = "YourAppNameHere";
static string silverlightOutOfBrowserFolder =
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
+ #"\Microsoft\Silverlight\OutOfBrowser";
private static string GetInstalledAppUri()
{
string xapFolderPath = Path.Combine(silverlightOutOfBrowserFolder, GetXapFolder());
string[] lines = File.ReadAllLines(Path.Combine(xapFolderPath, "metadata"), Encoding.Unicode);
string finalAppUriLine = lines.First(i => i.Contains("FinalAppUri="));
return "\"" + finalAppUriLine.Replace("FinalAppUri=", "") + "\"";
}
private static string GetXapFolder()
{
string AppXapFolder = "";
foreach (var dir in Directory.GetDirectories(silverlightOutOfBrowserFolder))
{
if (dir.Contains(appName))
{
AppXapFolder = dir;
}
}
return AppXapFolder ;
}
private static string silverlightExe
{
get
{
return Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
#"Microsoft Silverlight\sllauncher.exe");
}
}
private static void UninstallExisting(string xapUriToRemove)
{
string installArgs = "/uninstall" + " /origin:" + xapUriToRemove;
ProcessStartInfo pstart = new ProcessStartInfo(silverlightExe, installArgs);
Process p = new Process();
pstart.UseShellExecute = false;
p.StartInfo = pstart;
p.Start();
p.WaitForExit();
}
I hope this serves to save someone else the hours of time it took me to figure out about the metadata file and all the peculiarities of sllauncher.exe
I am trying to create a shortcut to a folder that my program creates when the user adds a profile to the device. The shortcut link to the folder and is supposed to appear in the users Favorites (the library to the side in Windows 7 Explorer) and have the logo.ico from our project. I thought I had to use the IWshRuntimeLibrary, but the code randomly stops on me and does not return any errors... any help?
Note: The profilePath, profileName, and executablePath all return the correct data and point to the correct locations for the items I am trying to access and modify.
public static bool CreateProfileShortcut(string profilePath, string profileName, string executablePath)
{
bool success = false;
string favoritesPath = "";
IWshRuntimeLibrary.IWshShell wsh;
IWshRuntimeLibrary.IWshShortcut shortcut;
try
{
if (!String.IsNullOrWhiteSpace(profilePath) && Directory.Exists(profile.Path))
{
favoritesPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (!File.Exists(favoritesPath + "\\Links\\" + profileName + ".lnk"))
{
wsh = new IWshRuntimeLibrary.WshShell();
shortcut = (IWshRuntimeLibrary.IWshShortcut)wsh.CreateShortcut(favoritesPath + "\\Links\\" + profileName + ".lnk");
shortcut.TargetPath = profilePath;
shortcut.IconLocation = executablePath + "\\Icons\\logo.ico";
shortcut.Save();
}
}
}
catch (Exception e)
{
MessageBox.Show(MessageBoxError(new string[] { e.TargetSite.Name, e.Message, "MinorError" }));
success = false;
}
return success;
}
Try looking at both this question and this question. If I'm understanding, I think you want to be able to create a shortcut in the "favorite" area of Windows 7.