Opening mp3 by not-default program C sharp - c#

How can I open mp3 file with RealPlayer while the default is MediaPlayer
I know Process and ProcessStartInfo method, but I would like to know how to "open program with..."
Can you help me, plz?

Okay, so thought I'd make this possible for you before I clock off for the night. I have thrown together a working console application which loads (known) installed programs from the registry's App Path key. The solution is far from perfect, won't be the safest, fastest, or most reliable solution, and it certainly shouldn't be seen amongst any production code, but it is more than enough to aid you, hopefully, in developing what it is you need:
So, here is the code, minus the namespace...
using System;
using System.IO;
using Microsoft.Win32;
using System.Diagnostics;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
if (args.Length >= 0 && !string.IsNullOrEmpty(args[0]) && File.Exists(args[0]))
{
var programs = new InstalledPrograms();
var programKey = "RealPlay.exe".ToLowerInvariant();
if (programs.ContainsKey(programKey))
{
var programPath = programs[programKey];
if (!string.IsNullOrEmpty(programPath) && File.Exists(programPath))
{
var process = new Process();
process.StartInfo = new ProcessStartInfo(programPath);
process.StartInfo.Arguments = args[0];
if (process.Start())
{
Console.WriteLine("That was easy!");
}
else
{
Console.WriteLine("Hell's bells and buckets of blood, we seem to have hit a snag!");
}
}
}
}
else
{
Console.WriteLine("Specify a file as an argument, silly!");
}
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
class InstalledPrograms : Dictionary<string, string>
{
static string PathKeyName = "Path";
static string RegistryKeyToAppPaths = #"SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths";
public InstalledPrograms()
{
Refresh();
}
public void Refresh()
{
Clear();
using (var registryKey = Registry.LocalMachine.OpenSubKey(RegistryKeyToAppPaths))
{
var executableFullPath = string.Empty;
foreach (var registrySubKeyName in registryKey.GetSubKeyNames())
{
using (var registrySubKey = registryKey.OpenSubKey(registrySubKeyName))
{
executableFullPath = registrySubKey.GetValue(string.Empty) as string;
Add(registrySubKeyName.ToLowerInvariant(), executableFullPath);
}
}
}
}
}
}
Though we check for file existence, and other minor but necessary checks are made, you would still need to tighten this up further when plugged into the environment of your own code, including, among other things, exception handling for, but not limited to, registry access issues.

Related

Performance of a program that uses System.Reflection.Metadata varies for unknow reasons

