** NORMAS DEL FORO **
Inicio del foro Inicio del foro > Access y VBA > Access y VBA
  Mensajes nuevos Mensajes nuevos RSS - Localizar Excel y leer datos
  Preguntas frecuentes Preguntas frecuentes  Buscar en el foro   Eventos   Registro Registro  Iniciar sesion Iniciar sesion

Tema cerradoLocalizar Excel y leer datos

 Responder Responder Página  12>
Autor
Mensaje
xavi Ver desplegable
Administrador
Administrador
Avatar
Terrassa-BCN

Unido: 10/Mayo/2005
Localización: Catalunya ||||
Estado: Sin conexión
Puntos: 14926
Enlace directo a este mensaje Tema: Localizar Excel y leer datos
    Enviado: 19/Septiembre/2019 a las 17:20
Buenas tardes,

Miro de poneros en situación.

Desde una aplicación Access debe poder localizarse 2 ficheros Excel que, teóricamente, están abiertos en la misma máquina. Si no estuvieran abiertos ya mandaríamos el correspondiente mensaje de error.

Una vez si si están abiertos, necesito recuperar el valor de una serie de celdas (las tengo mapeadas). 

Hàndicap: el nombre del fichero podría no ser siempre igual. Sería parecido pero con posibilidad de pequeños cambios. Más que nada que el propietario del fichero "versiona" en el nombre del fichero (lo que no critico ya que yo también lo he hecho)

