Board logo

標題: [發問] 有兩個獨立EXCEL,如何知道切換到另一個獨立EXCEL? [打印本頁]

作者: justintoolbox    時間: 2015-7-24 07:25     標題: 如果手動新增一個活頁簿時,這個活頁簿卻不會被計算到?

各位前輩們大家好,

疑問是:『如果手動新增一個活頁簿時,這個活頁簿卻不會被計算到?VBA該如何修正才能知道正確的活頁簿數量? 』

疑問發生如下:
我有一個檔案名稱為A.xlsm,在代碼上寫上debug.print workbooks.count能計算出 1 (實際上只有1個excel檔案)。
如果這時手動新增一個excel新檔,在A.xlsm執行debug.print workbooks.count還是只能顯示 1 (實際上有2個excel檔)

請問各位前輩們,代碼應該如何修正?請指導,感謝∼
  1. debug.print workbooks.count
複製代碼

作者: justintoolbox    時間: 2015-7-24 09:10

各位前輩們大家好,

疑問是:『如果手動新增一個活頁簿時,這個活頁簿卻不會被計算到?VBA該如何修正才能 ...
justintoolbox 發表於 2015-7-24 07:25


各位前輩們,對於『手動新增活頁簿』前面說的不清楚,底下再詳細說明。

手動新增活頁簿的方式為:按住shift鍵,並在a.xlsm檔上點一下左鍵(如下圖a),就會出現一個活頁簿了(如下圖b)。

[attach]21471[/attach]----圖a
[attach]21472[/attach]----圖b
=====================================================
只有以上這種的手動新增方式,workbooks.count為 1
但若是採用在 a.xlsm的檔案-->新增活頁簿的方式,這可以正確計算出來為 2

不知道為什麼會這樣子?
作者: Min    時間: 2015-7-25 00:32

這一層並非workbooks喔!
因該是workbooks的爸爸 application才對,所以因該沒有辦法用application.vba去計算到workbooks數量,但用API去算excel application因該還是可以辦的到!
作者: justintoolbox    時間: 2015-7-25 05:49

這一層並非workbooks喔!
因該是workbooks的爸爸 application才對,所以因該沒有辦法用application.vba去計 ...
Min 發表於 2015-7-25 00:32


原來是"不同層"的關係∼
非常感謝Min大解惑!
也提供一個解決問題的方向,非常感謝!

但是...因為對上一層API真的不瞭解。
前輩們能否再提醒一下這部分的關鍵字應該查詢什麼?
讓我可以更清楚的去找尋答案。感謝!
作者: justintoolbox    時間: 2015-7-25 10:02

回復 4# justintoolbox
各位前輩們大家好,
目前問題:如何能抓到第二個excel並讀取檔名?
打開工作管理員,知道這兩個workbook是不同一個excel。
[attach]21476[/attach]

用以下方法,只能讀取第一個excel的檔名(a.xlsm),代碼應該如何修正才能讀取第二個EXCEL檔名?
拜託,各位前輩們出手相救!
  1. Sub ex()
  2. Dim oXL   As Excel.Application
  3. Dim oWB   As Excel.Workbook

  4. Set oXL = GetObject(, "Excel.Application")
  5. For Each oWB In oXL.Workbooks
  6.         Debug.Print oWB.Name
  7. Next

  8. End Sub
複製代碼

作者: justintoolbox    時間: 2015-7-25 11:53

回復  justintoolbox
各位前輩們大家好,
目前問題:如何能抓到第二個excel並讀取檔名?
打開工作管理員 ...
justintoolbox 發表於 2015-7-25 10:02