The below program is a command line tool that takes a directory path as input, recursively scans it for files with .dll as extension and verifies whether those files are valid .NET assemblies. To do that verification, it uses the System.Reflection.Metadata NuGet package. Eventually, the name and version of all valid .NET assemblies is printed to the console.
The program works as intended but its performance strikes me as odd. Sometimes, if I let the program scan a directory with thousands of files it just takes dozens of seconds. During that time, there is a lot of disk load and both the first and the second time measurement of the program are unusually high. But running the program again with the same directory argument executes it very quickly, measured typically in a couple of 100 milliseconds.
I do not really understand that behavior. A possible explanation would be that the disk went sleeping but I have looked into this and it does not seem to explain the behavior. The program itself doesn't cache anything and terminates after the content has been printed to the console.
Is there a mechanism in Windows that explains why var dllFilePaths = new DirectoryInfo(args[0]).GetFiles("*.dll", SearchOption.AllDirectories) runs quicker when executed multiple times?
And why would the code that invokes readers from System.Reflection.Metadata run quicker when executed multiple times?
namespace AssemblyFinder
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
public static class Program
{
public static int Main(string[] args)
{
if (args.Length != 1)
{
Console.Error.WriteLine("Please specify the path of the directory to be scanned for .NET assemblies.");
return 1;
}
if (!Directory.Exists(args[0]))
{
Console.Error.WriteLine($"The specified argument \"{args[0]}\" is not an existing directory.");
return 2;
}
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var assemblies = new List<AssemblyInfo>();
var dllFilePaths = new DirectoryInfo(args[0])
.GetFiles("*.dll", SearchOption.AllDirectories)
.Select(dllFileInfo => dllFileInfo.FullName)
.ToList();
stopWatch.Stop();
Console.WriteLine($"Found {dllFilePaths.Count} dlls in {(long)stopWatch.Elapsed.TotalMilliseconds} milliseconds");
stopWatch.Restart();
foreach (var dllFilePath in dllFilePaths)
{
if (IsAssembly(dllFilePath, out var assemblyInfo))
{
assemblies.Add(assemblyInfo);
}
}
stopWatch.Stop();
foreach (var assembly in assemblies)
{
Console.WriteLine($"{assembly.FilePath} -> {assembly.AssemblyName}, {assembly.AssemblyVersion}");
}
Console.WriteLine($"Found {dllFilePaths.Count} dlls, identified {assemblies.Count} assemblies in {(long)stopWatch.Elapsed.TotalMilliseconds} milliseconds");
return 0;
}
private static bool IsAssembly(string filePath, out AssemblyInfo assemblyInfo)
{
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var peReader = new PEReader(fileStream))
{
if (!peReader.PEHeaders.IsDll || !peReader.HasMetadata)
{
assemblyInfo = null;
return false;
}
var metadataReader = peReader.GetMetadataReader();
if (!metadataReader.IsAssembly)
{
assemblyInfo = null;
return false;
}
var assemblyDefinition = metadataReader.GetAssemblyDefinition();
assemblyInfo = new AssemblyInfo()
{
AssemblyName = assemblyDefinition.Name.IsNil ? null : metadataReader.GetString(assemblyDefinition.Name),
AssemblyVersion = assemblyDefinition.Version,
FilePath = filePath,
};
return true;
}
}
}
public class AssemblyInfo
{
public string AssemblyName
{
get;
set;
}
public Version AssemblyVersion
{
get;
set;
}
public string FilePath
{
get;
set;
}
}
}

C#: Get Product Version using the Product Name using the WindowsInstaller and not WMI

