I've written a set of web services using Web Api 2. They eventually end up calling a CMD program that fires up a OpenEdge Progress client, passes a formatted XML string and then inserts records into a OpenEdge Progress database by calling a .p procedure (WebSpeed is not an option).
The .P file has a set of business logic to run against a Progress application. It subsequently generates an XML file on completion containing an < Error > node. If this node is empty - then it worked. If the file doesn't exist or the node contains text... it failed. I then read this XML file and pass the contents of the < Error > node back to the client in Web Api.
At the moment, there is a static delay of 10 seconds from calling the CMD/Progress applet, to attempting to read the XML file, to give the server time to run the .P file and create said XML file. This isn't great, though, and occasionally an error is returned to the client because it can't find the file, yet, the file was created 1 second after the response was returned because of abnormally high server loads. Alternatively, people are forced to wait 10 seconds when the response could have been handled in 2 seconds.
I need to come up with a way to "check until file exists" until a timeout period has elapsed. I've done some research and can't find anything suitable for a Web Api environment. Does anyone have any suggestions?
Code below - forgive me. I've very much been learning as I've been going along and am very new to this!
Controller
// the request date/time
DateTime requestDate = DateTime.Now;
// list of validation errors
List<string> ohValidation = new List<string>();
...
WebExtensions.callInsertProgram(xml, "JOBLOG");
ohValidation = XmlExtensions.ReadProgressXmlFileWithArray(job.logjob.placeref, requestDate, "joblogging");
CallInsertProgram
public static void callInsertProgram(string xml, string program)
{
try
{
using (Process p = new Process())
{
p.StartInfo.FileName = #"C:\Rubixx\runProgress.exe";
p.StartInfo.WorkingDirectory = #"C:\Rubixx";
// stop windows from appearing on the server
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
// set the arguments for running. The program name and xml are passed in as arguments
// wrapped in escaping "\" to stop spaces from being treated as a separator
p.StartInfo.Arguments = "\"" + program + "," + xml + "\"";
p.Start();
}
}
catch (Exception e)
{
throw new OpenHousingException(e.Message.ToString());
}
}
ReadProgressXMLWithArray
public static List<string> ReadProgressXmlFileWithArray(string reference, DateTime requestDateTime, string folder)
{
// new empty list
List<string> output
= new List<string>();
// wait X seconds before doing anything
// to ensure the XML file has time to be created
Delay_Start(fileDelay);
//
string filename = fullFileName(jobno, folder, requestDateTime);
string filepath = getFullFilepath(filename, folder);
if (checkXmlFileExists(filepath))
{
// if so check for the existence of an error message
output = getXmlErrorArray(filepath);
}
else
{
// if no file is found - the call to Progress hasn't executed. So tell the end user.
throw new OpenHousingException("No OpenHousing file could be found");
}
return output;
}
Delay_Start
private static void Delay_Start(int Seconds)
{
DateTime StartTime;
DateTime EndTime;
StartTime = DateTime.Now;
EndTime = StartTime.AddSeconds(Seconds);
do
{ StartTime = DateTime.Now; } while (StartTime < EndTime);
}
FullFileName (needed because I can't be sure of XML filename until created. File Format is UniqueReference_DateTimeFileCreated.xml (xxxxxxxx_20160401-1100.xml) So, I have to wildcard search a folder with a unique reference).
public static string fullFileName(string jobNo, string folder, DateTime createdDate)
{
string fileName = string.Empty;
string folderPath = fileLocation + folder;
DirectoryInfo dir = new DirectoryInfo(folderPath);
FileInfo[] files = dir.GetFiles(jobNo + "*", SearchOption.TopDirectoryOnly).Where(f => f.CreationTimeUtc > createdDate || f.LastWriteTimeUtc > createdDate).ToArray() ;
foreach (var item in files)
{
fileName = item.Name;
}
if (string.IsNullOrEmpty(fileName))
throw new OpenHousingException("No OpenHousing file could be found");
return fileName;
}
GetFullFilePath (Can probably be consolidated into fullFileName)
private static string getFullFilepath(string filename, string folder)
{
return fileLocation + folder + #"\" + filename;
}
CheckXMLFileExists
private static bool checkXmlFileExists(string filepath)
{
bool fileExists = false;
if (File.Exists(filepath))
{
fileExists = true;
}
return fileExists;
}
GetXMLErrorArray
private static List<string> getXmlErrorArray(string filepath)
{
List<string> output
= new List<string>();
// read the text from XML file
using (TextReader txtReader = new StreamReader(filepath))
{
XmlSerializer xs
= new XmlSerializer(typeof(JobError));
// de-serialise the xml text
// to a strongly typed object
JobError result = (JobError)xs.Deserialize(txtReader);
// if the xml file contains an error - return it to the client
if (!string.IsNullOrEmpty(result.ErrorText))
output.Add(result.ErrorText);
//check for SoR errors that are created under a different node
if (result.LineError != null)
{
List<LineError> lineErrs = result.LineError.ToList();
foreach (LineError le in lineErrs)
{
output.Add(le.SorCode + ":" + le.Error);
}
}
}
return output;
}
OK - so I think I was overcomplicating the problem.
Instead of waiting for the file to exist, I added a line into my CallInsertProgram method as below (as suggested by RB...
public static void callInsertProgram(string xml, string program)
{
try
{
using (Process p = new Process())
{
p.StartInfo.FileName = #"C:\Rubixx\runProgress.exe";
p.StartInfo.WorkingDirectory = #"C:\Rubixx";
// stop windows from appearing on the server
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
// set the arguments for running. The program name and xml are passed in as arguments
// wrapped in escaping "\" to stop spaces from being treated as a separator
p.StartInfo.Arguments = "\"" + program + "," + xml + "\"";
p.Start();
// ADDED
p.WaitForExit(60000);
}
}
catch (Exception e)
{
throw new OpenHousingException(e.Message.ToString());
}
}
This ensures that the Progress cmd applet is completed before moving onto the next line - at which point the XML will have been created (or not if it's failed). Initial testing is working well. Can anyone foresee any problems with this approach?
I've been searching to find out how to get a remote PC's System.Environment.TickCount.
Using this simple code gets the info I want from my local PC but I can't work out how to get the same info for each PC in our domain network. I want to run this from our server.
TimeSpan t = TimeSpan.FromMilliseconds(System.Environment.TickCount);
MessageBox.Show(t.Days.ToString() + "days, " + t.Hours.ToString() + "hrs & " + t.Minutes.ToString() + "mins.");
I've got this code to get all computer names in the network:
public List<String> ListNetworkComputers()
{
List<String> _ComputerNames = new List<String>();
String _ComputerSchema = "Computer";
System.DirectoryServices.DirectoryEntry _WinNTDirectoryEntries = new System.DirectoryServices.DirectoryEntry("WinNT:");
foreach (System.DirectoryServices.DirectoryEntry _AvailDomains in _WinNTDirectoryEntries.Children)
{
foreach (System.DirectoryServices.DirectoryEntry _PCNameEntry in _AvailDomains.Children)
{
if (_PCNameEntry.SchemaClassName.ToLower().Contains(_ComputerSchema.ToLower()))
{
_ComputerNames.Add(_PCNameEntry.Name);
}
}
}
return _ComputerNames;
}
How can I use this info to get the System.Environment.TickCount from each PC?
I've tried PsExec.exe but I've really got no clue how to get it to work for me. I tried this but it doesn't work:
var list = ListNetworkComputers();
foreach (var pc in list)
{
string output = "";
using (var process = new System.Diagnostics.Process())
{
process.StartInfo.FileName = #"C:\PsExec.exe";
process.StartInfo.Arguments = #"\\" + pc + " cmd /c echo " + "System.Environment.TickCount";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
output = process.StandardOutput.ReadToEnd();
}
int count = 0;
Int32.TryParse(output, out count);
TimeSpan ts = TimeSpan.FromMilliseconds(count);
MessageBox.Show(pc + ": " + ts.Days.ToString() + "days, " + ts.Hours.ToString() + "hrs & " + ts.Minutes.ToString() + "mins.");
}
Instead of using "cmd.exe", maybe you can use PowerShell? If so, it's a simple command to print that property: [System.Environment]::TickCount
I needed to do the same thing: get a remote PC's System.Environment.TickCount.
I came up with this solution (using Windows Management Instrumentation or WMI LocalDateTime - LastBootUpTime), but it's not 100% accurate compared to Environment.TickCount (see code comment below).
So I checked for other solutions online. Turns out #HansPassant suggested the same thing. For my use case, the +/-100 ticks discrepancy shouldn't matter.
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Options;
using System;
using System.Linq;
using System.Security;
namespace TickCountTest
{
class Program
{
/// <summary>
/// Print the system TickCount (converted from Win32_OperatingSystem LocalDateTime - LastBootUpTime properties).
/// Why? Because this technique can be used to get TickCount from a Remote machine.
/// </summary>
public static void Main(string[] args)
{
var tickCount = GetRemoteMachineTickCount("REMOTEMACHINENAME");
if (!tickCount.HasValue)
{
throw new NullReferenceException("GetRemoteMachineTickCount() response was null.");
}
Console.WriteLine($"TickCount: {tickCount}");
Console.ReadKey();
}
/// <summary>
/// Retrieves the duration (TickCount) since the system was last started from a remote machine.
/// </summary>
/// <param name="computerName">Name of computer on network to retrieve tickcount for</param>
/// <returns>WMI Win32_OperatingSystem LocalDateTime - LastBootUpTime (ticks)</returns>
private static int? GetRemoteMachineTickCount(string computerName)
{
string namespaceName = #"root\cimv2";
string queryDialect = "WQL";
DComSessionOptions SessionOptions = new DComSessionOptions();
SessionOptions.Impersonation = ImpersonationType.Impersonate;
var baseLineTickCount = Environment.TickCount; // Note: to determine discrepancy
CimSession session = CimSession.Create(computerName, SessionOptions);
string query = "SELECT * FROM Win32_OperatingSystem";
var cimInstances = session.QueryInstances(namespaceName, queryDialect, query);
if (cimInstances.Any())
{
var cimInstance = cimInstances.First();
var lastBootUpTime = Convert.ToDateTime(cimInstance.CimInstanceProperties["LastBootUpTime"].Value);
var localDateTime = Convert.ToDateTime(cimInstance.CimInstanceProperties["LocalDateTime"].Value);
var timeSpan = localDateTime - lastBootUpTime;
var tickCount = Convert.ToInt32(timeSpan.TotalMilliseconds);
var discrepancy = tickCount - baseLineTickCount; // Note: discrepancy about +/- 100 ticks
return tickCount;
}
return null;
}
}
}
how to check if OpenOffice is installed programatically using c#
public bool isOpenofficeInstalled()
{
//The registry key:
string SoftwareKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(SoftwareKey))
{
bool flag = false;
//Let's go through the registry keys and get the info we need:
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
//If the key has value, continue, if not, skip it:
// if (((sk.GetValue("DisplayName")).ToString() == "OpenOffice.org 3.2"))
if((sk.GetValue("DisplayName")).ToString() == "OpenOffice.org 3.2")
{
flag = true;
////install location ?
//if (sk.GetValue("InstallLocation") == null)
// Software += sk.GetValue("DisplayName") + " - Install path not known\n"; //Nope, not here.
//else
// Software += sk.GetValue("DisplayName") + " - " + sk.GetValue("InstallLocation") + "\n"; //Yes, here it is...
}
}
catch (Exception)
{
}
}
}
return flag;
}
}
Here is a solution that gets the startup location of the default program to open a odt file. As long as the file association has not been changed this works regardless of what version is installed.
(this is VB.NET)
Dim odt = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(".odt")
Dim linkedValue = odt.GetValue("")
Dim linkedKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(linkedValue)
Dim openWith = linkedKey.OpenSubKey("Shell\Open\Command").GetValue("")
Dim O As String = CStr(openWith)
If O.Contains("swriter.exe") Then
// proceed with code
Else
// error message
End If
Same as in any other language? Search the known locations on the file system for the executable that launches open office? Check for libraries? Parse the output of "which openoffice"?
There are lots of options, and I'd say that most of them would not be reliable.
How do I determine a mapped drive's actual path?
So if I have a mapped drive on a machine called "Z" how can I using .NET determine the machine and path for the mapped folder?
The code can assume it's running on the machine with the mapped drive.
I looked at Path, Directory, FileInfo objects, but can't seem to find anything.
I also looked for existing questions, but could not find what I'm looking for.
I expanded on ibram's answer and created this class (which has been updated per comment feedback). I've probably over documented it, but it should be self-explanatory.
/// <summary>
/// A static class to help with resolving a mapped drive path to a UNC network path.
/// If a local drive path or a UNC network path are passed in, they will just be returned.
/// </summary>
/// <example>
/// using System;
/// using System.IO;
/// using System.Management; // Reference System.Management.dll
///
/// // Example/Test paths, these will need to be adjusted to match your environment.
/// string[] paths = new string[] {
/// #"Z:\ShareName\Sub-Folder",
/// #"\\ACME-FILE\ShareName\Sub-Folder",
/// #"\\ACME.COM\ShareName\Sub-Folder", // DFS
/// #"C:\Temp",
/// #"\\localhost\c$\temp",
/// #"\\workstation\Temp",
/// #"Z:", // Mapped drive pointing to \\workstation\Temp
/// #"C:\",
/// #"Temp",
/// #".\Temp",
/// #"..\Temp",
/// "",
/// " ",
/// null
/// };
///
/// foreach (var curPath in paths) {
/// try {
/// Console.WriteLine(string.Format("{0} = {1}",
/// curPath,
/// MappedDriveResolver.ResolveToUNC(curPath))
/// );
/// }
/// catch (Exception ex) {
/// Console.WriteLine(string.Format("{0} = {1}",
/// curPath,
/// ex.Message)
/// );
/// }
/// }
/// </example>
public static class MappedDriveResolver
{
/// <summary>
/// Resolves the given path to a full UNC path if the path is a mapped drive.
/// Otherwise, just returns the given path.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <returns></returns>
public static string ResolveToUNC(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToUNC does not support relative paths.",
path)
);
}
// Is the path already in the UNC format?
if (path.StartsWith(#"\\")) {
return path;
}
string rootPath = ResolveToRootUNC(path);
if (path.StartsWith(rootPath)) {
return path; // Local drive, no resolving occurred
}
else {
return path.Replace(GetDriveLetter(path), rootPath);
}
}
/// <summary>
/// Resolves the given path to a root UNC path if the path is a mapped drive.
/// Otherwise, just returns the given path.
/// </summary>
/// <param name="path">The path to resolve.</param>
/// <returns></returns>
public static string ResolveToRootUNC(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
return Directory.GetDirectoryRoot(path);
}
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(path);
// Query WMI if the drive letter is a network drive, and if so the UNC path for it
using (ManagementObject mo = new ManagementObject()) {
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
DriveType driveType = (DriveType)((uint)mo["DriveType"]);
string networkRoot = Convert.ToString(mo["ProviderName"]);
if (driveType == DriveType.Network) {
return networkRoot;
}
else {
return driveletter + Path.DirectorySeparatorChar;
}
}
}
/// <summary>
/// Checks if the given path is a network drive.
/// </summary>
/// <param name="path">The path to check.</param>
/// <returns></returns>
public static bool isNetworkDrive(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and ResolveToRootUNC does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
return true;
}
// Get just the drive letter for WMI call
string driveletter = GetDriveLetter(path);
// Query WMI if the drive letter is a network drive
using (ManagementObject mo = new ManagementObject()) {
mo.Path = new ManagementPath(string.Format("Win32_LogicalDisk='{0}'", driveletter));
DriveType driveType = (DriveType)((uint)mo["DriveType"]);
return driveType == DriveType.Network;
}
}
/// <summary>
/// Given a path will extract just the drive letter with volume separator.
/// </summary>
/// <param name="path"></param>
/// <returns>C:</returns>
public static string GetDriveLetter(string path) {
if (String.IsNullOrWhiteSpace(path)) {
throw new ArgumentNullException("The path argument was null or whitespace.");
}
if (!Path.IsPathRooted(path)) {
throw new ArgumentException(
string.Format("The path '{0}' was not a rooted path and GetDriveLetter does not support relative paths.",
path)
);
}
if (path.StartsWith(#"\\")) {
throw new ArgumentException("A UNC path was passed to GetDriveLetter");
}
return Directory.GetDirectoryRoot(path).Replace(Path.DirectorySeparatorChar.ToString(), "");
}
}
I can't remember where I found this, but it works without p/invoke. It's what rerun posted before.
you need to reference System.Management.dll:
using System.IO;
using System.Management;
code:
public void FindUNCPaths()
{
DriveInfo[] dis = DriveInfo.GetDrives();
foreach( DriveInfo di in dis )
{
if(di.DriveType == DriveType.Network)
{
DirectoryInfo dir = di.RootDirectory;
// "x:"
MessageBox.Show( GetUNCPath( dir.FullName.Substring( 0, 2 ) ) );
}
}
}
public string GetUNCPath(string path)
{
if(path.StartsWith(#"\\"))
{
return path;
}
ManagementObject mo = new ManagementObject();
mo.Path = new ManagementPath( String.Format( "Win32_LogicalDisk='{0}'", path ) );
// DriveType 4 = Network Drive
if(Convert.ToUInt32(mo["DriveType"]) == 4 )
{
return Convert.ToString(mo["ProviderName"]);
}
else
{
return path;
}
}
Update:
Explicitly running as administrator will not show mapped drives. Here is an explanation of this behaviour:
https://stackoverflow.com/a/11268410/448100
(in short: administrator has a different user context, so no access to mapped drives of normal user)
Here are some code samples:
Using P/Invoke
All of the magic derives from a Windows function:
[DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int WNetGetConnection(
[MarshalAs(UnmanagedType.LPTStr)] string localName,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder remoteName,
ref int length);
Example invocation:
var sb = new StringBuilder(512);
var size = sb.Capacity;
var error = Mpr.WNetGetConnection("Z:", sb, ref size);
if (error != 0)
throw new Win32Exception(error, "WNetGetConnection failed");
var networkpath = sb.ToString();
I've written a method for this. It returns a UNC path if it is a mapped drive, otherwise it returns the path unchanged.
public static string UNCPath(string path)
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0]))
{
if (key != null)
{
path = key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString();
}
}
return path;
}
EDIT
You now can use the Method even with already UNC paths. The above version of the method throws an exception if given a UNC path.
public static string UNCPath(string path)
{
if (!path.StartsWith(#"\\"))
{
using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Network\\" + path[0]))
{
if (key != null)
{
return key.GetValue("RemotePath").ToString() + path.Remove(0, 2).ToString();
}
}
}
return path;
}
I think you can use the "Network" key From the "Current User" Hive, In the Registry.
The Mapped Drives Are Listed There With Their Shared Path On Server.
If there is no mapped drive in the system, so there is no "Network" Key In The "Current User" Hive.
Now, I'm using this way, no external dll nor anything else.
You can use WMI to interrogate the Win32_LogicalDrive collection on your machine. Here is an example of how to do it with scripting. Changing this over to C# is pretty well explained in other places.
Slightly modified VB.NET code from the article:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strComputer = "."
Dim objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Dim colDrives = objWMIService.ExecQuery("Select * From Win32_LogicalDisk Where DriveType = 4")
For Each objDrive In colDrives
Debug.WriteLine("Drive letter: " & objDrive.DeviceID)
Debug.WriteLine("Network path: " & objDrive.ProviderName)
Next
End Sub
End Class
I could not replicate ibram's or Vermis' answer due to the problem I mentioned in a comment under Vermis' answer, about a type initializer exception.
Instead, I discovered I could query for all the drives currently on the computer and then loop through them, like so:
using System.IO; //For DirectoryNotFound exception.
using System.Management;
/// <summary>
/// Given a local mapped drive letter, determine if it is a network drive. If so, return the server share.
/// </summary>
/// <param name="mappedDrive"></param>
/// <returns>The server path that the drive maps to ~ "////XXXXXX//ZZZZ"</returns>
private string CheckUNCPath(string mappedDrive)
{
//Query to return all the local computer's drives.
//See http://msdn.microsoft.com/en-us/library/ms186146.aspx, or search "WMI Queries"
SelectQuery selectWMIQuery = new SelectQuery("Win32_LogicalDisk");
ManagementObjectSearcher driveSearcher = new ManagementObjectSearcher(selectWMIQuery);
//Soem variables to be used inside and out of the foreach.
ManagementPath path = null;
ManagementObject networkDrive = null;
bool found = false;
string serverName = null;
//Check each disk, determine if it is a network drive, and then return the real server path.
foreach (ManagementObject disk in driveSearcher.Get())
{
path = disk.Path;
if (path.ToString().Contains(mappedDrive))
{
networkDrive = new ManagementObject(path);
if (Convert.ToUInt32(networkDrive["DriveType"]) == 4)
{
serverName = Convert.ToString(networkDrive["ProviderName"]);
found = true;
break;
}
else
{
throw new DirectoryNotFoundException("The drive " + mappedDrive + " was found, but is not a network drive. Were your network drives mapped correctly?");
}
}
}
if (!found)
{
throw new DirectoryNotFoundException("The drive " + mappedDrive + " was not found. Were your network drives mapped correctly?");
}
else
{
return serverName;
}
}
This works for x64 Windows 7, for .NET 4. It should be usable in case you're getting that exception that was mentioned above.
I did this using the stuff given from MSDN and bits from ibram's or Vermis' answers, though it was a bit difficult to find specific examples on the MSDN. Resources used:
MSDN : Win32_LogicalDisk Class
MSDN : System.Management namespace
MSDN : WMI Queries example:
using System;
using System.Management;
class Query_SelectQuery
{
public static int Main(string[] args)
{
SelectQuery selectQuery = new
SelectQuery("Win32_LogicalDisk");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(selectQuery);
foreach (ManagementObject disk in searcher.Get())
{
Console.WriteLine(disk.ToString());
}
Console.ReadLine();
return 0;
}
}
QueryDosDevice translates a drive letter into the path that it expands to.
Note that this will translate ALL drive letters, not just those that are mapped to network connections. You need to already know which are network paths, or to parse the output to see which are network.
Here's the VB signature
Declare Function QueryDosDevice Lib "kernel32" Alias "QueryDosDeviceA" (
ByVal lpDeviceName As String,
ByVal lpTargetPath As String,
ByVal ucchMax As Integer) As Integer
And the C# one
[DllImport("kernel32.dll")]
static extern uint QueryDosDevice(string lpDeviceName, IntPtr lpTargetPath, uint ucchMax);
Similar to ibram's answer with a few modifications:
public static String GetUNCPath(String path) {
path = path.TrimEnd('\\', '/') + Path.DirectorySeparatorChar;
DirectoryInfo d = new DirectoryInfo(path);
String root = d.Root.FullName.TrimEnd('\\');
if (!root.StartsWith(#"\\")) {
ManagementObject mo = new ManagementObject();
mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", root));
// DriveType 4 = Network Drive
if (Convert.ToUInt32(mo["DriveType"]) == 4)
root = Convert.ToString(mo["ProviderName"]);
else
root = #"\\" + System.Net.Dns.GetHostName() + "\\" + root.TrimEnd(':') + "$\\";
}
return Recombine(root, d);
}
private static String Recombine(String root, DirectoryInfo d) {
Stack s = new Stack();
while (d.Parent != null) {
s.Push(d.Name);
d = d.Parent;
}
while (s.Count > 0) {
root = Path.Combine(root, (String) s.Pop());
}
return root;
}
Seems it's need a P/Invoke: Converting a mapped drive letter to a network path using C#
This guy built a managed class to deal with it: C# Map Network Drive (API)
You can also use WMI Win32_LogicalDisk to get all the information you need. use the ProviderName from the class to get the UNC path.
As far as Windows cares, what's needed is a call to WNetGetConnection. I don't know of a front-end for that in .NET, so you may have to call it via P/Invoke (fortunately, it has only one parameter, the P/Invoke code isn't too awful).
This post describe how to get the absolute path of a drive which is mapped to a local folder?
For example, I have a "c:\test" folder and an "x:" drive which is
mapped to c:\test.
I'm looking for a function which will return "c:\test" when I pass in
"x:"
The answer is:
SUBST uses DefineDosDevice (XP and later) to create the drive/path
mapping. You can use the QueryDosDevice to get the path of a SUBSTed
drive:
[DllImport("kernel32.dll")]
private static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);
static String GetPhysicalPath(String path)
{
if (String.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
// Get the drive letter
string pathRoot = Path.GetPathRoot(path);
if(String.IsNullOrEmpty(pathRoot))
{
throw new ArgumentNullException("path");
}
string lpDeviceName = pathRoot.Replace("\\", "");
const String substPrefix = #"\??\";
StringBuilder lpTargetPath = new StringBuilder(260);
if (0 != QueryDosDevice(lpDeviceName, lpTargetPath, lpTargetPath.Capacity))
{
string result;
// If drive is substed, the result will be in the format of "\??\C:\RealPath\".
if (lpTargetPath..ToString().StartsWith(substPrefix))
{
// Strip the \??\ prefix.
string root = lpTargetPath.ToString().Remove(0, substPrefix.Length);
result = Path.Combine(root, path.Replace(Path.GetPathRoot(path), ""));
}
else
{
// TODO: deal with other types of mappings.
// if not SUBSTed, just assume it's not mapped.
result = path;
}
return result;
}
else
{
// TODO: error reporting
return null;
}
}
Here is a solution that doesn't care if it is local or remote
private string uncpath_check(string path)
{
string rval = path;
string driveprefix = path.Substring(0, 2);
string unc;
if (driveprefix != "\\")
{
ManagementObject mo = new ManagementObject();
try
{
mo.Path = new ManagementPath(String.Format("Win32_LogicalDisk='{0}'", driveprefix));
unc = (string)mo["ProviderName"];
rval = path.Replace(driveprefix, unc);
}
catch
{
throw;
}
}
if (rval == null)
{ rval = path; }
return rval;
}
How can i find the execution path of a installed software in c# for eg media player ,vlc player . i just need to find their execution path . if i have a vlc player installed in my D drive . how do i find the path of the VLC.exe from my c# coding
Using C# code you can find the path for some excutables this way:
private const string keyBase = #"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
private string GetPathForExe(string fileName)
{
RegistryKey localMachine = Registry.LocalMachine;
RegistryKey fileKey = localMachine.OpenSubKey(string.Format(#"{0}\{1}", keyBase, fileName));
object result = null;
if (fileKey != null)
{
result = fileKey.GetValue(string.Empty);
fileKey.Close();
}
return (string)result;
}
Use it like so:
string pathToExe = GetPathForExe("wmplayer.exe");
However, it may very well be that the application that you want does not have an App Paths key.
This method works for any executable located in a folder which is defined in the windows PATH variable:
private string LocateEXE(String filename)
{
String path = Environment.GetEnvironmentVariable("path");
String[] folders = path.Split(';');
foreach (String folder in folders)
{
if (File.Exists(folder + filename))
{
return folder + filename;
}
else if (File.Exists(folder + "\\" + filename))
{
return folder + "\\" + filename;
}
}
return String.Empty;
}
Then use it as follows:
string pathToExe = LocateEXE("example.exe");
Like Fredrik's method it only finds paths for some executables
I used the CurrentVersion\Installer\Folders registry key. Just pass in the product name.
private string GetAppPath(string productName)
{
const string foldersPath = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\Folders";
var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var subKey = baseKey.OpenSubKey(foldersPath);
if (subKey == null)
{
baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
subKey = baseKey.OpenSubKey(foldersPath);
}
return subKey != null ? subKey.GetValueNames().FirstOrDefault(kv => kv.Contains(productName)) : "ERROR";
}
None of the answers worked for me. After hours of searching online, I was able to successfully get the installation path. Here is the final code.
public static string checkInstalled(string findByName)
{
string displayName;
string InstallPath;
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
//64 bits computer
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
{
InstallPath = subkey.GetValue("InstallLocation").ToString();
return InstallPath; //or displayName
}
}
key.Close();
}
return null;
}
you can call this method like this
string JavaPath = Software.checkInstalled("Java(TM) SE Development Kit");
and boom. Cheers
Have a look at MsiEnumProductsEx
This stackoverflow.com article describes how to get the application associated with a particular file extension.
Perhaps you could use this technique to get the application associated with certain extensions, such as avi or wmv - either Medial Player or in your case VLC player?