I’m making an VB application which runs another process (windowed game). My app acts as a macro to that game.
I’m struggling to get mouse click working, although my code to send keys in the background works excellent (can provide if needed).
I mean the program to click into specific location within the process in the background, that means I could be browsing internet or playing another game when that mouse click takes action.
I'm getting an error message: Specified cast is not valid.
Error is on InputPressRightMouse Sub where:
PostMessage(Handle, WM_RBUTTONDOWN, 1, coord)
Private Const WM_RBUTTONDOWN As System.UInt32 = &H204
Private Const WM_RBUTTONUP As System.UInt32 = &H205
Dim coord = MakeDWord(-220, 85)
Private Function MakeDWord(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long
MakeDWord = (HiWord * &H10000) Or (LoWord And &HFFFF&)
End Function
<System.Runtime.InteropServices.DllImport("user32.dll")>
Public Shared Function PostMessage(<System.Runtime.InteropServices.In()> ByVal hWnd As System.IntPtr, <System.Runtime.InteropServices.In()> ByVal Msg As System.UInt32, <System.Runtime.InteropServices.In()> ByVal wParam As System.IntPtr, <System.Runtime.InteropServices.In()> ByVal lParam As System.UIntPtr) As System.Boolean
End Function
Public Sub InputPressKey(ByVal Handle As System.IntPtr, ByVal Key As System.Windows.Forms.Keys)
PostMessage(Handle, WM_KEYDOWN, CType(Key, System.IntPtr), CType(WM_KEYDOWNLPARAM, System.UIntPtr))
End Sub
Public Sub InputReleaseKey(ByVal Handle As System.IntPtr, ByVal Key As System.Windows.Forms.Keys)
PostMessage(Handle, WM_KEYUP, CType(Key, System.IntPtr), CType(WM_KEYUPLPARAM, System.UIntPtr))
End Sub
Public Sub InputPressRightMouse(ByVal Handle As System.IntPtr, ByVal Mouse As System.Windows.Forms.MouseButtons)
PostMessage(Handle, WM_RBUTTONDOWN, 1, coord)
End Sub
And code to perform mouse click:
InputPressRightMouse(Process.MainWindowHandle, System.Windows.Forms.MouseButtons.Right)
Code to send key (which works)
InputPressKey(Process.MainWindowHandle, System.Windows.Forms.Keys.V)
Can somebody help with what I did wrong here?
Related
I made some research, but I can't find something really "interesting". I tried my best to find any kind of documentation or questions that are closest to my case as following:
How to find main window title name of application
how to get the window title of a process
How to get the Title Bar Text by its Process Id
getting the name of a process
How do I get list of Process Names running
Check to see if process is running
How To Get Process Owner ID
How to get the title/name of the last active window?
Get Process ID from Window Title
and also
Process.GetProcessesByName Method
The code I am using to open the process window
Private Async Function ParentMethod() As Task
Dim filePath As String = Await Task.Run(
Function()
Return Directory.EnumerateFiles(My.Settings.Cartellasalvataggio, titolo & ".mp3",
SearchOption.AllDirectories).FirstOrDefault()
End Function)
If Not String.IsNullOrEmpty(filePath) Then
LinkLabel1.Text = "File exist already"
LinkLabel1.Visible = True
PictureBox7.Visible = True
Else
MsgBox("it doesn't exist")
End If
End Function
and the helper class
Imports System.IO
Imports System.Runtime.InteropServices
Public Class NativeMethods
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Function SHOpenFolderAndSelectItems(
pidlFolder As IntPtr, cidl As UInteger,
<[In], MarshalAs(UnmanagedType.LPArray)> apidl As IntPtr(),
dwFlags As UInteger) As Integer
End Function
<DllImport("shell32.dll", SetLastError:=True)>
Private Shared Sub SHParseDisplayName(
<MarshalAs(UnmanagedType.LPWStr)> name As String,
bindingContext As IntPtr, <Out> ByRef pidl As IntPtr,
sfgaoIn As UInteger, <Out> ByRef psfgaoOut As UInteger)
End Sub
Public Shared Sub OpenFolderAndSelectFile(filePath As String)
Dim dirPath As String = Path.GetDirectoryName(filePath)
Dim fileName As String = Path.GetFileName(filePath)
OpenFolderAndSelectFile(dirPath, fileName)
End Sub
Public Shared Sub OpenFolderAndSelectFile(dirPath As String, fileName As String)
Dim nativeFolder As IntPtr
Dim psfgaoOut As UInteger
SHParseDisplayName(dirPath, IntPtr.Zero, nativeFolder, 0, psfgaoOut)
If nativeFolder = IntPtr.Zero Then
' Log error, can't find folder
Return
End If
Dim nativeFile As IntPtr
SHParseDisplayName(Path.Combine(dirPath, fileName),
IntPtr.Zero, nativeFile, 0, psfgaoOut)
Dim fileArray As IntPtr()
If nativeFile = IntPtr.Zero Then
' Open the folder without the file selected if we can't find the file
fileArray = New IntPtr(-1) {}
Else
fileArray = New IntPtr() {nativeFile}
End If
SHOpenFolderAndSelectItems(nativeFolder, CUInt(fileArray.Length), fileArray, 0)
Marshal.FreeCoTaskMem(nativeFolder)
If nativeFile <> IntPtr.Zero Then
Marshal.FreeCoTaskMem(nativeFile)
End If
End Sub
End Class
then calling it with
NativeMethods.OpenFolderAndSelectFile(filepath,filename & "extension"))
Since I am opening the process this way and NOT with Process class, almost all of them are not suitable to be considered for my case as many of them refer to notepad, while I think the explorer window title and ID changes for every file ( obviously), while "notepad" process, stay "notepad".
I also tried BringToFront, but this latter moves a control in front of other controls, but in this case Explorer is not a control, right?
The least I want to do is to
Get a list of active windows & their process names
as It will waste memory and time usage for no reason as I will need to "filter" process to find my process.
Hope we can find a solution to this, Thanks in advance.
Mattia
This is the solution to it using FindWindowW e SetWindowPos Api.
It is showing Explorer folder on top of top most form.
<DllImport("user32.dll", EntryPoint:="FindWindowW")>
Public Shared Function FindWindowW(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpClassName As String, <MarshalAs(UnmanagedType.LPTStr)> ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll")>
Shared Function SetWindowPos(ByVal hWnd As IntPtr, ByVal hWndInsertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As Boolean
End Function
Shared ReadOnly HWND_TOPMOST As IntPtr = New IntPtr(-1)
Const SWP_NOSIZE As UInt32 = &H1
Const SWP_NOMOVE As UInt32 = &H2
Const SWP_SHOWWINDOW As UInt32 = &H40
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim inptr = FindWindowW("CabinetWClass", Nothing)
SetWindowPos(inptr, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZE Or SWP_SHOWWINDOW)
End Sub
Is there a way to Click the ENTER key on the keyboard, other than SendKeys.Send("{ENTER}"?
I have an Application I created in VB.net that will create a line of text then send it to another application with AppActivate, it works great when the receiving application was created in C++. But when the receiving application was created in Java the SendKeys.Send("{ENTER}" will not work. All the text is transferred to the Java application but the ENTER button will not click.
Is there another way to Click ENTER on the Keyboard or simulate it?
Thank You
Have you tried P/Invoke?
For example:
<DllImport("user32.dll", SetLastError:=True, EntryPoint:="PostMessageA")>
Public Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Boolean
Public Const WM_KEYDOWN As Integer = &H0100 'Key Down Event
Public Const VK_RETURN As Integer = &H0D 'Enter Key
Public Shared Sub SendKeyDownEvent(ByVal hWnd As IntPtr, ByVal key As Integer)
PostMessage(hWnd, WM_KEYDOWN, key, 0)
System.Threading.Thread.Sleep(500)
End Sub
And you could call it like:
SendKeyDownEvent(handleToApplication, VK_RETURN)
There are plenty of resources available for obtaining handles to other applications:
https://www.codeproject.com/Tips/825946/Csharp-VB-NET-and-WinAPI-How-to-Access-Window-of-O
https://kellyschronicles.wordpress.com/2008/06/23/get-window-handles-associated-with-process-in-vb-net/
I’m making an VB application which runs another process (windowed game). My app acts as a macro to that game.
I have managed to write code that sends keyboard input into the process in the background without using appActivate, however I’m struggling to get mouse click working the same way.
I mean the program to click into specific location within the process in the background, that means I could be browsing internet or playing another game when that mouse click takes action.
Hope I make sense, can somebody bring me on right track?
This code I use to send Q letter into app in background:
Public Const WM_KEYDOWNLPARAM As System.UInt32 = &H320001
Public Const WM_KEYUPLPARAM As System.UInt32 = 3224502273
Public Const WM_KEYDOWN As System.Int32 = &H100
Public Const WM_KEYUP As System.Int32 = &H101
<System.Runtime.InteropServices.DllImport("user32.dll")>
Public Shared Function PostMessage(<System.Runtime.InteropServices.In()> ByVal hWnd As System.IntPtr, <System.Runtime.InteropServices.In()> ByVal Msg As System.UInt32, <System.Runtime.InteropServices.In()> ByVal wParam As System.IntPtr, <System.Runtime.InteropServices.In()> ByVal lParam As System.UIntPtr) As System.Boolean
End Function
Public Sub InputPressKey(ByVal Handle As System.IntPtr, ByVal Key As System.Windows.Forms.Keys)
PostMessage(Handle, WM_KEYDOWN, CType(Key, System.IntPtr), CType(WM_KEYDOWNLPARAM, System.UIntPtr))
End Sub
Public Sub InputReleaseKey(ByVal Handle As System.IntPtr, ByVal Key As System.Windows.Forms.Keys)
PostMessage(Handle, WM_KEYUP, CType(Key, System.IntPtr), CType(WM_KEYUPLPARAM, System.UIntPtr))
End Sub
InputPressKey(Process.MainWindowHandle, System.Windows.Forms.Keys.Q)
I have made a custom task pane using a user control.
Every time I resize it I raise a method called SuspendDrawing the reason I do this is because the buttons on my custom task pane flicker when I resize it therefore I need to suspend it. I add these buttons using the designer
This works like a treat but then I need to call a method called ResumeDrawing when the user stops resizing however I don't know what event I should use to call the ResumeDrawing method as there is no ResizeEnd event for a user control.
Any suggestions would be appreciated as I'm out of ideas.
My code is as follows
Public Property Resizing As Boolean
<DllImport("user32.dll", EntryPoint:="SendMessageA", ExactSpelling:=True, CharSet:=CharSet.Ansi, SetLastError:=True)>
Private Shared Function SendMessage(ByVal hwnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Public Shared Sub SuspendDrawing(ByVal target As Control)
SendMessage(target.Handle, WM_SETREDRAW, 0, 0)
End Sub
Public Shared Sub ResumeDrawing(ByVal target As Control)
ResumeDrawing(target, True)
End Sub
Public Shared Sub ResumeDrawing(ByVal target As Control, ByVal redraw As Boolean)
SendMessage(target.Handle, WM_SETREDRAW, 1, 0)
If redraw Then
target.Refresh()
End If
End Sub
Private Sub SideBarPowerPoint_Resize(sender As Object, e As EventArgs) Handles MyBase.Resize
SuspendDrawing(Me)
End Sub
My Custom Task Pane
Dim taskPaneView = New PowerPointCommon.SideBarPowerPoint
sideBarTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(taskPaneView, " ")
If sideBarTaskPane.Visible = True Then
sideBarTaskPane.Visible = False
End If
sideBarTaskPane.Visible = True
sideBarTaskPane.DockPosition = Microsoft.Office.Core.MsoCTPDockPosition.msoCTPDockPositionRight
sideBarTaskPane.Width = 100
I have a very weird problem. I have a VB.NET 2.0 application that takes advantage of Windows 7 taskbar button progress features, i.e. displaying certain progress and application state in the Win7 taskbar button.
Everything works just fine - I can set and update progress, I can set the button to paused or ewrror state, I can set it to No progress. Everything works, except MARQUE (Indeterminate) mode. This is a total mistery, whenever I set state to TBPF_INDETERMINATE (value of 0x1), it simply changes back to NOPROGRESS type, i.e. it removes all progress inidcation from the taskbar button and sets it back to its default state - no animated marque is ever displayed!
I have read documentation on MSDN - http://msdn.microsoft.com/en-us/library/dd391697(v=vs.85).aspx ; tried various combinations like setting progress to 0 and then calling set state to indeterminate; or like setting it to normal first and then to indeterminate - nothing works. It's a total mistery - and there is no clue in the documentation as to why it is failing...
Here's the code:
The API implementation:
<StructLayout(LayoutKind.Sequential)> _
Public Structure RECT
Public left As Integer
Public top As Integer
Public right As Integer
Public bottom As Integer
Public Sub New(left As Integer, top As Integer, right As Integer, bottom As Integer)
Me.left = left
Me.top = top
Me.right = right
Me.bottom = bottom
End Sub
End Structure
Public Enum TBPFLAG
TBPF_NOPROGRESS = 0
TBPF_INDETERMINATE = &H1
TBPF_NORMAL = &H2
TBPF_ERROR = &H4
TBPF_PAUSED = &H8
End Enum
Public Enum TBATFLAG
TBATF_USEMDITHUMBNAIL = &H1
TBATF_USEMDILIVEPREVIEW = &H2
End Enum
Public Enum THBMASK
THB_BITMAP = &H1
THB_ICON = &H2
THB_TOOLTIP = &H4
THB_FLAGS = &H8
End Enum
Public Enum THBFLAGS
THBF_ENABLED = 0
THBF_DISABLED = &H1
THBF_DISMISSONCLICK = &H2
THBF_NOBACKGROUND = &H4
THBF_HIDDEN = &H8
End Enum
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure THUMBBUTTON
<MarshalAs(UnmanagedType.U4)> _
Public dwMask As THBMASK
Public iId As UInteger
Public iBitmap As UInteger
Public hIcon As IntPtr
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)> _
Public szTip As String
<MarshalAs(UnmanagedType.U4)> _
Public dwFlags As THBFLAGS
End Structure
<ComImportAttribute()> _
<GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")> _
<InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface ITaskbarList3
' ITaskbarList
<PreserveSig()> _
Sub HrInit()
<PreserveSig()> _
Sub AddTab(hwnd As IntPtr)
<PreserveSig()> _
Sub DeleteTab(hwnd As IntPtr)
<PreserveSig()> _
Sub ActivateTab(hwnd As IntPtr)
<PreserveSig()> _
Sub SetActiveAlt(hwnd As IntPtr)
' ITaskbarList2
<PreserveSig()> _
Sub MarkFullscreenWindow(hwnd As IntPtr, <MarshalAs(UnmanagedType.Bool)> fFullscreen As Boolean)
' ITaskbarList3
Sub SetProgressValue(<[In]()> ByVal hwnd As IntPtr, <[In]()> ByVal ullCompleted As UInt64, <[In]()> ByVal ullTotal As UInt64) 'hwnd As IntPtr, ullCompleted As UInt64, ullTotal As UInt64)
Sub SetProgressState(<[In]()> ByVal hwnd As IntPtr, <[In]()> ByVal tbpFlags As TBPFLAG) 'hwnd As IntPtr, tbpFlags As TBPFLAG) 'As Integer
Sub RegisterTab(hwndTab As IntPtr, hwndMDI As IntPtr)
Sub UnregisterTab(hwndTab As IntPtr)
Sub SetTabOrder(hwndTab As IntPtr, hwndInsertBefore As IntPtr)
Sub SetTabActive(hwndTab As IntPtr, hwndMDI As IntPtr, tbatFlags As TBATFLAG)
Sub ThumbBarAddButtons(hwnd As IntPtr, cButtons As UInteger, <MarshalAs(UnmanagedType.LPArray)> pButtons As THUMBBUTTON())
Sub ThumbBarUpdateButtons(hwnd As IntPtr, cButtons As UInteger, <MarshalAs(UnmanagedType.LPArray)> pButtons As THUMBBUTTON())
Sub ThumbBarSetImageList(hwnd As IntPtr, himl As IntPtr)
Sub SetOverlayIcon(hwnd As IntPtr, hIcon As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> pszDescription As String)
Sub SetThumbnailTooltip(hwnd As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> pszTip As String)
'[MarshalAs(UnmanagedType.LPStruct)]
Sub SetThumbnailClip(hwnd As IntPtr, ByRef prcClip As RECT)
End Interface
<GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")> _
<ClassInterfaceAttribute(ClassInterfaceType.None)> _
<ComImportAttribute()> _
Public Class CTaskbarList
End Class
And here are the actual procedures that use the code:
Friend Sub SetWindows7Progress(ByVal aValue As Integer)
If Not IsWin7orLater Then Exit Sub
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
CType(w7tb, ITaskbarList3).SetProgressValue(My.Forms.Form1.Handle, Math.Min(Math.Max(1, aValue), 1000), 1000)
End Sub
Friend Sub ResetWindows7Progress()
If Not IsWin7orLater Then Exit Sub
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
CType(w7tb, ITaskbarList3).SetProgressState(My.Forms.Form1.Handle, TBPFLAG.TBPF_NOPROGRESS)
End Sub
Friend Sub SetWindows7ProgressMon()
If Not IsWin7orLater Then Exit Sub
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
CType(w7tb, ITaskbarList3).SetProgressState(My.Forms.Form1.Handle, TBPF_INDETERMINATE)
End Sub
I even tried getting the HRESULT code from SetProgressState and checking to make sure no exception is thrown to no avail: SetProgressState always returns 0 (everything is fine); and no exceptions are thrown!
Any help in resolving the matter would be greatly appreciated! I just can't believe that everything works except the MARQUE/INDETERMINATE state!
Thanks.
First, it's very strange that your worker functions are accessing your form's instance using this code:
My.Forms.Form1.Handle
That implies that those functions are not defined in the same class as your form (because if they were, the compiler would prompt you to use Me, instead). And if that's the case, you really should be passing the handle to the form into the function as a parameter.
(The reason for this is so that your functions are reusable. If you hardcode a reference to a particular form, what happens when you rename that form, or display two instances of it on the screen at a time, or just want to show a progress indicator in the taskbar for a different form? Things break. Passing the form instance as a parameter is a much cleaner, more reusable approach.)
Second, there seems like an unnecessary amount of casting going on. Why not just declare the w7tb variable as type ITaskbarList3 in the first place, rather than casting back and forth between that and CTaskbarList?
Third, I'm not sure if this is a typo or the actual problem, but your SetWindows7ProgressMon function does not actually reference the correct value for TBPF_INDETERMINATE. You use an unqualified reference to that identifier, when it's actually defined in the TBPFLAG enumeration.
So, considering all of the above, I would rewrite the second block of code that you posted as follows:
Private w7tb As ITaskbarList3
Friend Sub SetWindows7Progress(ByVal frm As Form, ByVal aValue As Integer)
If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
Exit Sub
End If
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
w7tb.SetProgressValue(frm.Handle, Math.Min(Math.Max(1, aValue), 1000), 1000)
End Sub
Friend Sub ResetWindows7Progress(ByVal frm As Form)
If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
Exit Sub
End If
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
w7tb.SetProgressState(frm.Handle, TBPFLAG.TBPF_NOPROGRESS)
End Sub
Friend Sub SetWindows7ProgressMon(ByVal frm As Form)
If (Not IsWin7orLater()) OrElse (frm Is Nothing) Then
Exit Sub
End If
If w7tb Is Nothing Then
w7tb = CType(New CTaskbarList, ITaskbarList3)
End If
w7tb.SetProgressState(frm.Handle, TBPFLAG.TBPF_INDETERMINATE)
End Sub
This is tested to work perfectly on Windows 7 32-bit. Note that you can call the functions from code inside of your form class by simply specifying Me for the frm parameter.