-4
public static void GetInstalledApps()
{

 string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key);
    {
        foreach (string subkey_name in key.GetSubKeyNames())
        {
            RegistryKey subkey = key.OpenSubKey(subkey_name);
            {
                Console.WriteLine(subkey.GetValue("DisplayName"));
            }
        }
    }

I am using this method in a c# console application to list the programs installed in my windows computer.

I will kindly like to know how I can send the result of the output from this method to a file in my computer. Currently, the output is going to my console.

crthompson
  • 15,653
  • 6
  • 58
  • 80
  • 3
    http://msdn.microsoft.com/en-us/library/6ka1wd3w(v=vs.110).aspx – Habib Dec 02 '13 at 18:59
  • http://stackoverflow.com/questions/4501511/c-sharp-realtime-console-output-redirection – Nick Dec 02 '13 at 19:01
  • 1
    http://www.dotnetperls.com/streamwriter – L.B Dec 02 '13 at 19:02
  • @L.B That's the best link yet. It shows how to open a file and write to it as a stream. – user2864740 Dec 02 '13 at 19:04
  • but how can i use this in my program to redirect the output from GetInstalledApps to a file? – user3058620 Dec 02 '13 at 19:08
  • @user3058620 Don't redirect the console output to a file. Just open a file and write to it as shown in link. – L.B Dec 02 '13 at 19:10
  • File.AppendAllText(path, subkey.GetValue("DisplayName")); The best overloaded method match for 'System.IO.File.AppendAllText(string, string)' has some invalid arguments. Then i changed it to this: File.AppendAllText(path, subkey.GetValue("DisplayName").ToString()); but it threw an exception – user3058620 Dec 02 '13 at 19:43
  • Can someone kindly help me out, it still does not work thx alot – user3058620 Dec 02 '13 at 20:03
  • string path = @"c:\MyTestOutput.txt"; File.AppendAllText(path, subkey.GetValue("DisplayName")); The best overloaded method match for 'System.IO.File.AppendAllText(string, string)' has some invalid arguments – user3058620 Dec 02 '13 at 20:12

4 Answers4

1

You can use File.WriteAllText to create and write to the file

David Pilkington
  • 13,528
  • 3
  • 41
  • 73
  • While this "would work" it requires changing the code in non-required ways. For sake of consistency, a simple TextWriter.Write would probably make more sense. – user2864740 Dec 02 '13 at 19:01
  • This entirely rewrites the code (and currently rewrites it *differently*) - while it will work, introduces a number of difference concepts and requires pre-building the entire output. Files are streams. – user2864740 Dec 02 '13 at 19:02
  • @user2864740 How does it rewrite the code? I am not changing how he gets the names, all that is changing is that he is writing it to a file. I could give a solution that uses the loop and many more lines of code but instead I gave him a link to the MSDN site so that he can read for himself. – David Pilkington Dec 02 '13 at 19:05
  • -1 Review the transformation of the loops to LINQ and a join (which is the rewrite) and then review what the *original* code does (that this code does not). I down-voted for the latter, but was influenced by the former and lack of acknowledgement. – user2864740 Dec 02 '13 at 19:05
  • 2
    @user2864740 You are entitled to your vote and to your opinion. Firstly, that is not Linq but a String function. And I refuse to show a solution that is less efficient. It is not complex and the links are there for him to follow if he does not understand. This is a site for learning not spoonfeeding. – David Pilkington Dec 02 '13 at 19:08
  • This post is *still incorrect*. Please take the time to read he last comment and look back at the original post - going from the original post the WriteAllText requires use of a StringBuilder (or other string building) or LINQ query as it *entirely fails to capture* `OpenSubKey/GetValue`. However, I *was* previously wrong as it doesn't use LINQ (as it fails to capture the inner behavior); you are correct in that regard. – user2864740 Dec 02 '13 at 19:11
  • Also, I find the sentence "And I refuse to show a solution that is less efficient" is *misleading* in that it supposes that the suggestion of TextWriter.Write (or rather, not using WriteAllText) is somehow "less efficient". – user2864740 Dec 02 '13 at 19:21
  • @user2864740 it is done, there is an answer you obviously appreciate more. I have made my answer and you have downvoted it. Leave it there and enjoy your night. – David Pilkington Dec 02 '13 at 19:24
  • string path = @"c:\MyTestOutput.txt"; File.AppendAllText(path, subkey.GetValue("DisplayName")); The best overloaded method match for 'System.IO.File.AppendAllText(string, string)' has some invalid arguments – user3058620 Dec 02 '13 at 20:09
  • @user3058620 You probably need `subkey.GetValue("DisplayName").ToString()` – David Pilkington Dec 02 '13 at 20:11
  • i've already tried this and it throws a null reference exception – user3058620 Dec 02 '13 at 20:24
1

Here's a hint: System.Console.WriteLine() is pretty much just a wrapper for System.Console.Out.WriteLine().

System.Console.Out is an instance of System.IO.TextWriter, an abstract class. The concrete implementation of which you'll be wanting is a StreamWriter.

StreamWriter conveniently, wraps a Stream (another abstract class).

The concrete implementation of Stream that you'll be wanting is the very cleverly named FileStream, the fundamental means of writing to a file.

Note that the File and FileInfo classes both offer easy means of opening a file for writing and obtaining a StreamWriter for that file.

Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • string path = @"c:\MyTestOutput.txt"; File.AppendAllText(path, subkey.GetValue("DisplayName")); The best overloaded method match for 'System.IO.File.AppendAllText(string, string)' has some invalid arguments – user3058620 Dec 02 '13 at 20:10
0

