packages feed

Win32-extras-0.2.0.0: System/Win32/DLL/LoadFunction.hs

{-# LANGUAGE CPP #-}
{- |
   Module      :  System.Win32.DLL.LoadFunction
   Copyright   :  2012 shelarcy
   License     :  BSD-style

   Maintainer  :  shelarcy@gmail.com
   Stability   :  Provisional
   Portability :  Non-portable (Win32 API)

   Load a DLL's function.
-}
module System.Win32.DLL.LoadFunction
  ( module System.Win32.DLL.LoadFunction
  ) where
import Control.Exception            ( bracket, mask, finally )
import Foreign.C.String             ( withCAString )
import Foreign.Ptr                  ( FunPtr, castPtrToFunPtr )
import System.Win32.Error           ( failIfFalse_ )
import System.Win32.Exception.Unsupported
import System.Win32.DLL             ( c_GetProcAddress, c_LoadLibrary, freeLibrary, getModuleHandle )
import System.Win32.Types hiding    ( failIfFalse_ )
----------------------------------------------------------------
-- LoadFunctions
----------------------------------------------------------------
-- | This function is not stable yet.
-- If you worry about compatibility, use 'loadFunction'' instead.
loadFunction :: HMODULE
             -> String -- ^ Function name
             -> String -- ^ Advice to use function when user can't load function.
             -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
             -> IO b
loadFunction dll name reason conv
  = withCAString name $ \c_name -> do
        proc <- unsupportedIfNull (MissingFunction name reason)
                    $ c_GetProcAddress dll c_name
        conv $ castPtrToFunPtr proc

loadFunction' :: HMODULE
              -> String -- ^ Function name
              -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
              -> IO b
loadFunction' dll name conv = loadFunction dll name "" conv

-- | This function is not stable yet.
-- If you worry about compatibility, use 'loadSystemFunction'' instead.
loadSystemFunction :: HMODULE -> String -> String -> (FunPtr a -> IO b) -> IO b
loadSystemFunction dll name reason conv
  = withCAString name $ \c_name -> do
        -- Is failIfNull suitable or not?
        proc <- unsupportedIfNull (missingWin32Function name reason)
                    $ c_GetProcAddress dll c_name
        conv $ castPtrToFunPtr proc

loadSystemFunction' :: HMODULE -> String -> (FunPtr a -> IO b) -> IO b
loadSystemFunction' dll name conv = loadSystemFunction dll name "" conv

loadLibrary' :: FilePath -> IO HINSTANCE
loadLibrary' name =
  withTString name $ \ c_name ->
  unsupportedIfNull (missingLibrary name) $ c_LoadLibrary c_name

-- | This function is not stable yet.
-- If you worry about compatibility, use 'withLoadFunction'' instead.
withLoadFunction :: FilePath -- ^ DLL path
                 -> String   -- ^ Function name
                 -> String   -- ^ Advice to use function when user can't load function.
                 -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
                 -> IO b
withLoadFunction dllname name reason conv =
  mask $ \restore -> do
    -- loadLibrry is better than c_getModuleHandle when load non-system library.
    dll    <- loadLibrary' dllname
    restore $ finally (loadFunction dll name reason conv) (freeLibrary dll)
{-
    result <- restore $
                catch (loadFunction dllname dll name conv)
                  (\e -> case e of
                           MissingFunction _ _ -> do freeLibrary dll
                                                     throwIO e
                           _ -> throwIO e)
    _      <- freeLibrary a
    return result
-}
withLoadFunction' :: FilePath -- ^ DLL path
                  -> String   -- ^ Function name
                  -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
                  -> IO b
withLoadFunction' dllname name conv = withLoadFunction dllname name "" conv

-- | This function is not stable yet.
-- If you worry about compatibility, use 'withLoadSystemFunction'' instead.
withLoadSystemFunction :: String -- ^ System's DLL name
                       -> String -- ^ Function name
                       -> String -- ^ Advice to use function when user can't load function.
                       -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
                       -> IO b
withLoadSystemFunction dllname name reason conv
  = bracket (getModuleHandle (Just dllname)) (\_ -> return ())
      $ \dll -> loadSystemFunction dll name reason conv

withLoadSystemFunction' :: String -- ^ System's DLL name
                        -> String -- ^ Function name
                        -> (FunPtr a -> IO b) -- ^ Process with using loaded function.
                        -> IO b
withLoadSystemFunction' dllname name conv = withLoadSystemFunction dllname name "" conv

{-
-- Should we provide Maybe version's function to provide fallback functionality?

getModuleHandle :: String -> IO (Maybe HMODULE)
getModuleHandle moduleName = do
    ptr <- withTString moduleName c_GetModuleHandle
    if ptr == nullPtr
      then do
        err <- getLastError
        if err == eRROR_MOD_NOT_FOUND
          then return Nothing
          else failWith "GetModuleHandle" err
      else return $ Just ptr

getProcAddress :: HMODULE -> String -> IO (Maybe (FunPtr a))
getProcAddress hModule procName = do
    ptr <- withCAString procName $ c_GetProcAddress hModule
    if ptr == nullPtr
      then do
        err <- getLastError
        if err == eRROR_PROC_NOT_FOUND
          then return Nothing
          else failWith "GetProcAddress" err
      else return $ Just $ castPtrToFunPtr ptr
-}

-- portable version
setSearchPathMode :: SearchPathModeFlags -> IO ()
setSearchPathMode flag =
  withLoadSystemFunction "Kernel32.dll" "SetSearchPathMode" "" $ \c_SetSearchPathMode ->
    failIfFalse_ "SetDllDirectory" $ mkSetSearchPathMode c_SetSearchPathMode flag

type SearchPathModeFlags = DWORD
bASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE, bASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE, bASE_SEARCH_PATH_PERMANENT :: SearchPathModeFlags
bASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE = 0x00000001
bASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE = 0x00010000
bASE_SEARCH_PATH_PERMANENT = 0x00008000

foreign import WINDOWS_CCONV unsafe "dynamic" mkSetSearchPathMode ::
   FunPtr (SearchPathModeFlags -> IO BOOL) -> (SearchPathModeFlags -> IO BOOL)
{-
-- non portable version.
setSearchPathMode :: SearchPathModeFlags -> IO ()
setSearchPathMode flag =
  failIfFalse_ "SetDllDirectory" $ c_SetSearchPathMode flag

foreign import WINDOWS_CCONV unsafe "windows.h SetSearchPathMode"
  c_SetSearchPathMode :: SearchPathModeFlags -> IO BOOL

{enum SearchPathModeFlags,
 , bASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE  = BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE
 , bASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE = BASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE
 , bASE_SEARCH_PATH_PERMANENT               = BASE_SEARCH_PATH_PERMANENT
 }
-}

setDllDirectory :: String -> IO ()
setDllDirectory name =
  withTString name $ \ c_name ->
  failIfFalse_ (unwords ["SetDllDirectory", name]) $ c_SetDllDirectory c_name

foreign import WINDOWS_CCONV unsafe "windows.h SetDllDirectoryW"
  c_SetDllDirectory :: LPTSTR -> IO BOOL