Реализовать UnSubclassing для Excel - VB

Узнай цену своей работы

Формулировка задачи:

Написал прогу для постановки Excel в очередь получателей извещения об изменении содержимого буфера обмена. Взял за основу субклассинг от Jaafar Tribak. Возникла проблема с остановкой субклассинга. Вручную (не закрывая Excel) получается. Но хочется, чтобы этот процесс сам корректно завершался по нажатию крестика. Из-под события Thisworkbook_BeforeClose() не получается, т.к. Excel "падает" в тот момент, когда это событие еще не начало выполняться. Возможно ли по-другому успеть перехватить событие закрытия книги, чтобы во время завершить субклассинг (строка 106)?
Листинг программы
  1. 'CopyRights:
  2. 'Ross Donald (VB.NET) code of SetClipboardViewer - [url]http://www.radsoftware.com.au/articles/clipboardmonitor.aspx[/url]
  3. 'Jaafar Tribak - Code of Excel Subclassing - [url]http://www.mrexcel.com/forum/general-excel-discussion-other-questions/420673-challenging-problem-how-make-excel-subclassing-safe-stable.html#post2082195[/url]
  4. Option Explicit
  5. Private Declare Function SetWindowLong Lib "user32.dll" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
  6. Private Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal MSG As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  7. Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
  8. Private Declare Function GetDesktopWindow Lib "user32.dll" () As Long
  9. Private Declare Function ShowWindow Lib "user32.dll" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
  10. Private Declare Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByRef lParam As Any) As Long
  11. Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  12. Private Declare Function SetTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
  13. Private Declare Function KillTimer Lib "user32.dll" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
  14. Private Declare Function LockWindowUpdate Lib "user32.dll" (ByVal hwndLock As Long) As Long
  15. Private Declare Function GetProp Lib "user32" Alias "GetPropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
  16. Private Declare Function SetProp Lib "user32" Alias "SetPropA" (ByVal hwnd As Long, ByVal lpString As String, ByVal hData As Long) As Long
  17. Private Declare Function RemoveProp Lib "user32" Alias "RemovePropA" (ByVal hwnd As Long, ByVal lpString As String) As Long
  18. Private Declare Function SetClipboardViewer Lib "user32" (ByVal hwnd As Long) As Long
  19. Private Declare Function ChangeClipboardChain Lib "user32" (ByVal hwnd As Long, ByVal hWndNext As Long) As Long
  20. Private Const GWL_WNDPROC As Long = -4
  21. Private Const WM_USER As Long = &H400
  22. Private Const WM_NCMOUSEMOVE As Long = &HA0
  23. Private Const WM_SETREDRAW As Long = &HB
  24. 'Constants for Clipboard API Calls...
  25. Private Const WM_DRAWCLIPBOARD As Integer = &H308
  26. Private Const WM_CHANGECBCHAIN As Integer = &H30D
  27. 'UnSubClass
  28. Private Const WM_NCDESTROY As Long = &H82
  29. Private Const WM_DESTROY As Long = &H2
  30. Private Const VBE_CLASS_NAME As String = "wndclass_desked_gsk"
  31. Private Const EXCEL_CLASS_NAME As String = "XLMAIN"
  32. Private lOldWinProc As Long
  33. Private lVBEhwnd As Long
  34. 'Handle for next clipboard viewer...
  35. Private mNextClipBoardViewerHWnd As Long
  36. 'flag for sublassing accomplishing
  37. Public flag As Boolean
  38. Sub Safe_Subclass(hwnd As Long)
  39. 'don't subclass the window twice !
  40. If GetProp(GetDesktopWindow, "HWND") <> 0 Then
  41. MsgBox "The Window is already Subclassed.", vbInformation
  42. Exit Sub
  43. End If
  44. 'store the target window hwnd as a desktop
  45. 'window for later use property.
  46. SetProp GetDesktopWindow, "HWND", hwnd
  47. 'retrieve the VBE hwnd.
  48. lVBEhwnd = FindWindow(VBE_CLASS_NAME, vbNullString)
  49. 'prevent flickering of the screen
  50. 'before posting messages to reset
  51. 'the VBE window.
  52. LockWindowUpdate lVBEhwnd
  53. 'do the same with the desktop in the background.
  54. SendMessage GetDesktopWindow, ByVal WM_SETREDRAW, ByVal 0&, 0&
  55. 'stop and reset the VBE first to safely
  56. 'proceed with our subclassing of xl.
  57. PostMessage lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H30, ByVal 0&
  58. PostMessage lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H33, ByVal 0&
  59. PostMessage lVBEhwnd, ByVal WM_USER + &HC44, ByVal &H83, ByVal 0&
  60. 'run a one time timer and subclass xl
  61. 'from the timer callback function.
  62. 'if subclassing is not installed within
  63. 'the timer callback,xl will crash !
  64. flag = True
  65. SetTimer GetProp(GetDesktopWindow, "HWND"), 0&, 1, AddressOf TimerProc
  66. End Sub
  67. Sub UnSubClassExcel(hwnd As Long)
  68. 'Tear down
  69. ChangeClipboardChain GetProp(GetDesktopWindow, "HWND"), GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd")
  70. 'remove the subclass and cleanup.
  71. SetWindowLong hwnd, GWL_WNDPROC, lOldWinProc
  72. RemoveProp GetDesktopWindow, "HWND"
  73. RemoveProp GetDesktopWindow, "NextClipBoardViewerHWnd"
  74. lOldWinProc = 0
  75. End Sub
  76. Private Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  77. On Error Resume Next
  78. Select Case uMsg
  79. Case WM_DRAWCLIPBOARD 'The clipboard has changed...
  80. MsgBox "The clipboard has changed..."
  81. SendMessage GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd"), uMsg, wParam, lParam
  82. Case WM_CHANGECBCHAIN 'Another clipboard viewer has removed itself...
  83. If wParam = GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd") Then
  84. SetProp GetDesktopWindow, "NextClipBoardViewerHWnd", lParam
  85. Else
  86. SendMessage GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd"), uMsg, wParam, lParam
  87. End If
  88. 'Try UnSubClass
  89. Case WM_NCDESTROY
  90. Call UnSubClassExcel(Application.hwnd)
  91. 'Exit Function
  92. Case WM_DESTROY
  93. Call UnSubClassExcel(Application.hwnd)
  94. 'Exit Function
  95. End Select
  96. 'allow other msgs default processing.
  97. If flag Then WindowProc = CallWindowProc(lOldWinProc, hwnd, uMsg, wParam, lParam)
  98. End Function
  99.  
  100. Sub TimerProc(ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long)
  101. 'we lost the hwnd stored in the lVBEhwnd var
  102. 'after reseting the VBE so let's retrieve it again.
  103. lVBEhwnd = FindWindow(VBE_CLASS_NAME, vbNullString)
  104. 'we no longer need the timer.
  105. KillTimer GetProp(GetDesktopWindow, "HWND"), 0&
  106. 'allow back drawing on the desktop.
  107. SendMessage GetDesktopWindow, WM_SETREDRAW, ByVal 1, 0&
  108. 'hide the VBE.
  109. ShowWindow lVBEhwnd, 0&
  110. 'unlock the window update.
  111. LockWindowUpdate 0&
  112. 'add VBE window hwnd to the Clipboard Chain and save Next ClipBoardViewer handle in the desktop property
  113. SetProp GetDesktopWindow, "NextClipBoardViewerHWnd", SetClipboardViewer(GetProp(GetDesktopWindow, "HWND"))
  114. 'and at last we can now safely
  115. 'subclass our target window.
  116. lOldWinProc = SetWindowLong(GetProp(GetDesktopWindow, "HWND"), GWL_WNDPROC, AddressOf WindowProc)
  117. End Sub

