Function Windows_Services(@Service, @Params)
/***********************************************************************************************************************

    This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
    permission from SRP Computer Solutions, Inc.

    Name        :   Windows_Services

    Description :   Handler program for all module related services.

    Notes       :   The generic parameters should contain all the necessary information to process the services. Often
                    this will be information like the data Record and Key ID.

    Parameters  :
        Service         [in] -- Name of the service being requested
        Error          [out] -- Any errors that were created
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    History     :   (Date, Initials, Notes)
        05/14/13    dmb     Original programmer.
        05/15/13    dmb     Add the IsRunning and Close service. Update the GetHandle service to use the GetTopWindow
                            API function.
        05/22/13    dmb     Add Hide, Show, Maximize, and Minimize services (all based on the ShowWindow API).
        10/02/13    dmb     [SRPFW-18] Add IsVisible service.
        11/11/13    dmb     [SRPFW-75] Add RunEXE and OpenFile services.
        11/12/13    dmb     [SRPFW-76] Add KillEXE service.
        03/28/20    dmb     [SRPFW-304] Update services to use Error_Services when applicable.
        05/13/20    dmb     [SRPFW-312] Add MakeActive service.

***********************************************************************************************************************/

#pragma precomp SRP_PreCompiler

$insert LOGICAL
$insert SERVICE_SETUP

// General Windows API equates
Equ WM_USER              to 1024
Equ WM_CLOSE             to 16
Equ WM_COMMAND           to 273
Equ WM_LBUTTONDOWN       to 513    ; // 0x0201
Equ WM_LBUTTONUP         to 514    ; // 0x0202
Equ WM_LBUTTONDBLCLK     to 515    ; // 0x0203
Equ WM_RBUTTONDOWN       to 516    ; // 0x0204
Equ WM_RBUTTONUP         to 517    ; // 0x0205
Equ WM_PARENTNOTIFY      to 528    ; // 0x0210
Equ WM_SYSCOLORCHANGE    to 21    ; // 0x0015
Equ WM_SETCURSOR         to 32    ; // 0x0020
Equ WM_SIZE              to 5    ; // 0x0005
Equ WM_MOVE              to 3    ; // 0x0003
Equ WM_GETMINMAXINFO     to 36    ; // 0x0024
Equ WM_WINDOWPOSCHANGING to 70

// ShowWindow API equates
Equ SW_FORCEMINIMIZE   to 11
Equ SW_HIDE            to 0
Equ SW_MAXIMIZE        to 3
Equ SW_MINIMIZE        to 6
Equ SW_RESTORE         to 9
Equ SW_SHOW            to 5
Equ SW_SHOWDEFAULT     to 10
Equ SW_SHOWMAXIMIZED   to 3
Equ SW_SHOWMINIMIZED   to 2
Equ SW_SHOWMINNOACTIVE to 7
Equ SW_SHOWNA          to 8
Equ SW_SHOWNOACTIVATE  to 4
Equ SW_SHOWNORMAL      to 1

Declare function    Windows_Services, WinAPI_MoveWindow, WinAPI_GetWindowTextA, WinAPI_GetWindow, WinAPI_IsWindow
Declare function    FindWindow, PostMessage, ShowWindow, IsWindowVisible
Declare function    SRP_GetTopWindow, ShellExecute, Kill_Application, Utility
Declare subroutine  Windows_Services, SRP_SetForeGroundWindow

GoToService else
    Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
end