I need some help in retrieving the ProductVersion of a program in C#.
I did find a way to retrieve it using WMI but it is really slow.
Previously I had the same issue when using vbscript and the solution was to use the
Windows Installer (which is really fast)
Set objInstaller = CreateObject("WindowsInstaller.Installer")
Set colProducts = objInstaller.Products
For Each objProduct In colProducts
strProductName = objInstaller.ProductInfo(objProduct, "ProductName")
If strProductName = "MyProductName1" Then
var1 = objInstaller.ProductInfo(objProduct, "VersionString")
End If
If strProductName = "MyProductName2" Then
var2 = objInstaller.ProductInfo(objProduct, "VersionString")
End If
Next
The question is how do I do the same in C#? I'm currently using Avalonia as UI.
I also tried all the other methods in the search (Wix DTF, COM with P/Invoke), so please don't redirect to google....
Edit: I do not have a path for the msi or exe. I need to search in the registry or installer, that is why getfileversion or dtf is not working.
Thanks!
Edit1:
Here is my code after reading all the comments and resources.
public class Program
{
public static string getInstalledVersion(string findByName)
{
string info = null;
string registryKey = #"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey key = key32.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
string displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
info = subkey.GetValue("Version").ToString();
break;
else
info = "Not found";
}
key.Close();
}
return info;
}
}
Get the result in a variable:
public string MyVar => Program.getInstalledVersion("Microsoft Edge");
Edit2:
So the edit with the break in my latest version somewhat works. It finds Microsoft Edge but still doesn't find other programs (i will try other paths as fallbacks)
# Stein Asmul i did try your version but doesn't find anything, maybe Im doing something wrong?
public class ProgramX
{
public static string getPVersion(string findByName)
{
string info = null;
foreach (var p in ProductInstallation.AllProducts)
{
if (p.ProductName == findByName)
{
info = p.ProductVersion.ToString();
break;
}
else
info = "Not Found";
}
return info;
}
}
Call:
public string MyProgram => ProgramX.getPVersion("Microsoft Edge");
Edit3: Great success!
I managed to get it to work. The problem was subkey getvalue is "DisplayVersion" not "Version". I only need 64 for my programs.
public class ProgramI
{
public static string getInstalledVersion(string findByName)
{
string info = null;
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
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)))
{
string displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
{
info = subkey.GetValue("DisplayVersion").ToString();
break;
}
else
info = "Not found";
}
key.Close();
}
return info;
}
}
Thanks everyone!
There are many ways to do this. Using WiX DTF should be relatively easy, but you might not want the dependency on the DTF dlls? If so you can use interop and call the C++ API directly (no custom assembly dependencies).
For the DTF scenario (too late to add the C++ api call version tonight) you create a new project, then you add project references to the WiX DTF assemblies (standard path is: "C:\Program Files (x86)\WiX Toolset v3.11\bin"). The files you need are:
Microsoft.Deployment.WindowsInstaller.dll
Microsoft.Deployment.WindowsInstaller.Package.dll
Then you can try this code snippet - consult the DTF help file for more details - the help file you will find in the WiX installation folder ("doc" sub-folder):
using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
InstallPackage pkg = new InstallPackage(#"e:\MySetup.msi", DatabaseOpenMode.ReadOnly);
var version = pkg.Property["ProductVersion"];
Console.WriteLine(version);
}
}
}
Links:
Another answer on WiX DTF and MSI API from serverfault
You need to put a break in if statement.
if (displayName != null && displayName.Contains(findByName)){
info = subkey.GetValue("Version").ToString();
break;
}
else
info = "Not found";
In your case, Display Name is found but the next iteration of for loop setting it to "not found". Also, you need to add fallback mechanism in your code so that if it doesn't found name in 64-bit node then it should search in 32-bit one.
Product GUID Lookup: If you know the product GUID for the product (How can I find the product GUID of an installed MSI setup?), you can simply get the version and other properties for this product GUID:
using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
ProductInstallation i = new ProductInstallation("{8D6DECA5-17F9-42AF-A62B-8D7C5C11069B}");
Console.WriteLine(i.ProductVersion);
}
}
}
Product Name Lookup: If you need to search for your product by name I guess this could do:
using System;
using Microsoft.Deployment.WindowsInstaller;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
foreach (var p in ProductInstallation.AllProducts)
{
if (p.ProductName == "Inkscape")
{
Console.Write("Inkspace found in version: " + p.ProductVersion + "\n");
}
}
}
}
}
You can see this code snippet for more terse, advanced constructs using Linq. For example something like this:
var myproduct = ProductInstallation.AllProducts.FirstOrDefault(p => p.ProductName == "MyProductName");
MSI Package Estate: The link below takes you to a VBScript which will create a HTML-report of the MSI package estate: https://github.com/glytzhkof/all/blob/master/MsiHtmlReport-Mini-V4.vbs
Sample screenshot:
Links:
How to enumerate installed MSI Products and their MSP Patches using C#
Uninstalling program

C# Use comment when compiling and running code from path at runtime