You could replace

Console.WriteLine(subkey.GetValue("DisplayName"));

with

if (subkey.GetValue("DisplayName")!=null)
{
    File.AppendAllText(path, subkey.GetValue("DisplayName").ToString());
}

where path is the file to append the specified string to.

See File Methods

Harrison
  • 3,843
  • 7
  • 22
  • 49
  • Don't do this - not inside a loop anyway. Open the file once use it. Close it. – user2864740 Dec 02 '13 at 19:15
  • string path = @"c:\MyTestOutput.txt"; File.AppendAllText(path, subkey.GetValue("DisplayName")); The best overloaded method match for 'System.IO.File.AppendAllText(string, string)' has some invalid arguments – user3058620 Dec 02 '13 at 20:09
  • @user3058620. I had to add the `ToString` as `GetValue` returns an object. – Harrison Dec 02 '13 at 20:59
  • @user2864740. You're really going to give -1 because the file is opened more than once? I think for someone that hasn't ever used a stream before this is the easiest way with the smallest change to the current implementation. Although there are *slightly* better ways this isn't a wrong answer, and is the best jumping in point for a novice. – Harrison Dec 02 '13 at 21:03
  • @Harrison Yes, I am. The reason for my down-vote isn't strictly because it *shows* the approach; the reason is because this answer *doesn't address the issues* with the approach (nor does it introduce any relevant concepts that can be used to build upon). Start people off on the right foot. – user2864740 Dec 02 '13 at 21:06
  • @Harrison The only answer I've actually up-voted is the one that addresses concepts *without* even offering such example code (although it really should show how the concepts relate with a small example). Unfortunately, as you can see by user3058620's comments on that post, his/her path has already been tainted. Start people off on the right foot. – user2864740 Dec 02 '13 at 21:09
  • @user3058620. Your welcome, hope it helped. I noticed this was your first question and you are new to the site, so welcome. I wanted to point out that you should accept whichever answer helped you the most in solving your issue, or the best one of your choosing. And you get 2 points for doing it. – Harrison Dec 05 '13 at 17:34
0

As Nicholas Carey's answer points out, Console.WriteLine merely write to the Console.Out TextWriter Stream.

Knowing this information, we can rewrite the original method as follows:

public static void GetInstalledApps(TextWriter writer)
{    
    string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key);
    {
        foreach (string subkey_name in key.GetSubKeyNames())
        {
            RegistryKey subkey = key.OpenSubKey(subkey_name);
            {
                // While this won't throw an Exception when GetValue returns null,
                // as WriteLine will "output nothing" for null values,
                // this logic might be bugged: the next example has a possible solution.

                // No more hard-coding of Console.WriteLine, instead we use
                // writer.WriteLine, where writer is the TextWriter that
                // has been supplied as an argument.
                writer.WriteLine(subkey.GetValue("DisplayName"));
            }
        }
    }
}

Then the code can be called in the following fashion to obtain the original semantics of writing to the console.

GetInstalledApps(Console.Out);

Now, say we want to write to a different stream, we just have to specify it. From the link provided in a comment, we can see how easy it is to create a StreamWriter for a file. StreamWriter extends TextWriter can can thus be passed to our method. As such, it is easy to write to a file:

using (StreamWriter writer = new StreamWriter("theFile.txt"))
{
    GetInstalledApps(writer);
}

However, I would suggest moving the writing out of the method. That is, the method should do only one thing: it should only fetch the installed applications.

Thus, consider the following code which uses yield to create an implicit iterator. However, building/returning a List programatically or using LINQ to generate the Enumerable result would also have worked just fine. I've also modified the function such that it will return a value, even when no DisplayName is set.

public static IEnumerable<string> GetInstalledApps()
{    
    string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
    RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key);
    {
        foreach (string subkey_name in key.GetSubKeyNames())
        {
            RegistryKey subkey = key.OpenSubKey(subkey_name);
            {
                // I've also added fix for what I believe that was a "silent bug" 
                // that allowed entries without a display name to be "lost".
                // This code returns the subkey name if there is no display name.
                var value = subkey.GetValue("DisplayName") as string;
                if (!string.IsNullOrEmpty(value)) {
                    yield return value;
                } else {
                    // No display name, but there is still an entry!!                
                    yield return subkey_name;
                }
            }
        }
    }
}

Then we can move the usage of the data outside the function which fetches the data - this reduces coupling and makes the code more flexible. Perhaps later we want to use the code to update a ListBox instead of writing to a file.

using (StreamWriter writer = new StreamWriter("theFile.txt"))
{
   foreach (var app in GetInstalledApps()) {
       writer.WriteLine(app);
   }
}

If GetInstalledApps is written in the above manner where it separates out the IO concern, then can also be used with File.*AllText after simply transforming IEnumerable<string> into an appropriate string, such as with String.Join. However, I would still use a loop and WriteLine as per above unless I needed to return a string object.

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • thanks a lot this was meaningful – user3058620 Dec 02 '13 at 21:44
  • @user3058620 You're welcome. I fixed a bug with the second GetInstalledApps which would have resulted in a type error - in particular, I added `GetValue(..) as string` (as the return type of GetValue is object). – user2864740 Dec 02 '13 at 21:48
  • @user3058620 On further inspection, it appears that you'll want to return a value *even* when GetValue("DisplayName") returns null. I've made another edit to the second GetInstalledApps such that it will return the subkey_name if there is no title - this will avoid "missing" some installed apps. – user2864740 Dec 02 '13 at 22:08