I have an MSI file built from my C# Visual Studio 2010. The version is set through the Version property. I wanted to know if there is a way to determine the version without having to install the file. Currently when right click and view the properties it isn't displayed.
The following code may be helpful. But remember that you should first add a COM reference to the Microsoft Windows Installer Object Library and add the WindowsInstaller namespace to your code. The following function may be what you need.
public static string GetMsiInfo( string msiPath, string Info)
{
string retVal = string.Empty;
Type classType = Type.GetTypeFromProgID( “WindowsInstaller.Installer” );
Object installerObj = Activator.CreateInstance( classType );
Installer installer = installerObj as Installer;
// Open msi file
Database db = installer.OpenDatabase( msiPath, 0 );
// Fetch the property
string sql = String.Format(“SELECT Value FROM Property WHERE Property=’{0}’”, Info);
View view = db.OpenView( sql );
view.Execute( null );
// Read in the record
Record rec = view.Fetch();
if ( rec != null )
retVal = rec.get_StringData( 1 );
return retVal;
}
If you need the version, pass in the name of the MSI file you want, e.g.
string version = GetMsiInfo( "d:\product.msi", “ProductVersion” );
Yes - I think you need to inspect the MSI database however, which requires either some API calls or a wrapper utility.
Microsofts ORCA application should let you do this (although I've never tried it myself).
Instead of using the COM library, you can use the Microsoft.Deployment.WindowsInstaller library from the wixtoolset SDK. Once referenced, you can very similarly get the version info.
private string GetMsiInfo(string msiPath)
{
using (var database = new Microsoft.Deployment.WindowsInstaller.Database(msiPath))
{
var sql = "SELECT Value FROM Property WHERE Property ='ProductVersion'";
using (var view = database.OpenView(sql))
{
view.Execute();
using (var record = view.Fetch())
{
var version = record?.GetString(1);
return version;
}
}
}
}
I haven't found a way to get the correct assembly via nuget installer. However, after I installed the wixtoolset https://wixtoolset.org/releases/, I was able to add a reference in my project directly under assemblies -> extensions -> Microsoft.Deployment.WindowsInstaller.
Based on Gupta's answer, I added COM release calls. If you want to recreate or replace the file you accessed in your further workflow, it might be still in use and you will get an exception if the GC did not yet release the objects, so let's do this manually.
public static string GetMsiInfo(string msiPath, string info)
{
string retVal = string.Empty;
Type classType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
dynamic installer = Activator.CreateInstance(classType);
try
{
// Open msi file
var db = installer.OpenDatabase(msiPath, 0);
try
{
// Fetch the property
string sql = $"SELECT Value FROM Property WHERE Property ='{info}'";
var view = db.OpenView(sql);
try
{
view.Execute(null);
// Read in the record
var rec = view.Fetch();
if (rec != null)
retVal = rec.StringData(1);
return retVal;
}
finally
{
view.Close();
Marshal.ReleaseComObject(view);
}
}
finally
{
//db.Commit();
Marshal.ReleaseComObject(db);
}
}
finally
{
Marshal.ReleaseComObject(installer);
}
}
I think using this code, there is no need to add a COM reference or an extra namespace as mentioned by Gupta, because we use late binding here (see the dynamic).
Related
I'm trying to find out how to read/write to the extended file properties in C#
e.g. Comment, Bit Rate, Date Accessed, Category etc that you can see in Windows explorer.
Any ideas how to do this?
EDIT: I'll mainly be reading/writing to video files (AVI/DIVX/...)
For those of not crazy about VB, here it is in c#:
Note, you have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
public static void Main(string[] args)
{
List<string> arrHeaders = new List<string>();
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder;
objFolder = shell.NameSpace(#"C:\temp\testprop");
for( int i = 0; i < short.MaxValue; i++ )
{
string header = objFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
arrHeaders.Add(header);
}
foreach(Shell32.FolderItem2 item in objFolder.Items())
{
for (int i = 0; i < arrHeaders.Count; i++)
{
Console.WriteLine(
$"{i}\t{arrHeaders[i]}: {objFolder.GetDetailsOf(item, i)}");
}
}
}
Solution 2016
Add following NuGet packages to your project:
Microsoft.WindowsAPICodePack-Shell by Microsoft
Microsoft.WindowsAPICodePack-Core by Microsoft
Read and Write Properties
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
string filePath = #"C:\temp\example.docx";
var file = ShellFile.FromFilePath(filePath);
// Read and Write:
string[] oldAuthors = file.Properties.System.Author.Value;
string oldTitle = file.Properties.System.Title.Value;
file.Properties.System.Author.Value = new string[] { "Author #1", "Author #2" };
file.Properties.System.Title.Value = "Example Title";
// Alternate way to Write:
ShellPropertyWriter propertyWriter = file.Properties.GetPropertyWriter();
propertyWriter.WriteProperty(SystemProperties.System.Author, new string[] { "Author" });
propertyWriter.Close();
Important:
The file must be a valid one, created by the specific assigned software. Every file type has specific extended file properties and not all of them are writable.
If you right-click a file on desktop and cannot edit a property, you wont be able to edit it in code too.
Example:
Create txt file on desktop, rename its extension to docx. You can't
edit its Author or Title property.
Open it with Word, edit and save
it. Now you can.
So just make sure to use some try catch
Further Topic:
Microsoft Docs: Implementing Property Handlers
There's a CodeProject article for an ID3 reader. And a thread at kixtart.org that has more information for other properties. Basically, you need to call the GetDetailsOf() method on the folder shell object for shell32.dll.
This sample in VB.NET reads all extended properties:
Sub Main()
Dim arrHeaders(35)
Dim shell As New Shell32.Shell
Dim objFolder As Shell32.Folder
objFolder = shell.NameSpace("C:\tmp")
For i = 0 To 34
arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
Next
For Each strFileName In objfolder.Items
For i = 0 To 34
Console.WriteLine(i & vbTab & arrHeaders(i) & ": " & objfolder.GetDetailsOf(strFileName, i))
Next
Next
End Sub
You have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
Thank you guys for this thread! It helped me when I wanted to figure out an exe's file version. However, I needed to figure out the last bit myself of what is called Extended Properties.
If you open properties of an exe (or dll) file in Windows Explorer, you get a Version tab, and a view of Extended Properties of that file. I wanted to access one of those values.
The solution to this is the property indexer FolderItem.ExtendedProperty and if you drop all spaces in the property's name, you'll get the value. E.g. File Version goes FileVersion, and there you have it.
Hope this helps anyone else, just thought I'd add this info to this thread. Cheers!
GetDetailsOf() Method - Retrieves details about an item in a folder. For example, its size, type, or the time of its last modification. File Properties may vary based on the Windows-OS version.
List<string> arrHeaders = new List<string>();
Shell shell = new ShellClass();
Folder rFolder = shell.NameSpace(_rootPath);
FolderItem rFiles = rFolder.ParseName(filename);
for (int i = 0; i < short.MaxValue; i++)
{
string value = rFolder.GetDetailsOf(rFiles, i).Trim();
arrHeaders.Add(value);
}
Jerker's answer is little simpler. Here's sample code which works from MS:
var folder = new Shell().NameSpace(folderPath);
foreach (FolderItem2 item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
For those who can't reference shell32 statically, you can invoke it dynamically like this:
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
dynamic shellApp = Activator.CreateInstance(shellAppType);
var folder = shellApp.NameSpace(folderPath);
foreach (var item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
After looking at a number of solutions on this thread and elsewhere
the following code was put together. This is only to read a property.
I could not get the
Shell32.FolderItem2.ExtendedProperty function to work, it is supposed
to take a string value and return the correct value and type for that
property... this was always null for me and developer reference resources were very thin.
The WindowsApiCodePack seems
to have been abandoned by Microsoft which brings us the code below.
Use:
string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
Will return you the value of the extended property you want as a
string for the given file and property name.
Only loops until it found the specified property - not until
all properties are discovered like some sample code
Will work on Windows versions like Windows server 2008 where you will get the error "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'" if just trying to create the Shell32 Object normally.
public static string GetExtendedFileProperty(string filePath, string propertyName)
{
string value = string.Empty;
string baseFolder = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
//Method to load and execute the Shell object for Windows server 8 environment otherwise you get "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'"
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellAppType);
Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
//Parsename will find the specific file I'm looking for in the Shell32.Folder object
Shell32.FolderItem folderitem = shellFolder.ParseName(fileName);
if (folderitem != null)
{
for (int i = 0; i < short.MaxValue; i++)
{
//Get the property name for property index i
string property = shellFolder.GetDetailsOf(null, i);
//Will be empty when all possible properties has been looped through, break out of loop
if (String.IsNullOrEmpty(property)) break;
//Skip to next property if this is not the specified property
if (property != propertyName) continue;
//Read value of property
value = shellFolder.GetDetailsOf(folderitem, i);
}
}
//returns string.Empty if no value was found for the specified property
return value;
}
Here is a solution for reading - not writing - the extended properties based on what I found on this page and at help with shell32 objects.
To be clear this is a hack. It looks like this code will still run on Windows 10 but will hit on some empty properties. Previous version of Windows should use:
var i = 0;
while (true)
{
...
if (String.IsNullOrEmpty(header)) break;
...
i++;
On Windows 10 we assume that there are about 320 properties to read and simply skip the empty entries:
private Dictionary<string, string> GetExtendedProperties(string filePath)
{
var directory = Path.GetDirectoryName(filePath);
var shell = new Shell32.Shell();
var shellFolder = shell.NameSpace(directory);
var fileName = Path.GetFileName(filePath);
var folderitem = shellFolder.ParseName(fileName);
var dictionary = new Dictionary<string, string>();
var i = -1;
while (++i < 320)
{
var header = shellFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header)) continue;
var value = shellFolder.GetDetailsOf(folderitem, i);
if (!dictionary.ContainsKey(header)) dictionary.Add(header, value);
Console.WriteLine(header +": " + value);
}
Marshal.ReleaseComObject(shell);
Marshal.ReleaseComObject(shellFolder);
return dictionary;
}
As mentioned you need to reference the Com assembly Interop.Shell32.
If you get an STA related exception, you will find the solution here:
Exception when using Shell32 to get File extended properties
I have no idea what those properties names would be like on a foreign system and couldn't find information about which localizable constants to use in order to access the dictionary. I also found that not all the properties from the Properties dialog were present in the dictionary returned.
BTW this is terribly slow and - at least on Windows 10 - parsing dates in the string retrieved would be a challenge so using this seems to be a bad idea to start with.
On Windows 10 you should definitely use the Windows.Storage library which contains the SystemPhotoProperties, SystemMusicProperties etc.
https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties
And finally, I posted a much better solution that uses WindowsAPICodePack there
I'm not sure what types of files you are trying to write the properties for but taglib-sharp is an excellent open source tagging library that wraps up all this functionality nicely. It has a lot of built in support for most of the popular media file types but also allows you to do more advanced tagging with pretty much any file.
EDIT: I've updated the link to taglib sharp. The old link no longer worked.
EDIT: Updated the link once again per kzu's comment.
What is the best way to access Microsoft Access Database object's Properties (like in CurrentDb.Properties) from C# in Visual Studio 2010?
(Not essential: In fact I want to get rid of Replication in a few dozen databases "on demand". Replication is not in use for a few years, and it was OK for MS Access prior to 2013. Access 2013 rejects databases with this feature.)
You can iterate over and modify the properties in the Access database by using the Access DAO object library.
The following code iterates over the properties in the database as well over the different containers and its properties as well over the Documents and its properties in the Databases container. The output is written to the Debug Output window.
After that I pick a Property from different property collections and change it's value. Do note that using the indexer on the collection will throw an exception if the property doesn't exist.
Make sure you have a reference to the Primary Interop Assembly for Microsoft Office 12.0 Access database engine Object Library (your version migth vary) so that you can have the following in your using statements:
using Microsoft.Office.Interop.Access.Dao;
Your method would go like this:
// Open a database
var dbe = new DBEngine();
var db = dbe.OpenDatabase(#"C:\full\path\to\your\db\scratch.accdb");
// Show database properties
DumpProperties(db.Properties);
// show all containers
foreach (Container c in db.Containers)
{
Debug.WriteLine("{0}:{1}", c.Name, c.Owner);
DumpProperties(c.Properties);
}
//Show documents and properties for a specific container
foreach (Document d in db.Containers["Databases"].Documents)
{
Debug.WriteLine("--------- " + d.Name);
DumpProperties(d.Properties);
}
// set a property on the Database
Property prop = db.
Properties["NavPane Width"];
prop.Value = 300;
// set a property on the UserDefined document
Property userdefProp = db
.Containers["Databases"]
.Documents["UserDefined"]
.Properties["ReplicateProject"];
userdefProp.Value = true;
Property dumper helper
private static void DumpProperties(Properties props)
{
foreach (Property p in props)
{
object val = null;
try
{
val = (object)p.Value;
}
catch (Exception e)
{
val = e.Message;
}
Debug.WriteLine(
"{0} ({2}) = {1}",
p.Name,
val,
(DataTypeEnum) p.Type);
}
}
I used this to overcome an exception being thrown on dynamic types (as the Value property turns out to be)
I´m using C# Microsoft.Build.Evaluation.Project class to retrieve specific information from a VS project file like the TargetFramework, AssemblyName...etc. But I'm unable to find the way or the property name to identify if the project is a Web Project. For a VS Project in framework 1.1 I'm using Xml parsing like this.
string path = "C:\\Test.csproj";
XElement root = XElement.Load(path);
var type = from el in root.Elements("CSHARP")
select el.Attribute("ProjectType");
if (type.Count() > 0)
{
// Local or Web
return((XAttribute)type.First()).Value);
}
I'll appreciate if someone could give me the name of the property or another suggestion to solve this problem.
Thanks in advance.
My full method code
public bool IsWeb(string path)
{
bool isWeb = false;
try
{
Project project = new Project(path);
// I dont know the name of the propety to identify if its a web project
//string type = project.GetPropertyValue();
//if (type.Equals("Web"))
//{
// isWeb = true;
//}
}
catch (InvalidProjectFileException)
{
//It's fw 1.1
XElement root = XElement.Load(path);
var type = from el in root.Elements("CSHARP")
select el.Attribute("ProjectType");
if (type.Count() > 0)
{
if ((((XAttribute)type.First()).Value).Equals("Web"))
{
isWeb = true;
}
}
}
return isWeb;
}
I went through a few project files. In one of my mvc projects, I found this node in the PropertyGroup node <MvcBuildViews> it's definitely not an end-all answer to your question but it looks like more information than you were working with.
You can check if project type guid belongs to one of the known types.
I'm trying to find out how to read/write to the extended file properties in C#
e.g. Comment, Bit Rate, Date Accessed, Category etc that you can see in Windows explorer.
Any ideas how to do this?
EDIT: I'll mainly be reading/writing to video files (AVI/DIVX/...)
For those of not crazy about VB, here it is in c#:
Note, you have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
public static void Main(string[] args)
{
List<string> arrHeaders = new List<string>();
Shell32.Shell shell = new Shell32.Shell();
Shell32.Folder objFolder;
objFolder = shell.NameSpace(#"C:\temp\testprop");
for( int i = 0; i < short.MaxValue; i++ )
{
string header = objFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
arrHeaders.Add(header);
}
foreach(Shell32.FolderItem2 item in objFolder.Items())
{
for (int i = 0; i < arrHeaders.Count; i++)
{
Console.WriteLine(
$"{i}\t{arrHeaders[i]}: {objFolder.GetDetailsOf(item, i)}");
}
}
}
Solution 2016
Add following NuGet packages to your project:
Microsoft.WindowsAPICodePack-Shell by Microsoft
Microsoft.WindowsAPICodePack-Core by Microsoft
Read and Write Properties
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
string filePath = #"C:\temp\example.docx";
var file = ShellFile.FromFilePath(filePath);
// Read and Write:
string[] oldAuthors = file.Properties.System.Author.Value;
string oldTitle = file.Properties.System.Title.Value;
file.Properties.System.Author.Value = new string[] { "Author #1", "Author #2" };
file.Properties.System.Title.Value = "Example Title";
// Alternate way to Write:
ShellPropertyWriter propertyWriter = file.Properties.GetPropertyWriter();
propertyWriter.WriteProperty(SystemProperties.System.Author, new string[] { "Author" });
propertyWriter.Close();
Important:
The file must be a valid one, created by the specific assigned software. Every file type has specific extended file properties and not all of them are writable.
If you right-click a file on desktop and cannot edit a property, you wont be able to edit it in code too.
Example:
Create txt file on desktop, rename its extension to docx. You can't
edit its Author or Title property.
Open it with Word, edit and save
it. Now you can.
So just make sure to use some try catch
Further Topic:
Microsoft Docs: Implementing Property Handlers
There's a CodeProject article for an ID3 reader. And a thread at kixtart.org that has more information for other properties. Basically, you need to call the GetDetailsOf() method on the folder shell object for shell32.dll.
This sample in VB.NET reads all extended properties:
Sub Main()
Dim arrHeaders(35)
Dim shell As New Shell32.Shell
Dim objFolder As Shell32.Folder
objFolder = shell.NameSpace("C:\tmp")
For i = 0 To 34
arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
Next
For Each strFileName In objfolder.Items
For i = 0 To 34
Console.WriteLine(i & vbTab & arrHeaders(i) & ": " & objfolder.GetDetailsOf(strFileName, i))
Next
Next
End Sub
You have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.
Thank you guys for this thread! It helped me when I wanted to figure out an exe's file version. However, I needed to figure out the last bit myself of what is called Extended Properties.
If you open properties of an exe (or dll) file in Windows Explorer, you get a Version tab, and a view of Extended Properties of that file. I wanted to access one of those values.
The solution to this is the property indexer FolderItem.ExtendedProperty and if you drop all spaces in the property's name, you'll get the value. E.g. File Version goes FileVersion, and there you have it.
Hope this helps anyone else, just thought I'd add this info to this thread. Cheers!
GetDetailsOf() Method - Retrieves details about an item in a folder. For example, its size, type, or the time of its last modification. File Properties may vary based on the Windows-OS version.
List<string> arrHeaders = new List<string>();
Shell shell = new ShellClass();
Folder rFolder = shell.NameSpace(_rootPath);
FolderItem rFiles = rFolder.ParseName(filename);
for (int i = 0; i < short.MaxValue; i++)
{
string value = rFolder.GetDetailsOf(rFiles, i).Trim();
arrHeaders.Add(value);
}
Jerker's answer is little simpler. Here's sample code which works from MS:
var folder = new Shell().NameSpace(folderPath);
foreach (FolderItem2 item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
For those who can't reference shell32 statically, you can invoke it dynamically like this:
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
dynamic shellApp = Activator.CreateInstance(shellAppType);
var folder = shellApp.NameSpace(folderPath);
foreach (var item in folder.Items())
{
var company = item.ExtendedProperty("Company");
var author = item.ExtendedProperty("Author");
// Etc.
}
After looking at a number of solutions on this thread and elsewhere
the following code was put together. This is only to read a property.
I could not get the
Shell32.FolderItem2.ExtendedProperty function to work, it is supposed
to take a string value and return the correct value and type for that
property... this was always null for me and developer reference resources were very thin.
The WindowsApiCodePack seems
to have been abandoned by Microsoft which brings us the code below.
Use:
string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
Will return you the value of the extended property you want as a
string for the given file and property name.
Only loops until it found the specified property - not until
all properties are discovered like some sample code
Will work on Windows versions like Windows server 2008 where you will get the error "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'" if just trying to create the Shell32 Object normally.
public static string GetExtendedFileProperty(string filePath, string propertyName)
{
string value = string.Empty;
string baseFolder = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
//Method to load and execute the Shell object for Windows server 8 environment otherwise you get "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'"
Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
Object shell = Activator.CreateInstance(shellAppType);
Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
//Parsename will find the specific file I'm looking for in the Shell32.Folder object
Shell32.FolderItem folderitem = shellFolder.ParseName(fileName);
if (folderitem != null)
{
for (int i = 0; i < short.MaxValue; i++)
{
//Get the property name for property index i
string property = shellFolder.GetDetailsOf(null, i);
//Will be empty when all possible properties has been looped through, break out of loop
if (String.IsNullOrEmpty(property)) break;
//Skip to next property if this is not the specified property
if (property != propertyName) continue;
//Read value of property
value = shellFolder.GetDetailsOf(folderitem, i);
}
}
//returns string.Empty if no value was found for the specified property
return value;
}
Here is a solution for reading - not writing - the extended properties based on what I found on this page and at help with shell32 objects.
To be clear this is a hack. It looks like this code will still run on Windows 10 but will hit on some empty properties. Previous version of Windows should use:
var i = 0;
while (true)
{
...
if (String.IsNullOrEmpty(header)) break;
...
i++;
On Windows 10 we assume that there are about 320 properties to read and simply skip the empty entries:
private Dictionary<string, string> GetExtendedProperties(string filePath)
{
var directory = Path.GetDirectoryName(filePath);
var shell = new Shell32.Shell();
var shellFolder = shell.NameSpace(directory);
var fileName = Path.GetFileName(filePath);
var folderitem = shellFolder.ParseName(fileName);
var dictionary = new Dictionary<string, string>();
var i = -1;
while (++i < 320)
{
var header = shellFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header)) continue;
var value = shellFolder.GetDetailsOf(folderitem, i);
if (!dictionary.ContainsKey(header)) dictionary.Add(header, value);
Console.WriteLine(header +": " + value);
}
Marshal.ReleaseComObject(shell);
Marshal.ReleaseComObject(shellFolder);
return dictionary;
}
As mentioned you need to reference the Com assembly Interop.Shell32.
If you get an STA related exception, you will find the solution here:
Exception when using Shell32 to get File extended properties
I have no idea what those properties names would be like on a foreign system and couldn't find information about which localizable constants to use in order to access the dictionary. I also found that not all the properties from the Properties dialog were present in the dictionary returned.
BTW this is terribly slow and - at least on Windows 10 - parsing dates in the string retrieved would be a challenge so using this seems to be a bad idea to start with.
On Windows 10 you should definitely use the Windows.Storage library which contains the SystemPhotoProperties, SystemMusicProperties etc.
https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties
And finally, I posted a much better solution that uses WindowsAPICodePack there
I'm not sure what types of files you are trying to write the properties for but taglib-sharp is an excellent open source tagging library that wraps up all this functionality nicely. It has a lot of built in support for most of the popular media file types but also allows you to do more advanced tagging with pretty much any file.
EDIT: I've updated the link to taglib sharp. The old link no longer worked.
EDIT: Updated the link once again per kzu's comment.
I'd like to create a small application that can collect system information (Win32_blablabla) using WinRM as opposed to WMI. How can i do that from C#?
The main goal is to use WS-Man (WinRm) as opposed to DCOM (WMI).
I guess the easiest way would be to use WSMAN automation. Reference wsmauto.dll from windwos\system32 in your project:
then, code below should work for you. API description is here: msdn: WinRM C++ API
IWSMan wsman = new WSManClass();
IWSManConnectionOptions options = (IWSManConnectionOptions)wsman.CreateConnectionOptions();
if (options != null)
{
try
{
// options.UserName = ???;
// options.Password = ???;
IWSManSession session = (IWSManSession)wsman.CreateSession("http://<your_server_name>/wsman", 0, options);
if (session != null)
{
try
{
// retrieve the Win32_Service xml representation
var reply = session.Get("http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_Service?Name=winmgmt", 0);
// parse xml and dump service name and description
var doc = new XmlDocument();
doc.LoadXml(reply);
foreach (var elementName in new string[] { "p:Caption", "p:Description" })
{
var node = doc.GetElementsByTagName(elementName)[0];
if (node != null) Console.WriteLine(node.InnerText);
}
}
finally
{
Marshal.ReleaseComObject(session);
}
}
}
finally
{
Marshal.ReleaseComObject(options);
}
}
hope this helps, regards
I've got an article that describes an easy way to run Powershell through WinRM from .NET at http://getthinktank.com/2015/06/22/naos-winrm-windows-remote-management-through-net/.
The code is in a single file if you want to just copy it and it's also a NuGet package that includes the reference to System.Management.Automation.
It auto manages trusted hosts, can run script blocks, and also send files (which isn't really supported but I created a work around). The returns are always the raw objects from Powershell.
// this is the entrypoint to interact with the system (interfaced for testing).
var machineManager = new MachineManager(
"10.0.0.1",
"Administrator",
MachineManager.ConvertStringToSecureString("xxx"),
true);
// will perform a user initiated reboot.
machineManager.Reboot();
// can run random script blocks WITH parameters.
var fileObjects = machineManager.RunScript(
"{ param($path) ls $path }",
new[] { #"C:\PathToList" });
// can transfer files to the remote server (over WinRM's protocol!).
var localFilePath = #"D:\Temp\BigFileLocal.nupkg";
var fileBytes = File.ReadAllBytes(localFilePath);
var remoteFilePath = #"D:\Temp\BigFileRemote.nupkg";
machineManager.SendFile(remoteFilePath, fileBytes);
Hope this helps, I've been using this for a while with my automated deployments. Please leave comments if you find issues.
I would like to note that this shows an interop error by default in Visual Studio 2010.
c.f. http://blogs.msdn.com/b/mshneer/archive/2009/12/07/interop-type-xxx-cannot-be-embedded-use-the-applicable-interface-instead.aspx
There appear to be two ways to solve this. This first is documented in the article listed above and appears to be the correct way to handle the problem. The pertinent changes for this example is:
WSMan wsManObject = new WSMan();
This is in lieu of IWSMan wsman = new WSManClass(); which will throw the error.
The second resolution is to go to the VS2010—>Solution Explorer—>Solution—>Project—>References and select WSManAutomation. Right click or hit Alt-Enter to access the properties. Change the value of the "Embed Interop Types" property of the wsmauto reference.