How to import function from c++ dll - c#

Here is c++ dll code:
#include<stdlib.h>
#include "pch.h"
extern "C" _declspec(dllexport) int Add(int a, int b)
{
return a + b;
}
C# code, that i run in Visual Studio:
[DllImport(#"C:\Dll1.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int port, int speed);
public static void Main()
{
int res = Add(8, 11);
Console.WriteLine(res);
}
No problems, output: 19
PowerShell code:
Add-Type -Name MyFunctions -Namespace Cpp #'
[DllImport(#"C:\Dll1.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern int Add(int port, int speed);
'#
[Cpp.MyFunctions]::Add(8, 11)
Output:
An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)
But functions from user32.dll/kernel32.dll imports without any problems.
How to fix it?

Make sure that you are using 32 bit dll for 32 bit powershell process and 64 bit dll for 64 bit powershell process.
The error looks like as if you trying to load 32 bit dll into 64 bit process. Please note that .net applications compiled as "Any CPU" will run in 32 bit mode by default even on 64-bit operating system.
Update
If you have a sources for your dll, then you should recompile it for 64 bit environment and then import this 64-bit build.
If you need to execute PS script in a both 32- and 64-bit powerShell environments, then you should deploy two versions of dll and select the right one in runtime. I do not know how this can be done for PS, but for C# solution you can use #ifdef-based solution as described here: Using a 32bit or 64bit dll in C# DllImport
If you do not have sources for your dll, then all become complicated and I do not know any direct solution here. You can try to wrap your DLL in the 32-bit in-process (or even out-of-process) COM server (with help of C# or C++) and then use this server with New-Object. As I know, the Windows have special shims which allow to utilize 32-bit COM objects from 64-bit process and vice versa, but I am unsure if this will work for PS or not.
For build-in DLLs Windows has automatic file paths redirection from System32 to SysWOW64. So, when you requesting user32 import, you will get different user32 libraries depending on your process bitness. You can read more about this here: 64bit PowerShell calls the 32bit DLL

Related

Does cross-platform C++ require building separately on every single platform?

My question actually has two variants, but for some context, I have a C++ *.DLL that I use with Unity using P/Invoke. It's built on Windows 10 with MSBuild (platform toolset: ClangCL).
And now for my questions,
Does the size of a particular type stay constant after the DLL has been built? Or does it need to be rebuilt again on the target platform (say, Linux or Mac)?
My code looks a bit like this:
typedef wchar_t wchar;
static_assert(sizeof(wchar) == 2, "WideChar size-test failed.");
After I have recompiled the source on Windows, if I build the Unity project for the Android platform, will the size of wchar remain the same on there as well? Or am I in for a nasty surprise when my string-handling code just stops working?
Are some of the other language features maintained after the DLL is built, or do I need to change my workflow to account for something else?
In particular, let's look at something super-basic, like extern methods.
#define CALLING_CONVENTION __stdcall
#if _WIN32
#define DLLEXPORT(type) extern "C" __declspec(dllexport) type CALLING_CONVENTION
#else
#define DLLEXPORT(type) extern "C" type CALLING_CONVENTION
#endif
DLLEXPORT(void) DoSomething(const wchar* ManagedString);
// expands to
// extern "C" __declspec(dllexport) void __stdcall DoSomething(const wchar* ManagedString)
// on Windows
And for managed,
[DllImport("DllName", CharSet = CharSet.Unicode)]
public static extern void DoSomething([MarshalAs(UnmanagedType.LPWStr)] string input);
Will this maintain interoperability for other platforms as well? Or do I need to go out of my way to build the C++ DLL on Linux and then use that for an Android platform?
Each platform usually has a different ABI. For example, long on Windows is 32-bit (from memory), but long on Linux is 64-bit. In your example, wchar_t on Windows is 16-bit, but on Linux it is 32-bit. The way that C++ names are mangled is also platform-specific.
Both Windows and Linux have different syscalls (standard library functions like malloc usually end up calling platform-specific syscalls). But not only that; the way that each platform stores and loads executable code (and how it dynamically links to other executable code) is also different, and without some sort of translator in between (such as Wine), it's typically not possible to run executable code that was compiled for a different platform.
So in general, you will need to compile your code for each platform you intend to support. See also this post in the Unity forums.

How to call cygwin compiled C++ from .NET Core?

I am trying to do something similar to this:
I am working on Windows but my intention is to make my code work on Linux too later on (therefore I work with cygwin and clion for C++ ). VS2017 to compile the C# for a .NET Core app with a normal C# compiler. My problem is getting this error in visual studio:
"The program '[19944] dotnet.exe' has exited with code -1073741819
(0xc0000005) 'Access violation'."
Here is my cmake file (generated with clion):
cmake_minimum_required(VERSION 3.10) project(callFromCsharp)
set(CMAKE_CXX_STANDARD 14)
add_library(callFromCsharp SHARED library.cpp)
Here is my C++ code in library.cpp:
#include <cstdint>
extern "C" __declspec(dllexport) int32_t Test(){
return 10;
}
This is my cmake call generated by clion
C:\Users\Daant.CLion2018.1\system\cygwin_cmake\bin\cmake.exe --build
/cygdrive/c/Users/Daant/CLionProjects/callFromCsharp/cmake-build-release
--target callFromCsharp -- -j 6
Here is my C# code:
class Program
{
[DllImport("cygcallFromCsharp.dll", EntryPoint = "Test", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern Int32 Test();
[STAThread]
static void Main()
{
var res = Test();
Console.WriteLine($"Done! {res}");
Console.ReadLine();
}
}
How to solve this? I just want to call a C++ method without errors or exceptions.
Lets begin with what not to do
when loading Cygwin dll from C# (I guess from Visual studio it will be the same).
do not use AnyCPU as platform, prefer to use x64 or x86 platform, in respectively to the Cygwin dll.
for some reason, I didn't figured out yet why, calling sprintf, sscanf ,stringstream ... and prints to console methods from the dll cause the program to halt.
Now What you can do:
Make sure cygwin bin folder added to path, or copy your DLL's dependencies to the DLL's folder (dependencies are usually: Cygwin1.dll, cyggcc_s-seh-1.dll cygstdc++-6.dll. use Dependency Walker tool to check).
Just to make sure: add EXPORT_API macro, use it on each exported method. like:
#define EXPORT_API extern "C" __cdecl __declspec(dllexport)
Your DLL sample is very simple, compile your code using Cygwin console:
g++ -c library.cpp; g++ -o cygcallFromCsharp.dll library.o
I used from C# the following (debug running dir set to dll location):
DllImport(#"cygcallFromCsharp.dll", CallingConvention=CallingConvention.Cdecl)]
static extern int Test();
Hope it will help.
I implemented a dotnet loader which is compatible with cygwin.
You can find it here: https://github.com/smx-smx/EzDotnet
In order to be able to use Cygwin from .NET (without any crashes) the entry point MUST be a Cygwin application, compiled and linked under cygwin.
I added a cygwin sample that demonstrates the use of P/Invoke as well as read(2) and write(2) to redirect the C# stdin/stdout to cygwin (otherwise it wouldn't be visible)
./samples/cli/ezdotnet.exe ./CoreCLR/cygcoreclrhost.dll ./samples/Managed/Cygwin/bin/Debug/net5.0/Cygwin.dll ManagedSample.EntryPoint Entry

Call into 64bit Dephi DLL from C# on 64bit Machine

I my c# app I am trying to call into a Delphi DLL build as 64bit. I keep getting an error stating "An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B". I am running on a Windows 7 64 bit machine and have my C# project set to Any CPU.
API call
[DllImport("Cipher.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.ThisCall)]
public static extern void Encrypt(StringBuilder szPlainText, StringBuilder zCipherText);
Encrypt(plainString, encText);
If the Delphi DLL was build as 32bit this call works fine. Any ideas?
For some reason your app is not run as a 64-bit but rather a 32-bit process. If you need to do things like this, it's better to specify "x64" instead of "AnyCPU".

How to create win32 smartpone dll and call by C# DllImport in WinCE

I need to implement a Win32 DLL and need call it by C# DllImport in WinCE.
So I create a Win32 Smart Device Project and choice Windows Mobile 5.0 Smartphone SDK,
and create a dll project with Export symbols option.
Then I add extern "C" key word before function declare:
.h
#ifdef WINCE2_EXPORTS
#define WINCE2_API __declspec(dllexport)
#else
#define WINCE2_API extern "C" __declspec(dllimport)
#endif
extern "C" WINCE2_API int __cdecl Add(int A,int B);
.cpp
extern "C" WINCE2_API int __cdecl Add(int A,int B)
{
return A+B;
}
When I use DllImport in C#:
[DllImport("WinCE2.dll", EntryPoint = "Add")]
static extern int Add(int A, int B);
I always got a System.MissingMethodException in WinCE 5.0 emulator and WinCE6.0 Device.
I searched some information on google, found some solution.
First, Add .def in project:
LIBRARY "WinCE2"
EXPORTS
Add DATA
But in a forum someone say __declspec(dllexport) can replace the .def file.
But this solution also got System.MissingMethodException.
Then I found a solution on Stack Overflow:
May I need add __cdecl key word.
And I created a Win32 DLL Project, I found the project setting will add __cdecl in default.
But Win32 Smartphone project is not.
So I try it, but also got System.MissingMethodException.
Then I try the same code in Win32 DLL and call by C#, it can work.
So I don't why wince can't work.
I had copied the dll to wince executable file folder
Can anyone share me some expeience?
You built this using the Smartphone SDK, which is ARM-based. The CE 5.0 emulator emulated x86, so it's not going to be callable there. If your CE 6.0 device is likewise x86-based, it too is going to have the same problem. Try building it using an x86 SDK.
Your WinCE2.dll is not copied to WinCE device app executable folder. Try getting the same using FileInfo. I cannot find the file. Check the project settings and add the wince2.dll and set it as a content type and provide copy if newer option.

32 bit dllimport generating incorrect format error (0x8007000b) on win7 x64 platform

I'm trying to install and run a 32 bit application on a Win7 x64 machine. The application is built as a Win32 app. It runs fine on 32 bit platforms. On the x64 machine it installs correctly in the Programs(x86) directory and runs fine until I make a call into a 32 bit dll. At that time I get the incorrect format error (0x8007000b) indicating it is trying to load the dll of the wrong bitness. Indeed it is trying to load the 64 bit dll from the System32 directory rather than the 32 bit version in the SystemWOW64 directory.
Another 32 bit application provided by the dll vendor runs correctly and it does load the 32 bit dll from the SystemWOW64 directory. I do not have source to their application to see how they are accessing the DLL.
I'm using the DllImport function as shown below to access the dll. Is there a way to decorate the DllImport calls to force it to load the 32 bit version?
Any thoughts appreciated.
Thanks, DP
public static class Micronas
{
[DllImport(#"UAC2.DLL")]
public static extern short UacBuildDeviceList(uint uFlags);
[DllImport(#"UAC2.DLL")]
public static extern short UacGetNumberOfDevices();
[DllImport(#"UAC2.DLL")]
public static extern uint UacGetFirstDevice();
[DllImport(#"UAC2.DLL")]
public static extern uint UacGetNextDevice(uint handle);
[DllImport(#"UAC2.DLL")]
public static extern uint UacSetXDFP(uint handle, short adr, uint data);
[DllImport(#"UAC2.DLL")]
public unsafe static extern uint UacGetXDFP(uint handle, short adr, IntPtr data);
}
Force your .exe to run in 32-bit mode. Project + Properties, Build tab, Platform Target = x86.
Avoid getting confused by the project's Platform setting, it doesn't actually have to match the real setting. Which is Project + Properties, Build tab, Platform Target. Bit of a mess there.
Since your post is tagged with C#, I'm assuming this is a .NET app. It's probably not being set 32-bit only. You can verify by running corflags.exe on it, and setting the 32-bit only flag on it with corflags /32bit+ myapp.exe. If that fixes it, you need to fix your solution/project to build as x86 instead of Any CPU.
In order to force the application to run in 32-bit mode on 64-bit platform you can set it in IIS application pool: in advanced settings set "Enable 32-bits applications" to TRUE.

Categories