Detect when WPF/WInForm/Android runtime is included in a project - c#

I have an Extension class I share within multiple projects where the project can be Android (Xamarin/.NET MAUI) but also a WinForm/WPF app or even a WinForm one with some WPF windows and I'm wondering if there are existing preprocessor macros to enable or disable some code, e.g. 'System.Data.SqlClient' stuff is not available on Android and 'System.Windows' (which I have some extensions) is not part of a WinForm project unless some dll are manually referenced (such PresentationCore, PresentationFramework, WindowsBase, WindowsFormsIntegration etc.). So my question is:
is there any preprocessor macro to enable/disable code for Android, WPF and WinForm at compile time?
what i would is something like:
#if !(ANDROID_RUNTIME)
public static string SqlDataReaderStringExt(this SqlDataReader reader, int i)
{
...
}
#endif
#if WPF_RUNTIME
....
#endif
#if WINFORM_RUNTIME
....
#endif
so, stop manually comment the code in each project.

Related

Registered services not found in DI engine when Xamarin app published in Release mode

I'm writing an Android Wear app using Xamarin. I'm using the Microsoft.Extensions.DependencyInjection library to handle service dependency injection in my app. It works completely fine when I run the app in Debug mode through Visual Studio (Ctrl + F5), on my actual smartwatch. The app functions correctly and everything. When I publish the app using the "Archive..." function of the Xamarin toolset and then sideload the published version onto my Smartwatch, however, the app crashes at startup with an exception stating System.InvalidOperationException: A suitable constructor for type 'My.Library.SomeManager' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.. This spawns from a call to IServiceProvider.GetService for the SomeManager type.
I don't know why this would be. I'm logging to logcat when I register the services, and can see them being registered in the published version, but for some odd reason, the dependency injection engine is not able to find them. I don't know enough about the inner workings of Xamarin to choose a direction to troubleshoot this. Does anybody know what would be causing this odd behavior?
Assuming your Release configuration has the Linker set to something other than None?
Classes, constructors and/or methods that are only referenced via reflection calls for activation and used via interfaces (typical for DI) can not be seen by the static analysis that the Mono Linker performs and thus are stripped from the assembly(s) in order the get the final app bundle size down to a "acceptable" size.
Note: This process is comparable to the Proguard tool, and its replacement,R8, used to strip un-used Java code and has the some "limitation" and most of my Xamarin.Android projects end up with a custom Mono linker and Proguard/R8 config file.
If you do not "own" the code that is being stripped, you can manually reference a class/method so the Linker does not strip it:
[Preserve]
public static class LinkerPreserve
{
static LinkerPreserve()
{
throw new Exception(typeof(My.Library.SomeManager).FullName);
}
}
If you own the code, you can apply the PreserveAttribute to the class.
[Preserve]
public class SomeManager
{
~~~~
}
You can also apply the --linkskip=ASSEMBLY in the build options...
Refer the docs for details:
https://developer.xamarin.com/api/type/MonoTouch.Foundation.PreserveAttribute/
It take full control of the Mono linking process you can create a custom linking config file:
https://learn.microsoft.com/en-us/xamarin/cross-platform/deploy-test/linker

Can't change .NET Target Framework Version in VS 2015 C++ project

As the title says, I want to change the .NET Target Framework Version for my C++ project. I'm trying to compile with the /clr command which I think should enable it?
Here's a screenshot:
I'm trying to build a DLL for use in Unity and I want to be able to select the proper framework.
I've tried changing the information in the .vxproj file but I can't find the right tag and when I add it myself it throws errors.
EDIT:
this is the code that contains the methods that can be called in C# to use the C++ code I've written before. I only edited the .h file of the CLR Class library (so the .cpp file is only including the header which should be fine I think)
#pragma once
#include "PortAudioManager.h"
using namespace System;
namespace PortAudioWrapper {
public ref class PortAudioManaged
{
private:
PortAudioManager* audioManager;
public:
PortAudioManaged() : audioManager(new PortAudioManager()) {
}
virtual ~PortAudioManaged() {
this->!PortAudioManaged();
}
// = Object.Finalize
!PortAudioManaged() {
delete audioManager;
audioManager = nullptr;
}
void openStreamManaged() {
audioManager->openStream();
}
void stopStreamManaged() {
audioManager->stopStream();
}
};
}
You should be able to follow the guide at https://msdn.microsoft.com/en-us/library/ff770576.aspx
The .NET framework you can target in C++ is dependent on the toolset you choose. You may find it easier to just download an older version of VS that supports the framework you're looking to work with.
In project file I just created the section looks like the below:
<PropertyGroup Label="Globals">
<ProjectGuid>{48ACEC98-3369-486F-9033-8C433D408570}</ProjectGuid>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<Keyword>ManagedCProj</Keyword>
<RootNamespace>ClassLibrary1</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
Using VS2015, I had to upgrade the .Net target of a managed C++ DLL from 3.5 to 4.5.1. In the Configuration Properties-General settings, the ".Net Target Framework Version" is greyed out and its value box is not editable.
Open the Name.vcxproj file in notepad.
Navigate to: "<"PropertyGroup Label="Globals" ""
Add: "<"TargetFrameworkVersion""v4.5.1"<"/TargetFrameworkVersion"
Save the updated project file, and reload into VSS2015.
NOTE: Remove the "" around the angle brackets.
Then when the project is reloaded into VS2015, you can see the .Net version listed in settings. In my case it was V4.5.1.

Running NUnit test depending on a condition

