Getting "main" Assembly version number - windows-phone-7

I have a solution with libraries (DLLs) which are used in 2 identical projects (one for WP7, another for WP8). In one of the libraries I have the code which determines the version of the application.
private static Version mVersion;
public static Version Version {
get {
if (mVersion == default(Version)) {
var lcAssembly = Assembly.GetExecutingAssembly();
var parts = lcAssembly.FullName.Split(',');
var lcVersionStr = parts[1].Split('=')[1];
mVersion = new Version(lcVersionStr);
}
return mVersion;
}
}
The problem is that this code returns the version number of the library itself because of this Assembly.GetExecutingAssembly() code. How to get a MAIN Assembly version and not DLL's?

That's a great question on code-sharing between WP7 and WP8.
The simplest way for you to do that would be to read the AppManfiest.xml file at run-time, get the EntryType and use that to get at the entry point Assembly instance. Here's how a sample AppManfiest.xml looks like once MSBuild did its magic on it:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="myAssembly" EntryPointType="myNamespace.App" RuntimeVersion="4.7.50308.0">
<Deployment.Parts>
<AssemblyPart x:Name="myAssembly" Source="myAssembly.dll" />
</Deployment.Parts>
</Deployment>
And here's how you would read the file, get the attributes, then get the entry point type and finally the entry point assembly:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var appManfiest = XElement.Load("AppManifest.xaml");
var entryAssemblyName = appManfiest.Attribute("EntryPointAssembly").Value;
var entryTypeName = appManfiest.Attribute("EntryPointType").Value;
Type entryType = Type.GetType(entryTypeName + "," + entryAssemblyName);
Assembly entryAssembly = entryType.Assembly;
}
That's a simple solution and it works. However, that isn't the cleanest architectural solution. The way I'd implement this solution is to have an interface declared in the shared library, both WP7 and WP8 implement that interface and register their implementation with an IoC container.
For example, let's say you need to "DoSomething" in the shared library that's platform version specific. First you'll create have an IDoSomething interface. Let's also assume you have an IoC standing by.
public interface IDoSomething
{
}
public static class IoC
{
public static void Register<T>(T t)
{
// use some IoC container
}
public static T Get<T>()
{
// use some IoC container
}
}
In your WP7 app you'll implement the shared Interface for WP7 and register it once the WP7 starts up.
public App()
{
MainPage.IoC.Register(new MainPage.DoSomethingWP7());
}
private class DoSomethingWP7 : IDoSomething
{
}
You'll also do the same for WP8 in the WP8 app. And in your shared library you can then ask for the relevant interface regardless of its platform version specific implementation:
IDoSomething sharedInterface = IoC.Get<IDoSomething>();

I have a simpler answer. I think you are close with what you are doing. I just used your code with one modification so I can use it with the Telerik controls. Here's what I did. I located your code in my project's App class (codebehind of App.Xaml). I made one change that I think will take care of your problem:
private static Version mVersion;
public static Version Version {
get {
if (mVersion == default(Version)) {
var lcAssembly = typeof(App);
var parts = lcAssembly.FullName.Split(',');
var lcVersionStr = parts[1].Split('=')[1];
mVersion = new Version(lcVersionStr);
}
return mVersion;
}
}
Now I can get the version number by calling "App.Version".

This worked for me:
var appAssembly = Application.Current.GetType().Assembly;
var appAssemblyVersion = appAssembly.GetName().Version;
I tested with WP7.1 and WP8.0.

Related

Recompile assemblies to separate appdomains in NET 5

