Hosting CLR and programmatically providing app.config? - interop

I have a native third party game, which allows me to write native extensions for it by exporting a particular function in a native DLL. I have been using this to host the CLR using C++/CLI compiler-generated hosting code, allowing me to call C# code from the game. This has been working great and it is a very elegant solution.
Once the CLR is loaded by the game, it searches game.exe.config in the game executable folder, for further .NET assembly probing information:
<?xml version="1.0"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="#Arma2NET"/>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/></startup></configuration>
This arbitrary config file breaks my folder structure; I would like my project to be self-contained in the #Arma2NET folder and not have random config files in the game folder. At the moment, having the project files in the same folder as the native CLR-hosting DLL is impossible because of the game's folder requirements.
Is there a way to programmatically provide the CLR with this config from native code when it is starting, short of writing the entirety of the CLR-hosting code myself?

I ended up using the AssemblyResolve event to tell .NET where to find the rest of my assemblies:
Assembly^ ResolveAssembly(Object ^sender, ResolveEventArgs ^e)
{
String ^directory = Path::GetDirectoryName(Assembly::GetExecutingAssembly()->Location);
AssemblyName ^assemblyName = gcnew AssemblyName(e->Name);
for each (String ^extension in gcnew array<String^> { ".dll", ".exe" })
{
String ^fileName = Path::Combine(directory, assemblyName->Name + extension);
try
{
return Assembly::LoadFile(fileName);
}
catch (...)
{
}
}
return nullptr;
}

How about you change the app.config in the library project build action to "Embedded Resource" then use something like ConfigurationManager.OpenMappedExeConfiguration to read it.

Related

'IAsyncOperation<>' Xamarin Forms

When using the exact same code in a UWP App an Xamarin Forms app, I get the following error on the Xamarin.Forms program but not on the UWP program.
Error CS0012 The type 'IAsyncOperation<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'Windows.Foundation.FoundationContract, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime'.
Here is my code.
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
Task task = CopyAssetsDatabase();
}
private async Task CopyAssetsDatabase()
{
StorageFile file;
var root = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
var dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "ExpressEMR.db");
try
{
file = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync("ExpressEMR.db");
}
catch
{
StorageFile Importedfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/ExpressEMR.db"));
file = await Importedfile.CopyAsync(Windows.Storage.ApplicationData.Current.LocalFolder);
}
throw new NotImplementedException();
}
'IAsyncOperation<>' Xamarin Forms
The problem is that you have missed System.Runtime.WindowsRuntime assembly, you could add it manually. right click the Dependencies Add reference-> Browse -> find it from this path (C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5\System.Runtime.WindowsRuntime.dll).
Update
That assembly should be added automatically to a Xamarin Forms app, correct? I mean I have never had to add it manually before.
Sure, but in early version, we need add the reference manually. And I checked your thread again, I found you used StorageFile in forms app class. However, StorageFile is specific class for uwp platform please avoid use it in forms, if you want to file access, you could dependency service to access file for each platform.

Visual Studio extension: Json with schema item template

I'd like to create a Visual Studio extension to allow the user to create a json file and automatically apply a custom JSON schema to it. I followed this article and so far, I succeeded in allowing the user to create the JSON file, but I have no idea how to automatically bind the custom schema to it.
I'd like this schema to be in the extension solution, this way, when I update the extension, the schema gets updated. As I'm using SideWaffle, I noticed that there is this "JSON Schema Selector Extension" item template but it does not work. It requires "Microsoft.Json.Core.Schema" that is nowhere to be found (not even in the Microsoft.JSON Nuget package...).
Late to this question, but the solution involves referencing a non-SDK VS assembly as of VS2019 (and everything prior). To prove it is non-SDK, it even changed identity in VS2019, it is now Microsoft.WebTools.Languages.Json as of VS2019 (whereas it was indeed Microsoft.Json.Core for older versions), and of course, it may change again in the future.
Knowing this, if you search online opensource repos, you will find some ideas for how to achieve the reference. Some people bundle a copy of the DLL in their repo (which is probably against some licensing terms). Personally, I did not go that route and I instead went for an overcomplicated path relative to $(MSBuildToolsPath):
<Reference Include="Microsoft.WebTools.Languages.Json">
<HintPath>$(MSBuildToolsPath)\..\..\..\Common7\IDE\Extensions\Microsoft\Web Tools\Languages\Microsoft.WebTools.Languages.Json.dll</HintPath>
<SpecificVersion>False</SpecificVersion>
<Private>false</Private>
</Reference>
Just looking at this, it is obvious that this approach is brittle, and the path may move in the future, but it avoids bootstrapping the dll.
Anyway, whichever way you choose to reference the assembly, as for everything else that is meant to come from VS itself, make sure it is Private=false, as you do not want your final VSIX bundle to include it.
Then it is a simple matter of implementing IJsonSchemaSelector:
[Export(typeof(IJsonSchemaSelector))]
public class LaunchConfigJsonSchemaProvider : IJsonSchemaSelector
{
private static readonly string Schema = #"<somehow resolve your extension install path and find your schema json>";
public event EventHandler AvailableSchemasChanged { add { } remove { } }
public Task<IEnumerable<string>> GetAvailableSchemasAsync()
{
return Task.FromResult((IEnumerable<string>)new[] { Schema });
}
public string GetSchemaFor(string fileLocation)
{
string ext = Path.GetExtension(fileLocation);
if (!".greatjson".Equals(ext, StringComparison.OrdinalIgnoreCase))
return null;
return Schema;
}
}