I'm writing a project using Win7 x64. Some part of my tests requires using SQLServer CE which only represents support for x86. I'm using Visual Studio 2010 Express and I gotta change platform target for my projects manually editing *.cproj files to run, for example, schema export test (NHibernate). Howcome I run a part of my tests depending on a platform target.
Thanks!
I don't know if there's a built-in mechanism in NUnit to handle this scenario, but at the very least you can use preprocessor directives.
For instance, create a "Debug x86" solution configuration, targeting x86. Then define the DEBUG_X86 conditional compilation symbol (in the properties of the project). Finally, surround your unit test with preprocessor directives:
#if DEBUG_X86
[Test]
public void Test()
{
// This test will only run when compiled with Debug x86
}
#endif
Edit: Actually, you don't even have to create a new solution configuration, as it's possible to define the conditional symbols depending on the platform (https://stackoverflow.com/a/1313450/869621). So define a WIN32 compilation symbol, and surround your test with it:
#if WIN32
[Test]
public void Test()
{
// This test will only run when compiled for x86
}
#endif

DllImport incomplete names

I am using several P/Invokes under .NET. However, I want my library to work both in Windows and Linux, preferably with the same binaries.
Since the native library I depend on is available on multiple platforms, I was hoping to just have them along with my managed library's binaries.
Right now I'm using something like this:
[DllImport("/usr/lib/libMYLIBNAME.so.1")]
But this obviously only works for Linux. I was considering that I could possibly copy that binary from /usr/lib and distribute along with my application, so I could reduce the above to:
[DllImport("libMYLIBNAME.so")]
But this still is Linux-only.
Is there anyway to change the library name string so it'd look for libMYLIBNAME.so under Linux and MYLIBNAME.dll on Windows, or something very similar?
I would like to avoid anything that requires recompilation for each supported platform...
(Note: even better would be a solution that'd look for MYLIBNAME.dll on Windows and /usr/lib/libMYLIBNAME.so.1 on Linux, but this improvement is optional)
Two things
1- DllImport without the extension
This is supported on Windows, Linux and MAC and will import the appropriate library for the target platform.
[DllImport("libMYLIBNAME")] -
2- The preffered option is to use the <dllmap/> which allows you to map an import library name to the target platform library name. So if on Windows you have a dll called mylib.dll and the corresponding Linux so is mylinuxlib.so.3.6.1 you can import this using the windows DLL name
[DllImport("mylib.dll")]
And add a configuration to the config to map this name to the Linux library name
<configuration>
<dllmap dll="mylib.dll" target="mylinuxlib.so.3.6.1" />
</configuration>
Read more Here
One solution I've seen is to create an abstract wrapper class around your P/Invokes and to generate the appropriate one based on environment.
public abstract class Wrapper
{
public void SomeMethod()
{
WrappedMethod();
}
public static Wrapper GetWrapper()
{
//TODO: write some method to determine OS
return IsLinux() ? new LinuxWrapper() : new WindowsWrapper();
}
public abstract void WrappedMethod();
}
public class WindowsWrapper : Wrapper
{
//windows dll imports go here
public override void WrappedMethod()
{
//p/invokes go here
}
}
public class LinuxWrapper : Wrapper
{
//linux dll imports go here
public override void WrappedMethod()
{
//p/invokes go here
}
}
Windows isn't picky about the filename extension for a DLL. Changing them isn't unusual, .ocx for ActiveX controls, .scr for screen savers for example. But still a regular DLL. The Windows loader verifies the identity of the file from the content, the PE32 header is what makes it a true DLL.
So just rename your Window version of the .dll to .so. Change the linker's Output name setting or just rename the file.

What makes an app console or Windows Form application?

[Visual Studio 2008]
I created a new project for console application and modified it to look like this:
class Program
{
static void Main (string[] args) {
Thread.Sleep (2000);
}
}
Then I created another project for Windows Form application and modified it:
static class Program
{
//[STAThread] commented this line
static void Main (string[] args) { //Added args
//Commented following lines
//Application.EnableVisualStyles ();
//Application.SetCompatibleTextRenderingDefault (false);
//Application.Run (new Form1 ()); commented this line
Thread.Sleep (2000);
}
}
Now I have neither written Console functions (Console.Write etc.) in first application nor I have written forms related operations in second one. Looks identical to me.
Still first application shows BLACK window and second one doesn't show anything. What makes it work like this?
If you inspect the exe files usine ILDASM you can see that there is a difference in the Manifest (look for "subsystem").
In a Winforms application:
.subsystem 0x0002 // WINDOWS_GUI
In a console application:
.subsystem 0x0003 // WINDOWS_CUI
There may be more differencies in the IL code.
When it comes to what makes the compiler emit this differently in the two cases, this is controlled by the project file's OutputType value:
In a Winforms application:
<OutputType>WinExe</OutputType>
In a console application:
<OutputType>Exe</OutputType>
Out of curiosity I also checked that value for a Class Library project:
<OutputType>Library</OutputType>
In project properties, Application Tab, Output Type you can set to 'Windows Application' or 'Console Application'.
I believe that behind the scenes VS does exactly what Fredrik presented in his post.
Also, setting it to Console Application will show you the black console application for the windows Forms project.
Under the bonnet, there is no difference in a winform vs console exe except for a flag in the PE-header that says "I need a console". The PE header is not controlled from your C# (since it is a compile thing, not a runtime thing), so this is defined in the project file instead (<OutputType>...</OutputType>).
Or at the command-line (csc /target:exe vs csc /target:winexe).
Arguably, they could have used an assembly-level attribute that the compiler intercepted - but would that really have helped? Probably not.
If you look in the project file (csproj) you'll see that the target is defined there as either a console or windows app.

Categories