I'm developing an application which needs to get the windows names that have been active while the application is running, currently i'm performing a Call to GetForegroundwindow() every half a second, but that's hardly accurate and i don't wanna put it to 100ms interval, i think it's just bad and not accurate, is there any system event that can done that for me?
Thanks.
-- UPDATE --
You could use SetWindowsHookEx to monitor the current Desktop, but this doesn't work in managed code. Instead, you must use SetWinEventHook.
You had asked how to use the SetWinEventHook p/Invoke method. Here is your answer:
First make sure this line is at the top of your code file:
Imports System.Runtime.InteropServices
Next, declare all that you need to invoke the call:
Public Const WINEVENT_OUTOFCONTEXT = &H0
'' Events are ASYNC
Public Const WINEVENT_SKIPOWNTHREAD = &H1
'' Don't call back for events on installer's thread
Public Const WINEVENT_SKIPOWNPROCESS = &H2
'' Don't call back for events on installer's process
Public Const WINEVENT_INCONTEXT = &H4
'' Events are SYNC, this causes your dll to be injected into every process
Public Declare Function SetWinEventHook Lib "user32.dll" _
(eventMin As UInteger, _
eventMax As UInteger, _
hmodWinEventProc As IntPtr, _
lpfnWinEventProc As IntPtr, _
idProcess As UInteger, _
idThread As UInteger, _
dwflags As UInteger) As IntPtr
Public Declare Function UnhookWinEvent Lib "user32.dll" _
(hWinEventHook As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
Public Delegate Sub WinEventProc( _
hWinEventHook As IntPtr, _
[event] As UInteger, _
hwnd As IntPtr, _
idObject As Integer, _
idChild As Integer, _
dwEventThread As UInteger, _
dwmsEventTime As UInteger)
Next, you declare a function and a new variable as a function with an address to that function:
Public Sub EventCallBack( _
hWinEventHook As IntPtr, _
[event] As UInteger, _
hwnd As IntPtr, _
idObject As Integer, _
idChild As Integer, _
dwEventThread As UInteger, _
dwmsEventTime As UInteger)
' Some code goes here
End Sub
Private eventProc As New WinEventProc(AddressOf EventCallBack)
Private hEventHook As IntPtr
Finally, you tie it all together, and you pass the following line of code to create your hook (0 and 255 are arbitrary numbers, replace them with the min and max message codes you want to watch):
hEventHook = SetWinEventHook(0, _
255, _
IntPtr.Zero, _
Marshal.GetFunctionPointerForDelegate(eventProc), _
0, _
0, _
WINEVENT_OUTOFCONTEXT)
And when your program has finished add the following line to an Application termination event, or the form's Dispose or Finalize methods:
UnhookWinEvent(hEventHook)
And this runs as expected on my test application.
Related
I'm using this code to enumerate all values in a registry key.
Private ReadOnly HKeyLocalMachine As New IntPtr(-2147483646)
Private Const KeyQueryValueWow64Key As Integer = &H101
Private Const ErrorNoMoreItems As Integer = &H103
Private Const errorSuccess As Integer = &H0
Dim keyHandle As IntPtr = Nothing
RegOpenKeyEx(HKeyLocalMachine, newPath, 0, KeyQueryValueWow64Key, keyHandle)
If keyHandle = Nothing Then
Return "Error accessing registry key"
End If
Dim index As Integer = 0
Dim valueName As New StringBuilder(1000)
Dim valueLenght As UInteger
Dim valueDataLenght As IntPtr
If RegQueryInfoKey(keyHandle, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, Nothing, valueLenght, valueDataLenght, Nothing, Nothing) = errorSuccess Then
Debug.WriteLine("SUCCESS IN REGQUERYINFOKEY")
End If
Do
returnValue = RegEnumValue(keyHandle, index, valueName, valueLenght, Nothing, Nothing, datalenght, valueDataLenght)
If returnValue = errorSuccess Then
Debug.WriteLine("Success")
End If
index = index + 1
Loop Until returnValue = ErrorNoMoreItems
Here are my API declarations:
<DllImport("advapi32.dll", CharSet:=CharSet.Unicode)> _
Private Shared Function RegOpenKeyEx( _
hKey As IntPtr, _
subKey As String, _
ulOptions As Integer, _
samDesired As Integer, _
ByRef hkResult As IntPtr _
) As Integer
End Function
<DllImport("advapi32.dll", SetLastError:=True)> _
Private Shared Function RegEnumValue( _
ByVal hKey As IntPtr, _
ByVal dwIndex As Integer, _
ByVal lpValueName As StringBuilder, _
ByRef lpcValueName As UInteger, _
ByVal lpReserved As IntPtr, _
ByVal lpType As IntPtr, _
ByVal lpData As IntPtr, _
ByVal lpcbData As IntPtr _
) As Integer
End Function
<DllImport("advapi32.dll")> _
Private Shared Function RegQueryInfoKey( _
hkey As IntPtr, _
ByRef lpClass As StringBuilder, _
ByRef lpcbClass As UInteger, _
lpReserved As IntPtr, _
ByRef lpcSubKeys As UInteger, _
ByRef lpcbMaxSubKeyLen As UInteger, _
ByRef lpcbMaxClassLen As UInteger, _
ByRef lpcValues As UInteger, _
ByRef lpcbMaxValueNameLen As UInteger, _
ByRef lpcbMaxValueLen As IntPtr, _
ByRef lpcbSecurityDescriptor As UInteger, _
lpftLastWriteTime As IntPtr _
) As Integer
End Function
And i'm getting AccessViolationException when i pass the last parameter of RegEnumValue non-null, if i pass a null IntPtr the function succeeds but no data is retrieved, only the name.
I've tried changing the API variables with no luck, the other two functions always succeed.
The reason that the code fails is that you did not initialise valueDataLenght. Incidentally, you mean to name this variable valueDataLength.
You need the IntPtr variable valueDataLength to refer to a DWORD that contains the length of the data buffer. Using IntPtr here makes life difficult for you. I'd declare the parameter like this instead:
ByRef lpcbData As Integer
I currently have it defined as
Private Declare Function ReadProcessMemory1 Lib "kernel32" Alias "ReadProcessMemory" (ByVal hProcess As IntPtr, ByVal lpBaseAddress As Integer, ByRef lpBuffer As Integer, ByVal nSize As Integer, ByRef lpNumberOfBytesWritten As Integer) As Integer
and I have another declaration for each type.
I'm trying to use this instead
<DllImport("kernel32.dll", SetLastError:=True)> _
Public Shared Function ReadProcessMemory( _
ByVal hProcess As IntPtr, _
ByVal lpBaseAddress As Integer, _
<Out()> ByRef lpBuffer As Byte(), _
ByVal dwSize As Integer, _
ByRef lpNumberOfBytesRead As Integer) As Boolean
End Function
Which is off pvinvoke.net, heres how I'm trying to use it:
Public Shared Function Int(address As Integer)
Dim buffer(3) As Byte
ReadProcessMemory(pHandle, address, buffer, 4, 0)
Return BitConverter.ToInt32(buffer, 0)
End Function
This errors and says Attempt to read or write protected memory, but I use the old RPM declaration I have like this and it works fine.
Public Shared Function Int(address As Integer)
Dim buffer As Integer
ReadProcessMemory(pHandle, address, buffer, 4, 0)
Return buffer
End Function
What am I doing wrong?
Your p/invoke declaration is wrong. It should be:
<DllImport("kernel32.dll", SetLastError:=True)> _
Public Shared Function ReadProcessMemory( _
ByVal hProcess As IntPtr, _
ByVal lpBaseAddress As IntPtr, _
<Out()> ByVal lpBuffer As Byte(), _
ByVal dwSize As IntPtr, _
ByRef lpNumberOfBytesRead As IntPtr) As Boolean
End Function
Using Spy++ I've been trying to control form buttons on an external program with my own program..
The picture shows what control I am attempting to mimic and here's my following code below...
Dim hWnd As IntPtr = FindWindow(vbNullString, ListView4.SelectedItems(0).SubItems(3).Text)
If hWnd.Equals(IntPtr.Zero) Then
Return
End If
Dim hWndButton As IntPtr = _
FindWindowEx(hWnd, IntPtr.Zero, "Button", "Load Settings")
If hWndButton.Equals(IntPtr.Zero) Then
Return
End If
However nothing happens when I run the code.. one possibility is the window name I'm grabbing is wrong, but if that's not the case is my code correct?
Well, as Alex pointed out, I wasn't actually initiating the button press.. now that he pointed that out I was able to fix my code.. Here's what I now use:
Private Declare Auto Function FindWindow Lib "user32.dll" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String _
) As IntPtr
Private Declare Auto Function FindWindowEx Lib "user32.dll" ( _
ByVal hwndParent As IntPtr, _
ByVal hwndChildAfter As IntPtr, _
ByVal lpszClass As String, _
ByVal lpszWindow As String _
) As IntPtr
Declare Auto Function SendMessage Lib "user32" (ByVal hwnd As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
Private Const BM_CLICK = &HF5
Dim hwndParent As Long = FindWindow(vbNullString, ListView4.SelectedItems(0).SubItems(3).Text)
Debug.Print("findwindow: " & hwndParent)
Dim hwndButton As Long = FindWindowEx(hwndParent, IntPtr.Zero, "Button", "Save as")
Debug.Print("OK: " & hwndButton)
hwndButton = SendMessage(hwndButton, BM_CLICK, 0, 0)
Debug.Print("Clicked: " & hwndButton)
I have compiled an old vb6 app into a dll. Eventually we will convert to .NET but for now I am trying to load the forms from the DLL. So far I have been able to load and see the vb6 form in .NET but I cannot activate the controls. So the setparent in the code below is working but the sendmessage and following code is not. By the way in debug mode this all works fine. It's only when I used the compiled dll that I have a problem.
My declare's are:
Private Shared Function SetParent(ByVal hWndChild As IntPtr, _
ByVal hWndNewParent As IntPtr) As Int32
End Function
Public Declare Auto Function SendMessage Lib "user32.dll" ( _
ByVal hWnd As IntPtr, _
ByVal wMsg As Int32, _
ByVal wParam As Int32, _
ByVal s As String _
) As Int32
Private Shared Function SetForegroundWindow(ByVal hwnd As IntPtr) As IntPtr
End Function
Private Shared Function LockWindowUpdate(ByVal hwndLock As IntPtr) As Boolean
End Function
The code I am using is as follows:
SetParent(mintFormHandle, Me.Handle.ToInt32)
SendMessage(mintFormHandle, WM_ACTIVATE, 1, IntPtr.Zero)
SetForegroundWindow(mintFormHandle)
LockWindowUpdate(0)
Me.Refresh()
Please no comments about old vb6 code. Yes in a perfect world a conversion would have been done years ago.
I'm trying to find a method to identify a Open File Dialog and send a message to the "file name" field. Afterwards it needs to send an "Enter" key or "Open" command to the button.
I'm doing this in VB, but I'm sure I can cope if someone can help in C# as well.
I've been digging through the API this entire day and came up with a couple of possibilities but I am unfamiliar with how to implement this in DotNet4.
I used to work with API in VB6 but it seems like things are a bit different now.
If someone could provide me with a small example I'd be grateful.
Some of the API I've looked at is FindWindow and FindWindowEx.
Edit:
I found some code that is worth looking at. This code needs to be used inside a module.
I will post more as I find more answers.
Imports System.Runtime.InteropServices
Imports System.Text
Module modEnumWindows
Private windowList As New ArrayList
Private errMessage As String
Public Delegate Function MyDelegateCallBack(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
Declare Function EnumWindows Lib "user32" (ByVal x As MyDelegateCallBack, ByVal y As Integer) As Integer
Declare Auto Function GetClassName Lib "user32" _
(ByVal hWnd As IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, _
ByVal nMaxCount As Integer) As Integer
Declare Auto Function GetWindowText Lib "user32" _
(ByVal hWnd As IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, _
ByVal nMaxCount As Integer) As Integer
Private Function EnumWindowProc(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
'working vars
Dim sTitle As New StringBuilder(255)
Dim sClass As New StringBuilder(255)
Try
Call GetClassName(hwnd, sClass, 255)
Call GetWindowText(hwnd, sTitle, 255)
windowList.Add(sClass.ToString & ", " & hwnd & ", " & sTitle.ToString)
Catch ex As Exception
errMessage = ex.Message
EnumWindowProc = False
Exit Function
End Try
EnumWindowProc = True
End Function
Public Function getWindowList(ByRef wList As ArrayList, Optional ByVal errorMessage As String = "") As Boolean
windowList.Clear()
Try
Dim del As MyDelegateCallBack
del = New MyDelegateCallBack(AddressOf EnumWindowProc)
EnumWindows(del, 0)
getWindowList = True
Catch ex As Exception
getWindowList = False
errorMessage = errMessage
Exit Function
End Try
'wList.Clear()
wList = windowList
End Function
End Module
By using this you'll be able to identify the Window Text, HWND and Class. Hope this helps people a bit. The next step for me will be to identify the field I wish to send the data to.