X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=docs%2Fusers_guide%2Fwin32-dlls.xml;h=bf243a2cd1c780eb56a1f39656503e2f84420cb8;hb=12f7b86504e94e3507e9e51cb27b14c049d943f8;hp=703bb1877dd636a7ef49e8d03a351b06e314068f;hpb=5a99cd502b29503578bc6a227bf80f2db9742e79;p=ghc-hetmet.git diff --git a/docs/users_guide/win32-dlls.xml b/docs/users_guide/win32-dlls.xml index 703bb18..bf243a2 100644 --- a/docs/users_guide/win32-dlls.xml +++ b/docs/users_guide/win32-dlls.xml @@ -71,7 +71,7 @@ Notice how the "%1" argument is quoted (or not). This problem doesn't just affect GHCi, it affects any GHC-compiled program that wants to catch console events. See the GHC.ConsoleHandler + url="&libraryBaseLocation;/GHC-ConsoleHandler.html">GHC.ConsoleHandler module. @@ -210,7 +210,8 @@ make-sessions running under cygwin. Making Haskell libraries into DLLs doesn't work on Windows at the -moment; we hope to re-instate this facility in the future. Note that +moment; we hope to re-instate this facility in the future +(see ). Note that building an entire Haskell application as a single DLL is still supported: it's just multi-DLL Haskell programs that don't work. The Windows distribution of GHC contains static libraries only. @@ -402,227 +403,152 @@ non-static to static linking is simply a question of adding Making DLLs to be called from other languages -If you want to package up Haskell code to be called from other languages, -such as Visual Basic or C++, there are some extra things it is useful to -know. This is a special case of ; we'll deal with - the DLL-specific issues that arise below. Here's an example: + This section describes how to create DLLs to be called from other languages, + such as Visual Basic or C++. This is a special case of + ; we'll deal with the DLL-specific issues that + arise below. Here's an example: - - - - -Use foreign export declarations to export the Haskell -functions you want to call from the outside. For example, - + Use foreign export declarations to export the Haskell functions you want to + call from the outside. For example: + +-- Adder.hs +{-# LANGUAGE ForeignFunctionInterface #-} module Adder where -adder :: Int -> Int -> IO Int –– gratuitous use of IO +adder :: Int -> Int -> IO Int -- gratuitous use of IO adder x y = return (x+y) foreign export stdcall adder :: Int -> Int -> IO Int - - - - -Compile it up: - - -ghc -c adder.hs -fglasgow-exts - - -This will produce two files, adder.o and adder_stub.o + Add some helper code that starts up and shuts down the Haskell RTS: - - - - -compile up a DllMain() that starts up the Haskell -RTS-––a possible implementation is: - -#include <windows.h> +// StartEnd.c #include <Rts.h> extern void __stginit_Adder(void); -static char* args[] = { "ghcDll", NULL }; - /* N.B. argv arrays must end with NULL */ -BOOL -STDCALL -DllMain - ( HANDLE hModule - , DWORD reason - , void* reserved - ) +void HsStart() { - if (reason == DLL_PROCESS_ATTACH) { - /* By now, the RTS DLL should have been hoisted in, but we need to start it up. */ - startupHaskell(1, args, __stginit_Adder); - return TRUE; - } - return TRUE; -} - + int argc = 1; + char* argv[] = {"ghcDll", NULL}; // argv must end with NULL -Here, Adder is the name of the root module in the module -tree (as mentioned above, there must be a single root module, and hence a -single module tree in the DLL). - -Compile this up: + // Initialize Haskell runtime + char** args = argv; + hs_init(&argc, &args); - -ghc -c dllMain.c - - - + // Tell Haskell about all root modules + hs_add_root(__stginit_Adder); +} - +void HsEnd() +{ + hs_exit(); +} + -Construct the DLL: - + Here, Adder is the name of the root module in the module + tree (as mentioned above, there must be a single root module, and hence a + single module tree in the DLL). Compile everything up: + -ghc –shared -o adder.dll adder.o adder_stub.o dllMain.o +ghc -c Adder.hs +ghc -c StartEnd.c +ghc -shared -o Adder.dll Adder.o Adder_stub.o StartEnd.o - - - - - -Start using adder from VBA-––here's how I would -Declare it: - - -Private Declare Function adder Lib "adder.dll" Alias "adder@8" - (ByVal x As Long, ByVal y As Long) As Long - - -Since this Haskell DLL depends on a couple of the DLLs that come with GHC, -make sure that they are in scope/visible. + Now the file Adder.dll can be used from other + programming languages. Before calling any functions in Adder it is necessary + to call HsStart, and at the very end call + HsEnd. - -Building statically linked DLLs is the same as in the previous section: it -suffices to add to the commands used to compile up -the Haskell source and build the DLL. + Warning: It may appear tempting to use + DllMain to call + hs_init/hs_exit, but this won't work + (particularly if you compile with -threaded). There are + severe restrictions on which actions can be performed during + DllMain, and hs_init violates these + restrictions, which can lead to your dll freezing during startup (see + bug + #3605). - - - - - - - -Beware of DllMain()! - -The body of a DllMain() function is an -extremely dangerous place! This is because the order in which DLLs are -unloaded when a process is terminating is unspecified. This means that -the DllMain() for your DLL may be called when other DLLs containing -functions that you call when de-initializing your DLL have already -been unloaded. In other words, you can't put shutdown code inside -DllMain(), unless your shutdown code only requires use of certain -functions which are guaranteed to be available (see the Platform SDK -docs for more info). - -In particular, if you are writing a DLL that's statically -linked with Haskell, it is not safe to call -hs_exit() from DllMain(), since -hs_exit() may make use of other DLLs (see also ). What's more, if you -wait until program shutdown to execute your deinitialisation code, Windows will have -terminated all the threads in your program except the one calling -DllMain(), which can cause even more -problems. - -A solution is to always export Begin() and End() functions from your -DLL, and call these from the application that uses the DLL, so that -you can be sure that all DLLs needed by any shutdown code in your -End() function are available when it is called. - -The following example is untested but illustrates the idea (please let us - know if you find problems with this example or have a better one). Suppose we have a DLL called Lewis which makes use of 2 -Haskell modules Bar and Zap, -where Bar imports Zap and is -therefore the root module in the sense of . Then the main C++ unit for the DLL would -look something like: + +Using from VBA + + An example of using Adder.dll from VBA is: + - // Lewis.cpp -- compiled using GCC - #include <Windows.h> - #include "HsFFI.h" - - #define __LEWIS_DLL_EXPORT - #include "Lewis.h" - - #include "Bar_stub.h" // generated by GHC - #include "Zap_stub.h" - - BOOL APIENTRY DllMain( HANDLE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ){ - return TRUE; - } - - extern "C"{ - - LEWIS_API HsBool lewis_Begin(){ - int argc = ... - char *argv[] = ... - - // Initialize Haskell runtime - hs_init(&argc, &argv); - - // Tell Haskell about all root modules - hs_add_root(__stginit_Bar); - - // do any other initialization here and - // return false if there was a problem - return HS_BOOL_TRUE; - } +Private Declare Function Adder Lib "Adder.dll" Alias "adder@8" _ + (ByVal x As Long, ByVal y As Long) As Long - LEWIS_API void lewis_End(){ - hs_exit(); - } +Private Declare Sub HsStart Lib "Adder.dll" () +Private Declare Sub HsEnd Lib "Adder.dll" () - LEWIS_API HsInt lewis_Test(HsInt x){ - // use Haskell functions exported by - // modules Bar and/or Zap +Private Sub Document_Close() +HsEnd +End Sub - return ... - } +Private Sub Document_Open() +HsStart +End Sub - } // extern "C" +Public Sub Test() +MsgBox "12 + 5 = " & Adder(12, 5) +End Sub + + + This example uses the + Document_Open/Close functions of + Microsoft Word, but provided HsStart is called before the + first function, and HsEnd after the last, then it will + work fine. + + -and some application which used the functions in the DLL would have a main() function like: + +Using from C++ - // MyApp.cpp - #include "stdafx.h" - #include "Lewis.h" + + An example of using Adder.dll from C++ is: + - int main(int argc, char *argv[]){ - if (lewis_Begin()){ - // can now safely call other functions - // exported by Lewis DLL + +// Tester.cpp +#include "HsFFI.h" +#include "Adder_stub.h" +#include <stdio.h> + +extern "C" { + void HsStart(); + void HsEnd(); +} - } - lewis_End(); - return 0; - } +int main() +{ + HsStart(); + // can now safely call functions from the DLL + printf("12 + 5 = %i\n", adder(12,5)) ; + HsEnd(); + return 0; +} - -Lewis.h would have to have some appropriate #ifndef to ensure that the -Haskell FFI types were defined for external users of the DLL (who -wouldn't necessarily have GHC installed and therefore wouldn't have -the include files like HsFFI.h etc). + + This can be compiled and run with: + +$ ghc -o tester Tester.cpp Adder.dll.a +$ tester +12 + 5 = 17 + + + + @@ -630,7 +556,6 @@ the include files like HsFFI.h etc).