FileVersionInfo GetVersionInfo() returns empty result - c#

I've written a simple C# program that compares the file version of the local file with that of server and if there is a mismatch it should overwrite the local copy with that of server. Code snippet below.
using System;
using System.Diagnostics;
using System.Security.Principal;
namespace Test
{
public class FileVersionComparer
{
public static void Main()
{
// Print the user name
Console.WriteLine("This app is launched by - {0}",WindowsIdentity.GetCurrent().Name);
// Get the server file version
string serverFilePath = #"\\myserver\folder\test.exe";
FileVersionInfo serverVersionInfo = FileVersionInfo.GetVersionInfo(serverFilePath);
string serverFileVersion = serverVersionInfo.FileVersion;
Console.WriteLine("Server file version : {0}", serverFileVersion);
// Get the local file version
string localFilePath = #"C:\Windows\test.exe";
FileVersionInfo localVersionInfo = FileVersionInfo.GetVersionInfo(localFilePath);
string localFileVersion = localVersionInfo.FileVersion;
Console.WriteLine("Local file version : {0}", localFileVersion);
// Compare and overwrite if version is not same
if(!string.Equals(localFileVersion, serverFileVersion,StringComparison.OrdinalIgnoreCase))
{
File.Copy(serverFilePath, localFilePath, true);
}
else
Console.WriteLine("File version is same");
}
}
}
This program is launched as a child process and the parent application and hence the child runs under NT AUTHORITY\SYSTEM account. The program is working fine on my machine but failing to retrieve the local file version (returns empty string, no exception) on couple of machines. On the machines where it is returning empty file version, if I run the program from command prompt as a normal user and as an independent process, it is able to retrieve the local file version correctly.
NOTE: It is able to retrieve the server file version even when launched as child process on all machines.
I also tried to copy the local file from C:\Windows to C:\Temp (using the File.Copy() API) to test if the properties can be read when the same file is in a different location. But then the program throws a File Access exception (when run from the parent app). There are no access restrictions on C:\Temp. Also, from technical forums, I understand that NT AUTHORITY\SYSTEM is an Admin account.
I don't understand why the program is not able to retrieve the local file version when it is run under NT AUTHORITY\SYSTEM account and why it is successful when run with my account as a normal process (NOT as a child process).
Can somebody please help? Thanks.

Related

Downloading an exe without asking for administrator privileges C#

For the .NET Framework program I'm working on, I have an automatic updater set-up. It downloads a 7-zip Self-Extracting archive (in the form of an exe) from the latest github release, and runs it to update the program. The issue is, despite the fact that the program doesn't get installed anywhere that requires administrator privileges, the Update.exe always requires an admin to run it once installed, despite not being necessary when downloading it via browser. Is there any way to prevent this? Here's my current setup:
using (WebClient client = new WebClient()) {
// Add the user agent header, otherwise we will get access denied.
client.Headers.Add("User-Agent: Other");
// Full asset streamed into a single string. I.E: https://github.com/soopercool101/BrawlCrate/releases/download/BrawlCrate_v0.14_Hotfix2/BrawlCrate.v0.14.6873.39137.exe
string html = client.DownloadString(Asset.Url);
client.DownloadFile(URL, AppPath + "/Update.exe"); // AppPath is defined as whereever the program is installed
}
As according to Daniel, it was the name "update.exe" that was causing Windows to assume it needed admin privileges. Downloading it as "temp.exe" instead causes it to no longer prompt for admin approval.

Get BaseDirectory of an application from a Windows Service

I have a winform application ABC. I'm installing this on my client devices using clickonce. In it's BaseDirectory (which is AppData\Local\Apps\xx\yy\zz\ for my application). In the zz folder I have a zip folder which I need to access from a windows service. Is there any way I can get the AppData location from my win service? Is it even possible? I had the assumption it's not possible since it means a third party can affect the application.
The best option seems to be what Alex K suggested in the comments, save it in the registry upon installation and retrieve it with your service.
Another option might be using MSI and there's a C# wrapper for it on GitHub.
An example:
// Look for installed products containing 'Word' in their name and show their installed location
foreach (var p in InstalledProduct.Enumerate())
{
try
{
if (p.InstalledProductName.Contains("Word"))
Console.Out.WriteLine("{0} is intalled in {1}", p.GUID, p.InstallLocation);
}
catch (MSIException ex)
{
// Some products might throw an exception trying to access InstalledProductName propoerty.
}
}
It is partially possible. You can acquire the appdata local directory on the client system from your service code using:
var p = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
Then you can add the part that is specific to your application ("xx/yy/zz") as that would not be anywhere in the environment. I would suggest using:
Path.Combine(p, "xx/yy/zz/yourfile.zip");
If the special folder above is not the one you need you can refer to the rest of the enumerated values here on msdn for the SpecialFolder Enumeration

