I am trying to build a Windows DLL from my Haskell code. The functions in this DLL are supposed to be called from a managed code in C#. And, atleast one of the function (defined in the c# code) is to be called from a function in this DLL.
At the risk of over explaining, here's a small diagram to depict what I want:
+----------------------+ +------------------------+
| Managed C# code | | Haskell code (in DLL) |
| | (1) | |
| fn_calling_hs() -----------------> fn_called_from_cs() |
| | | |
| | | |
| fn_called_from_hs() <--------------- fn_calling_cs() |
| | (2) | |
+----------------------+ +------------------------+
I managed to make the (1) work perfectly, i.e., a Haskell function in the DLL is called by C# code, with correct marshalling of structures and arrays, and the results from the function execution in Haskell is also correct. So far, so good.
The problem is with (2), i.e., a function from Haskell (in the DLL) calling a managed function defined in C#. The problem is in the build itself - I have not yet gone past that to actually check the results of (2).
As the fn_called_from_hs() in the c# managed code is defined in C#, I only have the function symbol "imported" in the Haskell code (in DLL):
foreign import ccall fn_called_from_hs :: IO CString
Now, when I build my Haskell project with stack, it builds the Haskell DLL without problems, but the build continues to also link "main.exe" - and this fails (obviously), because there is no function fn_called_from_hs() defined anywhere in the Haskell code (it is defined in c#).
Is there any way that I can stop stack from continuing to build main.exe after building HsDLL.dll? I am ok with HsDLL.dll having unresolved symbol (fn_called_from_hs()) because this symbol will be found by the runtime linker during the loading of this DLL by the managed C# code.
So far, I have tried these steps, but none of them helped:
Added the GHC option: -no-hs-main
in the package.yaml. The package.yaml
portion that contains building of HsDLL looks like this:
library:
source-dirs:
- src
- src/csrc
include-dirs: src/csrc
ghc-options:
- -shared
- -fno-shared-implib
- -no-hs-main
-dynamic
flag in the ghc-options in the hopes that GHC will assume that the unresolved symbols will be defined elsewhere, but this gave other problems: GHC now complains that it needs "dyn" libraries of base, etc.So, finally, I always end up with this:
PS C:\workspace\Haskell\hscs\src\csrc> stack build
hscs-0.1.0.0: configure (lib)
Configuring hscs-0.1.0.0...
hscs-0.1.0.0: build (lib)
Preprocessing library for hscs-0.1.0.0..
Building library for hscs-0.1.0.0..
Linking main.exe ...
.stack-work\dist\5c8418a7\build\HsLib.o:fake:(.text+0x541): undefined reference to `fn_called_from_hs'
collect2.exe: error: ld returned 1 exit status
`gcc.exe' failed in phase `Linker'. (Exit code: 1)
-- While building custom Setup.hs for package hscs-0.1.0.0 using:
C:\tools\HaskellStack\setup-exe-cache\x86_64-windows\Cabal-simple_Z6RU0evB_2.0.1.0_ghc-8.2.2.exe --builddir=.stack-work\dist\5c8418a7 build lib:hscs --ghc-options " -ddump-hi -ddump-to-file -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
So, my questions are: (1) I have absolutely no idea how to stop linking "main.exe"! I know that the function fn_called_from_hs() is not defined within the HsDLL, but, as I said, I am ok because it is defined in the managed c# code. I just want main.exe not to be built.
OR
(2) Should I go ahead with adding -dynamic
flag to GHC (keeping all the other flags as above)? In this case, how do I get stack to install the "dyn" libraries that GHC is complaining about?
Can somebody help me? Thanks in advance for your patience in reading this (rather) long question!
Ways: 1. Insert your exe file that is made with c++ to your html code in <a href="cppfile.exe"> And you will have an tag and after clicking it will download your cppfile.exe 2. The second way is to ofstream html tags from c++ file.
You can include or embed C code in an HTML document, but it will not do anything; it will taken just as text data. There is no support in browsers for using C as client-side scripting language. Theoretically, you could write an interpreter for C (or a subset of C) in JavaScript.
Press Ctrl+K. You can also right-click the text or picture and click Link on the shortcut menu. Under Link to, click Existing File or Web Page. In the Look in box, click the down arrow, and find and select the file that you want to link to.
And so finally, I managed to solve this myself! After a week of struggle, that is. And any helpful comments to add it this answer is welcome.
I did this as follows:
In C# class DLL:
I had to find a way to "export" my function fn_called_from_hs()
to unsafe native code. I found this is not really straight-forward, and there are really quite some amount of articles on the internet to explain how this is done. Everything amounts to actually disassembling the .NET DLL via the tool ildasm
, and in the intermediate IL file generated, adding an ".export" prefix to the function that we want to export, and then again assembling the IL file back to the DLL form using ilasm
.
I found all these steps are automated by the NUGetPackage Unmanaged Exports, so the first step is to install this package as a part of your .NET project, and then adding the DLLExport
attribute to your function to be exported. Make sure you have RGiesecke.DllExport
in your list of imports:
using RGiesecke.DllExport;
[DllExport("fn_called_from_hs", CallingConvention=CallingConvention.Cdecl)]
public static string FnCalledFromHs()
{
// Your function code here
}
As you can see, I have named the actual function as FnCalledFromHs()
(in accordance with the naming convention in C#), but exported the same function as fn_called_from_hs
(in accordance with the naming convention in Haskell). This way, when you look at the Haskell code, you will not see anything that looks out of place.
One of the most important steps for this to actually work is to make sure that the project in which you are exporting the function is made to target x64 or x86 - On default the projects target "Any CPU" - RGiesecke.DllExport
does not work if the project targets "Any CPU".
Now build the project to get the csharp.dll which contains your exported fn_called_from_hs
.
Before linking Haskell code
Mingw GCC (which ghc on Windows internally uses) can actually directly link with DLLs, provided they were created with gcc before. However, since we have created our C# DLL using the .NET compiler csc, we need to specifically create an import library that our Haskell can see.
We use two tools to our aid: gendef
and dlltool
, both of which are in the "mingw\bin" folder within your ghc installation (so, of course, you need to have this in your PATH env variable to access these tools).
Here's how I went about it:
Created a .def file which in-turn can be used for creating an import library:
gendef csharp.dll
Created an import library with dlltool:
dlltool -k -d csharp.def -l csharp.lib
Copied the above import lib to the same directory in which the DLL was present.
The last step (below) now will use this import library for actually linking with the csharp DLL.
Linking Haskell code with the above import library
This was a little trickier, and has possibly made me hit a bug in stack / GHC (not sure), but have already filed here.
I went about this as follows:
Added extra-lib-dirs
in my stack.yaml, and added the directory in which the above import-lib was created:
extra-lib-dirs: ["<drive>:\\path\\to\\importlib"]
(Note that this could have also been added to your package.yaml under "libraries", but I chose to have it in my stack.yaml).
Added extra-libraries
to my stack.yaml, under libraries.
extra-libraries: csharp
And, added also the options -l and -L to my ghc-options for linking my library. This is what I did to circumvent the (possible) bug that stack somehow is not passing the extra-lib-dirs
and extra-libraries
to ghc and ld during linking. So, my final "library" section in package.yaml looks like this (compare it to how it was before in my question above):
library:
source-dirs:
- src
- src/csrc
include-dirs: src/csrc
ghc-options:
- -shared
- -fno-shared-implib
- -lcslib
- -L<drive>:\\path\\to\\importlib
extra-libraries: csharp
Conclusion
With all this done, my Haskell code now simply builds well with the normal stack build
command, without any "unreferenced symbols" error. On executing my Haskell code, I also checked that the c# function fn_called_from_hs
was actually called, and the results got returned correctly.
Of course, there is more to this from the c# side: correct marshalling of parameters, etc., and I had to also work on those to get my result correct. The only place I can cover all of these nitty-gritties is in a blog :-)
Please feel free to cross-verify my solution, and also comment on any better way of doing this. This was the best way I could figure out after my struggles!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With