I have a NET 5.0 console application, from which I am trying to compile and execute external code BUT also be able to update the code, unload the previously created appdomain and re-compile everything.
This is my entire static class that handles code compilation and assembly loading
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;
namespace Scripting
{
public static class ScriptCompiler
{
public static Dictionary<string, AppDomain> _appDomainDict = new();
public static object CompileScript(string scriptpath)
{
var tree = SyntaxFactory.ParseSyntaxTree(File.ReadAllText(scriptpath));
//Adding basic references
List<PortableExecutableReference> refs = new List<PortableExecutableReference>();
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));
// A single, immutable invocation to the compiler
// to produce a library
string hash_name = scriptpath.GetHashCode();
if (_appDomainDict.ContainsKey(hash_name))
{
AppDomain.Unload(_appDomainDict[hash_name]);
_appDomainDict.Remove(hash_name);
}
AppDomain new_domain = AppDomain.CreateDomain(hash_name);
_appDomainDict[hash_name] = new_domain;
var compilation = CSharpCompilation.Create(hash_name)
.WithOptions(
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
allowUnsafe:true))
.AddReferences(refs.ToArray())
.AddSyntaxTrees(tree);
MemoryStream ms = new MemoryStream();
EmitResult compilationResult = compilation.Emit(ms);
ms.Seek(0, SeekOrigin.Begin);
if (compilationResult.Success)
{
// Load the assembly
Assembly asm = new_domain.Load(ms.ToArray());
object main_ob = asm.CreateInstance("SomeClass");
ms.Close();
return main_ob;
}
else
{
foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
{
string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
$" Location: { codeIssue.Location.GetLineSpan()}," +
$" Severity: { codeIssue.Severity}";
Callbacks.Logger.Log(typeof(NbScriptCompiler), issue, LogVerbosityLevel.WARNING);
}
return null;
}
}
}
}
Its all good when I am trying load the assembly in the current domain and execute from the instantiated object. The problem with this case is that since I wanna do frequent updates to the code, even if I make sure that the assembly names are different. I'll end up loading a ton of unused assemblies to the current domain.
This is why I've been trying to create a new domain and load the assembly there. But for some reason I get a platform not supported exception. Is this not possible to do in NET 5? Are there any workarounds or am I doing something wrong here.
Ok, it turns out that AppDomain support for NET Core + is very limited and in particular there seems to be only one appdomain
On .NET Core, the AppDomain implementation is limited by design and
does not provide isolation, unloading, or security boundaries. For
.NET Core, there is exactly one AppDomain. Isolation and unloading are
provided through AssemblyLoadContext. Security boundaries should be
provided by process boundaries and appropriate remoting techniques.
Source: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain?view=net-6.0
And indeed, when trying to use AssemblyLoadContext and create object instances through these contexts everything worked like a charm!
One last note is that if the created context is not marked as collectible, its not possible to unload it. But this can be very easily set during AssemblyLoadContext construction.

Requesting Android permissions in a class (Xamarin)

