2016-10-10 2 views
1

У меня есть несколько макросов, где я хочу, чтобы он запускал некоторый код, а затем предлагаю пользователю экспортировать книгу Excel из другой программы, а затем запускать больше кода после открытия экспорта. Сложная часть заключается в том, что некоторые программы экспортируются в новый экземпляр Excel, а другие программы экспортируются в текущий экземпляр.Capture Opened Workbook в новом экземпляре Excel

Текущий рабочий процесс (код в нижней части):

  1. Вызов центральный «Capture» Модуль с именем экспорта (некоторые программы экспорта «Book [х]» некоторые делают «учебное пособие [ x] 'и т. д.) и процедуру , которую вы хотите запустить, как только обнаружен экспорт.

  2. Модуль Capture Module получает список всех существующих имен книг из всех экземпляров Excel и сохраняет их как строку уровня модуля.

  3. Модуль Capture использует Application.OnTime, поэтому каждые 3 секунды он просматривает список всех книг во всех экземплярах Excel.

  4. Если он находит книгу, которая не в ранее сохраненном списке всех существующих имен рабочих книг, и которая содержит имя экспорта, он хранит эту книгу в качестве переменного уровня общественного модуля, и бежит сохраненная процедура с шага 1, которая может ссылаться на справочную книгу .

Это работает очень хорошо во всех обстоятельствах, КРОМЕ для одного. Если у меня уже открыт Book1.xlsx в моем текущем экземпляре Excel, а сторонняя программа экспортирует Book1.xlsx в новый экземпляр Excel, программа не распознает это как экспорт, так как Book1.xlsx находится в существующем рабочая строка имен уже содержит массив строк.

Мое решение состоит в том, чтобы найти способ уникальной идентификации каждой книги, которая лучше, чем «Имя» или «Путь». Я попытался сохранить имя каждой книги в существующей строке имен книг как [application.hwnd]! [Название книги], но это было неустойчивое исправление и часто ломалось (я не совсем понимаю, как работает hwnd, поэтому я не могу сказать, почему) ,

Любые идеи? Благодаря!

Процедуры Примеры, использующие MCaptureExport

Public Sub GrabFXAllExport() 

    Const sSOURCE As String = "GrabFXAllExport" 

    On Error GoTo ErrorHandler 

    If Not TAAA.MCaptureExport.bCaptureExport("FXALL", "TAAA.FXAllEmail.ProcessFXAllExport") Then Err.Raise glHANDLED_ERROR 

ErrorExit: 

    Exit Sub 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Sub 
Public Sub ProcessFXAllExport() 

    Const sSOURCE As String = "ProcessFXAllExport" 

    On Error GoTo ErrorHandler 

    If MCaptureExport.mwbCaptured Is Nothing Then 
     MsgBox "Exported Workbook Not Found. Please try again.", vbCritical, gsAPP_NAME 
     GoTo ErrorExit 
    End If 

    Dim wsSourceSheet As Worksheet 
    Set wsSourceSheet = MCaptureExport.mwbCaptured.Worksheets(1) 
    Set MCaptureExport.mwbCaptured = Nothing 

    [I now have the export and can work with it as a I please] 

ErrorExit: 

    Exit Sub 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Sub 

MCaptureExport Модуль Функции

Option Explicit 
Option Base 1 

' Description: This module contains the central error 
'    handler and related constant declarations. 
Private Const msMODULE As String = "MCaptureExport" 

Private sExistingWorkbookList() As String 
Public mwbCaptured As Workbook 
Public msCaptureType As String 
Private sReturnProcedure As String 
Private bListening As Boolean 
Public Function bCaptureExport(sCaptureType As String, sRunAfterCapture As String) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bCaptureExport()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    If Not bWorkbookNamesAsArray(sExistingWorkbookList, True, False) Then Err.Raise glHANDLED_ERROR 

    sReturnProcedure = sRunAfterCapture 
    bListening = True 
    msCaptureType = sCaptureType 
    TAAA.MCaptureExport.WaitForCapture sCaptureTypeToNameContains(msCaptureType) 
    MsgBox "Waiting for " & msCaptureType & " Export", vbInformation, gsAPP_NAME 

ErrorExit: 

    bCaptureExport = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 

Private Sub WaitForCapture(sNameContains As String) 

    Const sSOURCE As String = "WaitForCapture" 

    On Error GoTo ErrorHandler 

    Dim wbCaptureCheck As Workbook 
    If Not bCaptureCheck(sNameContains, wbCaptureCheck) Then Err.Raise glHANDLED_ERROR 

    If wbCaptureCheck Is Nothing Then 
     If bListening Then _ 
      Application.OnTime Now + TimeSerial(0, 0, 3), "'TAAA.MCaptureExport.WaitForCapture " & Chr(34) & sNameContains & Chr(34) & "'" 
    Else 
     Dim bSameApp As Boolean 
     If Not bWorkbooksInSameApp(ThisWorkbook, wbCaptureCheck, bSameApp) Then Err.Raise glHANDLED_ERROR 

     If Not bSameApp Then 
      Dim sTempFilePath As String 
      sTempFilePath = ThisWorkbook.Path & "\temp_" & Format(Now, "mmddyyhhmmss") & ".xls" 
      wbCaptureCheck.SaveCopyAs sTempFilePath 
      wbCaptureCheck.Close SaveChanges:=False 
      Set wbCaptureCheck = Application.Workbooks.Open(sTempFilePath) 
     End If 

     Set mwbCaptured = wbCaptureCheck 
     bListening = False 
     Application.Run sReturnProcedure 
    End If 

ErrorExit: 

    Exit Sub 

ErrorHandler: 
    If bCentralErrorHandler(msMODULE, sSOURCE, , True) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Sub 
Private Function sCaptureTypeToNameContains(sCaptureType As String) As String 

    sCaptureTypeToNameContains = "*" 

    On Error Resume Next 

    Select Case UCase(sCaptureType) 
     Case "SOTER": sCaptureTypeToNameContains = "workbook" 
     Case "THOR": sCaptureTypeToNameContains = "Book" 
     Case "FXALL": sCaptureTypeToNameContains = "search_results_export" 
    End Select 

End Function 
Private Function bCaptureCheck(sNameContains As String, wbResult As Workbook) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bCaptureCheck()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim i As Long, wb As Workbook 
    Dim xlApps() As Application 
    If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR 
    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 

      If wb.Name Like "*" & sNameContains & "*" _ 
       And Not bIsInArray(wb.Name, sExistingWorkbookList) Then 

       Set wbResult = wb 
       GoTo ErrorExit 

      End If 
     Next 
    Next 

ErrorExit: 

    bCaptureCheck = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 

Подсобные используемые MCaptureExport

Public Function bWorkbookNamesAsArray(sResult() As String, Optional bAllInstances As Boolean = True) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bWorkbookNamesAsArray()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim i As Long, wb As Workbook 
    Dim xlApps() As Application 

    Dim ResultArray() As String 
    Dim Ndx As Integer, wbCount As Integer 

    If bAllInstances Then 
     If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR 
    Else 
     ReDim xlApps(0) 
     Set xlApps(0) = Application 
    End If 

    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 
      wbCount = wbCount + 1 
     Next 
    Next 

    ReDim ResultArray(1 To wbCount) 

    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 
      Ndx = Ndx + 1 
      ResultArray(Ndx) = wb.Name 
     Next 
    Next 

    sResult = ResultArray() 

ErrorExit: 

    bWorkbookNamesAsArray = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 


End Function 
Public Function bGetAllExcelInstances(xlApps() As Application) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bGetAllExcelInstances()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim n As Long 

    Dim hWndMain As LongPtr 

    Dim app As Application 

    ' Cater for 100 potential Excel instances, clearly could be better 
    ReDim xlApps(1 To 100) 

    hWndMain = FindWindowEx(0&, 0&, "XLMAIN", vbNullString) 

    Do While hWndMain <> 0 
     If Not bGetExcelObjectFromHwnd(hWndMain, app) Then Err.Raise glHANDLED_ERROR 

     If Not (app Is Nothing) Then 
      If n = 0 Then 
       n = n + 1 
       Set xlApps(n) = app 
      ElseIf bCheckHwnds(xlApps, app.Hwnd) Then 
       n = n + 1 
       Set xlApps(n) = app 
      End If 
     End If 
     hWndMain = FindWindowEx(0&, hWndMain, "XLMAIN", vbNullString) 

    Loop 

    If n Then 
     ReDim Preserve xlApps(1 To n) 
     'GetAllExcelInstances = n 
    Else 
     Erase xlApps 
    End If 

ErrorExit: 

    bGetAllExcelInstances = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 


Private Function bCheckHwnds(xlApps() As Application, Hwnd As LongPtr) As Boolean 

    On Error Resume Next 

    Dim i As Integer 

    For i = LBound(xlApps) To UBound(xlApps) 
     If Not xlApps(i) Is Nothing Then 
      If xlApps(i).Hwnd = Hwnd Then 
       bCheckHwnds = False 
       Exit Function 
      End If 
     End If 
    Next i 

    bCheckHwnds = True 

End Function 
Public Function bWorkbooksInSameApp(wb1 As Workbook, wb2 As Workbook, bSameApp As Boolean) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bWorkbooksInSameApp()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    bSameApp = wb1.Application.Hwnd = wb2.Application.Hwnd 

ErrorExit: 

    bWorkbooksInSameApp = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 


End Function 
Private Function bGetExcelObjectFromHwnd(ByVal hWndMain As LongPtr, aAppResult As Application) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bGetExcelObjectFromHwnd()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim hWndDesk As LongPtr 
    Dim Hwnd As LongPtr 
    Dim strText As String 
    Dim lngRet As Long 
    Dim iid As UUID 
    Dim obj As Object 

    hWndDesk = FindWindowEx(hWndMain, 0&, "XLDESK", vbNullString) 

    If hWndDesk <> 0 Then 

     Hwnd = FindWindowEx(hWndDesk, 0, vbNullString, vbNullString) 

     Do While Hwnd <> 0 

     strText = String$(100, Chr$(0)) 
     lngRet = CLng(GetClassName(Hwnd, strText, 100)) 

     If Left$(strText, lngRet) = "EXCEL7" Then 

      Call IIDFromString(StrPtr(IID_IDispatch), iid) 

      If AccessibleObjectFromWindow(Hwnd, OBJID_NATIVEOM, iid, obj) = 0 Then 'S_OK 

       Set aAppResult = obj.Application 
       GoTo ErrorExit 

      End If 

     End If 

     Hwnd = FindWindowEx(hWndDesk, Hwnd, vbNullString, vbNullString) 
     Loop 

    End If 

ErrorExit: 

    bGetExcelObjectFromHwnd = bReturn 
    Exit Function 

ErrorHandler: 
    MsgBox Err.Number 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 
+0

Tough обрабатывать вне процесса вещи, как это, есть способ, вместо этого вы можете предложить пользователю экспортировать/сохранить, а выходной файл Excel? Тогда вам просто нужен FileDialog и предложите пользователю выбрать (экспортированный) файл из другого приложения. –

+0

Одна из идей, которая должна работать, - вместо того, чтобы кэшировать список имен открытой книги, присваивать каждой книге «CustomDocumentProperty», которую вы можете разумно гарантировать, не будет существовать в экспортированных файлах XLSX. Затем вы можете просто сканировать приложения/книги для файла (по имени), который ** не имеет этого свойства **. –

+0

@DavidZemens Это интересная идея! Если мое решение ниже с hWnd не работает, я собираюсь попробовать следующее. Большое спасибо за помощь! –

ответ

1

У меня есть потенциальное решение. Однако я хочу оставить вопрос открытым. Это довольно сложная проблема, и я уверен, что есть более элегантные решения, чем то, что я предлагаю.

Поэтому я обновил формат sExistingWorkbookList до [Application.hWnd]! [Workbook.name]. Я пробовал это раньше, но я думаю, что он работает на этот раз.

Мысли?

обновленная версия bWorkbookNamesAsArray

Добавлено wb.Application.Hwnd & "!" & в ResultArray(Ndx) = wb.name

Public Function bWorkbookNamesAsArray(sResult() As String, Optional bAllInstances As Boolean = True) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bWorkbookNamesAsArray()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim i As Long, wb As Workbook 
    Dim xlApps() As Application 

    Dim ResultArray() As String 
    Dim Ndx As Integer, wbCount As Integer 

    If bAllInstances Then 
     If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR 
    Else 
     ReDim xlApps(0) 
     Set xlApps(0) = Application 
    End If 

    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 
      wbCount = wbCount + 1 
     Next 
    Next 

    ReDim ResultArray(1 To wbCount) 

    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 
      Ndx = Ndx + 1 
      ResultArray(Ndx) = wb.Application.Hwnd & "!" & wb.Name 
     Next 
    Next 

    sResult = ResultArray() 

ErrorExit: 

    bWorkbookNamesAsArray = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 


End Function 

Новая функция полезности

Public Function bGetWorkbookFromHwndAndName(ByVal sWorkbookReference As String, ByRef wbResult As Workbook) 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bGetWorkbookFromHwndAndName()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim xlApp As Application 

    If Not bGetExcelObjectFromHwnd(CLng(Split(sWorkbookReference, "!")(0)), xlApp) Then Err.Raise glHANDLED_ERROR 

    Set wbResult = xlApp.Workbooks(Split(sWorkbookReference, "!")(1)) 

ErrorExit: 

    bGetWorkbookFromHwndAndName = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 

Обновлено MCaptureExport.bCaptureCheck()

Private Function bCaptureCheck(sNameContains As String, wbResult As Workbook) As Boolean 

    Dim bReturn As Boolean 
    Const sSOURCE As String = "bCaptureCheck()" 

    On Error GoTo ErrorHandler 
    bReturn = True 

    Dim i As Long, wb As Workbook, sFullWorkbookReference As String 
    Dim xlApps() As Application 
    If Not bGetAllExcelInstances(xlApps) Then Err.Raise glHANDLED_ERROR 
    For i = LBound(xlApps) To UBound(xlApps) 
     For Each wb In xlApps(i).Workbooks 

      sFullWorkbookReference = wb.Application.Hwnd & "!" & wb.Name 

      If wb.Name Like "*" & sNameContains & "*" _ 
       And Not bIsInArray(sFullWorkbookReference, sExistingWorkbookList) Then 

       If Not bGetWorkbookFromHwndAndName(sFullWorkbookReference, wbResult) Then Err.Raise glHANDLED_ERROR 
       GoTo ErrorExit 

      End If 
     Next 
    Next 

ErrorExit: 

    bCaptureCheck = bReturn 
    Exit Function 

ErrorHandler: 
    bReturn = False 
    If bCentralErrorHandler(msMODULE, sSOURCE) Then 
     Stop 
     Resume 
    Else 
     Resume ErrorExit 
    End If 

End Function 
Смежные вопросы