Doing some explorer/shell stuff on Win8/64bit with WindowsAPICodePack. Having some problems with the propertysystem causing an AccessViolationException when iterating over fileproperties with x64 platform target. Seems to be some problem in PropVariant.cs. Switching to x86 fixes the problems, but causes incomplete directory listings (f.e. "etc" missing in system32/drivers). Any ideas?
using System;
using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;
namespace ApiCodepackTest
{
class Program
{
const string path = #"c:\windows\system32\drivers";
static void Main(string[] args)
{
var shellObject = (ShellFolder)ShellObject.FromParsingName(path);
showProperties(shellObject);
showItems(shellObject);
Console.ReadLine();
}
static void showProperties(ShellFolder folder)
{
var sys = folder.Properties.System;
foreach (var prop in sys.GetType().GetProperties())
{
try
{
var shellProperty = prop.GetValue(sys) as IShellProperty;
if (shellProperty != null && shellProperty.ValueAsObject != null)
Console.WriteLine(shellProperty.CanonicalName + " " + shellProperty.ValueAsObject);
}
catch{} //you should not pass!
}
}
static void showItems(ShellFolder folder)
{
foreach (var i in folder)
Console.WriteLine(i.Name);
}
}
I'm not really into pinvoke and c++ stuff, but I've recompiled the source with a little fix in PropVariant.cs :
//[FieldOffset(12)] original
[FieldOffset(16)]
IntPtr _ptr2;
and this fixed the issue
For anyone else finding this question, you can now avoid recompiling the source to achieve this bug fix.
As discussed in this answer, the open source WindowsAPICodePack is now being maintained by a different developer (Pierre Sprimont). It has been updated as recently as July 2022 and is available on GitHub or as a series of Nuget packages directly through Visual Studio. The developer's website discussing his fork of the project is here, which has links to each Github repo and Nuget page.
The AccessViolationException bug in PropVariant.cs discussed in the accepted answer by #Aleksey has been fixed, along with many other bug fixes and improvements (including support for .NET Core).
Related
GitLab ships a wrong C# code encoding. Maybe someone might know a reason for this and a way to fix this?
I am working on a project for something like 3 months. All that time I worked from the office, so I did not have to deploy the project locally at my home computer, only at the computer on work. There on work I somehow managed to deploy a project successfully, but here at home while deploying and setting everything up I come across errors in the project.
All due to that piece of code:
public static object ParseReferred(this JObject item, JsonSerializer serializer, RetypeEnum retype)
{
if (item == null)
{
return null;
}
if (retype == RetypeEnum.)
{
return serializer.Deserialize(item.CreateReader(), typeof(TypeRegNumber));
}
return serializer.Deserialize(item.CreateReader(), typeof(TypeTaskNumber));
}
Here is how I see that piece in GitLab:
Here is the GitLab raw view:
Raw code:
public static object ParseReferred(this JObject item, JsonSerializer serializer, RetypeEnum retype)
{
if (item == null)
{
return null;
}
if (retype == RetypeEnum.�)
{
return serializer.Deserialize(item.CreateReader(), typeof(TypeRegNumber));
}
return serializer.Deserialize(item.CreateReader(), typeof(TypeTaskNumber));
}
I suspect that the only one who could have helped me with the issue was my team lead. So, I asked him what I could do about it. He suggested the following: clearing NuGet cache, restoring NuGets, cleaning the solution, and then rebuilding the solution. I followed the steps, but with no success. I feel myself very stupid, I must have missed something.
Is it true that the only way to fix the issue is to communicate with someone who has known the codebase for sometime? Or are there other possible ways to research the issue? I am really stuck, I do not know what I can try to research the issue.
When I hover over the symbol here is what I see:
'RetypeEnum' does not contain a definition for 'ä'
When I open the RetypeEnum definition (by Ctrl+click on the RetypeEnum) here is what I see:
#region Assembly foo, Version=2.0.8.0, Culture=neutral, PublicKeyToken=null
// foo.dll
#endregion
using System.CodeDom.Compiler;
namespace foo
{
[GeneratedCode("xsd", "4.6.1055.0")]
public enum RetypeEnum
{
д = 0,
з = 1
}
}
Try to check out code files line endings.
I have a c# library which provides some functionallity to upload data onto connected (android) devices. The dll itself gets exported via UnmangedExports to be used by an delphi application.
Here is the function which gets called by the delphi application:
[DllExport]
[return: MarshalAs(UnmanagedType.LPStr)]
public static string getDevices()
{
try
{
var devices = string.Empty;
var collection = new PortableDeviceCollection();
collection.Refresh();
foreach (var device in collection)
{
device.Connect();
if (devices != string.Empty)
{
devices += ";";
}
devices += device.FriendlyName;
device.Disconnect();
}
return devices;
}
catch (Exception e)
{
SomeClass.WriteErrorToLogFile(e);
return "ERROR";
}
}
Here is the class PortableDeviceCollection:
public class PortableDeviceCollection : Collection<PortableDevice>
{
private readonly PortableDeviceApiLib.PortableDeviceManagerClass _deviceManager;
public PortableDeviceCollection()
{
this._deviceManager = new PortableDeviceApiLib.PortableDeviceManagerClass();
}
public bool Refresh()
{
this._deviceManager.RefreshDeviceList();
// Determine how many WPD devices are connected
var deviceIds = new string[1];
uint count = 1;
this._deviceManager.GetDevices(null, ref count);
if (count > 0)
{
// Retrieve the device id for each connected device
deviceIds = new string[count];
this._deviceManager.GetDevices(deviceIds, ref count);
foreach (var deviceId in deviceIds)
{
Add(new PortableDevice(deviceId));
}
return true;
}
else
return false;
}
}
I can create the dll with visual studio and use this inside of the delphi application. When the delphi application calls the getDevices() function, i get an error on the instantiation of the PortableDeviceCollection class:
The file or assembly "Interop.PortableDeviceApiLib, Version = 1.0.0.0,
Culture = neutral, PublicKeyToken = null" or a dependency of it was
not found. The assembly is created by a runtime that is more recent
than the currently loaded runtime and can not be loaded.
ProductXY.PortableDeviceCollection..ctor()
ProductXY.ProductXYMain.getDevices()
The targetframework for the c# project is set to .Net Framework 4. Using any lower version i get an error when i try to compile the project:
The primary reference "Interop.PortableDeviceApiLib" could not be
resolved because it has an indirect dependency on the .NET Framework
assembly "mscorlib, version = 4.0.0.0, Culture = neutral,
PublicKeyToken = b77a5c561934e089", which is a higher version 4.0.0.0
than version 2.0.0.0 in the current target framework.
Please note. I have neither written the c# library nor the delphi application. Both have worked together for years. Now i have to add a functionallity to the c# library. I have not added any code to the project. I just tried to compile it again and use the dll. The only thing i did was updating the RGiesecke.DLLExport.Metadata via NuGet Packetmanager. Without updating i got an error
"Microsoft.Build.Utilities.ToolLocationHelper could not find
ildasm.exe"
I am aware of this Enumerating Windows Portable Devices in C# question. But my error is thrown before the code which is treaded by the question is reached. I still tried the solution to the question, but the action (deassamble, find and replace in the dll) which is described in the answere has already been done (otherwise my code would not have compiled).
The error message doesn't make sense to me. Interop.PortableDeviceApiLib is a COM-Lib which is not available for download in different framework-versions. I think I am missing something here.
Can anyone help me?
I was finally able to solve this problem. To be honest I don't know what finally solved this. For every one who stumbles up on this, here are the things i tried to fix this problem. They are in no specific order (since i tried everything multiple times):
Updating the RGiesecke.DLLExport packet
Changing the plattform in the Konfigurations-Manager to x86
Disassamble, edit and reassable the Interop.PortableDeviceApiLib like in this question (answeres of Christophe Geers and Bruno Klein)
Delete the reference to the Interop.PortableDeviceApiLib
Delete the reference to the Interop.PortableDeviceTypesLib
Readding the reference to the Interop.PortableDeviceApiLib
Readding the reference to the Interop.PortableDeviceTypesLib
Rebuild the project
Setting the Interoptyp embeddet on both to false (I found various statements to NOT do this, but the project was set up like this when i got it and it worked (be carefull)) on both Interop-Libs.
At least this helped me.
As the title says, I currently cannot find any answers for this problem.
I'm currently using C# to do the checking.
Mostly answers are for version 2013 and below.
If you guys have any suggestion, do share.
Thanks.
The basic answer is: Do not bother if it is there at runtime. Put it into your installer. Let it be executed as part of the normal "Elevated Rights required" Installation process.
If it was already there, the Installer will just do nothing.
If it was not there, it will now run under Administrator rights and be there afterwards.
If it was damaged, hopefully the installer will fix the installation.
If that somehow did not work, there is nothing your puny usercode can do to fix it at runtime. It is the administrators job.
Every installer does that, not the least of which are the Visual Studio and SQL Server ones. The only slight modification I know off is Steam, which runs those installers under Elevated rights before a program is executed for the first time. But that is just "making certain it is there" from a slightly different angle.
I only know one kind of programmer that does not do that: The one never tested his program on a freshly installed Windows (Virtual Machines work) and thus is not aware the requirements even exists (because every other program installs VC Redist and current DX versions).
It is hard to get all registry values for VC 2015 so I have written a small function which will go through all dependencies and match on specified version(C++ 2015 x86)
public static bool IsVC2015x86Installed()
{
string dependenciesPath = #"SOFTWARE\Classes\Installer\Dependencies";
using (RegistryKey dependencies = Registry.LocalMachine.OpenSubKey(dependenciesPath))
{
if (dependencies == null) return false;
foreach (string subKeyName in dependencies.GetSubKeyNames().Where(n => !n.ToLower().Contains("dotnet") && !n.ToLower().Contains("microsoft")))
{
using (RegistryKey subDir = Registry.LocalMachine.OpenSubKey(dependenciesPath + "\\" + subKeyName))
{
var value = subDir.GetValue("DisplayName")?.ToString() ?? null;
if (string.IsNullOrEmpty(value)) continue;
if (Regex.IsMatch(value, #"C\+\+ 2015.*\(x86\)")) //here u can specify your version.
{
return true;
}
}
}
}
return false;
}
Dependencies:
using System;
using System.Text.RegularExpressions;
using Microsoft.Win32;
EDIT:
C++ 2017 is valid replacement for C++ 2015 so if you want to check it as well edit the regex like this:
Regex.IsMatch(value, #"C\+\+ (2015|2017).*\(x86\)")
As mentioned in comments and answer, one way is to let the installer run and see if a more recent version is installed. The installer will display an error and quit.
If the installer is run with /quiet flag, then no error is being displayed. Other way is to simply check the registry values:
HKEY_LOCAL_MACHINE\SOFTWARE[\Wow6432Node]\Microsoft\VisualStudio\vs-version\VC\Runtimes\{x86|x64|ARM} key
Here vs-version is the version of Visual Studio (14.0 for Visual Studio 2015 and 2017)
The key is ARM, x86 or x64 depending upon the platform.
The version number is stored in the REG_SZ string value Version. If the package being installed is less than the version of the installed, then no need to install.
More info here: https://learn.microsoft.com/en-us/cpp/ide/redistributing-visual-cpp-files?view=vs-2017
I modified #ssamko 's Version to check for x64 and x86 redistributables. Hope it will help someone:
public static bool IsVC2015Installed()
{
string dependenciesPath = #"SOFTWARE\Classes\Installer\Dependencies";
using (RegistryKey dependencies = Registry.LocalMachine.OpenSubKey(dependenciesPath))
{
if (dependencies == null) return false;
foreach (string subKeyName in dependencies.GetSubKeyNames().Where(n => !n.ToLower().Contains("dotnet") && !n.ToLower().Contains("microsoft")))
{
using (RegistryKey subDir = Registry.LocalMachine.OpenSubKey(dependenciesPath + "\\" + subKeyName))
{
var value = subDir.GetValue("DisplayName")?.ToString() ?? null;
if (string.IsNullOrEmpty(value))
{
continue;
}
if (Environment.Is64BitOperatingSystem)
{
if (Regex.IsMatch(value, #"C\+\+ 2015.*\((x64|x86)\)"))
{
return true;
}
}
else
{
if (Regex.IsMatch(value, #"C\+\+ 2015.*\(x86\)"))
{
return true;
}
}
}
}
}
return false;
}
Dependencies:
using System;
using System.Text.RegularExpressions;
using Microsoft.Win32;
I am creating an app that will add some files and folders to an existing project which is loaded in Visual studio. That works but it will always pop up a message telling the user (me) to refresh the project to show the new files.
When using Entity Framework and adding a migration it will add a file to a project that is currently loaded and it doesn't ask the user. I would like to be able to do the same thing.
Is this possible? If someone doesn't know the answer do they know how I might delve into EF and add migration to see how they do it?
Here is the code that I am using to edit the project file:
using Microsoft.Build.Evaluation;
internal class ProjectFileHandler
{
private void AddMigrationFolder(Project project, string projectFileLocation, string name)
{
var loc = Path.Combine(projectFileLocation.Substring(0, projectFileLocation.LastIndexOf("\\")), name);
project.AddItem("Folder", loc);
}
internal void AddMigrationFile(string projectfileLocation, string migrationFolderName, string fileLocation)
{
var project = new Project(projectfileLocation);
AddMigrationFolder(project, projectfileLocation, migrationFolderName);
project.AddItem("Compile", fileLocation);
project.Save();
}
}
Pretty old, but I'll leave a comment. Maybe it helps someone else.
The problem is the "project.Save();"
The solution recognizes this as an "external" change.
Try your code without this line.
Even older, but I had to solve this problem today, so for what it's worth, here's what I did.
Firstly, as far as I can tell, you can't update a project using Microsoft.Build.Evaluation without Visual Studio prompting the user to reload it. You have to call project.Save() to write your updates to the project XML, and Visual Studio 'sees' the file change and displays the prompt.
What you can do - and this looks to be what Entity Framework does, albeit with Powershell - is use EnvDTE instead of Microsoft.Build.Evaluation. EnvDTE is available on NuGet and the latest version supports .NET Framework 4.5 and .NET Standard 2, so it should be pretty widely compatible.
With EnvDTE you get a reference to a Project object and call AddFromFile(absoluteFilePath) - there are also other Add() methods if needed.
If you have the full file path to your project, you can get a project file using:
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE");
var project = dte
.Solution
.EnumerateProjects()
.First(p => p.FullName == fullPathToProject);
...where EnumerateProjects() is the following extension method:
using EnvDTE;
using System.Collections;
using System.Collections.Generic;
internal static class EnvDteExtensions
{
public static IEnumerable<Project> EnumerateProjects(
this Solution solution)
{
return Enumerate(solution.Projects);
}
private static IEnumerable<Project> Enumerate(IEnumerable items)
{
foreach (var item in items)
{
var candidateProject = item;
if (candidateProject is ProjectItem projectItem &&
projectItem.SubProject != null)
{
candidateProject = projectItem.SubProject;
}
if (!(candidateProject is Project project))
{
continue;
}
yield return project;
try
{
if (project.ProjectItems == null)
{
continue;
}
}
catch
{
continue;
}
foreach (var subProject in Enumerate(project.ProjectItems))
{
yield return subProject;
}
}
}
}
A Project in EnvDTE might actually be a project, or it might be a solution folder containing projects - the candidateProject is ProjectItem projectItem && projectItem.SubProject != null check handles the latter scenario.
EnvDTE's Project.AddFromFile() pops the file into Solution Explorer with no project reload prompt.
Finally, AddFromFile() seems to be idempotent, so you can spam the same file into the project without worrying if it already exists.
I was under the impression Mono's compiler was usable in Microsoft.NET
edit: updated blog posting here that I originally missed that explains some of it (is consistent with Justin's answers)
I created a simple class to try to use it
[TestFixture]
class Class1
{
[Test]
public void EXPR()
{
Evaluator.Run("using System;");
int sum = (int)Evaluator.Evaluate("1+2");
}
}
And a project in Visual Studio 2010 that references C:\Program Files (x86)\Mono-2.10.1\lib\mono\4.0\Mono.CSharp.dll.
However when I try to run this task I get the following exception, thrown at the Evaluator.Run call:
System.TypeInitializationException was unhandled by user code
Message=The type initializer for 'Mono.CSharp.Evaluator' threw an exception.
Source=Mono.CSharp
TypeName=Mono.CSharp.Evaluator
StackTrace:
at Mono.CSharp.Evaluator.Run(String statement)
at Experiments.Class1.EXPR() in W:\Experiments\Class1.cs:line 16
InnerException: System.TypeLoadException
Message=Method 'Mono.CSharp.Location.ToString()' is security transparent, but is a member of a security critical type.
Source=Mono.CSharp
TypeName=Mono.CSharp.Location.ToString()
StackTrace:
at Mono.CSharp.Evaluator..cctor()
InnerException:
A google confirms one other person asking this question but no answer. I tried to start reading the microsoft article on security transparent code but got confused quite quickly. Would someone be able to suggest a quick workaround to allow me to use this? And possibly summarise the security implications, if any, to me (in the context of my situation - in the future I hope to package it with a thick client application, to be used both internally and by end-users)
It has worked under .NET since April of last year.
Small point but I notice you are missing a semi-colon in your expression for sum.
int sum = (int)Evaluator.Evaluate("1+2;");
I only have Mono 2.11 (from git) at the moment and they have changed to using a multi-instance version of the compiler instead of the static version. So, my code looks a little different:
using System;
using Mono.CSharp;
namespace REPLtest
{
class MainClass
{
public static void Main (string[] args)
{
var r = new Report (new ConsoleReportPrinter ());
var cmd = new CommandLineParser (r);
var settings = cmd.ParseArguments (args);
if (settings == null || r.Errors > 0)
Environment.Exit (1);
var evaluator = new Evaluator (settings, r);
evaluator.Run("using System;");
int sum = (int) evaluator.Evaluate("1+2;");
Console.WriteLine ("The sum of 1 + 2 is {0}", sum);
}
}
}
EDIT: I guess I should confirm that I did in fact successfully execute this on .NET 4 (using Visual C# Express 2010 on Windows XP)
EDIT AGAIN: If you have Visual Studio, you can download the latest version of Mono.CSharp and compile it yourself. There is a .sln (solution file) included with the source so you can build it on Windows without Mono. The resulting assembly would run the code above. Miguel has a post explaining the new Mono.CSharp here.
FINAL EDIT: I uploaded the compiled Mono.CSharp.dll assembly that I actually used here. Include it as a reference to compile the code above.
It looks like this is a bug in Mono.
.NET 4 abandoned Code Access Security but kept the concept of Security Transparent Code. In a nutshell, low-level code that does stuff, like call unmanaged code, must be "security critical". Application level code is marked "transparent". "Transparent" code cannot call into "security critical" code.
It sounds like Mono.CSharp.Location.ToString() needs to be marked with the [SecuritySafeCritical] attribute if you want the Mono 2.10 code to work with .NET 4. Maybe even better would be marking all of Mono.CSharp as SecuritySafeCritical.
http://msdn.microsoft.com/en-us/library/system.security.securitycriticalattribute.aspx
PS. Sorry to have multiple answers for one question. After I realized that 2.11 would work, I became more curious about what the error with 2.10 meant. I cannot really combine this answer with the others.
I decided I should have kept the code more like the question but I did not want to overwrite my previous answer:
The code below works with version 2.11 of Mono.CSharp (available here including a solution file for building with Visual Studio/.NET). It was tested with .NET 4 on Windows XP. I do not have access to Mono 2.10 at the moment.
[TestFixture]
class Class1
{
private Evaluator evaluator;
public Class1()
{
var report = new Report(new ConsoleReportPrinter());
evaluator = new Evaluator(new CompilerSettings(), report);
}
[Test]
public void EXPR()
{
evaluator.Run("using System;");
int sum = (int)evaluator.Evaluate("1+2;");
}
}
EDIT: I uploaded the Mono.CSharp.dll assembly that I actually used here. Include it as a reference to compile the code above.