Camel Case the project name for a Visual Studio Project Template

I am trying to make a Visual Studio Project Template. I can easily replace the names of things I want to have the same name as the project using $safeprojectname$
So if my project was named WidgetHunter and I named a file $safeprojectname$.js it would be called WidgetHunter.js when the project was created.
But what if I wanted to name the file widgetHunter.js or widget-hunter.js?
Is there away to make a new variable that does not have a static value? (I need to perform a string operation on the supplied project name.)
So this will not be as straight forward as you allude. Project Templating is not very powerful, it's basic replacement, you can't really run code AFAIK.
However, you can "easily" add a Wizard Step (IWizard) that adds additional keys to the replacement dictionary. For example, you could add $safeprojectnameforjs$ which has the manipulated value.
public class ExampleWizard : IWizard
{
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
replacementsDictionary.Add("safeprojectnameforjs",
YourCustomMethodForManipulatingName(replacementsDictionary["safeprojectname"])
}
// there are a few other IWizard methods you'll need to
// implement but don't need to do anything in
}
To wire in your ExampleWizard, you'll need to add a tag in your .vstemplate
<VSTemplate>
<WizardExtension>
<Assembly>ExampleWizard.ExampleWizard, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=a76e3e75702e3ee4</Assembly>
<FullClassName>ExampleWizard.ExampleWizard</FullClassName>
</WizardExtension>
</VSTemplate>
Note: You'll need to have your Wizard in an assembly. Easiest way is to create a new Class Library project. Also, it will need to be signed (at least, pretty sure that's still a requirement).
Finally, you'll need to update your VSIX manifest, so that you have the Wizard assembly wired up as an assembly dependency:
<PackageManifest>
<Assets>
<Asset Type="Microsoft.VisualStudio.Assembly" d:Source="Project" d:ProjectName="ExampleWizard.ExampleWizard" Path="|ExampleWizard.ExampleWizard|" AssemblyName="|ExampleWizard.ExampleWizard;AssemblyName|" />
</Assets>
</PackageManifest>

How can I create a new Windows Phone 7 assembly from scratch using CCI or Mono.Cecil

