2016-10-10 104 views
1

我有一些宏需要它运行一些代码,然后提示用户从另一个程序中导出Excel工作簿,然后在导出打开后运行更多代码。棘手的部分是某些程序导出到Excel的新实例,而其他程序导出到当前实例。在新的Excel实例中捕获打开的工作簿

当前工作流程(在底部代码):

  1. 呼叫中心“捕获”模块与所述出口的名称(一些 程序导出“书[X]”一些做“工作簿[ x]'等)以及您希望在找到导出后运行的 过程。

  2. Capture Module从所有 Excel实例中获取所有现有工作簿名称的列表,并将其保存为模块级字符串。

  3. Capture Module使用Application.OnTime,以便每隔3秒扫描所有Excel实例中所有工作簿的列表。

  4. 如果找到一个工作簿,是不是在 所有现有的工作簿名先前保存的列表,包含 出口的名称,它存储工作簿作为公共模块级变量, 并运行从步骤1中保存的程序,其可以参考 商店工作簿。

这工作得很好在所有情况下,除非一个。如果我已在当前​​的Excel实例中打开Book1.xlsx,并且第三方程序将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

很难处理这样的进程外的东西,有没有一种方法,而不是你可以提示用户导出/保存为输出Excel文件?然后您只需要一个FileDialog并提示用户从另一个应用程序中选择(导出)文件。 –

+0

应该起作用的一个想法是,不是缓存打开的工作簿名称列表,而是为每个工作簿分配一个'CustomDocumentProperty',您可以合理确保在导出的XLSX文件中不存在'CustomDocumentProperty'。然后,您可以简单地扫描文件(按名称)的应用程序/工作簿,该文件没有**属性。 –

+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 
相关问题