Return Response else ''


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Service Parameter Options
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Options BOOLEAN     = True$, False$


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Services
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//----------------------------------------------------------------------------------------------------------------------
// SetSize
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
// Param4 - X Position.
// Param5 - Y Position.
// Param6 - Width.
// Param7 - Height.
//
// Sets the size of the window.
//----------------------------------------------------------------------------------------------------------------------
Service SetSize(Handle, ClassName, CaptionText, XPos, YPos, Width, Height)

    If Len(Handle) EQ 0 then
        Begin Case
            Case Len(ClassName)
                Handle = Windows_Services('GetHandle', ClassName)

            Case Len(CaptionText)
                Handle = Windows_Services('GetHandle', '', CaptionText)

        End Case
    end

    If Handle GT 0 then
        rv = WinAPI_MoveWindow(Handle, XPos, YPos, Width, Height, True$)
    end else
        Error_Services('Add', 'No valid window was passed in to the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// IsRunning
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Returns True if the window is running or False is not.
//----------------------------------------------------------------------------------------------------------------------
Service IsRunning(Handle, ClassName, CaptionText)

    Begin Case
        Case Len(Handle)
            If WinAPI_IsWindow(Handle) else Handle = ''          

        Case Len(ClassName)
            Handle = Windows_Services('GetHandle', ClassName)

        Case Len(CaptionText)
            Handle = Windows_Services('GetHandle', '', CaptionText)

    End Case

    Response    = (Handle GT 0)

end service


//----------------------------------------------------------------------------------------------------------------------
// IsVisible
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Returns True if the window is visible or False is not.
//----------------------------------------------------------------------------------------------------------------------
Service IsVisible(Handle, ClassName, CaptionText)

    Begin Case
        Case Len(Handle)
            If WinAPI_IsWindow(Handle) else Handle = ''          

        Case Len(ClassName)
            Handle = Windows_Services('GetHandle', ClassName)

        Case Len(CaptionText)
            Handle = Windows_Services('GetHandle', '', CaptionText)

    End Case

    Response    = (IsWindowVisible(Handle) GT 0)

end service


//----------------------------------------------------------------------------------------------------------------------
// RunEXE
//
// Param1 - Full path and file name of the executable to run.
//
// Runs an executable program. This uses the RUNWIN service without any special flags. Therefore all executables will be
// launched modelessly and in normal mode.
//----------------------------------------------------------------------------------------------------------------------
Service RunEXE(PathToEXE)

    If Len(PathToEXE) then
        AppInfo = Utility('RUNWIN', PathToEXE)
        If Len(AppInfo<2>) else
            Error_Services('Add', Quote(PathToExe) : ' is an invalid executable file provided to the ' : Service : ' service.')
        end
    end else
        Error_Services('Add', 'No path to an executable file was provided to the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// OpenFile
//
// Param1 - Full path and file name to open.
//
// Opens an OS file. This uses the ShellExecute Windows API without any special flags.
//----------------------------------------------------------------------------------------------------------------------
Service OpenFile(PathToFile)

    If Len(PathToFile) then
        rv = ShellExecute('', 'open' : \00\, PathToFile : \00\, '' : \00\, '' : \00\, 0)
    end else
        Error_Services('Add', 'No path to a file was provided to the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// Close
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Close the window. If the class name or window caption is passed in, only the first instance of any window will be
// closed. The developer will need to call the Close service multiple times until there are no more matching windows.
//----------------------------------------------------------------------------------------------------------------------
Service Close(Handle, ClassName, CaptionText)

    If Len(Handle) EQ 0 then
        Begin Case
            Case Len(ClassName)
                Handle = Windows_Services('GetHandle', ClassName)

            Case Len(CaptionText)
                Handle = Windows_Services('GetHandle', '', CaptionText)

        End Case
    end

    If Handle GT 0 then
        rv = PostMessage(Handle, WM_CLOSE, 0, 0)
    end else
        Error_Services('Add', 'No valid window was passed in to the ' : Service : ' service.')
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// KillEXE
//
// Param1 - Full path and file name of the executable to run.
// Param2 - Caption text of the window.
//
// Attempts to kill a running executable program. Returns a 1 if successful, or a negative number if unsuccessful.
//----------------------------------------------------------------------------------------------------------------------
Service KillEXE(EXEName, CaptionText)

    Results     = ''

    If Len(EXEName) OR Len(CaptionText) then
        Results = Kill_Application(EXEName, CaptionText, TimeoutDelay, True$)
    end else
        Error_Services('Add', 'No valid executable file or caption was provided to the ' : Service : ' service.')
    end

    Response    = Results

end service


//----------------------------------------------------------------------------------------------------------------------
// Hide
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Hide the window. If the class name or window caption is passed in, only the first instance of any window will be
// hidden. The developer will need to call the Hide service multiple times until there are no more matching windows.
//----------------------------------------------------------------------------------------------------------------------
Service Hide(Handle, ClassName, CaptionText)

    CmdShow = SW_HIDE
    GoSub ShowWindowAPI

end service


//----------------------------------------------------------------------------------------------------------------------
// Show
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Activates the window and displays it in its current size and position. If the class name or window caption is passed
// in, only the first instance of any window will be hidden. The developer will need to call the Hide service multiple
// times until there are no more matching windows.
//----------------------------------------------------------------------------------------------------------------------
Service Show(Handle, ClassName, CaptionText)

    CmdShow = SW_SHOW
    GoSub ShowWindowAPI

end service


//----------------------------------------------------------------------------------------------------------------------
// Maximize
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Maximizes the specified window. If the class name or window caption is passed in, only the first instance of any
// window will be hidden. The developer will need to call the Hide service multiple times until there are no more
// matching windows.
//----------------------------------------------------------------------------------------------------------------------
Service Maximize(Handle, ClassName, CaptionText)

    CmdShow = SW_MAXIMIZE
    GoSub ShowWindowAPI

end service


//----------------------------------------------------------------------------------------------------------------------
// Minimize
//
// Param1 - Handle to the window.
// Param2 - Class Name to the window. This is ignored if there is a handle.
// Param3 - Caption text of the window. This is ignored if there is a handle or class name.
//
// Minimizes the specified window. If the class name or window caption is passed in, only the first instance of any
// window will be hidden. The developer will need to call the Hide service multiple times until there are no more
// matching windows.
//----------------------------------------------------------------------------------------------------------------------
Service Minimize(Handle, ClassName, CaptionText)

    CmdShow = SW_MINIMIZE
    GoSub ShowWindowAPI

end service


//----------------------------------------------------------------------------------------------------------------------
// MakeActive
//
// Handle   - Handle to the window. - [Required]
//
// Brings the window to the foreground and makes it active. This uses the SetForegroundWindow API:
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow
//----------------------------------------------------------------------------------------------------------------------
Service MakeActive(Handle)

    If Handle NE '' then
        SRP_SetForeGroundWindow(Handle)
    end

end service


//----------------------------------------------------------------------------------------------------------------------
// GetHandle
//
// Param1 - Class Name to the window.
// Param2 - Caption text of the window. This is ignored if there is a handle or class name.
// Param3 - Flag that determines if all handles should be returned rather than the first match. Only works when finding
//          handles by CaptionText.
//
// Gets the handle for the window.
//----------------------------------------------------------------------------------------------------------------------
Service GetHandle(ClassName, CaptionText, AllHandles)

    Handle      = ''
    If AllHandles NE True$ then AllHandles = False$

    Begin Case
        Case Len(ClassName)
            // The FindWindow API returns the handle of a valid class name that is already running.
            Handle = FindWindow(ClassName : \00\, '')

        Case Len(CaptionText)
            // The GetTopWindow API will return the handle of the first window running in z-order.
            StartHandle = SRP_GetTopWindow('')
            NextHandle  = StartHandle
            TextBuffer  = Str(\00\, 100)
            // The GetWindowText API will return the caption text of the window.
            rv          = WinAPI_GetWindowTextA(NextHandle, TextBuffer, Len(TextBuffer))
            ThisCaption = TextBuffer[1, \00\]
            If ThisCaption EQ CaptionText then Handle = NextHandle : @FM

            // If the top window is not a match, then use the GetWindow API to retrieve the next running window's
            // handle. Continue to loop through each window until a match is found or there are no more windows
            // to process.
            If Len(Handle) EQ 0 OR AllHandles = True$ then
                Loop
                Until (NextHandle EQ 0) OR (Len(Handle) AND AllHandles EQ False$)
                    NextHandle  = WinAPI_GetWindow(NextHandle, 2)
                    TextBuffer  = Str(\00\, 100)
                    rv          = WinAPI_GetWindowTextA(NextHandle, TextBuffer, Len(TextBuffer))
                    ThisCaption = TextBuffer[1, \00\]
                    If ThisCaption EQ CaptionText then
                        Locate NextHandle in Handle using @FM setting fPos else
                            Handle := NextHandle : @FM
                        end
                    end
                Repeat
            end
            Handle[-1, 1]   = ''    ; // Strip off the final @FM

    End Case

    Response = Handle

end service


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////?

ShowWindowAPI:

    If Len(Handle) EQ 0 then
        Begin Case
            Case Len(ClassName)
                Handle = Windows_Services('GetHandle', ClassName)

            Case Len(CaptionText)
                Handle = Windows_Services('GetHandle', '', CaptionText)

        End Case
    end

    If Handle GT 0 then
        rv = ShowWindow(Handle, CmdShow)
    end else
        Error_Services('Add', 'No valid window was passed in to the ' : Service : ' service.')
    end

return