Решение задачи: «Реализовать UnSubclassing для Excel»

textual
Листинг программы
  1. 'UnSubClass
  2. Private Const WM_SYSCOMMAND As Long = &H112&
  3. Private Const SC_CLOSE As Long = &HF060&
  4. ....
  5. Private Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  6.     Select Case uMsg
  7.         Case WM_DRAWCLIPBOARD 'The clipboard has changed...
  8.            MsgBox "The clipboard has changed..."
  9.             SendMessage GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd"), uMsg, wParam, lParam
  10.  
  11.         Case WM_CHANGECBCHAIN 'Another clipboard viewer has removed itself...
  12.            If wParam = GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd") Then
  13.                 SetProp GetDesktopWindow, "NextClipBoardViewerHWnd", lParam
  14.             Else
  15.                 SendMessage GetProp(GetDesktopWindow, "NextClipBoardViewerHWnd"), uMsg, wParam, lParam
  16.             End If
  17.         Case WM_SYSCOMMAND
  18.             If wParam = SC_CLOSE Then
  19.                 Call UnSubClassExcel(Application.hwnd)
  20.                 WindowProc = 0
  21.                 PostMessage hwnd, WM_SYSCOMMAND, SC_CLOSE, lParam
  22.             End If
  23.    End Select
  24.    If flag Then WindowProc = CallWindowProc(lOldWinProc, hwnd, uMsg, wParam, lParam)
  25. End Function

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

9   голосов , оценка 4 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут