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.
Related
I have x64 native C++ library that I have to pass to C# project.
I built C++/CLI wrapper, based on this tutorial, and everything was ok.
But, the project compiles only on x86 architecture.
When I tried adding that native C++ library to project, I received runetime error.
Project doesn't work on x64 architecture because wrapper for some reasons requires x86. And, on the other hand, it doesn't work on x86 because that library requires x64.
I have very little experience with C++/CLI, wrappers, and C# in general, and I don't have much idea how to go around this problem.
When tring to compile Solution, I receive runetime error
System.BadImageFormatException: Could not load file or assembly 'Wrapper, Version=1.0.7178.20781, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An attempt was ma
de to load a program with an incorrect format..
Link to error documentation
Here is my Wrapper
using namespace System;
namespace CLI {
template<class T>
public ref class Wrapper
{
protected:
T* m_Instance;
public:
Wrapper(T* instance)
:m_Instance(instance)
{
}
virtual ~Wrapper()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
!Wrapper()
{
if (m_Instance != nullptr)
{
delete m_Instance;
}
}
T* GetInstance()
{
return m_Instance;
}
};
}
...And here is a C++/CLI class that is using this wrapper
//**********************header file***********************
#include "Wrapper.h"
#include "../Core/Core.h"
using namespace System;
namespace CLI
{
public ref class Model : public Wrapper<Core::Impl>
{
public:
Model();
bool test();
};
//**********************Implementation******************************
#include "Model.h"
namespace CLI
{
Model::Model()
:Wrapper(new Core::Impl())
{
Console::WriteLine("Creating new Impl-wrapper object!!");
}
bool Model::test()
{
return m_Instance->test();
}
}
Its pretty much exacly the same as from the tutorial that I used.
I can not modify that native C++ library, so it has to work on x64 architecture.
Can you please explain to me, why wrapper doesn't want to compile on x64, but works perfectly on x86, and is there a way to go around this. Perfect answer would provide an example of C++/CLI Wrapper that is working on x64 architecture.
Thanks in advance
EDIT,
oh, and I forget to add properties of my project so.
OS is Win10 (x64); .NET target framework 4.5.1; Core project(the lowest layer project, not presented here) is built as static .lib, and Wrapper is a dynamic .dll.
VisualStudio 2017 v15.9.14
Double check your project setting, especially linker. Check Command line tab for linker. Recently I encountered wild X86 flag there in Additional options that gave me similar errors.
In advanced, check Target machine.
Try to enable verbose output for linker and compiler, and check for any occurrence of x86.
If all of that is ruled out, make sure that your lib was really compiled and is valid, eg. via dependency walker.
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
I would like to create a new Excel function (user Defined Function), for that i did the same steps as in this link: https://excel-dna.net/
I created a class Library Project
I installed the package ExcelDna.Integration
I created a method sayHello
But when i try to call my function from a cell in excel (i put =sayHello("World"), the function didn't appear, it seems it's not added to excel functions.
is there some missing steps? how can i make my function appear to be able to use it ?
You should install the package "ExcelDna.AddIn" to make the add-in (that will set up the add-in including the important .dna file, and also bring in the "ExcelDna.Integration" reference library).
So the steps would be:
Create a new C# Class Library project (targeting .NET Framework not .NET Standard)
Install the ExcelDna.AddIn package
Add some code, e.g.
public static class MyFunctions
{
public static string SayHello(string name) { return "Hello " + name; }
}
Press F5 to compile and load the add-in in Excel
(Note that on some Excel installations, the Debug setting for the project get an extra %1 in the executable path - just remove this from the end if you get an error when debugging)
The best support for Excel-DNA is the Google group at https://groups.google.com/forum/#!forum/exceldna
Above is my folder structure. I have a Cordova app and a Windows Runtime Component - IBscanUltimate. The include folder has the C# code calling into the unmanaged IBscanUltimate.dll. Class1.cs is like this:
using System;
namespace IBscanUltimate
{
public sealed class Class1
{
public static String getSDK()
{
IBscanUltimate.DLL.IBSU_SdkVersion a = new DLL.IBSU_SdkVersion();
IBscanUltimate.DLL._IBSU_GetSDKVersion(ref a);
return "SDKVersion: " + a;
}
}
The IBScanUltimateApi.cs & _IBSU_GetSDKVersion look something like this:
internal partial class DLL
{
[DllImport("IBScanUltimate.DLL")]
private static extern int IBSU_GetSDKVersion(ref IBSU_SdkVersion pVerinfo);
public static int _IBSU_GetSDKVersion(ref IBSU_SdkVersion pVerinfo)
{
int nRc = IBSU_STATUS_OK;
nRc = IBSU_GetSDKVersion(ref pVerinfo);
return nRc;
}
}
I have placed the DLL in many locations to see if it'll get picked up and they all have the above properties. But when I try to run my app, it says unable to locate the IBScanUltimate.DLL
This is how the output is coming:
I am not sure what is it that I am doing wrong and why the DLLImport cannot find my dll. Thank you for your help.
Exact error is:
System.DllNotFoundException: Unable to load DLL 'IBScanUltimate.DLL': The specified module could not be found. (Exception from HRESULT: 0x8007007E)
Update #1:
I have come across https://msdn.microsoft.com/en-us/library/windows/desktop/hh447159(v=vs.85).aspx This article is explaining that LoadPackagedLibrary function can be used to load the dll. I am not seeing any example on how to use this in C#.
Update #2:
Specify the search path for DllImport in .NET Mentions that SetDllDirectory or AddDllDirectory can be used. He has a code snippet for SetDllDirectory, but the argument is string[] paths. How would I specify the relative argument?
Update #3:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetDllDirectory(string lpPathName);
public static bool setPath(String path)
{
//Windows.Storage.
//return SetDllDirectory("ms-appx:///");
return SetDllDirectory(path);
}
I tried calling the SetDllDirectory(path) method with various locations that my app should have access to but I am keep getting "false". Few examples that I have tried:
NativeMethods.setPath(Package.Current.InstalledLocation.Path.ToString());
StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFolder folder = Windows.Storage.KnownFolders.MusicLibrary;
This is where my app is installed:
C:\Users\AAA\App\hello\platforms\windows\build\windows\Debug\x64\AppX
and I can see that my DLL is there. But still I'm getting the exception that DLL cannot be found. Do I have to put something on the manifest regarding this?
Update #4:
I ran a dumpbin on the DLL and i see the below DLL in the dumpbin:
WINUSB.DLL
mfc90.dll
MSVCR90.dll
KERNEL32.dll
USER32.dll
GDI32.dll
VERSION.dll
MSVCP90.dll
SETUPAPI.dll
I guess I'd like to check on each dll above separately to see if my windows runtime can pick it? One of them could be the culprit that's not being loaded?
Update #5:
Upon seeing the answer from Peter Torr - MSFT, and googling for MFC I came across this article https://msdn.microsoft.com/en-us/library/d06h2x6e.aspx Which states:
The MFC classes and their members cannot be used in applications that execute in the Windows Runtime.
I guess to conclude this wild hunt now. I would close this up that the library I tried to load is dependent on libraries not available for Windows Runtime.
I had this feeling because Windows form application would run but the the code converted to Windows Runtime would give the error that the DLL is not being found. Thanks to Peter for guiding in the right direction.
The DLL you are trying to load was clearly built for desktop apps (it has User, GDI, and MFC imports) and will not work as a UWP binary. I suspect also that the DLL does not have the AppContainer flag set (an option you pass to the linker). You will need to find another way to accomplish what you need (if necessary, please make any feature requests via the Windows Platform UserVoice.
I suspect that it can find your DLL just fine, but it fails to find one or more of its dependencies. Unfortunately, both of these cases result in extremely generic DllNotFoundException that mentions the DLL your try to P/Invoke to.
There is an easy way to figure out what's missing! Windows contains a feature called "loader snaps", which, when enabled, will log all the things that Windows DLL loader does for your process. That means it will also print what DLLs it fails to load. To enable it, run this in admin command prompt:
gflags.exe -i "<executableName>.exe" +sls
Where executable name is just the name of your executable without the folder. To see the output, you will also need to enable either native or mixed mode debugger. You can do that in your project properties debugging tab in Visual Studio.
You can read more about load snaps here:
https://blogs.msdn.microsoft.com/junfeng/2006/11/20/debugging-loadlibrary-failures/
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.