Recently I try to compile and run C# code stored somewhere else. My goal is to import a .txt file, compile it and run it. I followed this article on Simeon's blog about compiling and running C# code within the program, and everything work well.
Then I try making something a bit more complex by importing the C# code from my computer, so I created a .txt file with the following lines that is store for instance at "C:\program.txt" :
(the text file)
using System;
namespace Test
{
public class DynaCore
{
static public int Main(string str)
{
Console.WriteLine("Cool it work !");
return str.Length;
}
}
}
I do some coding based on the same article and that is my code :
(the C# program)
using System;
using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using System.IO;
using Microsoft.CSharp;
using System.Reflection;
namespace DynaCode
{
class Program
{
static void Main(string[] args)
{
string[] lines = System.IO.File.ReadAllLines(#"C:\program.txt");
string bigLine = string.Empty;
foreach(string s in lines)
{
bigLine += s;
}
string[] finalLine = new string[1] { bigLine };
CompileAndRun(finalLine);
Console.ReadKey();
}
static void CompileAndRun(string[] code)
{
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll" };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
if (compile.Errors.HasErrors)
{
string text = "Compile error: ";
foreach (CompilerError ce in compile.Errors)
{
text += "rn" + ce.ToString();
}
throw new Exception(text);
}
Module module = compile.CompiledAssembly.GetModules()[0];
Type mt = null;
MethodInfo methInfo = null;
if (module != null)
{
mt = module.GetType("Test.DynaCore");
}
if (mt != null)
{
methInfo = mt.GetMethod("Main");
}
if (methInfo != null)
{
Console.WriteLine(methInfo.Invoke(null, new object[] { "here in dyna code. Yes it work !!" }));
}
}
}
}
This work well, and I got the following output as expected :
Cool it work !
33
Note that I put all the code of the .txt file in one big line that I do myseft, because as Simeon said :
CompileAssemblyFromSource consumes is a single string for each block (file) worth of C# code, not for each line.
Even now this sentence still a bit obscure for me.
( I tried CompileAndRun(new string[1] { lines.ToString() }); before but there was an error when compiling the .txt file, that's why I do the big line myself. )
And here is my problem : I ask myself : "What if I add a comment in my .txt file ?", so I edit it and that how it look : (the text file)
using System;
namespace Test
{
//This is a super simple test
public class DynaCore
{
static public int Main(string str)
{
Console.WriteLine("Cool it work !");
return str.Length;
}
}
}
And of course I got an error (CS1513) because I convert the .txt file in one big string, so everything after the // is ignored. So how can I use comment using // inside my .txt file and got the program work ?
I also try CompileAndRun(lines);, but after launching the program it crash when compiling the .txt file because of the exception.
I do some search about it and I didn't find anythings about comment. I guess there is somethings wrong about passing only one big line in the CompileAndRun method, but passing several lines don't work as I say upper.
(Another note : Comment using /* insert comment */ works.)
Each element given to CompileAssemblyFromSource is supposed to be a file, not a single line of code. So read the whole file into a single string and give it to the method and it'll work just fine.
static void Main(string[] args)
{
var code = System.IO.File.ReadAllText(#"C:\program.txt");
CompileAndRun(code);
Console.ReadKey();
}
static void CompileAndRun(string code)
{
CompilerParameters CompilerParams = new CompilerParameters();
string outputDirectory = Directory.GetCurrentDirectory();
CompilerParams.GenerateInMemory = true;
CompilerParams.TreatWarningsAsErrors = false;
CompilerParams.GenerateExecutable = false;
CompilerParams.CompilerOptions = "/optimize";
string[] references = { "System.dll" };
CompilerParams.ReferencedAssemblies.AddRange(references);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerResults compile = provider.CompileAssemblyFromSource(CompilerParams, code);
// ...
}

Running vshost.exe as a different user

Can I start appname.vshost.exe from debug folder under different username than the one used to start VisualStudio?
There is appname.vshost.exe.config with the following content. Is there a config for username? I have tried searching for it but couldn't find anything.
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
If you're trying to run your debugg executable
You can try shift right click and Run as different user.
Or do you want to run as different user via configuration?
I don't think you can start vshost.exe under different user than the one you have used to start Visual Studio. So now I am starting main console app under different user from another console app and attaching debugger to it and it works.
I have copied my code below if it helps anyone.
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using EnvDTE80;
using Process = System.Diagnostics.Process;
namespace StartService
{
class Program
{
static void Main(string[] args)
{
var secure = new SecureString();
foreach (var c in "password-from-config")
{
secure.AppendChar(c);
}
Process process = null;
try
{
process = Process.Start(#"C:\Test Projects\WcfServiceTest\WcfServiceTest\bin\Debug\WcfServiceTest.exe",
"TestUser", secure, "DomainName");
Attach(GetCurrent());
Console.ReadKey();
}
finally
{
if (process != null && !process.HasExited)
{
process.CloseMainWindow();
process.Close();
}
}
}
public static void Attach(DTE2 dte)
{
var processes = dte.Debugger.LocalProcesses;
foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf("WcfServiceTest.exe") != -1))
proc.Attach();
}
internal static DTE2 GetCurrent()
{
var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.12.0"); // Specific to VS2013
return dte2;
}
}
}

How do I compile a C# solution with Roslyn?

I have a piece of software that generates code for a C# project based on user actions. I would like to create a GUI to automatically compile the solution so I don't have to load up Visual Studio just to trigger a recompile.
I've been looking for a chance to play with Roslyn a bit and decided to try and use Roslyn instead of msbuild to do this. Unfortunately, I can't seem to find any good resources on using Roslyn in this fashion.
Can anyone point me in the right direction?
You can load the solution by using Roslyn.Services.Workspace.LoadSolution. Once you have done so, you need to go through each of the projects in dependency order, get the Compilation for the project and call Emit on it.
You can get the compilations in dependency order with code like below. (Yes, I know that having to cast to IHaveWorkspaceServices sucks. It'll be better in the next public release, I promise).
using Roslyn.Services;
using Roslyn.Services.Host;
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
static void Main(string[] args)
{
var solution = Solution.Create(SolutionId.CreateNewId()).AddCSharpProject("Foo", "Foo").Solution;
var workspaceServices = (IHaveWorkspaceServices)solution;
var projectDependencyService = workspaceServices.WorkspaceServices.GetService<IProjectDependencyService>();
var assemblies = new List<Stream>();
foreach (var projectId in projectDependencyService.GetDependencyGraph(solution).GetTopologicallySortedProjects())
{
using (var stream = new MemoryStream())
{
solution.GetProject(projectId).GetCompilation().Emit(stream);
assemblies.Add(stream);
}
}
}
}
Note1: LoadSolution still does use msbuild under the covers to parse the .csproj files and determine the files/references/compiler options.
Note2: As Roslyn is not yet language complete, there will likely be projects that don't compile successfully when you attempt this.
I also wanted to compile a full solution on the fly. Building from Kevin Pilch-Bisson's answer and Josh E's comment, I wrote code to compile itself and write it to files.
Software Used
Visual Studio Community 2015 Update 1
Microsoft.CodeAnalysis v1.1.0.0 (Installed using Package Manager Console with command Install-Package Microsoft.CodeAnalysis).
Code
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.MSBuild;
namespace Roslyn.TryItOut
{
class Program
{
static void Main(string[] args)
{
string solutionUrl = "C:\\Dev\\Roslyn.TryItOut\\Roslyn.TryItOut.sln";
string outputDir = "C:\\Dev\\Roslyn.TryItOut\\output";
if (!Directory.Exists(outputDir))
{
Directory.CreateDirectory(outputDir);
}
bool success = CompileSolution(solutionUrl, outputDir);
if (success)
{
Console.WriteLine("Compilation completed successfully.");
Console.WriteLine("Output directory:");
Console.WriteLine(outputDir);
}
else
{
Console.WriteLine("Compilation failed.");
}
Console.WriteLine("Press the any key to exit.");
Console.ReadKey();
}
private static bool CompileSolution(string solutionUrl, string outputDir)
{
bool success = true;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
Solution solution = workspace.OpenSolutionAsync(solutionUrl).Result;
ProjectDependencyGraph projectGraph = solution.GetProjectDependencyGraph();
Dictionary<string, Stream> assemblies = new Dictionary<string, Stream>();
foreach (ProjectId projectId in projectGraph.GetTopologicallySortedProjects())
{
Compilation projectCompilation = solution.GetProject(projectId).GetCompilationAsync().Result;
if (null != projectCompilation && !string.IsNullOrEmpty(projectCompilation.AssemblyName))
{
using (var stream = new MemoryStream())
{
EmitResult result = projectCompilation.Emit(stream);
if (result.Success)
{
string fileName = string.Format("{0}.dll", projectCompilation.AssemblyName);
using (FileStream file = File.Create(outputDir + '\\' + fileName))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(file);
}
}
else
{
success = false;
}
}
}
else
{
success = false;
}
}
return success;
}
}
}

Categories