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
Related
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
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.
I have some C++ code originally written for Linux that I can build and run without problems under 64-bit Windows using gcc under cygwin. Now, however, I want to turn it into a DLL that I can invoke from a C# program.
I find I can get C# to call C++ functions compiled under cygwin without problems, as long as they don't use any of the standard C/C++ library functions defined in cygwin1.dll. But as soon as I do a printf, for example, the whole thing exits with a "Segmentation fault".
Searching for answers online, I see it claimed that you need to call cygwin_dll_init() first. However, when I add that, it simply terminates with no error message at all!
What am I doing wrong? I'm pretty sure that the .NET runtime is successfully finding both my DLL and cygwin1.dll, because I get a DllNotFoundException instead if either isn't present.
Here's a minimal test case that produces the same symptoms. First, the cygwin/C++ code for the DLL:
#include <cstdio>
extern "C" __declspec(dllexport) int test();
int test() {
printf("Test successful");
return 14;
}
I compile this with "g++ -o test.dll -shared test.cpp".
Here's the C# code that invokes it:
using System;
using System.Runtime.InteropServices;
class MainClass {
[DllImport("cygwin1.dll")]
private static extern void cygwin_dll_init();
[DllImport("test.dll")]
private static extern int test();
public static void Main(string[] args) {
Console.WriteLine("running...");
// Remove this line if you want a seg fault
cygwin_dll_init();
test();
// Seg fault or no, this never gets run
Console.WriteLine("done");
}
}
I strongly recommend that you compile your DLL using the mingw64 cross compiler. This allows you to build an DLLs that does not use the cygwin1.dll. Instead, it will call the Windows runtime DLLs only. As long as you do not need to call any other cygwin DLLs, this should work fine.
you will need to install the mingw64 compiler package using the cygwin setup app. Install the mingw64-x86_64-gcc-g++ package.
create GNUmakefile as follows:
# GNUmakefile - for cygwin make will use this instead of Makefile
CXX=x86_64-w64-mingw32-g++
CXXFLAGS=-Wall -Wextra -Werror
all: libtest.dll
clean:
rm -f *.dll
libtest.dll: test.cpp
$(CXX) $(CXXFLAGS) -shared -o libtest.dll test.cpp
At a cygwin bash prompt, run make clean all
Create Makefile as follows:
# Makefile - for VS nmake
CS=csc
CSFLAGS=-nologo -w:3 -warnaserror+ -optimize- -debug+
all : main.exe
clean :
del /q *.exe 2> nul:
del /q *.pdb 2> nul:
main.exe : main.cs
$(CS) $(CSFLAGS) -t:exe -out:main.exe main.cs
Run this at a cmd.exe prompt as nmake -nologo clean all.
Run the exe as main.exe. As usual libtest.dll must be in the same directory as main.exe, or in the path.
The makefiles here are cut down from versions that build with and without using the cygwin1.dll and, I hope don't, but may contain errors.
I have C++ DLL as below
#include "stdafx.h"
extern "C" __declspec(dllexport)double Add(double a, double b);
extern double Add(double a, double b)
{
return a + b;
}
n here m trying to link this DLL with my C# app
using System.Text;
using System.Runtime.InteropServices;
namespace test
{
class Program
{
[DllImport("DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern double Add(double a, double b);
static void Main(string[] args)
{
Console.WriteLine(Add(1.0, 3.0)); // error here
Console.ReadLine();
}
}
}
m getting error:
"Unable to load DLL 'DLL.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)"
please help me out ...how can i link c++ dll with c# ?
The calling convention determines how function parameters are placed on the stack prior to a function invocation, and how they are removed (caller vs. callee) when the function returns. You can find out much more about this in about a million StackOverflow questions, or goto here and read up a little.
Regarding placement of the DLL within reach of the C# (aka .NET) application you're writing, I'm afraid I cannot comment on that except to say general DLL's must be in your lib-search path (PATH in Windows) the current directory, or the kernel's home directory (generally c:\windows\system32. Do NOT copy files to system32, btw. just setup your application to "run from" the directory where your DLL is residing and you should be fine. There are exceptions to this, and configuration settings that can radically alter this, but were I you i'd stick with simple for now. Complex can always come later.
You will either need to plant the dll in the same location as the C# exe or pack the dll inside the exe. The first option is simple enough. For the second option, check out Embedding DLL's into .exe in in Visual C# 2010
you got this error because DLL.dll wasn't in your Debug/Release Folder,
as far as i know visual studio doesn't know how to copy those files to your output folder manualy.
add the dll file to your C# solution
and then on files properties set build action to content
and set copy to output directory to copy if newer this will automate the copying
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.