File.Exists not working on network drive

My console app checks for the presence of a file on a network drive and logs a message when it does not exist. Today I deployed my app to a QA machine and File.Exists() has been returning false for files that do exist. I am running the app via windows task scheduler. When I ran it from the command line it seems to work fine. But either way I don't trust it now. Has anyone seen this behavior or have any insight?:
Using System.IO;
private static void Main()
{
var fileName = #"x:\folder\file1.txt"; //be a network share
If (!File.Exists(fileName)
{
LogMessage("File is not on disk.");
}
else
{
LogMessage("File is on disk.");
}
}
I suspect that the drives are not mapped when running from task scheduler. Try a UNC path
var fileName = #"\\server\share\folder\file1.txt";

How do i change the hosts file in a windows program?

How would a program in C++/ C / C# program change the C:\Windows\System32\drivers\etc\hosts file content in windows?
I know this sounds like phishing, honestly not.
Hosts file has a very simple format where each line may contain "ip host" records
All you need is regular file appending :
using (StreamWriter w = File.AppendText(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "drivers/etc/hosts")))
{
w.WriteLine("123.123.123.123 FQDN");
}
Beware that by default you'll need elevated privileges to write to the hosts file...
In order to revert back, better take a backup of the file and restore it once you are done.
First, you should request for administrative permission from the user. You can do this through your Program class in your application. The below code will request the user for administrative access, the user then has the option to allow or deny it. If they deny it, this example does not run the application.
Once your application is run in administrative mode, its plain text with simple formatting. You do not even need all the Microsoft comments included in the file, and simple string parsing will do just fine. The comments by MSFT in the HOSTS file are all the documentation you really need as far as the HOSTS file itself goes.
namespace Setup {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Setup.Forms;
using System.Security.Principal;
using System.Diagnostics;
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
bool administrativeMode = principal.IsInRole(WindowsBuiltInRole.Administrator);
if (!administrativeMode) {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.Verb = "runas";
startInfo.FileName = Application.ExecutablePath;
try {
Process.Start(startInfo);
}
catch {
return;
}
return;
}
Application.Run(new ShellForm());
}
}
}
The file is usually located at C:\Windows\System32\drivers\etc\hosts. Rather than hard coding the C:\Windows part though, you should use Environment.GetEnvironmentVariable("SystemRoot") to safely determine the system root directory.
Otherwise you can write to it like any other file, assuming you have the proper permissions.
The hosts file is just plain text. The format is each line contains the IP and the hostname that IP should resolve to, separated by whitespace. # denotes a comment.
Example:
# This is a comment-
127.0.0.1 mysuperhost.com
The file is located here: C:\Windows\system32\drivers\etc\hosts. You will (with good reason), need administrator privileges to write to it.
The most accurate way of finding the HOSTS file location is to read the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\DataBasePath registry key, and appending hosts to the end.
This will always point to the correct location for the current machine configuration and works for all Windows NT based platforms since Windows NT 4.0.
As a guy who struggled with this problem, easy way out, copy the hosts file to temp folder, modify it and copy it back with overwrite. Running the application as admin, will be the best.

Where will the file without path get created in client system

I have the following code in an Winform Application
String[] lines = { "LAST MESSAGE", "101" };
File.WriteAllLines("MPP_Config.txt", lines);
On my Development system, the file gets created under Bin\Debug...
Where will the file be created in the client once it is installed in the client system ?
I deploy to a website using Click once Deployment...
I believe it will be created in current working directory of the application. The application might not have access to this directory, especially on systems with UAC, Vista and windows 7. You should probably think about using the application data directory instead.
String[] lines = { "LAST MESSAGE", "101" };
String fileName = Path.combine(System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory,"MPP_Config.txt");
File.WriteAllLines(fileName, lines);
I'm guessing it gets created in the debug folder becuse you have been running it in debug mode. If you ran it in release mode then it will be saved in the bin/release folder.
In other words it will be created in the directory in which the application resides. Why not try it? I.e. copy your exe across...
In the current directory. This means that it might be hard to predict with 100% certainty. If you want to control it, you can use Environment.CurrentDirectory but (as Sam points out in the comments) this may not be a good idea since other code may also depend on the current directory for other purposes.
For instance, the following program will create two different files called "somefile.txt" (given that the exe is not run from the c:\temp directory):
static void Main(string[] args)
{
File.WriteAllText("somefile.txt", "some text");
Environment.CurrentDirectory = #"c:\temp";
File.WriteAllText("somefile.txt", "some text");
}

Categories