Autor Tema: Aplicación como servicio no muestra form  (Leído 2245 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Jeronimo

  • Gigabyte
  • ****
  • Mensajes: 402
  • Reputación: +33/-2
    • Ver Perfil
Aplicación como servicio no muestra form
« en: Junio 10, 2016, 10:43:00 am »
¡Hola!
Me cansé de exprimir mi pequeño cerebrito y les vengo con un pedido de ayuda.
Hice una aplicación que cargué como servicio de Windows (creé como servicio el archivo srvany.exe que, a su vez, ejecuta mi aplicación). Antes, cuando no estaba como servicio, iniciaba maximizado y mostrata el ícono en la barra de tareas. Ahora ya no se ve por ningún lado (salvo en la lista de servicios, donde figura en ejecución y realmente está funcionando).
Entonces pensé en crear otro ejecutable y usar el mecanismo de Leandro para comunicar dos ejecutables, de manera de pasarle un comando para que se maximice y se visibilice. No funcionó.
Luego los conecté con un winsock y tampoco funcionó.
Aclaro que estas dos últimas opciones funcionan perfectamente con WinXP, pero el servidor en el que estoy trabajando corre Windows Server Enterprise de 32 bits.
¿Se les ocurre cómo puedo hacerlo?
Muchas gracias.

Jerónimo

Albertomi

  • Gigabyte
  • ****
  • Mensajes: 281
  • Reputación: +153/-0
    • Ver Perfil
Re:Aplicación como servicio no muestra form
« Respuesta #1 en: Junio 10, 2016, 01:42:51 pm »
Estimado Jeronimo





Por principios de la arquitectura de la plataforma Microsoft Windows, los servicios no disponen de interface de usuario (Ventana o Formulario). Los servicios van hacer llamados a otros procesos llamados Workers que son los que en realidad se encargan de ejecutar las tareas que el servicio tiene a su cargo.


Para comunicarte con un servicio tienes que usar uno de los tantos mecanismo que existen para comunicarse entre procesos.
Si compartes tu código se podría revisar y ver qué es lo que ocurre.








Saludos, desde algún lugar de Lima-Perú
Saludos, desde algún lugar de Lima-Perú

Jeronimo

  • Gigabyte
  • ****
  • Mensajes: 402
  • Reputación: +33/-2
    • Ver Perfil
Re:Aplicación como servicio no muestra form
« Respuesta #2 en: Junio 10, 2016, 05:12:57 pm »
Hola,  Albertomi.
Muchas gracias por tu explicación. No tenía idea de lo que comentaste.
Mi aplicación, en condiciones normales y antes de intentar ponerla como servicio, se ejecutaba al iniciar sesión en Windows, de manera invisible (Form.Visible = False) pero con un ícono en la barra de tareas. Entonces, al hacer doble clic sobre el ícono se maximizaba el formulario principal y se ponía visible.
Esto mismo quise hacer pero poniendo la aplicación como servicio de Windows, pero, como comenté, no está funcionando. La aplicación carga y hace lo que tiene que hacer pero no puedo verla.
Te comento, en primer lugar, cómo creo el servicio.
En una pantalla de "DOS" ejecuto lo siguiente:
sc create NombreServicio binpath= "C:\Program Files\Windows Resource Kits\Tools\srvany.exe"
Luego voy al registro de Windows: local machine/system/currentcontrolset/services. Busco el servicio NombreServicio (el que creé) y le agrego una carpeta donde indico la ruta del archivo que quiero que ejecute y otra donde le pongo la ruta + el nombre del archivo que quiero que ejecute.
Finalmente, voy a los servicios de Windows y pongo que este se inicie automáticamente.
También probé tildando la opción "permitir que los servicios interactúen con el escritorio de Windows" pero obtuve casi el mismo resultado. La primera vez que se ejecuta el servicio aparece una ventana donde dice que un servicio incompatible quiere mostrar un mensaje. Cuando le doy al botón "mostrar mensaje" abre el formulario de mi aplicación, pero me obliga a hacer clic en "volver" (de la misma ventana que había aparecido) y a partir de ahí no se puede ver más el form de mi aplicación. Incluso, ejecutando el servicio de nuevo o reiniciando la computadora.
Luego probé con el módulo de clase clsDDE de Leandro, que contiene el siguiente código:
Código: (VB) [Seleccionar]
Option Explicit
'Autor: Leandro Ascierto
'Web:   www.leandroascierto.com
'Refer: DDE from Cobein(http://www.advancevb.com.ar/)
'       SubClass: http://www.activevb.de/tutorials/tut_subclass_asm/tut_subclass_asm.html
'       VarType: Oleg Gdalevich http://www.vbip.com/winsock-api/csocket-class/csocket-class-01.asp

Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function CreateWindowEx Lib "user32.dll" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal x As Long, ByVal y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, ByRef lpParam As Any) As Long
Private Declare Function DestroyWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function GetModuleHandle Lib "kernel32.dll" Alias "GetModuleHandleA" (ByVal lpModuleName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long
Private Declare Function VirtualAlloc Lib "kernel32.dll" (ByRef lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32.dll" (ByRef lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function IsWindow Lib "user32.dll" (ByVal hwnd As Long) As Long
Private Declare Function RegisterWindowMessage Lib "user32.dll" Alias "RegisterWindowMessageA" (ByVal lpString As String) As Long

Private Const GWL_WNDPROC               As Long = -4

Private Const MEM_COMMIT                As Long = &H1000
Private Const PAGE_EXECUTE_READWRITE    As Long = &H40
Private Const MEM_RELEASE               As Long = &H8000&

Private Const WM_DESTROY                As Long = &H2
Private Const WM_COPYDATA               As Long = &H4A

Private Const ENUM_PROPS                As Long = &H2222

Private Type COPYDATASTRUCT
    dwData As Long
    cbData As Long
    lpData As Long
End Type

Private Type tProp
    lpString As String
    hData As Long
End Type


Public Event Connected(ByVal hwnd As Long)
Public Event ConnectionRequest(ByVal hwnd As Long)
Public Event ConnectionClose(ByVal hwnd As Long)
Public Event DataArrival(ByVal hwnd As Long, ByVal vData As Variant)

Private WM_CONNECTION_ACEPT     As Long
Private WM_CONNECTION_REQUEST   As Long
Private WM_CONNECTION_CLOSE     As Long

Private pASMWrapper             As Long
Private PrevWndProc             As Long
Private hWin                    As Long

Private bConnected              As Boolean
Private hServer                  As Long
Private cConnections            As Collection


Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
    On Error Resume Next

    Select Case uMsg
   
        Case WM_CONNECTION_REQUEST
            RaiseEvent ConnectionRequest(wParam)
           
        Case WM_CONNECTION_CLOSE
            Dim i As Long
           
            For i = 1 To cConnections.Count
                If cConnections(i) = wParam Then
                    cConnections.Remove i
                    Exit For
                End If
            Next
           
            RaiseEvent ConnectionClose(wParam)
           
        Case WM_CONNECTION_ACEPT
            RaiseEvent Connected(wParam)
            hServer = wParam
            bConnected = True
           
        Case WM_COPYDATA
   
            Dim CDS As COPYDATASTRUCT
            Dim arrBuffer() As Byte
            Dim varData As Variant
           
           
            Call CopyMemory(CDS, ByVal lParam, Len(CDS))
     
            ReDim arrBuffer(CDS.cbData - 1)
           
            Call CopyMemory(arrBuffer(0), ByVal CDS.lpData, CDS.cbData)
   
   
            Select Case CDS.dwData
                Case vbArray + vbByte
                    varData = arrBuffer()
                Case vbBoolean
                    Dim blnData As Boolean
                    CopyMemory blnData, arrBuffer(0), LenB(blnData)
                    varData = blnData
                Case vbByte
                    Dim bytData As Byte
                    CopyMemory bytData, arrBuffer(0), LenB(bytData)
                    varData = bytData
                Case vbCurrency
                    Dim curData As Currency
                    CopyMemory curData, arrBuffer(0), LenB(curData)
                    varData = curData
                Case vbDate
                    Dim datData As Date
                    CopyMemory datData, arrBuffer(0), LenB(datData)
                    varData = datData
                Case vbDouble
                    Dim dblData As Double
                    CopyMemory dblData, arrBuffer(0), LenB(dblData)
                    varData = dblData
                Case vbInteger
                    Dim intData As Integer
                    CopyMemory intData, arrBuffer(0), LenB(intData)
                    varData = intData
                Case vbLong
                    Dim lngData As Long
                    CopyMemory lngData, arrBuffer(0), LenB(lngData)
                    varData = lngData
                Case vbSingle
                    Dim sngData As Single
                    CopyMemory sngData, arrBuffer(0), LenB(sngData)
                    varData = sngData
                Case vbString
                    Dim strData As String
                    strData = StrConv(arrBuffer(), vbUnicode)
                    varData = strData
            End Select
   
            RaiseEvent DataArrival(wParam, varData)
   
        Case WM_DESTROY
            Call StopListen
           
    End Select
       
    WindowProc = CallWindowProc(PrevWndProc, hwnd, uMsg, wParam, lParam)
   
End Function

Public Function StartListen(ByVal sKey As String) As Boolean
   
    If FindWindow("Static", sKey) <> 0 Then: Exit Function
   
    If PrevWndProc = 0 And pASMWrapper <> 0 Then
        hWin = CreateWindowEx(0, "Static", sKey, 0, 0, 0, 0, 0, 0, 0, App.hInstance, 0&)
        PrevWndProc = SetWindowLong(hwnd, GWL_WNDPROC, pASMWrapper)
        StartListen = PrevWndProc <> 0
    End If
   
End Function

Public Sub StopListen()
   
    If PrevWndProc <> 0 Then
   
        Call RemoveAllClient
       
        If IsWindow(hServer) Then
            Call SendMessage(hServer, WM_CONNECTION_CLOSE, Me.hwnd, ByVal 0&)
            hServer = 0
        End If
       
        Call SetWindowLong(hWin, GWL_WNDPROC, PrevWndProc): PrevWndProc = 0
        Call DestroyWindow(hWin): hWin = 0
    End If
   
End Sub

Public Sub Disconnect()
    If bConnected Then
        Call SendMessage(hServer, WM_CONNECTION_CLOSE, Me.hwnd, ByVal 0&)
        StopListen
        bConnected = False
        hServer = 0
    End If
End Sub

Public Function AddClient(ByVal hwnd As Long) As Boolean
'    If IsWindow(hwnd) And Me.hwnd <> 0 Then
    If Me.hwnd <> 0 Then
        If SendMessage(hwnd, WM_CONNECTION_ACEPT, Me.hwnd, ByVal 0&) = 0 Then
            cConnections.Add hwnd
            AddClient = True
        End If
    End If
End Function

Public Function RemoveClient(ByVal hwnd As Long) As Boolean
    Dim i As Long

    For i = 1 To cConnections.Count
        If cConnections(i) = hwnd Then
            cConnections.Remove i
            If IsWindow(hwnd) Then
               Call SendMessage(hwnd, WM_CONNECTION_CLOSE, Me.hwnd, ByVal 0&)
            End If
            RemoveClient = True
            Exit For
        End If
    Next

End Function

Public Sub RemoveAllClient()
    Dim i As Long
    If cConnections.Count > 0 Then
        For i = cConnections.Count To 1 Step -1
            If IsWindow(cConnections(i)) Then
               Call SendMessage(cConnections(i), WM_CONNECTION_CLOSE, Me.hwnd, ByVal 0&)
            End If
            cConnections.Remove i
        Next
    End If
End Sub

Public Property Get ClientCount() As Long
    ClientCount = cConnections.Count
End Property

Public Property Get GetClient(Index As Long) As Long
    GetClient = cConnections.Item(Index)
End Property

Public Property Get hwnd() As Long
    hwnd = hWin
End Property

Public Property Get hwndSever() As Long
    hwndSever = hServer
End Property

Public Function Join(ByVal DestWindow As Variant) As Long
    Dim hwnd As Long
   
    If IsNumeric(DestWindow) Then
        hwnd = DestWindow
    Else
        hwnd = FindWindow("Static", DestWindow)
    End If
       
    If IsWindow(hwnd) And bConnected = False Then
        If StartListen(App.ThreadID) Then
            Join = SendMessage(hwnd, WM_CONNECTION_REQUEST, hWin, ByVal 0&) = 0
        End If
    End If
   
End Function

Public Sub BroadcastData(ByVal varData As Variant)
    Dim i As Long
   
    For i = 1 To cConnections.Count
        Call SendData(cConnections(i), varData)
    Next
End Sub

Public Function SendData(ByVal DestWindow As Variant, ByVal varData As Variant) As Boolean
    Dim CDS As COPYDATASTRUCT
    Dim hwnd As Long
    Dim arrData() As Byte
       
    If IsNumeric(DestWindow) Then
        hwnd = DestWindow
    Else
        hwnd = FindWindow("Static", DestWindow)
    End If
   
    If IsWindow(hwnd) Then
 
        Select Case VarType(varData)
            Case vbArray + vbByte
                arrData() = varData
            Case vbBoolean
                Dim blnData As Boolean
                blnData = CBool(varData)
                ReDim arrData(LenB(blnData) - 1)
                CopyMemory arrData(0), blnData, LenB(blnData)
            Case vbByte
                Dim bytData As Byte
                bytData = CByte(varData)
                ReDim arrData(LenB(bytData) - 1)
                CopyMemory arrData(0), bytData, LenB(bytData)
            Case vbCurrency
                Dim curData As Currency
                curData = CCur(varData)
                ReDim arrData(LenB(curData) - 1)
                CopyMemory arrData(0), curData, LenB(curData)
            Case vbDate
                Dim datData As Date
                datData = CDate(varData)
                ReDim arrData(LenB(datData) - 1)
                CopyMemory arrData(0), datData, LenB(datData)
            Case vbDouble
                Dim dblData As Double
                dblData = CDbl(varData)
                ReDim arrData(LenB(dblData) - 1)
                CopyMemory arrData(0), dblData, LenB(dblData)
            Case vbInteger
                Dim intData As Integer
                intData = CInt(varData)
                ReDim arrData(LenB(intData) - 1)
                CopyMemory arrData(0), intData, LenB(intData)
            Case vbLong
                Dim lngData As Long
                lngData = CLng(varData)
                ReDim arrData(LenB(lngData) - 1)
                CopyMemory arrData(0), lngData, LenB(lngData)
            Case vbSingle
                Dim sngData As Single
                sngData = CSng(varData)
                ReDim arrData(LenB(sngData) - 1)
                CopyMemory arrData(0), sngData, LenB(sngData)
            Case vbString
                Dim strData As String
                strData = CStr(varData)
                ReDim arrData(Len(strData) - 1)
                arrData() = StrConv(strData, vbFromUnicode)
        End Select

        With CDS
            .dwData = VarType(varData)
            .cbData = UBound(arrData) + 1
            .lpData = VarPtr(arrData(0))
        End With
       
        SendData = SendMessage(hwnd, WM_COPYDATA, hWin, CDS) = 0
       
    End If
End Function

Private Sub Class_Initialize()

    Dim ASM(0 To 103) As Byte
    Dim pVar As Long
    Dim ThisClass As Long
    Dim CallbackFunction As Long
    Dim pVirtualFree
    Dim i As Long
    Dim sCode As String
   
    pASMWrapper = VirtualAlloc(ByVal 0&, 104, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
    If pASMWrapper <> 0 Then

        ThisClass = ObjPtr(Me)
        Call CopyMemory(pVar, ByVal ThisClass, 4)
        Call CopyMemory(CallbackFunction, ByVal (pVar + 28), 4)
        pVirtualFree = GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualFree")

        sCode = "90FF05000000006A0054FF742418FF742418FF742418FF7424186800000000B800000000FFD0FF0D00000000A10000000085C075" & _
                "0458C21000A10000000085C0740458C2100058595858585868008000006A00680000000051B800000000FFE00000000000000000"
               
        For i = 0 To Len(sCode) - 1 Step 2
            ASM(i / 2) = CByte("&h" & Mid$(sCode, i + 1, 2))
        Next

        Call CopyMemory(ASM(3), pASMWrapper + 96, 4)
        Call CopyMemory(ASM(40), pASMWrapper + 96, 4)
        Call CopyMemory(ASM(58), pASMWrapper + 96, 4)
        Call CopyMemory(ASM(45), pASMWrapper + 100, 4)
        Call CopyMemory(ASM(84), pASMWrapper, 4)
        Call CopyMemory(ASM(27), ThisClass, 4)
        Call CopyMemory(ASM(32), CallbackFunction, 4)
        Call CopyMemory(ASM(90), pVirtualFree, 4)
        Call CopyMemory(ByVal pASMWrapper, ASM(0), 104)
       
        WM_CONNECTION_REQUEST = RegisterWindowMessage("WM_CONNECTION_REQUEST")
        WM_CONNECTION_CLOSE = RegisterWindowMessage("WM_CONNECTION_CLOSE")
        WM_CONNECTION_ACEPT = RegisterWindowMessage("WM_CONNECTION_ACEPT")
       
        Set cConnections = New Collection
    End If

End Sub


Private Sub Class_Terminate()

    Dim Counter As Long

    If pASMWrapper <> 0 Then
   
        Call StopListen

        Call CopyMemory(Counter, ByVal (pASMWrapper + 104), 4)
       
        If Counter = 0 Then
            Call VirtualFree(ByVal pASMWrapper, 0, MEM_RELEASE)
        Else
            Call CopyMemory(ByVal (pASMWrapper + 108), 1, 4)
        End If
       
    End If

End Sub

Desde un aplicativo le pasaba el parámetro "maximizar" y la aplicación en cuestión lo recibía, ejecutaba el código para maximizarse y visibilizarse, pero no pasaba nada (no se veía). Me daba cuenta porque a cada paso le puse instrucciones para que escriba un log para ver dónde se detenía y llegaba a ejecutar todo, pero seguía sin mostrarse.
En Windows XP funciona perfectamente, pero en Windows 2008 Server Einterprise no.
Espero haber sido claro. Si necesitás alguna otra información, por favor decime y la brindo.
Muchas gracias por tu tiempo.

Jerónimo