各位前輩們大家好
目前找到了方法(代碼如下)可以知道有幾個獨立EXCEL(不知道該如何稱呼,暫時先稱為獨立EXCEL),
想繼續尋根究底的知道,代碼應該如何修正才能知道第二個獨立EXCEL的檔名?
  1. Option Explicit
  2. Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias _
  3. "FindWindowExA" ( _
  4. ByVal hWnd1 As Long, ByVal hWnd2 As Long, _
  5. ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
  6. Private Declare PtrSafe Function GetDesktopWindow Lib "user32.dll" () As Long

  7. Sub GetXLhWnds() '資料:http://www.pcreview.co.uk/threads/getobject-when-more-than-1-excel-is-running.3575757/
  8. Dim n As Long
  9. Dim hWndXL As Long

  10. Do
  11.         hWndXL = FindWindowEx(GetDesktopWindow, hWndXL, "XLMAIN", vbNullString)
  12.         If hWndXL Then
  13.         n = n + 1
  14.         End If
  15. Loop Until hWndXL = 0
  16. Debug.Print "獨立EXCEL有:" & n &"個"
  17. End Sub
複製代碼

作者: no3-taco    時間: 2015-7-25 11:59

Sub testxxx()
bookcount = Application.Windows.Count
For i = 1 To bookcount
    Debug.Print Application.Windows(i).Caption
Next
End Sub
作者: no3-taco    時間: 2015-7-25 12:17

本帖最後由 no3-taco 於 2015-7-25 12:20 編輯

回復 6# justintoolbox

不好意思,剛剛我貼的行不通
我照你的方法開啟excel,真的不行
作者: justintoolbox    時間: 2015-7-25 17:37

回復  justintoolbox

不好意思,剛剛我貼的行不通
我照你的方法開啟excel,真的不行
no3-taco 發表於 2015-7-25 12:17


no3-taco前輩,謝謝你的幫忙。

我有查到網路上的類似的問題。我把代碼放在底下(上一封的代碼是我依照下面的代碼修正而來,可顯示多少個獨立EXCEL)。

測試結果:
我在a.xlsm執行過底下代碼的test程序,結果debug.print出來是一串長整數。
如果這時候:『按下shit鍵並在a.xlsm按下左鍵』,則會新增出一個獨立的excel檔。
這時候在a.xlsm執行test程序,會出現兩串長整數。

感想:
只知道這兩串各代表著兩個獨立EXCEL,但卻不知道如何利用?
我有思考:若知道這兩串數字,就說不定可以操控個別獨立EXCEL了(也就可以知道另一個獨立EXCEL檔名了)

懇請各位前輩或高手們,出手相救∼ 感謝!∼
  1. Option Explicit
  2. Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias _
  3. "FindWindowExA" ( _
  4. ByVal hWnd1 As Long, ByVal hWnd2 As Long, _
  5. ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
  6. Private Declare PtrSafe Function GetDesktopWindow Lib "user32.dll" () As Long

  7. Sub test()
  8. Dim i As Long
  9. Dim arrXLhWnd() As Long

  10. If GetXLhWnds(arrXLhWnd) Then
  11. For i = LBound(arrXLhWnd) To UBound(arrXLhWnd)
  12. Debug.Print arrXLhWnd(i)
  13. Next
  14. End If

  15. End Sub

  16. Function GetXLhWnds(arrXLhWnd() As Long) As Long
  17. Dim n As Long
  18. Dim hWndXL As Long, hWndDT As Long

  19. ReDim arrXLhWnd(1 To 100) ' cater for 100 potential Excelinstances
  20. hWndDT = GetDesktopWindow

  21. Do
  22. hWndXL = FindWindowEx(hWndDT, hWndXL, "XLMAIN", vbNullString)
  23. If hWndXL Then
  24. n = n + 1
  25. arrXLhWnd(n) = hWndXL
  26. End If
  27. Loop Until hWndXL = 0
複製代碼

作者: azrael19    時間: 2015-7-25 18:22

no3-taco前輩,謝謝你的幫忙。

我有查到網路上的類似的問題。我把代碼放在底下(上一封的代碼是我依 ...
justintoolbox 發表於 2015-7-25 17:37


試試看(測試環境 : Win7 + Office2013)
  1. Option Explicit
  2. Option Base 1

  3. #If Win64 Then
  4. Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" _
  5.         (ByVal hWnd1 As LongPtr, ByVal hWnd2 As LongPtr, ByVal lpsz1 As String, _
  6.          ByVal lpsz2 As String) As LongPtr
  7. Private Declare PtrSafe Function IIDFromString Lib "ole32" _
  8.         (ByVal lpsz As LongPtr, ByRef lpiid As GUID) As LongPtr
  9. Private Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" _
  10.         (ByVal hWnd As LongPtr, ByVal dwId As LongPtr, ByRef riid As GUID, _
  11.          ByRef ppvObject As Object) As Long

  12. #Else
  13. Private Declare Function FindWindowEx Lib "User32" Alias "FindWindowExA" _
  14.         (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
  15.          ByVal lpsz2 As String) As Long
  16. Private Declare Function IIDFromString Lib "ole32" _
  17.         (ByVal lpsz As Long, ByRef lpiid As GUID) As Long
  18. Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
  19.         (ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As GUID, _
  20.          ByRef ppvObject As Object) As Long
  21. #End If
  22.          
  23. Private Type GUID
  24.     Data1 As Long
  25.     Data2 As Integer
  26.     Data3 As Integer
  27.     Data4(7) As Byte
  28. End Type

  29. Private Const S_OK As Long = &H0
  30. Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
  31. Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0

  32. Private Function GetXLapp(hWinXL, xlApp As Object) As Boolean
  33.     Dim hWinDesk, hWin7, obj As Object
  34.     Dim iid As GUID
  35.     Call IIDFromString(StrPtr(IID_IDispatch), iid)
  36.     hWinDesk = FindWindowEx(hWinXL, 0&, "XLDESK", vbNullString)
  37.     hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)
  38.     If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iid, obj) = S_OK Then
  39.         Set xlApp = obj.Application
  40.         GetXLapp = True
  41.     End If
  42. End Function

  43. Private Function IsCollectionExists(ByVal oCol As Collection, ByVal vKey As Variant) As Boolean
  44.     On Error Resume Next
  45.     oCol.Item vKey
  46.     IsCollectionExists = (Err.Number = 0)
  47.     Err.Clear
  48.     On Error GoTo 0
  49. End Function

  50. Public Function GetXLInstanceInfo(ByRef col As Object) As Long
  51.     Dim hWndXL, i As Long
  52.     Dim xlApp As Object, wb As Object

  53.     Set col = Nothing
  54.     Set col = New Collection

  55.     hWndXL = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)
  56.     While hWndXL > 0
  57.         If GetXLapp(hWndXL, xlApp) Then
  58.             For Each wb In xlApp.Workbooks
  59.                 If Not IsCollectionExists(col, wb.Name) Then
  60.                      col.Add Array(hWndXL, xlApp, wb.Name, wb.Path), wb.Name
  61.                 End If
  62.             Next
  63.         End If
  64.         hWndXL = FindWindowEx(0, hWndXL, "XLMAIN", vbNullString)
  65.     Wend
  66.     GetXLInstanceInfo = col.Count
  67.    
  68. End Function

  69. Sub Ex()
  70.     Dim col As Collection
  71.     Dim i As Long
  72.     Dim xlApp As Excel.Application, AR As Variant
  73.    
  74.     i = GetXLInstanceInfo(col)
  75.     Debug.Print "獨立EXCEL有:" & i & "個"
  76.    
  77.     '測試 : 在所有Excel檔第1個工作表儲存格A1上寫入自己的檔名
  78.     For i = 1 To col.Count
  79.         AR = col(i)    ' AR(1): HWND (視窗控制代碼)
  80.                        ' AR(2): xlApp (Excel檔所屬的父項 Excel.Application)
  81.                        ' AR(3): 檔名
  82.                        ' AR(4): 檔案路徑
  83.         Set xlApp = AR(2)
  84.         xlApp.Workbooks(AR(3)).Sheets(1).Range("A1").Value = "檔名:" & AR(3)
  85.     Next
  86.    
  87.     Set xlApp = Nothing
  88.     Set col = Nothing
  89.    
  90. End Sub