I'm trying to request a permission at runtime for my app. I use a service provider to talk between the portable class and Android.
I start by calling this code on button press in the PCL:
using (new Busy(this))
{
var locationHelper = scope.Resolve<ILocationHelper>();
locationHelper.GetLocation(this);
}
This calls my Android level service:
public class AndroidLocationHelper : ILocationHelper, ILocationListener
{
readonly string[] PermissionsLocation =
{
Manifest.Permission.AccessCoarseLocation
};
const int RequestLocationId = 0;
public void GetLocation(SearchViewModel viewModel)
{
try
{
const string permission = Manifest.Permission.AccessCoarseLocation;
if (((int)Build.VERSION.SdkInt < 23) || (CheckSelfPermission(permission) == Permission.Granted))
{
}
else
RequestPermissions(PermissionsLocation, RequestLocationId);
}
catch (Exception ex)
{
Debug.WriteLine("Error while getting Location service");
Debug.WriteLine(ex.Message);
Messaging.AlertUser("There was an error with determining your location");
}
}
However, I get two errors on CheckSelfPermission and RequestPermissions. These two methods are only available to activities. The code works fine in MainActivity; however, I want to ask for permissions when the user hits a button, not in OnCreate or OnResume, etc.
Thanks for any help.
In your Android project, You can use this and use the Dependency Service to call it in Xamarin.Forms PCL project later:
var thisActivity = Forms.Context as Activity;
ActivityCompat.RequestPermissions(thisActivity, new string[] {
Manifest.Permission.AccessFineLocation }, 1);
ActivityCompat.RequestPermissions(thisActivity,
new String[] { Manifest.Permission.AccessFineLocation },
1);
You can try with ContextCompat.CheckSelfPermission, passing the application context, like this:
ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission)
Update
In case of ActivityCompat.RequestPermissions, which requires an activity reference, you can keep track of the current activity. There is a very handy lib for that, called "CurrentActivityPlugin". You can find at https://github.com/jamesmontemagno/CurrentActivityPlugin
Rafael came up with a solution but I found another option that is a lot less effort just using MessagingCenter. In the MainActivity's OnCreate add a receiver that runs all the location code, that way you have access to all of the activities methods (and there are a bunch of tutorials on doing location services in MainActivity). Then add the Send inside of your service (the class).
To expound Rafael Steil's answer, I tried the suggested CurrentActivityPlugin and it worked on me. In my case I am trying to execute a voice call which needs CALL_PHONE permission. Here is the code snippet in your case: I used the ContextCompat & ActivityCompat so that I don't need to check the VERSION.SdkInt
using Plugin.CurrentActivity;
public void GetLocation(SearchViewModel viewModel){
var context = CrossCurrentActivity.Current.AppContext;
var activity = CrossCurrentActivity.Current.Activity;
int YOUR_ASSIGNED_REQUEST_CODE = 9;
if (ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) == (int)Android.Content.PM.Permission.Granted)
{
//Permission is granted, execute stuff
}
else
{
ActivityCompat.RequestPermissions(activity, new string[] { Manifest.Permission.AccessCoarseLocation }, YOUR_ASSIGNED_REQUEST_CODE);
}
}
It's dead simple
public bool CheckPermission()
{
const string permission = Manifest.Permission.ReceiveSms;
return ContextCompat.CheckSelfPermission(Forms.Context, permission) == (int) Permission.Granted;
}

Generate Bare Definitions for a Project or Namespace (Visual Studio)

In developing an SDK for use within our product, we want to provide users (developers) a Visual Studio plugin, mainly to provide them Intellisense during their development and ensure their code compiles for them. To do this, we strip the contents of all of our SDK APIs and put them all in a separate project.
For example:
public IEnumerable<string> AvailableConnections(bool querySystem) {
var connections = ConnectionList();
if(querySystem)
connections = connections.Concat(SystemConnections());
... // Filter connections somehow
return connections;
}
public void WriteToStream(Stream strFrom, Stream strTo) {
byte[] buffer = new byte[32 * 1024]; // 32 KiB
int len;
while ( (len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
Becomes:
public IEnumerable<string> AvailableConnections(bool querySystem) { return null; }
public void WriteToStream(Stream strFrom, Stream strTo) { }
My question: Does a tool exist to automate this, whether for a particular project or particular namespace? Ideally, it would intake a project or namespace and output all of the public classes/functions replacing their definitions with a simple return of the return type's default value. Visual Studio seems to do almost this when you view a class from which you don't have the source (e.g., you'll see IEnumerable<T> [from metadata]).
It sounds like you want to provide interfaces to your API.
You can build this into your project and essentially you will always have an assembly that shows all the public members without containing your implementation code.
Create a project that contains only the API, and reference that from your main project so that your concrete code (your implementation) implements the interfaces.
The API assembly would contain mostly interfaces and perhaps some abstract classes an helper, which you could share with developers.
Taking your example, you would have an interface like
public interface IMySdkThing
{
IEnumerable<string> AvailableConnections(bool querySystem);
void WriteToStream(Stream strFrom, Stream strTo);
}
Your implementation would be declared like:
public class MySdkThing : IMySdkThing
{
// all the code you showed, just as it is
}
All that said, it isn't clear how this will be useful to the developer. He or she will need a dll with some actual, executable code in it to use your library. Intellisense and compile-time checking come for free; you don't have to do anything special.

Fetch windows setting value

How do I fetch the Measurement System setting value in javascript?
I'm guessing that it would be throw some WinJS call.
The logical place would be Windows.Globalization, but not seeing if offered there. One pretty simple workaround - faster to write than to research the setting :) is to create a Windows Runtime Component in C# that calls in to System.Globalization:
namespace WindowsRuntimeComponent
{
public sealed class RegionalSettings
{
public bool isMetric()
{
return System.Globalization.RegionInfo.CurrentRegion.IsMetric;
}
}
}
Then add as a reference to your JavaScript app and invoke there:
var r = new WindowsRuntimeComponent.RegionalSettings;
var isMetric = r.isMetric();

cross-AppDomain event issues

I use the following helper class with POS for .Net to get a reference to the hardware in a separate AppDomain (getting around some limitations of requiring <NetFx40_LegacySecurityPolicy enabled="true"/>
public static class PosHelper
{
private static AppDomain _posAppDomain { get; set; }
private static AppDomain PosAppDomain
{
get
{
if (_posAppDomain == null)
{
AppDomainSetup currentAppDomainSetup = AppDomain.CurrentDomain.SetupInformation;
AppDomainSetup newAppDomainSetup = new AppDomainSetup()
{
ApplicationBase = currentAppDomainSetup.ApplicationBase,
LoaderOptimization = currentAppDomainSetup.LoaderOptimization,
ConfigurationFile = currentAppDomainSetup.ConfigurationFile
};
newAppDomainSetup.SetCompatibilitySwitches(new[] { "NetFx40_LegacySecurityPolicy" });
_posAppDomain = AppDomain.CreateDomain("POS Hardware AppDomain", null, newAppDomainSetup);
}
return _posAppDomain;
}
}
public static T GetHardware<T>() where T : PosHardware, new()
{
T hardware = (T)PosAppDomain.CreateInstanceFromAndUnwrap(Assembly.GetAssembly(typeof(T)).Location, typeof(T).FullName);
hardware.FindAndOpenDevice();
return hardware;
}
}
I have a basic class to handle when a POS scanner scans data. In that class I have an event that I want to fire when data is scanned. Here's a snippet:
public class ScannerDevice : PosHardware
{
public event Action<string> DataScanned;
...
_scanner.DataEvent += new DataEventHandler(Scanner_DataEvent);
...
private void Scanner_DataEvent(object sender, DataEventArgs e)
{
ASCIIEncoding encoder = new ASCIIEncoding();
if (DataScanned != null)
DataScanned(encoder.GetString(_scanner.ScanDataLabel));
_scanner.DataEventEnabled = true; // enable for subsequent scans
}
Note that the PosHardware abstract class inherits MarshalByRefObject and is marked [Serializable]
In my main AppDomain I try to use the event like so:
Scanner = PosHelper.GetHardware<ScannerDevice>();
Scanner.DataScanned += m =>
{
Debug.WriteLine(m);
};
When it hits the line trying to add the lambda to the DataScanned event I get this error:
Could not load file or assembly 'MyAssemlyName, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' or one of its dependencies. The
system cannot find the file specified.
This has to be related to trying to communicate between AppDomains. Not really sure what to do. Do I need to register "MyAssemblyName" in the separate AppDomain used for Pos for .Net?
I use prism, so some modules are loaded at runtime (in a subfolder in my output directory)... including the one in which I use the last code snippet above (Scanner = PosHelper.GetHardware....)
I believe I solved my problem. Since my prism modules are loaded at runtime within a subdirectory I needed to add this to the AppDomain so that the AppDomain could find the assemblies in the subdirectories folder.:
PrivateBinPath = #"Modules"
http://msdn.microsoft.com/en-us/library/system.appdomainsetup.privatebinpath.aspx
Edit
This only partially solved my problem. I also had to override InitializeLifetimeService() and return null so that my MarshalByRefObject's would not be disposed while the program is running (I believe the default timeout is 5 minutes).
Also , this now works:
Scanner.DataScanned += m =>
{
Debug.WriteLine(m);
}
but when I try something like this
Scanner.DataScanned += m =>
{
DoSomething(m);
}
Where DoSomething is not in a Serializable and MarshalByRefObject class, it craps out since all classes that are used in the communication between AppDomain's need to have those. So where I'm at now is looking at using WCF named pipes to pass data around... and other similar solutions.

Resources