I am working on a tool to generate assemblies for WP7. I am doing this from the full framework. Since Reflection.Emit doesn't work with WP7 but either CCI or Mono.Cecil do I am wondering if there is a way to create new assemblies from scratch. I already know that I can modify existing assemblies, but being able to create one would be pretty useful.
I guess a workaround would be to generate an empty assembly in visual studio and keep it as a template, but I think that there should be a better way.
It's pretty easy to do with Mono.Cecil:
using Mono.Cecil;
using Mono.Cecil.Cil;
class Demo
{
static void Main()
{
var winphoneAssemblies = #"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone";
var assemblyResolver = new DefaultAssemblyResolver();
assemblyResolver.AddSearchDirectory(winphoneAssemblies);
var winphoneCorlib = assemblyResolver.Resolve("mscorlib");
var module = ModuleDefinition.CreateModule("Test", new ModuleParameters
{
AssemblyResolver = assemblyResolver,
Runtime = TargetRuntime.Net_2_0,
Kind = ModuleKind.Dll,
});
// trick to force the module to pick the winphone corlib
module.Import(winphoneCorlib.MainModule.GetType("System.Object"));
var type = new TypeDefinition("Test", "Type", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract, module.TypeSystem.Object);
module.Types.Add(type);
var method = new MethodDefinition("Identity", MethodAttributes.Public | MethodAttributes.Static, module.TypeSystem.Int32);
method.Parameters.Add(new ParameterDefinition("i", ParameterAttributes.None, module.TypeSystem.Int32));
type.Methods.Add(method);
var il = method.Body.GetILProcessor();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ret);
module.Write("Test.dll");
}
}
A few things to note:
The need to create the module with an assembly resolver targeting the winphone assemblies.
A little trick to make sure the module picks up the proper winphone mscorlib (will be fixed in the next version of Cecil).
Silverlight assemblies have the metadata version of the .net 2.0 framework.
It's worth pointing out that while you may be able to generate dynamic assemblies from within the phone's runtime using alternate framework's, you're not goint to be able to load / execute them. Those APIs will throw an exception if executed by application code.

Is it possible to reference Silverlight 3 class library by .Net 3.5-based project in VS.net 2008?

First, I know Silverlight project can't reference to non-Silverlight based project like Windows class library or Asp.net MVC project. But I need to create my projects which can support both Silverlight-based project & Asp.net MVC project.
So, I created Silverlight-based project for my sharing source code. It works fine on VS.net 2008 & .Net 3.5 SP1. However, I found some error when I try to use some method of Silverlight-based project from .Net-based project like the following code.
Silverlight-based Method
public static void InitializeInstance(object obj)
{
// Initialize Field Value
foreach (FieldInfo fi in obj.GetType().GetFields())
{
foreach (Attribute attr in fi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
fi.SetValue(obj, dv.Value);
}
}
}
// Initialize Property Value
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
foreach (Attribute attr in pi.GetCustomAttributes(true))
{
if (attr is DefaultValueAttribute)
{
DefaultValueAttribute dv = (DefaultValueAttribute)attr;
if (pi.CanWrite)
{
pi.SetValue(obj, dv.Value, null);
}
}
}
}
}
.Net-based Method
private void Form1_Load(object sender, EventArgs e)
{
InitializeInstance(this);
}
Error Detail
System.IO.FileNotFoundException:
Could not load file or assembly
'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' or
one of its dependencies. The system
cannot find the file specified. File
name: 'System, Version=2.0.5.0,
Culture=neutral,
PublicKeyToken=7cec85d7bea7798e' at
InitializeInstance(Object obj)
Finally, I try to solve this problem by copying system.dll of Silverlight to output directory and reference it. It still shows same error. So, I think this error may be limitation of both .Net & Silverlight platform. Do you have any idea for avoid this issue?
PS. I know I can use this technique for a few sharing code. But it’s impossible to do this for my projects. Because it’s very complicate & very complex more than directly create Silverlight-based or .Net-based class library.
Thanks,
The trouble here is that those types share an assembly with a different strong name: System.Windows in Silverlight, PresentationFramework or PresentationCore on the desktop CLR.
So at runtime, the intended type cannot be loaded, and there are no type forwarders for the Silverlight-to-desktop types.
My recommended solution
Consider using file links, instead of actually trying to reference the same built binary.
This way, you can have a source structure for your project that may look like this:
MyApp\
Silverlight\
Page.xaml
Page.xaml.cs
(link) ..\AspMvc\MySharedDataFile.cs
AspMvc\
MySharedDataFile.cs
MyApp.cs
This way, the source will be re-compiled with both projects. We use this on the Silverlight Toolkit to build many controls, including the charting and data visualization controls, for both WPF and Silverlight. This is by rebuilding for each platform, instead of referencing the binaries from both.
To insert a link in Visual Studio, just right-click on your project or one of its folder, Add Existing Item, then find it in the explorer open file dialog. however, instead of just clicking the button, click on the little down arrow drop-down on the Add file button, and select the "Add as link" option.
Then, it simply builds that file from another location, but it is not a copy, so you can maintain it in one place and use in both.
A crazy solution
You can use .NET reflection from your desktop app that is of a much higher trust to actually create a new app domain, hook up to the assembly resolution event, and see what that does. You may be able to instead return the type from the desktop CLR, or just no-op these warnings.
No clue if it works.

Resources