I'm trying to port one of my iOS apps from the Mono versions of the
ServiceStack.Text libraries to the PCL versions for JSON
serialization/deserialization.
I have the libraries working in a regular Windows console application. When trying to port this into the iOS application, I'm getting an "Object reference not set to an instance of an object" exception from on the ServiceStack extension method classes. The error message is a red herring; the true error is getting swollowed by a try/catch.
The static constructor in ServiceStack.Text.PCL.PclExport is doing late binding for a
platform specific DLL and can't find that DLL. The build log bears out that the
Client, Interface, and Pcl libraries aren't being packaged with the .app file even though the reference are included at the project level.
I've been able to reproduce this in a basic iOS Hello World application. If I
add a line of code that references a method in the Pcl DLL, the three missing
libraries are copied. However, I'm still getting the same "Object reference not
set to an instance of an object" error.
I suspect that this is caused by the fact the Xamarin compiler doesn't translate the DLLs into iOS binaries unless they are actually used, and since they're late bound, it doesn't know they're used and so ignores them.
I didn't think late binding was legal under the licensing on at actual iPhone? doesn't the link merge everything into one exe?
See the Xamarin Linker Docs try the -nolinkaway option.
Related
I have a legacy Windows project using the legacy 32 Bit C++ compiler. For various reasons I need to use the Windows 8+ function PathCchCanonicalizeEx. C++Builder seems to provide the header and some module definition file for that, but I can't find any library to link against:
[ilink32 Error] Error: Unresolved external 'PathCchCanonicalizeEx' referenced from C:\[...]\WIN32\DEBUG\TMP\FILE.OBJ
How am I supposed to fix this? Do I need to add a Windows 8.1 SDK? Is the necessary lib simply named differently and I can't find it? Something completely different?
According my tests, one has two options:
IMPLIB/MKEXP
I'm developing/testing a some Windows 10 21H2, which provides an implementation for PathCchCanonicalizeEx in some DLL already. So if that source DLL is known, one can use IMPLIB or MKEXP to create an import library manually. I did that and after adding the created library from IMPLIB to my project, the linker errors were instantly gone.
Though, it's not that easy to know where PathCchCanonicalizeEx is placed in. One pretty easily finds the api-ms-win-core-path-l1-1-0.dll, but that thing is NOT an actual file on the disk and therefore can't be used by IMPLIB or MKEXP. That name is only a virtual concept for the library loader to address the same named API set of modern Windows, the extension .dll doesn't mean it's a file at all.
You can use an API set name in the context of a loader operation such as LoadLibrary or P/Invoke instead of a DLL module name to ensure a correct route to the implementation no matter where the API is actually implemented on the current device. However, when you do this you must append the string .dll at the end of the contract name. This is a requirement of the loader to function properly, and is not considered actually a part of the contract name. Although contract names appear similar to DLL names in this context, they are fundamentally different from DLL module names and do not directly refer to a file on disk.
https://learn.microsoft.com/en-us/windows/win32/apiindex/windows-apisets#api-set-contract-names
What you really need to work with is KernelBase.dll, which is even documented by MS.
implib "KernelBase x86.lib" C:\Windows\SysWOW64\KernelBase.dll
implib "KernelBase x86-64.lib" C:\Windows\System32\KernelBase.dll
Module Definition File
The downside of manually creating LIB files is that one needs to maintain those with the project. Things depend on if the target is 32 or 64 Bit, DEBUG or RELEASE, so paths might become a bit complex, one might need to create relative paths for libraries in the project settings using placeholders for the target and stuff like that.
It seems that all of this can be avoided with Module Definition Files, which's purpose is to provide IMPORT and EXPORT statements to either consume exported functions by other DLLs or make that possible for others with own functions. I've successfully resolved my linker problems by simply creating a file named like my app using the extension .def alongside my other project files. That file needs to be added to the project, though.
dbxml.cbproj
dbxml.cbproj.local
dbxml.cpp
dbxml.def
dbxml.res
[...]
The following content made the app use the correct function from the correct DLL. Though, what didn't work was using the API set name, which resulted in an error message by the linker.
IMPORTS
KernelBase.PathCchCanonicalizeEx
IMPORTS
api-ms-win-core-path-l1-1-0.PathCchCanonicalizeEx
[ilink32 Error] Invalid command line switch for "ilink32". Parameter "ItemSpec" cannot be null.
[ilink32 Error] Fatal: Error processing .DEF file
The latter is after restarting C++Builder, so I guess the format of the file is simply wrong because of the API set name.
After upgrading to Visual Studio 16.3.0 (Visual Studio for Mac 8.3.1805) Autofac throwns an exeception when doing builder.Build() in a project that previously worked:
The type 'Autofac.Features.Indexed.KeyedServiceIndex'2' does not
implement the interface 'Autofac.Features.Indexed.IIndex'2'
With this release of Visual Studio (and maybe earlier versions too), iOS is compiled using Mono 6.4.0.198 and I suspected that this might be what is introducing the braking change. Alternatively the linker behaves differently with the latest tool-chain. All I can say is that this used to work and now it doesn't.
Following these guidelines is not sufficient: Cross-Platform and Native Applications
To work around the issue, first make sure that Autofac is installed both in the iOS project, Android and the .NET Standard shared project, then choose one of these two options:
Set the linker to Don't link or Link Framework SDK's Only
Or, you can; add --linkskip=Autofac (case sensitive!) to the Additional mtouch arguments in iOS Build found in the iOS project properties.
Or, you can; create a Custom Linker Configuration file, which I btw think is the best option if you need linking. It's a little bit more work, but when using a Linker Description file the compiler will give you usable feedback when you build, if you've made a mistake like misspelling an assembly etc. Also, the Linker Description file has an identical format across Android and iOS.
Note: Option 2 and 3 will only increase your app size a small amount, as it will ensure that all of the Autofac assembly is included in your app package, while all other assemblies not specified as part of 2 or 3 will continue to be linked.
I assume that the needs for one of these work-arounds is an indication that Autofac is currently not linker safe.
Anytime I try to reference an NUNIT data type in the iOS framework, I am given a compiler error saying it can't find the IConvertible type
Error CS7069: Reference to type System.IConvertible' claims it is defined assemblymscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089', but it could not be found (CS7069) (logic)
I was able to reproduce this by creating a brand new Xamarin.Forms project that only targets iOS.
Immediately after creating it, I added a second Xamarin.Forms project, this time a PCL.
I then needed to add the Xamarin.iOS library so that I can reference the iOS specific APIs, specifically the Photos API. I did this by navigating to
~/Library/Frameworks/Xamarin.iOS.framework/Versions/9.0.1.29/lib/mono/Xamarin.iOS/Xamarin.iOS.dll
Lastly, I added a blank class to the new PCL project, and created a locally scoped NUNIT field. This is where the compiler error happens. This happens in my current project when I try to get the number of photos in a PHAssetCollection, as that is an NUNIT data type.
using System;
namespace logic
{
public class EmptyClass
{
public EmptyClass()
{
nuint test = 5;
}
}
}
This gives me the compiler error I've referenced above. How can I solve this? This has become a blocking issue for me, am I referencing the wrong Xamarin.iOS.dll? It's not available in the list of nuget packages when I scan for them, so I can't add it via NuGet. NuGet only has Xamarin.Forms.dll available.
Update 1
After trying a few different combinations, I think that I have a working solution. Instead of creating a Xamarin.Forms Library, I created a normal iOS PCL library instead. This solved that problem.
I don't know why the Xamarin.Forms library wouldn't work. Is it not intended to be used for platform specific code?
Xamarin Forms PCL's need to be platform agnostic. If you have a NUnit project that needs access to a Xamarin.iOS specific reference, then you need to build within the iOS project / iOS PCL.
Xamarin.iOS.dll can only be referenced from a iOS related project.
I am working on a project using Mono for Android. After reading a cross-platform document with Xamarin. I had my data layer in a separate project and was going to move it to my Mono for Android project and then link those files to my Mono Touch project, and one day perhaps a Windows Phone 8 project.
When I moved the code, I am now unable to compile because of the System.Xml.Linq reference. The reference shows in the Mono for Android project, but I am unable to use it. I thought aiming for Froyo may be the problem, but upgrading the project to Gingerbread or Ice Cream Sandwich didn't help.
Does anybody know what I am doing wrong? I am a seasoned .NET developer and am well aware of how references work in project, but this one has me stumped.
The project the original code was working on was targeting the Mono/.NET 4 framework
Is your reference to System.Xml.Linq pointing to the .Net version or the Mono version? The one for Mono will have Version 2.0.5.0.
Also, is the reference to System.Xml.Linq in a Android Class Library/Application or in a regular .Net Class library. If it is a .Net Class library, I would try referencing the library in your Android application as a compiled (dll) reference and not a project reference. It is not a perfect solution, but I have found that this works when I needed to reference libraries that I use across multiple solutions. The reference will generally work out fine, but if there is anything that is not supported in the Mono version, then it can cause issues.
Assuming you have your code in a class library, your best bet may be to create a Mono specific version of your library project. You can import and use the same class files, but just add some Conditional compilation symbols to have it compile properly for Mono. If you are not familiar with this, take a look at how Json.Net or ServiceStack manages code for multiple platforms.
Just curious if MonoTouch has support for "Module Initializers"?
http://blogs.msdn.com/b/junfeng/archive/2005/11/19/494914.aspx
Related question: Does Mono support "Module Initializers"?
Your first link answer, at least half, your own question. The Mono runtime supports Module Initializers and the test done (to prove this) was executed with Mono's JIT.
MonoTouch uses the JIT when executing on the iOS simulator, so there's no reason why it would not work there.
It likely works (or it's a bug) for devices too, i.e. where the AOT compiler is being used. As Jason said, it's a matter of testing it. If you cannot test it yourself simply fill a bug report and attach a (self-contained) test case to it and we'll confirm if it works (or keep the bug open until it does).
From the MSDN article you linked to
Since C# does not support global functions, C# does not support module
initializer.