複製代碼

作者: justintoolbox    時間: 2015-7-26 06:11

試試看(測試環境 : Win7 + Office2013)
azrael19 發表於 2015-7-25 18:22


哇!哇!哇!真的太....太棒了!
非常感謝azrael19大,出手相救!(連檔案路徑都幫忙擷取出來了,太感動了!)
原來需要這麼多的代碼才能知道檔名(汗顏中...),
看來這需要花一些時間去理解與吸收,再次感謝azrael19大!
作者: bobomi    時間: 2015-7-26 06:36

連附上原創者答案的連結都會莫名其妙被砍.... 無言
作者: justintoolbox    時間: 2015-7-26 06:44

連附上原創者答案的連結都會莫名其妙被砍.... 無言
bobomi 發表於 2015-7-26 06:36


也非常感謝bobomi大姊!
昨天我也有看到你貼超連結,只是今早就沒看見了,可能是系統的問題吧。

能否再提供你昨天的網站連結呢?
讓我也一併吸收,再次感謝您!
作者: justintoolbox    時間: 2015-7-26 07:05     標題: 有兩個獨立EXCEL,如何知道切換到另一個獨立EXCEL?

各位前輩們大家好,

A.xlsm 為中心(請搭配下圖),若切換到 C.xlsx(屬同一個獨立EXCEL 1),則會觸發 Private Sub Workbook_Deactivate() 程序。
若從 A.xlsm 切換到 A2.xlsx(屬於另一個獨立EXCEL 2),卻不會觸發 Private Sub Workbook_Deactivate() 程序。
[attach]21483[/attach]