He encontrado algunos códigos para localizar el handle de la primera ventana que contenga un determinado texto:
Option Compare Database
Option Explicit

Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindowText Lib "User32" Alias "GetWindowTextA" (ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowTextLength Lib "User32" Alias "GetWindowTextLengthA" (ByVal Hwnd As Long) As Long

Private Const GW_HWNDNEXT = 2

Private Type udtHandle
    Hwnd        As Long
    WindowCaption   As String
End Type
Dim uHandle As udtHandle






Public Function TestFind(strQueBuscamos As String)

    Dim lhWndP As Long
    If GetHandleFromPartialCaption(lhWndP, strQueBuscamos) = True Then
        MsgBox uHandle.Hwnd & " - " & uHandle.WindowCaption
    End If

End Function

Private Function GetHandleFromPartialCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean

    Dim lhWndP As Long
    Dim sStr As String
    GetHandleFromPartialCaption = False
    lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
    Do While lhWndP <> 0
        sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
        GetWindowText lhWndP, sStr, Len(sStr)
        sStr = Left$(sStr, Len(sStr) - 1)
        If InStr(1, sStr, sCaption) > 0 Then
            GetHandleFromPartialCaption = True
            lWnd = lhWndP
            Exit Do
        End If
        lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
    Loop
    uHandle.Hwnd = lWnd
    uHandle.WindowCaption = sStr
End Function

Con eso puedo encontrar el handle de la ventana que busco. ¿Y después que?

Si algo no se entiende no dudéis en preguntar.

Gracias de antemano
Xavi, un minyó de Terrassa

Mi web
Arriba
Erick Gamer Ver desplegable
Asiduo
Asiduo
Avatar

Unido: 08/Mayo/2016
Localización: Mexico
Estado: Sin conexión
Puntos: 387
Enlace directo a este mensaje Enviado: 19/Septiembre/2019 a las 19:03
Pues hice algo parecido en .NET con una aplicación externa JAVA.
Busco la ventana del JAVA y la activo, una vez activa y como no tengo el nombre de los controles del JAVA uso las coordenadas de ellos para mandar parametros y pulsaciones de teclas (SendKeys) uso TAB para cambiar entre pantalla de mi Aplicación a JAVA.

A lo que voy es si ya encontro la ventana de Excel entonces activarla y luego extraer la informacion que necesita, enviando parametros o SendKeys.

Para vba encontre esto realmente no se si te sirva de algo, espero y si Xavi.


Erick Gamer
Aprendiz de todos, maestro de nadie.
Arriba
pitxiku Ver desplegable
Colaborador
Colaborador
Avatar

Unido: 27/Septiembre/2017
Localización: En mi casa
Estado: Sin conexión
Puntos: 1536
Enlace directo a este mensaje Enviado: 19/Septiembre/2019 a las 19:22
Otra posibilidad es usar GetObject para instanciar la aplicación Excel que este abierta. Y a partir de ahí, trabajas igual que con una instancia abierta con CreateObject:

- https://docs.microsoft.com/es-es/office/vba/language/reference/user-interface-help/getobject-function
Arriba
guarracuco Ver desplegable
Moderador
Moderador


Unido: 24/Abril/2004
Localización: EEUU
Estado: Sin conexión
Puntos: 3239
Enlace directo a este mensaje Enviado: 19/Septiembre/2019 a las 19:38
Y si tienes código en esos archivos que exporten la información a un archivo txt?
Arriba
happy Ver desplegable
Moderador
Moderador


Unido: 29/Enero/2005
Localización: España
Estado: Sin conexión
Puntos: 3200
Enlace directo a este mensaje Enviado: 19/Septiembre/2019 a las 23:26
Hola Xavi, ahora no estoy seguro de si la opción que te comenta pitxiku del GetObject te podría servir. Creo que necesitarías saber la ruta absoluta del libro excel para poder referenciarlo (si no recuerdo mal ...) y creo que con las rutinas para encontrar el handle no lo consigues, ni lo podrás conseguir.

Pero he encontrado un código por ahí que creo que te podría servir. Ponlo en un módulo estandar:

' función para obtener un objeto COM desde un handle
Private Declare Function AccessibleObjectFromWindow Lib "OLEACC.DLL" _
                        (ByVal Hwnd As Long, _
                        ByVal dwId As Long, _
                        riid As GUID, _
                        ppvObject As Any) As Long
                   
' función extendida del API FindWindow
Private Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" _
                        (ByVal hWnd1 As Long, _
                        ByVal hWnd2 As Long, _
                        ByVal lpsz1 As String, _
                        ByVal lpsz2 As String) As Long
                        
' función para crear un GUID desde una "cadena" GUID
Private Declare Function IIDFromString Lib "ole32" _
                         (ByVal lpsz As Long, _
                         ByRef lpiid As GUID) As Long
                        
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Const S_OK As Long = &H0
' GUID del objeto COM para Excel
Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0

Function GetXLapp(hWinXL As Long, xlApp As Object) As Boolean
Dim hWinDesk As Long, hWin7 As Long
Dim obj As Object
Dim iid As GUID
    
    ' obtenemos un GUID en variable iid según la cadena IID_IDispatch
    Call IIDFromString(StrPtr(IID_IDispatch), iid)
    ' obtenemos la ventana XLDESK de la ventana principal de Excel
    hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
    ' obtenemos la ventana contenedora del Workbook
    hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)
    ' obtenemos el objeto COM del Workbook en la variable obj
    If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj) = S_OK Then
        ' devolvemos el objeto Application del Workbook
        Set xlApp = obj.Application
        GetXLapp = True
    End If
    
End Function

Para utilizar esta función deberás llamarla de la siguiente manera (tomando la variable Hwnd que ponías en el código que exponías antes):

        If GetXLapp(uHandle.Hwnd, xlApp) = True Then
            ' si llegamos aquí es que xlApp contiene un objeto
            ' Excel.Application del Excel que te interesa
        End If

Espero que te sirva 

(Edito para poner algunos comentarios que expliquen qué hace ese código)


Editado por happy - 20/Septiembre/2019 a las 00:05
Saludos,

Juan M. Afan de Ribera
Arriba
prga Ver desplegable
Moderador
Moderador


Unido: 16/Noviembre/2004
Localización: España
Estado: Sin conexión
Puntos: 3535
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 00:15
Hola.
Sí he entendido bien, con este código, aparentemente más sencillo, se puede "llegar" a los excel abiertos:

Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As Long) As Long

Public Sub Detectaexcel()
Dim hWnd As Long
Dim miexcel As Excel.Application
Dim milibro As Excel.Workbook
    hWnd = FindWindow("XLMAIN", 0)
    
    If hWnd = 0 Then    ' 0 quiere decir que Excel no se está ejecutando .
        
        MsgBox ("NO ESTÁ EXCEL ABIERTO")
        
        Exit Sub
    Else
    
     Set miexcel = GetObject(, "excel.application")
     For Each milibro In miexcel.Workbooks
      MsgBox (milibro.FullName)
     Next
     'miexcel.Quit
     Set miexcel = Nothing

    End If
