require_relative '../../../puppet/ffi/windows'

module Puppet::FFI::Windows
  module Functions

    extend FFI::Library
    include Puppet::FFI::Windows::Constants

    ffi_convention :stdcall

    # https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-sethandleinformation
    # BOOL SetHandleInformation(
    #   HANDLE hObject,
    #   DWORD  dwMask,
    #   DWORD  dwFlags
    # );
    ffi_lib :kernel32
    attach_function_private :SetHandleInformation, [:handle, :dword, :dword], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-seterrormode
    # UINT SetErrorMode(
    #   UINT uMode
    # );
    ffi_lib :kernel32
    attach_function_private :SetErrorMode, [:uint], :uint

    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
    # BOOL CreateProcessW(
    #   LPCWSTR               lpApplicationName,
    #   LPWSTR                lpCommandLine,
    #   LPSECURITY_ATTRIBUTES lpProcessAttributes,
    #   LPSECURITY_ATTRIBUTES lpThreadAttributes,
    #   BOOL                  bInheritHandles,
    #   DWORD                 dwCreationFlags,
    #   LPVOID                lpEnvironment,
    #   LPCWSTR               lpCurrentDirectory,
    #   LPSTARTUPINFOW        lpStartupInfo,
    #   LPPROCESS_INFORMATION lpProcessInformation
    # );
    ffi_lib :kernel32
    attach_function_private :CreateProcessW,
      [:lpcwstr, :lpwstr, :pointer, :pointer, :win32_bool,
       :dword, :lpvoid, :lpcwstr, :pointer, :pointer], :bool

    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
    # HANDLE OpenProcess(
    #   DWORD dwDesiredAccess,
    #   BOOL  bInheritHandle,
    #   DWORD dwProcessId
    # );
    ffi_lib :kernel32
    attach_function_private :OpenProcess, [:dword, :win32_bool, :dword], :handle

    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass
    # BOOL SetPriorityClass(
    #   HANDLE hProcess,
    #   DWORD  dwPriorityClass
    # );
    ffi_lib :kernel32
    attach_function_private :SetPriorityClass, [:handle, :dword], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithlogonw
    # BOOL CreateProcessWithLogonW(
    #   LPCWSTR               lpUsername,
    #   LPCWSTR               lpDomain,
    #   LPCWSTR               lpPassword,
    #   DWORD                 dwLogonFlags,
    #   LPCWSTR               lpApplicationName,
    #   LPWSTR                lpCommandLine,
    #   DWORD                 dwCreationFlags,
    #   LPVOID                lpEnvironment,
    #   LPCWSTR               lpCurrentDirectory,
    #   LPSTARTUPINFOW        lpStartupInfo,
    #   LPPROCESS_INFORMATION lpProcessInformation
    # );
    ffi_lib :advapi32
    attach_function_private :CreateProcessWithLogonW,
      [:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpcwstr, :lpwstr,
       :dword, :lpvoid, :lpcwstr, :pointer, :pointer], :bool

    # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019
    # intptr_t _get_osfhandle(
    #    int fd
    # );
    ffi_lib FFI::Library::LIBC
    attach_function_private :get_osfhandle, :_get_osfhandle, [:int], :intptr_t

    begin
      # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-errno?view=vs-2019
      # errno_t _get_errno(
      #    int * pValue
      # );
      attach_function_private :get_errno, :_get_errno, [:pointer], :int
    rescue FFI::NotFoundError
      # Do nothing, Windows XP or earlier.
    end

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
    # DWORD WINAPI WaitForSingleObject(
    #   _In_  HANDLE hHandle,
    #   _In_  DWORD dwMilliseconds
    # );
    ffi_lib :kernel32
    attach_function_private :WaitForSingleObject,
      [:handle, :dword], :dword, :blocking => true

    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitformultipleobjects
    #   DWORD WaitForMultipleObjects(
    #   DWORD        nCount,
    #   const HANDLE *lpHandles,
    #   BOOL         bWaitAll,
    #   DWORD        dwMilliseconds
    # );
    ffi_lib :kernel32
    attach_function_private :WaitForMultipleObjects,
      [:dword, :phandle, :win32_bool, :dword], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-createeventw
    # HANDLE CreateEventW(
    #   LPSECURITY_ATTRIBUTES lpEventAttributes,
    #   BOOL                  bManualReset,
    #   BOOL                  bInitialState,
    #   LPCWSTR               lpName
    # );
    ffi_lib :kernel32
    attach_function_private :CreateEventW,
      [:pointer, :win32_bool, :win32_bool, :lpcwstr], :handle

    # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createthread
    # HANDLE CreateThread(
    #   LPSECURITY_ATTRIBUTES   lpThreadAttributes,
    #   SIZE_T                  dwStackSize,
    #   LPTHREAD_START_ROUTINE  lpStartAddress,
    #   __drv_aliasesMem LPVOID lpParameter,
    #   DWORD                   dwCreationFlags,
    #   LPDWORD                 lpThreadId
    # );
    ffi_lib :kernel32
    attach_function_private :CreateThread,
      [:pointer, :size_t, :pointer, :lpvoid, :dword, :lpdword], :handle, :blocking => true

    # https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent
    # BOOL SetEvent(
    #   HANDLE hEvent
    # );
    ffi_lib :kernel32
    attach_function_private :SetEvent,
      [:handle], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx
    # BOOL WINAPI GetExitCodeProcess(
    #   _In_   HANDLE hProcess,
    #   _Out_  LPDWORD lpExitCode
    # );
    ffi_lib :kernel32
    attach_function_private :GetExitCodeProcess,
      [:handle, :lpdword], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683179(v=vs.85).aspx
    # HANDLE WINAPI GetCurrentProcess(void);
    ffi_lib :kernel32
    attach_function_private :GetCurrentProcess, [], :handle

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683187(v=vs.85).aspx
    # LPTCH GetEnvironmentStrings(void);
    ffi_lib :kernel32
    attach_function_private :GetEnvironmentStringsW, [], :pointer

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms683151(v=vs.85).aspx
    # BOOL FreeEnvironmentStrings(
    #   _In_ LPTCH lpszEnvironmentBlock
    # );
    ffi_lib :kernel32
    attach_function_private :FreeEnvironmentStringsW,
      [:pointer], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms686206(v=vs.85).aspx
    # BOOL WINAPI SetEnvironmentVariableW(
    #     _In_     LPCTSTR lpName,
    #     _In_opt_ LPCTSTR lpValue
    #   );
    ffi_lib :kernel32
    attach_function_private :SetEnvironmentVariableW,
      [:lpcwstr, :lpcwstr], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684320(v=vs.85).aspx
    # HANDLE WINAPI OpenProcess(
    #   _In_   DWORD DesiredAccess,
    #   _In_   BOOL InheritHandle,
    #   _In_   DWORD ProcessId
    # );
    ffi_lib :kernel32
    attach_function_private :OpenProcess,
      [:dword, :win32_bool, :dword], :handle

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa379295(v=vs.85).aspx
    # BOOL WINAPI OpenProcessToken(
    #   _In_   HANDLE ProcessHandle,
    #   _In_   DWORD DesiredAccess,
    #   _Out_  PHANDLE TokenHandle
    # );
    ffi_lib :advapi32
    attach_function_private :OpenProcessToken,
      [:handle, :dword, :phandle], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-queryfullprocessimagenamew
    # BOOL WINAPI QueryFullProcessImageName(
    #   _In_   HANDLE hProcess,
    #   _In_   DWORD dwFlags,
    #   _Out_  LPWSTR lpExeName,
    #   _In_   PDWORD lpdwSize,
    # );
    ffi_lib :kernel32
    attach_function_private :QueryFullProcessImageNameW,
      [:handle, :dword, :lpwstr, :pdword], :win32_bool

    # https://msdn.microsoft.com/en-us/library/Windows/desktop/aa379180(v=vs.85).aspx
    # BOOL WINAPI LookupPrivilegeValue(
    #   _In_opt_  LPCTSTR lpSystemName,
    #   _In_      LPCTSTR lpName,
    #   _Out_     PLUID lpLuid
    # );
    ffi_lib :advapi32
    attach_function_private :LookupPrivilegeValueW,
      [:lpcwstr, :lpcwstr, :pointer], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa446671(v=vs.85).aspx
    # BOOL WINAPI GetTokenInformation(
    #   _In_       HANDLE TokenHandle,
    #   _In_       TOKEN_INFORMATION_CLASS TokenInformationClass,
    #   _Out_opt_  LPVOID TokenInformation,
    #   _In_       DWORD TokenInformationLength,
    #   _Out_      PDWORD ReturnLength
    # );
    ffi_lib :advapi32
    attach_function_private :GetTokenInformation,
      [:handle, TOKEN_INFORMATION_CLASS, :lpvoid, :dword, :pdword ], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
    # BOOL WINAPI GetVersionEx(
    #   _Inout_  LPOSVERSIONINFO lpVersionInfo
    # );
    ffi_lib :kernel32
    attach_function_private :GetVersionExW,
      [:pointer], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/dd318123(v=vs.85).aspx
    # LANGID GetSystemDefaultUILanguage(void);
    ffi_lib :kernel32
    attach_function_private :GetSystemDefaultUILanguage, [], :word

    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-openscmanagerw
    # SC_HANDLE OpenSCManagerW(
    #   LPCWSTR lpMachineName,
    #   LPCWSTR lpDatabaseName,
    #   DWORD   dwDesiredAccess
    # );
    ffi_lib :advapi32
    attach_function_private :OpenSCManagerW,
      [:lpcwstr, :lpcwstr, :dword], :handle

    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-openservicew
    # SC_HANDLE OpenServiceW(
    #   SC_HANDLE hSCManager,
    #   LPCWSTR   lpServiceName,
    #   DWORD     dwDesiredAccess
    # );
    ffi_lib :advapi32
    attach_function_private :OpenServiceW,
      [:handle, :lpcwstr, :dword], :handle

    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-closeservicehandle
    # BOOL CloseServiceHandle(
    #   SC_HANDLE hSCObject
    # );
    ffi_lib :advapi32
    attach_function_private :CloseServiceHandle,
      [:handle], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-queryservicestatusex
    # BOOL QueryServiceStatusEx(
    #   SC_HANDLE      hService,
    #   SC_STATUS_TYPE InfoLevel,
    #   LPBYTE         lpBuffer,
    #   DWORD          cbBufSize,
    #   LPDWORD        pcbBytesNeeded
    # );
    SC_STATUS_TYPE = enum(
      :SC_STATUS_PROCESS_INFO, 0,
    )
    ffi_lib :advapi32
    attach_function_private :QueryServiceStatusEx,
      [:handle, SC_STATUS_TYPE, :lpbyte, :dword, :lpdword], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-queryserviceconfigw
    # BOOL QueryServiceConfigW(
    #   SC_HANDLE               hService,
    #   LPQUERY_SERVICE_CONFIGW lpServiceConfig,
    #   DWORD                   cbBufSize,
    #   LPDWORD                 pcbBytesNeeded
    # );
    ffi_lib :advapi32
    attach_function_private :QueryServiceConfigW,
      [:handle, :lpbyte, :dword, :lpdword], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryserviceconfig2w
    # BOOL QueryServiceConfig2W(
    #   SC_HANDLE hService,
    #   DWORD     dwInfoLevel,
    #   LPBYTE    lpBuffer,
    #   DWORD     cbBufSize,
    #   LPDWORD   pcbBytesNeeded
    # );
    ffi_lib :advapi32
    attach_function_private :QueryServiceConfig2W,
      [:handle, :dword, :lpbyte, :dword, :lpdword], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/Winsvc/nf-winsvc-startservicew
    # BOOL StartServiceW(
    #   SC_HANDLE hService,
    #   DWORD     dwNumServiceArgs,
    #   LPCWSTR   *lpServiceArgVectors
    # );
    ffi_lib :advapi32
    attach_function_private :StartServiceW,
      [:handle, :dword, :pointer], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-startservicectrldispatcherw
    # BOOL StartServiceCtrlDispatcherW(
    #   const SERVICE_TABLE_ENTRYW *lpServiceStartTable
    # );
    ffi_lib :advapi32
    attach_function_private :StartServiceCtrlDispatcherW,
      [:pointer], :win32_bool, :blocking => true

    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-setservicestatus
    # BOOL SetServiceStatus(
    #   SERVICE_STATUS_HANDLE hServiceStatus,
    #   LPSERVICE_STATUS      lpServiceStatus
    # );
    ffi_lib :advapi32
    attach_function_private :SetServiceStatus,
      [:handle, :pointer], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-controlservice
    # BOOL ControlService(
    #   SC_HANDLE        hService,
    #   DWORD            dwControl,
    #   LPSERVICE_STATUS lpServiceStatus
    # );
    ffi_lib :advapi32
    attach_function_private :ControlService,
      [:handle, :dword, :pointer], :win32_bool

    #   DWORD LphandlerFunctionEx(
    #   DWORD dwControl,
    #   DWORD dwEventType,
    #   LPVOID lpEventData,
    #   LPVOID lpContext
    # )
    callback :handler_ex, [:dword, :dword, :lpvoid, :lpvoid], :void

    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-registerservicectrlhandlerexw
    # SERVICE_STATUS_HANDLE RegisterServiceCtrlHandlerExW(
    #   LPCWSTR               lpServiceName,
    #   LPHANDLER_FUNCTION_EX lpHandlerProc,
    #   LPVOID                lpContext
    # );
    ffi_lib :advapi32
    attach_function_private :RegisterServiceCtrlHandlerExW,
      [:lpcwstr, :handler_ex, :lpvoid], :handle

    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-changeserviceconfigw
    # BOOL ChangeServiceConfigW(
    #   SC_HANDLE hService,
    #   DWORD     dwServiceType,
    #   DWORD     dwStartType,
    #   DWORD     dwErrorControl,
    #   LPCWSTR   lpBinaryPathName,
    #   LPCWSTR   lpLoadOrderGroup,
    #   LPDWORD   lpdwTagId,
    #   LPCWSTR   lpDependencies,
    #   LPCWSTR   lpServiceStartName,
    #   LPCWSTR   lpPassword,
    #   LPCWSTR   lpDisplayName
    # );
    ffi_lib :advapi32
    attach_function_private :ChangeServiceConfigW,
      [
        :handle,
        :dword,
        :dword,
        :dword,
        :lpcwstr,
        :lpcwstr,
        :lpdword,
        :lpcwstr,
        :lpcwstr,
        :lpcwstr,
        :lpcwstr
      ], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-changeserviceconfig2w
    # BOOL ChangeServiceConfig2W(
    #   SC_HANDLE hService,
    #   DWORD     dwInfoLevel,
    #   LPVOID    lpInfo
    # );
    ffi_lib :advapi32
    attach_function_private :ChangeServiceConfig2W,
      [:handle, :dword, :lpvoid], :win32_bool

    # https://docs.microsoft.com/en-us/windows/desktop/api/winsvc/nf-winsvc-enumservicesstatusexw
    # BOOL EnumServicesStatusExW(
    #   SC_HANDLE    hSCManager,
    #   SC_ENUM_TYPE InfoLevel,
    #   DWORD        dwServiceType,
    #   DWORD        dwServiceState,
    #   LPBYTE       lpServices,
    #   DWORD        cbBufSize,
    #   LPDWORD      pcbBytesNeeded,
    #   LPDWORD      lpServicesReturned,
    #   LPDWORD      lpResumeHandle,
    #   LPCWSTR      pszGroupName
    # );
    SC_ENUM_TYPE = enum(
      :SC_ENUM_PROCESS_INFO, 0,
    )
    ffi_lib :advapi32
    attach_function_private :EnumServicesStatusExW,
      [
        :handle,
        SC_ENUM_TYPE,
        :dword,
        :dword,
        :lpbyte,
        :dword,
        :lpdword,
        :lpdword,
        :lpdword,
        :lpcwstr
      ], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365512(v=vs.85).aspx
    # BOOL WINAPI ReplaceFile(
    #   _In_        LPCTSTR lpReplacedFileName,
    #   _In_        LPCTSTR lpReplacementFileName,
    #   _In_opt_    LPCTSTR lpBackupFileName,
    #   _In_        DWORD dwReplaceFlags - 0x1 REPLACEFILE_WRITE_THROUGH,
    #                                      0x2 REPLACEFILE_IGNORE_MERGE_ERRORS,
    #                                      0x4 REPLACEFILE_IGNORE_ACL_ERRORS
    #   _Reserved_  LPVOID lpExclude,
    #   _Reserved_  LPVOID lpReserved
    # );
    ffi_lib :kernel32
    attach_function_private :ReplaceFileW,
      [:lpcwstr, :lpcwstr, :lpcwstr, :dword, :lpvoid, :lpvoid], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240(v=vs.85).aspx
    # BOOL WINAPI MoveFileEx(
    #   _In_      LPCTSTR lpExistingFileName,
    #   _In_opt_  LPCTSTR lpNewFileName,
    #   _In_      DWORD dwFlags
    # );
    ffi_lib :kernel32
    attach_function_private :MoveFileExW,
      [:lpcwstr, :lpcwstr, :dword], :win32_bool

    # BOOLEAN WINAPI CreateSymbolicLink(
    #   _In_  LPTSTR lpSymlinkFileName, - symbolic link to be created
    #   _In_  LPTSTR lpTargetFileName, - name of target for symbolic link
    #   _In_  DWORD dwFlags - 0x0 target is a file, 0x1 target is a directory
    # );
    # rescue on Windows < 6.0 so that code doesn't explode
    begin
      ffi_lib :kernel32
      attach_function_private :CreateSymbolicLinkW,
        [:lpwstr, :lpwstr, :dword], :boolean
    rescue LoadError
    end

    # https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcurrentdirectory
    # DWORD GetCurrentDirectory(
    #   DWORD  nBufferLength,
    #   LPTSTR lpBuffer
    # );
    ffi_lib :kernel32
    attach_function_private :GetCurrentDirectoryW,
      [:dword, :lpwstr], :dword

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364944(v=vs.85).aspx
    # DWORD WINAPI GetFileAttributes(
    #   _In_  LPCTSTR lpFileName
    # );
    ffi_lib :kernel32
    attach_function_private :GetFileAttributesW,
      [:lpcwstr], :dword

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365535(v=vs.85).aspx
    # BOOL WINAPI SetFileAttributes(
    #   _In_  LPCTSTR lpFileName,
    #   _In_  DWORD dwFileAttributes
    # );
    ffi_lib :kernel32
    attach_function_private :SetFileAttributesW,
      [:lpcwstr, :dword], :win32_bool

    # HANDLE WINAPI CreateFile(
    #   _In_      LPCTSTR lpFileName,
    #   _In_      DWORD dwDesiredAccess,
    #   _In_      DWORD dwShareMode,
    #   _In_opt_  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    #   _In_      DWORD dwCreationDisposition,
    #   _In_      DWORD dwFlagsAndAttributes,
    #   _In_opt_  HANDLE hTemplateFile
    # );
    ffi_lib :kernel32
    attach_function_private :CreateFileW,
      [:lpcwstr, :dword, :dword, :pointer, :dword, :dword, :handle], :handle

    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createdirectoryw
    # BOOL CreateDirectoryW(
    #   LPCWSTR               lpPathName,
    #   LPSECURITY_ATTRIBUTES lpSecurityAttributes
    # );
    ffi_lib :kernel32
    attach_function_private :CreateDirectoryW,
      [:lpcwstr, :pointer], :win32_bool

    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectoryw
    # BOOL RemoveDirectoryW(
    #   LPCWSTR lpPathName
    # );
    ffi_lib :kernel32
    attach_function_private :RemoveDirectoryW,
      [:lpcwstr], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx
    # BOOL WINAPI DeviceIoControl(
    #   _In_         HANDLE hDevice,
    #   _In_         DWORD dwIoControlCode,
    #   _In_opt_     LPVOID lpInBuffer,
    #   _In_         DWORD nInBufferSize,
    #   _Out_opt_    LPVOID lpOutBuffer,
    #   _In_         DWORD nOutBufferSize,
    #   _Out_opt_    LPDWORD lpBytesReturned,
    #   _Inout_opt_  LPOVERLAPPED lpOverlapped
    # );
    ffi_lib :kernel32
    attach_function_private :DeviceIoControl,
      [:handle, :dword, :lpvoid, :dword, :lpvoid, :dword, :lpdword, :pointer], :win32_bool

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364980(v=vs.85).aspx
    # DWORD WINAPI GetLongPathName(
    #   _In_  LPCTSTR lpszShortPath,
    #   _Out_ LPTSTR  lpszLongPath,
    #   _In_  DWORD   cchBuffer
    # );
    ffi_lib :kernel32
    attach_function_private :GetLongPathNameW,
      [:lpcwstr, :lpwstr, :dword], :dword

    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa364989(v=vs.85).aspx
    # DWORD WINAPI GetShortPathName(
    #   _In_  LPCTSTR lpszLongPath,
    #   _Out_ LPTSTR  lpszShortPath,
    #   _In_  DWORD   cchBuffer
    # );
    ffi_lib :kernel32
    attach_function_private :GetShortPathNameW,
      [:lpcwstr, :lpwstr, :dword], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
    # DWORD GetFullPathNameW(
    #   LPCWSTR lpFileName,
    #   DWORD   nBufferLength,
    #   LPWSTR  lpBuffer,
    #   LPWSTR  *lpFilePart
    # );
    ffi_lib :kernel32
    attach_function_private :GetFullPathNameW,
      [:lpcwstr, :dword, :lpwstr, :pointer], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw
    # SHFOLDERAPI SHGetFolderPathW(
    #   HWND   hwnd,
    #   int    csidl,
    #   HANDLE hToken,
    #   DWORD  dwFlags,
    #   LPWSTR pszPath
    # );
    ffi_lib :shell32
    attach_function_private :SHGetFolderPathW,
      [:hwnd, :int, :handle, :dword, :lpwstr], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderlocation
    # SHSTDAPI SHGetFolderLocation(
    #   HWND             hwnd,
    #   int              csidl,
    #   HANDLE           hToken,
    #   DWORD            dwFlags,
    #   PIDLIST_ABSOLUTE *ppidl
    # );
    ffi_lib :shell32
    attach_function_private :SHGetFolderLocation,
      [:hwnd, :int, :handle, :dword, :pointer], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shgetfileinfoa
    # DWORD_PTR SHGetFileInfoA(
    #   LPCSTR      pszPath,
    #   DWORD       dwFileAttributes,
    #   SHFILEINFOA *psfi,
    #   UINT        cbFileInfo,
    #   UINT        uFlags
    # );
    ffi_lib :shell32
    attach_function_private :SHGetFileInfo,
      [:dword, :dword, :pointer, :uint, :uint], :dword

    # https://docs.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-pathisdirectoryemptyw
    # BOOL PathIsDirectoryEmptyW(
    #   LPCWSTR pszPath
    # );
    ffi_lib :shlwapi
    attach_function_private :PathIsDirectoryEmptyW,
      [:lpcwstr], :win32_bool
  end
end
