Can somebody explain how it can be that the same API call returns so much quicker with VB6 than with VB.NET?
Here is my VB6 code:
Public Declare Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hWnd As Long) As Long
Public Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Function GetWindowTextEx(ByVal uHwnd As Long) As String
Dim lLen&
lLen = GetWindowTextLength(uHwnd) + 1
Dim sTemp$
sTemp = Space(lLen)
lLen = GetWindowText(uHwnd, sTemp, lLen)
Dim sRes$
sRes = Left(sTemp, lLen)
GetWindowTextEx = sRes
End Function
And here is my VB.NET code:
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
Dim sText As String = Space(Int16.MaxValue)
GetWindowText(hwnd, sText, Int16.MaxValue)
I ran each version 1000 times.
The VB6 version needed 2.04893359351538 ms.
The VB.NET version needed 372.1322491699365 ms.
Both Release and Debug version are about the same.
What is happening here?
Do not use the *A version, just skip the suffix, and use StringBuilder instead of String:
Private Declare Auto Function GetWindowText Lib "user32" (ByVal hwnd As Integer, ByVal lpWindowText As StringBuilder, ByVal cch As Integer) As Integer
Private Declare Function GetWindowTextLength Lib "user32" (ByVal hwnd As Integer) As Integer
Dim len As Integer = GetWindowTextLength (hwnd)
Dim str As StringBuilder = new StringBuilder (len)
GetWindowText (hwnd, str, str.Capacity)
Related
I want to locate an recently opened pdf file and save it in a target folder, right now i can able to locate the pdf file using its name and close it but i want to use Save As function.
Code tried to Locate and close the PDF
Option Explicit
Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Declare Function PostMessage Lib "user32" _
Alias "PostMessageA" (ByVal hwnd As Long, _
ByVal wMsg As Long, ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" _
(ByVal dwDesiredAccess As Long, _
ByVal bInheritHandle As Long, _
ByVal dwProcessId As Long) As Long
Private Declare Function GetWindowThreadProcessId _
Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Declare Function GetParent Lib "user32" (ByVal hwnd As Long) 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 GetWindow Lib "user32" (ByVal hwnd As Long, _
ByVal wCmd As Long) As Long
Const GW_HWNDNEXT = 2
Const WM_CLOSE = &H10
Const SYNCHRONIZE = &H100000
Public Sub Test()
Dim hWindow As Long
Dim hProcess As Long
Dim lProcessId As Long
Dim lngReturnValue As Long
hWindow = SearchHndByWndName_Parent("vieworder.pdf.php")
hProcess = OpenProcess(SYNCHRONIZE, 0&, lProcessId)
lngReturnValue = PostMessage(hWindow, WM_CLOSE, 0&, 0&)
End Sub
Private Function SearchHndByWndName_Parent(strSearch As String) As Long
Dim strTMP As String * 100
Dim nhWnd As Long
nhWnd = FindWindow(vbNullString, vbNullString)
Do While Not nhWnd = 0
If GetParent(nhWnd) = 0 Then
GetWindowText nhWnd, strTMP, 100
If InStr(strTMP, strSearch) > 0 Then
SearchHndByWndName_Parent = nhWnd
Exit Do
End If
End If
nhWnd = GetWindow(nhWnd, GW_HWNDNEXT)
Loop
End Function
I am working in a restricted environment so I cant add any Adobe dll as reference, also the file which i am looking for is not yet saved in any folder.
Please share your suggestions.
Thank You
I have found a previous thread that shows how to get all open windows and their names using C# : get the titles of all open windows
However, I need to do this in VBA: does anyone know if that's possible?
Thank you so much.
Best regards
Here's a scrape from this microsoft answer by Andreas Killer that uses EnumWindows() which did the trick for me:
Option Explicit
#If Win64 Then
Private Declare PtrSafe Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long
#Else
Private Declare Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As Long) 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 GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
#End If
Private Sub debug_ListWindows()
ListWindows
End Sub
Private Function ListWindows()
EnumWindows AddressOf EnumFindWindowLikeProc, 0
End Function
#If Win64 Then
Private Function EnumFindWindowLikeProc(ByVal hWnd As LongPtr, ByVal lParam As Long) As Long
#Else
Private Function EnumFindWindowLikeProc(ByVal hWnd As Long, ByVal lParam As Long) As Long
#End If
If IsWindowVisible(hWnd) Then
If Len(Trim(WindowTitle(hWnd))) > 0 Then
Debug.Print WindowTitle(hWnd)
End If
End If
EnumFindWindowLikeProc = 1
End Function
#If Win64 Then
Private Function IsWindowVisible(ByVal hWnd As LongPtr) As Boolean
#Else
Private Function IsWindowVisible(ByVal hWnd As Long) As Boolean
#End If
Const GWL_STYLE = -16 'Sets a new window style
Const WS_VISIBLE = &H10000000 'The window is initially visible
Dim lngStyle As Long
lngStyle = GetWindowLong(hWnd, GWL_STYLE)
IsWindowVisible = ((lngStyle And WS_VISIBLE) = WS_VISIBLE)
End Function
#If Win64 Then
Property Get WindowTitle(ByVal hWnd As LongPtr) As String
#Else
Property Get WindowTitle(ByVal hWnd As Long) As String
#End If
Dim Contents As String, i As Long
Contents = Space$(GetWindowTextLengthA(hWnd) + 1)
i = GetWindowText(hWnd, Contents, Len(Contents))
WindowTitle = Left$(Contents, i)
End Property
and here is a pretty truncated version that loops using GetWindow() which should work on sufficiently new office installations:
Option Explicit
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As LongPtr, ByVal lpString As String, _
ByVal cch As LongPtr) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" ( _
ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr
Private Sub debug_ListWindows()
ListWindows
End Sub
Private Function ListWindows()
Const GWL_STYLE = -16 'Sets a new window style
Const WS_VISIBLE = &H10000000 'The window is initially visible
Const GW_HWNDNEXT = 2 'The retrieved handle identifies the window below the specified window in the Z order
Dim hWnd As LongPtr
hWnd = FindWindow(vbNullString, vbNullString)
While hWnd
Dim sTitle As String
sTitle = Space$(GetWindowTextLengthA(hWnd) + 1)
sTitle = Left$(sTitle, GetWindowText(hWnd, sTitle, Len(sTitle)))
If (GetWindowLong(hWnd, GWL_STYLE) And WS_VISIBLE) = WS_VISIBLE Then 'only list visible windows
If Len(Trim(sTitle)) > 0 Then 'ignore blank window titles
Debug.Print sTitle
End If
End If
hWnd = GetWindow(hWnd, GW_HWNDNEXT)
Wend
End Function
Sub ListWindows()
Dim wn As Excel.Window
For Each wn In Application.Windows
Debug.Print wn.Caption
Next wn
End Sub
if you wanted to activate any of them simply use:
wn.Activate
I am working on converting some very old VBA code to run on AutoCAD 2014. I have so far converted everything but there is an issue with the forms (they are modeless and require an activation callback to modify the window propertis). The following is the VBA6 source code:
IN THE FORM:
Private Sub UserForm_Activate()
#If ACAD2000 = 0 Then
If Not bPopup Then
Call EnumWindows(AddressOf EnumWindowsProc, vbNull)
Call SubClass
bPopup = True
End If
#End If
End Sub
MODULE (named modModeLessFormFocus):
Option Explicit
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function EnumWindows Lib "user32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private ThisHwnd As Long
Public Const GWL_STYLE = -16
Public Const WS_POPUP = &H80000000
Function EnumWindowsProc(ByVal hwnd As Long, ByVal lParam As Long) As Integer
Dim title As String * 32
Call GetWindowText(hwnd, ByVal title, 32)
If InStr(title, "About") Then
ThisHwnd = hwnd
EnumWindowsProc = False
ElseIf InStr(title, "Preferences") Then
ThisHwnd = hwnd
EnumWindowsProc = False
ElseIf InStr(title, "Display Block Attributes") Then
ThisHwnd = hwnd
EnumWindowsProc = False
Else
EnumWindowsProc = True
End If
End Function
Public Function SubClass() As Long
Dim Flags As Long
Flags = GetWindowLong(ThisHwnd, GWL_STYLE)
Flags = Flags Xor WS_POPUP
SetWindowLong ThisHwnd, GWL_STYLE, Flags
End Function
The error I get when running is a "Type Mismatch" in UserForm_Activate on 'AddressOf EnumWindowsProc.' I have tried to convert it to 64bit using PtrSafe and PtrLong but inevitably it fails and the program crashes.
If anyone is smart enough to convert this or point me in the right direction I would be very appreciative.
Thanks
I found the API for 64-bit VBA7 in http://www.jkp-ads.com/articles/apideclarations.asp
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function GetWindowLongPtr Lib "USER32" Alias "GetWindowLongPtrA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
#Else
Private Declare PtrSafe Function GetWindowLongPtr Lib "USER32" Alias "GetWindowLongA" (ByVal hWnd As LongPtr, ByVal nIndex As Long) As LongPtr
#End If
Private Declare PtrSafe Function GetWindowText Lib "USER32" Alias "GetWindowTextA" _
(ByVal hWnd As LongPtr, ByVal lpString As String, _
ByVal cch As LongPtr) As Long
#Else
Private Declare Function GetWindowLongPtr Lib "USER32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function GetWindowText Lib "USER32" Alias "GetWindowTextA" _
(ByVal hWnd As Long, ByVal lpString As String, _
ByVal cch As Long) As Long
#End If
You can also look at http://msdn.microsoft.com/en-us/library/aa383663(VS.85).aspx for the updated API
I want to detect currently opened swf file name. Here is my code:
Private Const GW_HWNDNEXT = 2
Private Declare Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) 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 Sub Form_Load()
ListWins "*.swf*"
End Sub
Sub ListWins(Optional Title = "*", Optional Class = "*")
Dim hWndThis As Long
hWndThis = FindWindow(vbNullString, vbNullString)
While hWndThis
Dim sTitle As String, sClass As String
sTitle = Space$(255)
sTitle = Left$(sTitle, GetWindowText(hWndThis, sTitle, Len(sTitle)))
sClass = Space$(255)
sClass = Left$(sClass, GetClassName(hWndThis, sClass, Len(sClass)))
If sTitle Like Title And sClass Like Class Then
Debug.Print sTitle, sClass
List1.AddItem (sTitle)
End If
hWndThis = GetWindow(hWndThis, GW_HWNDNEXT)
Wend
End Sub
This code is working fine to detect *.doc, *.xls files name, but not working on *.swf files.
NOTE
I have tested it in VBA. I am sure it will work in VB6 as well.
Try this (Paste this code in a module and run the Sub Sample)
Private Declare Function GetWindowTextLength Lib "user32" Alias _
"GetWindowTextLengthA" (ByVal HWnd As Long) 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 GetDesktopWindow Lib "user32" () As Long
Public Declare Function EnumChildWindows Lib "user32" _
(ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Public Function APIWindowCaption(ByVal HWnd As Long, ByVal lParam As Long) As Long
Static winnum As Integer
Dim MyStr As String
winnum = winnum + 1
MyStr = String(GetWindowTextLength(HWnd) + 1, Chr$(0))
GetWindowText HWnd, MyStr, Len(MyStr)
'~~> This will give you the caption of the window
If InStr(1, MyStr, ".swf", vbTextCompare) Then Debug.Print MyStr
APIWindowCaption = 1
End Function
Sub Sample()
Dim retval As Long, DesktophWnd As Long
DesktophWnd = GetDesktopWindow
retval = EnumChildWindows(DesktophWnd, AddressOf APIWindowCaption, ByVal 0&)
End Sub
SNAPSHOT
I am using the ROT to find any active MSWord instances. In some versions of word the document does not get registered in the table, instead it is registered as the NORMAL template and so I can not find the document by it's title as documented by microsoft. Anyone know of a hotfix for this?
Is FindWindowPartial API any use to you? It will allow you to search for windows with Microsoft Word in the title.
Option Explicit
Private Const GW_HWNDNEXT = 2
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
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
Function FindWindowPartial(ByVal Title As String) As String
Dim hWndThis As Long
hWndThis = FindWindow(vbNullString, vbNullString)
While hWndThis
Dim sTitle As String, sClass As String
sTitle = Space$(255)
sTitle = Left$(sTitle, GetWindowText(hWndThis, sTitle, Len(sTitle)))
If InStr(sTitle, Title) > 0 Then
FindWindowPartial = sTitle & "|" & FindWindowPartial
End If
hWndThis = GetWindow(hWndThis, GW_HWNDNEXT)
Wend
End Function