End Sub

Espero que ayude a resolver la duda.
Un saludo a todos
Arriba
happy Ver desplegable
Moderador
Moderador


Unido: 29/Enero/2005
Localización: España
Estado: Sin conexión
Puntos: 3200
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 09:43
Hola,

no se si esto pasa en todas las versiones diferentes de Excel o no, pero al menos con la versión 2013, que es la que tengo yo instalada, puedes tener más de una instancia de Excel abierta. Es decir, el código que ha expuesto prga es válido, pero sólo referenciará la primera instancia de Excel que tenga el sistema en ese momento y por tanto sólo accederá a los workbooks abiertos que cuelguen de esa instancia. 

Por ejemplo, y ya digo, al menos con esta versión 2013 que tengo yo, si voy al explorador de archivos y abro un libro excel, y después otro y después otro, esos 3 libros excel colgarán de la misma instancia de la aplicación. En ese caso el código con GetObject funcionará bien. Pero si al mismo tiempo abro simplemente el programa excel y desde ahí abro otro libro, se creará una segunda instancia de la aplicación. En ese momento, el GetObject ya no servirá, porque cuando lo ejecute volverá a localizar la primera instancia que haya abierto. No tengo manera, que yo sepa, de decirle que abra la segunda instancia de Excel, que es a lo mejor, la que contiene el excel que me interesa manipular. Ahí ya necesitaría indicarle en el primer parámetro la ruta absoluta del excel al que quiero acceder.

Smile
Saludos,

Juan M. Afan de Ribera
Arriba
Mihura Ver desplegable
Administrador
Administrador
Avatar

Unido: 06/Mayo/2005
Localización: En la dehesa
Estado: Sin conexión
Puntos: 14428
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 10:05
Desde la ignorancia de un novillo que está aprendiendo .... Big smile

¿ No hay alguna manera de recorrer todos los programas que están abiertos en windows e ir analizando uno por uno su nombre / tipo / .. ?       me suena haberlo visto por algún sitio.


Yo uso 2010 y también puedes tener distintas instancias de Excel, es más yo los abro así porque me es más cómodo.






Jesús Mansilla Castells.
Saludos desde Móstoles.

Access Aplicaciones
Tecsys.es
Arriba
pitxiku Ver desplegable
Colaborador
Colaborador
Avatar

Unido: 27/Septiembre/2017
Localización: En mi casa
Estado: Sin conexión
Puntos: 1536
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 11:06
Puedes usar EnumWindows para recuperar los hWnd de las ventanas abiertas, y a partir de ahí comprobar cuál es de Excel:

- https://renenyffenegger.ch/notes/development/languages/VBA/Win-API/examples/EnumWindows

(Creo que en AllApi había otro ejemplo, pero ya no está online )

En el ejemplo usan 2 apis para sacar el título de la ventana y su clase. Lo que no sé es si con eso se puede tomar la instancia de Excel y comprobar qué documentos tiene cargados.

Editado por pitxiku - 20/Septiembre/2019 a las 11:06
Arriba
happy Ver desplegable
Moderador
Moderador


Unido: 29/Enero/2005
Localización: España
Estado: Sin conexión
Puntos: 3200
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 11:32
En realidad lo que conseguirá Xavi con EnumWindows es lo mismo que obtiene con el código que expone en su primer mensaje, sólo que con un bucle y utilizando GetWindow (a lo mejor EnumWindows es una encapsulación de ese otro método en bucle o algo similar).

El problema que tiene Xavi es, una vez localizado el handle de la ventana Excel que contiene el libro de trabajo que le interesa (del cual comenta que puede incluso variar el nombre), cómo obtener una instancia del objeto COM o de automatización para esa instancia de Excel y así poder manejar con automatización el workbook, los worksheets que contenga, celdas, rangos, etc. El código que he expuesto obtiene el objeto COM de la instancia Excel indicada utilizando únicamente el handle (identificador de ventana) de la ventana principal (XLMAIN) de esa instancia Excel.

Muchas de estas cosas las han resuelto con la plataforma .NET en donde, por lo que he visto, en vez de utilizar directamente APIs de Windows, han encapsulado estos métodos o pequeños procesos (igual al que estamos tratando aquí) en clases y objetos, de modo que el programador no tenga que lidiar con todos estos problemas. Que yo sepa, la mayoría de estas funciones típicas del API de Windows están escritas en C de los primeros tiempos. PUede que algunas las hayan actualizado un poco para adecuarlas, primero a 32 y luego a 64 bits, pero básicamente hacen lo mismo y tienen las mismas dificultades para el programador de VB (me refiero sobre todo al paso de argumentos a esas funciones y a los tipos de variables a utilizar para ello, así como - importante! - la manera de pasar ByVal o ByRef esas variables o valores a las funciones del API) Wink


Editado por happy - 20/Septiembre/2019 a las 11:34
Saludos,

Juan M. Afan de Ribera
Arriba
xavi Ver desplegable
Administrador
Administrador
Avatar
Terrassa-BCN

Unido: 10/Mayo/2005
Localización: Catalunya ||||
Estado: Sin conexión
Puntos: 14926
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 14:14
Buenas. Por alusiones, jejejeje

El primer código de pitxiku con el nombre completo (ahí el "problema") funciona OK.

Probaré el codigo de Happy a ver si me permite acotar más. 

De todas formas también le puedo decir al cliente (el que ya sabeis...) que, o me pone la ruta completa y el mismo nombre siempre o el sistema no funciona. Bastaría con evaluar el WindowCaption de mi primer código para ver que coincida con el esperado.

Os mantengo informados.

Muchas gracias a todos por las aportaciones

Xavi, un minyó de Terrassa

Mi web
Arriba
pitxiku Ver desplegable
Colaborador
Colaborador
Avatar

Unido: 27/Septiembre/2017
Localización: En mi casa
Estado: Sin conexión
Puntos: 1536
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 16:55
Por partes:

- Mihura preguntó por un código para recorrer las ventanas, por eso hablé del EnumWindows. Otra cosa es que para este caso no sirva de mucho.

- Con el código de prga se obtiene la instancia de Excel abierta. Una vez tenemos la instancia, podemos recorrer los libros abiertos y comprobar si algo (nombre de hoja, de libro, valor en una celda,... ) coincide con un estándar dado por el cliente para que sea ese el libro a tratar.

- Lo que no consigo es abrir 2 instancias de Excel. Me explico: abro una con una hoja en blanco, abro un libro directamente, vuelvo a abrir otro Excel desde el menú con otra hoja en blanco ... Y lo que ocurre es que la primera instancia de Excel, pillada con el GetObject, incrementa el número de libros abiertos en su colección WorkBooks.
Arriba
Emilio Ver desplegable
Administrador
Administrador

Santander

Unido: 08/Agosto/2004
Localización: España
Estado: Sin conexión
Puntos: 18836
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 17:22
Buenas, 

Pitxiku, para abrir dos instancias de Excel has de abrir Excel desde "su icono" en W10 puedes pulsar con el botón derecho del ratón sobre el botón de la instancia abierta y seleccionar Microsoft Excel.
Si abres un libro Excel directamente siempre (que la haya) se abrirá en la instancia abierta.
Saludos a todos desde Huelva

http://www.mvp-access.es/emilio/
Arriba
Mihura Ver desplegable
Administrador
Administrador
Avatar

Unido: 06/Mayo/2005
Localización: En la dehesa
Estado: Sin conexión
Puntos: 14428
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 17:39
Jesús Mansilla Castells.
Saludos desde Móstoles.

Access Aplicaciones
Tecsys.es
Arriba
prga Ver desplegable
Moderador
Moderador


Unido: 16/Noviembre/2004
Localización: España
Estado: Sin conexión
Puntos: 3535
Enlace directo a este mensaje Enviado: 20/Septiembre/2019 a las 18:52
A partir de no se que versión de oficce, sólo se pueden abrir 2 o más instancias de excel o word por código (createobject).
Utilizando directamente el excel, al menos yo, no he podido conseguir dos instancias diferentes del excel 2019, incluso si se abre con shell o followhyperlink. Eso sí, se crean 2 ventanas diferentes, pero una sola instancia
Arriba
 Responder Responder Página  12>
  Compartir tema   

Ir al foro Permisos de foro Ver desplegable