Is it possible to use platform specific code like so:
#if __ANDROID__
using Android.App;
using Android.Content;
using Android.Locations;
#endif
public GPSNavigation()
{
#if __ANDROID__
manager = (LocationManager)DependencyService.Get<Activity>().GetSystemService(Context.LocationService);
Criteria criteria = new Criteria
{
Accuracy = Accuracy.Fine
};
IList<string> acceptableLocationProviders = manager.GetProviders(criteria, true);
if (acceptableLocationProviders.Any())
{
locationProvider = acceptableLocationProviders.First();
}
else
{
locationProvider = "";
}
#endif
#if __IOS__
#endif
}
The code compiles and runs but I get a runtime error of "Unknown identifier: " for all Android specific code such as "Unknown identifier: LocationManager" when executed. Is it not possible to reference platform specific services/APIs within a shared project?
Yes, in answer to your question, this is possible. Shared projects get whatever references the project that references them have. Otherwise it wouldn't compile. You must have a logic error in your code.
From your code I suspect the value of Context.LocationService is LocationManager and the call to the DependencyService is not resolving it. Why this is occurring is not directly related to using a shared project, but something that needs to be debugged.
Additionally, based on your code you might be able to take advantage of #elif IOS instead of the separate #endif and #if IOS statements but that is more an organizational preference.
Related
I'm writing a class that I wish to use on both Windows & Linux.
One of the methods that is in this class is accessing the Windows Registry
What I'm hoping to achieve is to somehow disable this particular method from being used when using a Linux machine.First of all I did some research to see if there was something for .Net Core that would allow me to check which operating system is in use, I found this and sure enough that works.When I implemented it into my code when accessing a method, I was hoping to disable the method that's accessing the windows registry, however the closest I could get to this was using a switch statement, something like this
switch (OS)
{
case OSX:
return;
case LINUX:
return
}
To return if the operating system was not supported, this worked, however I then thought disabling it from being accessed all together would be much better rather than throwing an error for an operating system thats not supported for that particular methodI then went on to look at preprocessor directives thinking that if I'm able to detect and disable parts of code depending the frameworks etc, maybe I could use something like this to disable parts of code depending on the operating system that way they could never be called even when trying to access the methodI went on from there to see if I could disable parts of code using preprocessor directives.I found this.I understand that it is for C++ however it seems to be the closest I could find for what I'm trying to achieve within .Net Core In a perfect world, it would look something like this
/// <summary>
/// Get the file mime type
/// </summary>
/// <param name="filePathLocation">file path location</param>
/// <returns></returns>
`#if WINDOWS`
public static string GetMimeType(this string filePathLocation)
{
if (filePathLocation.IsValidFilePath())
{
string mimeType = "application/unknown";
string ext = Path.GetExtension(filePathLocation).ToLower();
Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
if (regKey != null && regKey.GetValue("Content Type") != null)
{
mimeType = regKey.GetValue("Content Type").ToString();
}
return mimeType;
}
return null;
}
`#endif`
I did see #Define so I tried something like this #define IS_WINDOWS and added it to my class along with #if IS_WINDOWS
however, I couldn't see how to change that value if I'm hoping to just reuse the static class over and over.
While you could pursue a route involving #define, it's compile-time and you'll loose a lot of .Net's multi-platform goodness. You'll also have to juggle multiple configurations, multiple builds, etc.
Where possible, hide the platform-dependent behavior behind a platform-independent abstraction and do the check at runtime using System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform:
interface IPlatform
{
void DoSomething();
}
class WindowsImpl : IPlatform
{
public void DoSomething()
{
// Do something on Windows
}
}
class LinuxImpl : IPlatform
{
public void DoSomething()
{
// Do something on Linux
}
}
// Somewhere else
var platform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new WindowsImpl() : new LinuxImpl();
platform.DoSomething();
This works well for many things including PInvoke. You will be able to use the same binaries on either platform, and it will be easier to add OSX later.
If you need to isolate the platform-dependent code at compile-time (perhaps a package is Windows-only), MEF2/System.Composition can help you make a plugin framework where each platform gets its own assembly:
// In Windows.dll class library project
using System.Composition;
[Export(typeof(IPlatform))]
public class WindowsImpl : IPlatform
{
public void DoSomething()
{
//...
}
}
And then in your main program:
using System.Composition.Hosting;
var configuration = new ContainerConfiguration();
var asm = Assembly.LoadFrom(pathToWindowsDll);
configuration.WithAssembly(asm);
var host = configuration.CreateContainer();
var platform = host.GetExports<IPlatform>().FirstOrDefault();
I have a use-case where preprocessor directives are necessary. I found here that I can do this. As the site tells us, add the following to your project (.csproj) file:
<PropertyGroup>
<TargetFramework>...</TargetFramework>
<OutputType>...</OutputType>
<!-- insert the following -->
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
</PropertyGroup>
... then add these for each preprocessor option:
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<DefineConstants>Windows</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsOSX)'=='true'">
<DefineConstants>OSX</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(IsLinux)'=='true'">
<DefineConstants>Linux</DefineConstants>
</PropertyGroup>
It is the individual <PropertyGroup Condition> child element that defines the constant so I can do this:
#if Linux
private const string GLFW_LIB = "glfw";
#elif OSX
private const string GLFW_LIB = "libglfw.3";
#elif Windows
private const string GLFW_LIB = "glfw3";
#else
// some error condition - unsupported platform
#endif
I have a libary which needs to behave differently for console applications, desktop application (e.g. WPF), and for UWP apps.
How can I determine at run-time into which application type my libary is loaded?
Determining if it is a console application seems easy: How to tell if there is a console
For UWP, I can probably determine if WinRT is loaded. But how?
What distinguishing attributes do desktop applications have?
I ended up defining following enum:
public enum ExecutionMode
{
Console,
Desktop,
UniversalWindowsPlatform
}
which is passed to the constructor of the main class of my libary. Not a new idea, but very reliable (if used correctly).
Create a CustomAttribute in an assembly that is available to all of the applications like so
using System;
namespace MyNamespace.Reflection {
[System.AttributeUsage(AttributeTargets.Assembly)]
public class ApplicationTypeAttribute : Attribute {
public enum ApplicationTypes {
Console,
Desktop,
UWP,
ClassLibrary
}
public ApplicationTypeAttribute(ApplicationTypes appType) {
ApplicationType = appType;
}
public ApplicationTypes ApplicationType { get; private set; } = ApplicationTypes.Console;
}
}
Then add this attribute to your AssemblyInfo.cs file for a console application
using MyNamespace.Reflection;
[assembly: ApplicationType(ApplicationTypeAttribute.ApplicationTypes.Console)]
or a Desktop application
[assembly: ApplicationType(ApplicationTypeAttribute.ApplicationTypes.Desktop)]
etc.
Then wherever you want to check the calling type of the application that was started, use this
using MyNamespace.Reflection;
var assy = System.Relection.Assembly.GetEntryAssembly();
var typeAttribute = assy.GetCustomAttribute(typeof(ApplicationTypeAttribute));
if (typeAttribute != null) {
var appType = ((ApplicationTypeAttribute)typeAttribute).ApplicationType;
}
There is one caveat to this method. .NET Core apps have a different project structure and the AssemblyInfo.cs file is auto-generated at build time by default. You can override this behavior by specifying the following in the .csproj file in the Project node.
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
To match the old project file structure, you can create a Properties directory in the project directtory and then you can add an AssemblyInfo.cs file to that directory. Otherwise you can place the Custom Attribute definition in any file (after the usings and before the namespace declaration).
I need to get a value from configuration manager in C# Preprocessor directive
And want to do as like below,
#if System.Configuration.ConfigurationManager.AppSettings["Language"].Equals("en-US");
{
bool languageCheck=TRUE
}
#endif
Is it possible ?
No, pre-processors mean "pre-compilation", and at that point it does not know what values are stored in objects or configurations. However, you can add a different build configuration (by clicking on project properties going to build tab), and add the language flag to it to do a similar thing.
public void SayLanguage()
{
#if en_US
Console.WriteLine("en_US");
#else
Console.WriteLine("Language not defined.");
#endif
}
In my .net application, I have added new build modes for the configurations.
I currently have: Debug, Release, BetaDebug & BetaRelease.
Each having a specific purpose for software engineering purposes.
I wish to know if I can get a C# string containing this text. So I can then do:
string configurationBuild = GetConfigurationBuild();
if(configurationBuild.Contains("Beta") {
//do something
}
else {
//do something else
}
You can configure that each configuration will define Conditional Compilation symbols use preprocessor instructions like #if to find out which build configurationis being used.
Here is a link on msdn forum http://social.msdn.microsoft.com/Forums/vstudio/en-US/7d574468-c890-49d2-984e-16ad068a006e/build-configuration-in-preprocessor
You can use conditional compilation symbols ( http://msdn.microsoft.com/query/dev10.query?appId=Dev10IDEF1&l=EN-US&k=k(CS.PROJECTPROPERTIESBUILD);k(TargetFrameworkMoniker-%22.NETFRAMEWORK%2cVERSION%3dV4.0%22)&rd=true ) for your build modes.
In your code you can then use the #if directive http://msdn.microsoft.com/de-de/library/4y6tbswk(v=vs.100).aspx
I'm looking for a method that let's me validate code and generator code as part of the build process, using Visual Studio 2010 (not express) and MSBuild.
Background Validation:
I'm writing a RESTful web service using the WCF Web Api. Inside the service class that represents the web service I have to define an endpoint, declaring additionally parameters as plain test. When the parameter name inside the endpoint declaration differs from the parameter of the C# method I get a error - unfortunately at run time when accessing the web service, not at compile time. So I thought it would be nice to analyze the web service class as part of the compile step for flaws like this, returning an error when something is not right.
Example:
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public string MyMethod(string param1, string parameter2) {
// Accessing the web service now will result in an error,
// as there's no fitting method-parameter named "param2".
}
Also I'd like to enforce some naming rules, such as GET-Methods must start with the "Get" word. I believe this will help the service to remain much more maintainable when working with several colleagues.
Background Generation:
I will be using this REST web service in a few other projects, there for I need to write a client to access this service. But I don't want to write a client for each of these, always adjusting whenever the service changes. I'd like the clients to be generated automatically, based upon the web service code files.
Previous approach:
So far I tried to use a T4 template using the DTE interface to parse the code file and validate it, or generate the client. This worked fine in Visual Studio when saving manually, but integrating this in the build process turned out to be not so working well, as the Visual Studio host is not available using MSBuild.
Any suggestion is welcome. :)
Instead of using DTE or some other means to parse the C# code you could use reflection (with Reflection-Only context) to examine the assembly after it's compiled. Using reflection is a more robust solution and probably faster also (especially if you use Mono.Cecil to do the reflecting).
For the MSBuild integration I would recommend writing a custom MSBuild task - it's fairly easy and more robust/elegant than writing a command line utility that's executed by MSBuild.
This may be a long shot but still qualifies as "any suggestion" :)
You could compile the code, then run a post-build command which would be a tool that you'd have to write which uses reflection to compare the parsed UriTemplate text with the method parameter names, catching errors and outputting them in a manner that MSBuild will pickup. Look at This Link for information on how to output so MSBuild will put the errors in the visual studio error list. The post-build tool could then delete the compiled assemblies if errors were found, thus "simulating" a failed build.
Here's the SO Link that lead me to the MSBuild Blog too, just for reference.
HTH
For the enforcement side of things, custom FxCop rules would probably be a very good fit.
For the client code generation, there are quite a few possibilities. If you like the T4 approach, there is probably a way to get it working with MSBuild (but you would definitely need to provide a bit more detail regarding what isn't working now). If you're want an alternative anyway, a reflection-based post-build tool is yet another way to go...
Here is a short, extremely ugly program that you can run over an assembly or group of assemblies (just pass the dlls as arguments) to perform the WebGet UriTemplate check. If you don't pass anything, it runs on itself (and fails, appropriately, as it is its own unit test).
The program will print out to stdout the name of the methods that are missing the parameters and the names of the missing parameters, and if any are found, will return a non-zero return code (standard for a program failing), making it suitable as a post-build event. I am not responsible if your eyes bleed:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;
namespace ConsoleApplication1
{
class Program
{
static int Main(string[] args)
{
var failList = new ConcurrentDictionary<MethodInfo, ISet<String>>();
var assembliesToRunOn = (args.Length == 0 ? new[] {Assembly.GetExecutingAssembly()} : args.Select(Assembly.LoadFrom)).ToList();
assembliesToRunOn.AsParallel().ForAll(
a => Array.ForEach(a.GetTypes(), t => Array.ForEach(t.GetMethods(BindingFlags.Public | BindingFlags.Instance),
mi =>
{
var miParams = mi.GetParameters();
var attribs = mi.GetCustomAttributes(typeof (WebGetAttribute), true);
if (attribs.Length <= 0) return;
var wga = (WebGetAttribute)attribs[0];
wga.UriTemplate
.Split('/')
.ToList()
.ForEach(tp =>
{
if (tp.StartsWith("{") && tp.EndsWith("}"))
{
var tpName = tp.Substring(1, tp.Length - 2);
if (!miParams.Any(pi => pi.Name == tpName))
{
failList.AddOrUpdate(mi, new HashSet<string> {tpName}, (miv, l) =>
{
l.Add(tpName);
return l;
});
}
}
});
})));
if (failList.Count == 0) return 0;
failList.ToList().ForEach(kvp => Console.Out.WriteLine("Method " + kvp.Key + " in type " + kvp.Key.DeclaringType + " is missing the following expected parameters: " + String.Join(", ", kvp.Value.ToArray())));
return failList.Count;
}
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillPass(String param1, String param2) { }
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillFail() { }
[WebGet(UriTemplate = "Endpoint/{param1}/{param2}")]
public void WillFail2(String param1) { }
}
}