心得
觸發Private Sub Workbook_Deactivate() 只能在同一個獨立EXCEL內的活頁簿間切換,才就會執行該程序。
但,若是切換另一個獨立EXCEL時,對A.xlsm而言是沒有變化的。

問題
代碼該如何修正?才能知道有切換到另一個獨立EXCEL。
請各位前輩們出手相救!感恩!
  1. Private Sub Workbook_Deactivate()
  2. msgbox "有切換"  
  3. End Sub
複製代碼

作者: azrael19    時間: 2015-7-26 08:47

也非常感謝bobomi大姊!
昨天我也有看到你貼超連結,只是今早就沒看見了,可能是系統的問題吧。

能 ...
justintoolbox 發表於 2015-7-26 06:44


網址:
https://social.msdn.microsoft.com/Forums/office/en-US/e3e99712-01a7-483e-bf0e-52bb1f94889c/how-to-use-accessibleobjectfromwindow-api-in-vba-to-get-excel-application-object-from-excel?forum=exceldev

還有不好意思讓bobomi前輩您不開心,以後回文我會注意加上參考資料來源,希望您能見諒!
作者: justintoolbox    時間: 2015-7-27 09:27

各位前輩們大家好,

以 A.xlsm 為中心(請搭配下圖),若切換到 C.xlsx(屬同一個獨立EXCEL 1),則會觸發 ...
justintoolbox 發表於 2015-7-26 07:05


各位前輩們,不知道能否做到 切換到另一個獨立EXCEL時觸發程序?
拜託各位高手前輩們解惑!
作者: GBKEE    時間: 2015-7-27 14:02

本帖最後由 GBKEE 於 2015-7-27 14:03 編輯

回復 16# justintoolbox

[attach]21526[/attach]

Module1的程式碼
  1. Option Explicit
  2. Public xApp As New Class1
  3. Dim NewApp As New Application
  4. Sub Ex()
  5.     Set xApp.The_App = NewApp
  6. End Sub
複製代碼
物件類別模組 Class1 的程式碼
  1. Option Explicit
  2. Dim WithEvents APP As Application
  3. Property Set The_App(P As Object)
  4.     Set APP = P    ' 將 APP 屬性值設成物件。
  5.     With APP
  6.         .Visible = True
  7.         With .Workbooks.Add
  8.             .Sheets(1).[a1] = 5
  9.         End With
  10.     End With
  11. End Property

  12. Private Sub APP_NewWorkbook(ByVal Wb As Workbook)
  13.     MsgBox Wb.Name
  14. End Sub
  15. Private Sub APP_SheetChange(ByVal Sh As Object, ByVal Target As Range)
  16.     MsgBox Target.Address(, , , 1, 1)
  17. End Sub
複製代碼

作者: justintoolbox    時間: 2015-7-28 09:32

回復  justintoolbox



Module1的程式碼物件類別模組 Class1 的程式碼
GBKEE 發表於 2015-7-27 14:02


非常感謝GBKEE版主出手相救!
GBKEE版的代碼 是利用Ex程序,建立出一個獨立EXCEL, 並讀取新建立的EXCEL的檔名與滑鼠選擇的位置.

只是.....
可能我前面沒有詳述清楚,真的很抱歉.讓GBKEE大誤會了.
我原本期望是:
已經存在的兩個EXCEL,但這兩個都是各自獨立的(不同一個application),
如果這時候我從A.xlsm切換到另一個EXCEL時, 我希望能藉由觸發程序告訴我有切換到另一個獨立EXCEL,不需要知道檔名沒關係.
(兩個活頁簿若在同一個application時,可以利用Workbook_Deactivate觸發程序,就知道有切換到另一個程序,但若是兩個獨立EXCEL就不能用這個方法了)

不知道這樣解釋有沒有讓GBKEE清楚一些,抱歉因為同一個討論串問了許多問題,容易讓人不知道現在的問題是什麼,會容易搞混..抱歉.
作者: GBKEE    時間: 2015-7-28 11:05

本帖最後由 GBKEE 於 2015-7-28 11:07 編輯

回復 18# justintoolbox
我希望能藉由觸發程序告訴我有切換到另一個獨立EXCEL.
VBA Application 物件的事件找不到支援的事件.
這就是兩個獨立EXCEL.物件類別的Application事件.可得知另一個獨立EXCEL的活動.
  1. Option Explicit
  2. Dim WithEvents APP As Application
  3. Property Set The_App(P As Object)
  4.     Set APP = P    ' 將 APP 屬性值設成物件。
  5.     With APP
  6.         .Visible = True
  7.     End With
  8. End Property

  9. Private Sub APP_NewWorkbook(ByVal Wb As Workbook)
  10.     MsgBox Wb.Name
  11. End Sub
  12. Private Sub APP_SheetChange(ByVal Sh As Object, ByVal Target As Range)
  13.     MsgBox Target.Address(, , , 1, 1)
  14. End Sub

  15. Private Sub APP_WindowActivate(ByVal Wb As Workbook, ByVal Wn As Window)
  16.    
  17.     MsgBox "進入 " & Wb.Name
  18. End Sub

  19. Private Sub APP_WindowDeactivate(ByVal Wb As Workbook, ByVal Wn As Window)
  20.     MsgBox "退出 " & Wb.Name
  21. End Sub

  22. Private Sub APP_WindowResize(ByVal Wb As Workbook, ByVal Wn As Window)
  23.     MsgBox Wn.Caption & "變化視窗"
  24. End Sub
複製代碼

作者: justintoolbox    時間: 2015-7-28 14:36

回復  justintoolbox
我希望能藉由觸發程序告訴我有切換到另一個獨立EXCEL.
VBA Application 物件的事件 ...
GBKEE 發表於 2015-7-28 11:05


十萬分感謝GBKEE版主指點迷津!
讓我對這方面有新的認識!
感謝!




歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)