Can VB.net take control of a VBA form? - vb.net

I have an Excel VBA macro containing a form. I would like for VB.net to either hide the form or take control of it and click the "X" button or the custom "Exit" button.
Does anybody have experience with this?

Sendkeys are highly unreliable.
If you can automate the Excel from VB.Net the I would recommend that. If you are interested in automating Excel from VB.Net then see this.
If you don't want to automate but want to directly handle it then I would recommend using the FindWindow and Sendmessage API to get control of the form. See this Example.
Let's say that there is a userform in Excel which looks like this
Use this code form VB.Net
CODE:
Imports System.Runtime.InteropServices
Imports System.Text
Public Class Form1
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function FindWindow( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, _
ByVal Msg As UInteger, _
ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function
Private Const WM_CLOSE = &H10
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim Ret As Integer
'~~> Get the handle of the Excel Userform named "Example"
Ret = FindWindow(vbNullString, "Example")
'~~> If Found
If Ret <> 0 Then
'~~> Close the Window
SendMessage(Ret, WM_CLOSE, CLng(0), CLng(0))
End If
End Sub
End Class

Related

Visual Studio Console-Like Blinking Cursor?

Is there any way to make a cursor look something like the blinking, square cursor used in console applications, say, inside of a text box instead of the regular vertical line? Example:
https://gyazo.com/db9f5661d493c32e48434eed2fa45252
You can control the caret (cursor) using the Win API Caret Functions.
A simple custom WinForm TextBox can be given a blinking caret like this:
Public Class MyTB : Inherits TextBox
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function CreateCaret(ByVal hWnd As IntPtr, ByVal hBitmap As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function DestroyCaret() As Boolean
End Function
<DllImport("user32.dll")>
Private Shared Function SetCaretBlinkTime(ByVal uMSeconds As UInt32) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function ShowCaret(ByVal hWnd As IntPtr) As Boolean
End Function
Protected Overrides Sub OnGotFocus(e As EventArgs)
MyBase.OnGotFocus(e)
MyTB.CreateCaret(Me.Handle, Nothing, 5, Me.Font.Height)
MyTB.SetCaretBlinkTime(500UI)
MyTB.ShowCaret(Me.Handle)
End Sub
Protected Overrides Sub OnLostFocus(e As EventArgs)
MyBase.OnLostFocus(e)
MyTB.DestroyCaret()
End Sub
End Class

VB: ShowWindow focus issue

I'm attempting to send automated keystrokes to an application that does not support copy+paste via a small VB form. The form loads data from a text file and uses SendKeys to fire it over once I click a button.
Everything appears to work except for the ShowWindow portion. I'm currently testing using Notepad and, with one exception, I can't seem to get ShowWindow to kick focus to Notepad. Obviously I'm worried it will do the same to the application I'll eventually be running this against (I don't currently have access to it). The only ShowWindow parameter that makes Notepad active is SW_SHOWMAXIMIZED. SW_SHOW and SW_SHOWNORMAL don't appear to do anything while SW_RESTORE will restore Notepad if minimized but my VB form remains the active window.
I'm not a programmer but I had made the mistake of telling my boss I dabbled in Pascal Turbo in high school (over a decade ago) so I'm the one stuck with trying to make this work. My current code cobbled together from S.O. and other sources:
(I'm running Windows 7 and using MVSE2013)
Imports System.Runtime.InteropServices
Public Class Form1
Private Declare Function FindWindow _
Lib "user32" _
Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As IntPtr
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
Enum ShowWindowCommands As Integer
SW_SHOWNORMAL = 1
SW_SHOWMAXIMIZED = 3
SW_RESTORE = 9
End Enum
Private Sub Form1_Load
[form]
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Dim lHwnd As IntPtr = FindWindow("Notepad", vbNullString)
If lHwnd <> IntPtr.Zero Then
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
SendKeys.Send(TextBox1.Text)
Else
[blah blah error handling]
End If
End Sub
I'd try another technique like SetForegroundWindow but I read it doesn't play nice with Windows 7.
Found what I hope will be a passable workaround from PInvoke. I ended up swapping this block:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow _
As ShowWindowCommands) As Boolean
End Function
For this:
Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As IntPtr) As Boolean
And then this line:
ShowWindow(lHwnd, ShowWindowCommands.SW_SHOWNORMAL)
For this:
BringWindowToTop(lHwnd)
I realize there are functional differences between the two but the change works in my specific instance so I'm happy.

how to hide/unhide window by process name or processid using VB.NET?

im trying to hide/unhide window process by name or PID i have tried
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommands) As Boolean
End Function
but ShowWindowCommands what is and how to find it ?
thanks !
there is many ways to do this the easiest one should be this :
'GENERAL IMPORT
Imports System.Runtime.InteropServices
'FORM CLASS DECLARATION
<DllImport("user32.dll")> _
Private Shared Function ShowWindow(ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
End Function
'then if you want to hide firefox's window :
Dim mywindow As Integer
Dim processRunning As Process() = Process.GetProcesses()
For Each pr As Process In processRunning
If pr.ProcessName = "Firefox" Then
mywindow = pr.MainWindowHandle.ToInt32()
ShowWindow(mywindow , 0)
End If
Next
You can do it like this:
Form.FromHandle(Process.GetProcessById(PID).MainWindowHandle).Show()

Need help getting child windows

I am working in vb with visual studio 2010.
The app AnyOrder is no longer supported and I am trying to make an app where our users can type in info and then have the info populate into AnyOrder (and also other sites - they have to populate a lot of redundant info) but I am having no luck at all getting the child windows of any order to populate once the data is in the VB window app.
I can get the parent window handle just fine but can't seem to get a child window handle to save my life. I can't even get the first level down and the blanks that I will need to populate are great grand-children of the parent window.
It won't let me post a screencap of the spy++ since I just signed up and don't have the 10 rep but here is a link to the capture.
Thank you in advance for any assistance that you can provide.
Figured out the answer somehow.
#Region "functions"
Private Declare Function FindWindow Lib "user32.dll" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Int32
Public Delegate Function EnumWindowProcess(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
<DllImport("User32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Private Shared Function EnumChildWindows(ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowProcess, ByVal lParam As IntPtr) As Boolean
End Function
Private Shared Function EnumWindow(ByVal Handle As IntPtr, ByVal Parameter As IntPtr) As Boolean
Dim ChildrenList As List(Of IntPtr) = CType(GCHandle.FromIntPtr(Parameter).Target, Global.System.Collections.Generic.List(Of Global.System.IntPtr))
ChildrenList = CType(GCHandle.FromIntPtr(Parameter).Target, Global.System.Collections.Generic.List(Of Global.System.IntPtr))
If ChildrenList Is Nothing Then Throw New Exception("GCHandle Target could not be cast as List(Of IntPtr)")
ChildrenList.Add(Handle)
Return True
End Function
Private Shared Function GetChildWindows(ByVal ParentHandle As IntPtr) As IntPtr()
Dim ListHandle As GCHandle = GCHandle.Alloc(ChildrenList)
Try
EnumChildWindows(ParentHandle, AddressOf EnumWindow, GCHandle.ToIntPtr(ListHandle))
Finally
If ListHandle.IsAllocated Then ListHandle.Free()
End Try
Return ChildrenList.ToArray
End Function
#End Region
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
iParentHandle = FindWindow(vbNullString, AOParentName)
GetChildWindows(FindWindow(vbNullString, AOParentName))
Dim Childlist As String = String.Join(". ", ChildrenList)
MsgBox("list of child windows: " & Childlist)
End Sub

How to add more items to the window context menu

I'm making an app for the company I work for and I was wondering how to customise the window's context menu like PuTTY's (aka, it has "New Session..." etc.). I've looked all over Google and can't find the answer I'm looking for.
make a new module and add Imports System.Runtime.InteropServices on top
then declare this
<Flags()> _
Public Enum MenuFlags As Integer
MF_BYPOSITION = 1024
MF_REMOVE = 4096
MF_SEPARATOR = 2048
MF_STRING = 0
End Enum
<DllImport("user32.dll", CallingConvention:=CallingConvention.Cdecl)> _
Public Function GetSystemMenu(ByVal hWnd As IntPtr, Optional ByVal bRevert As Boolean = False) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Function AppendMenu(ByVal hMenu As IntPtr, ByVal uFlags As MenuFlags, ByVal uIDNewItem As Int32, ByVal lpNewItem As String) As Boolean
End Function
then on your form load handler add this code
Dim sysmenu As IntPtr = GetSystemMenu(Me.Handle)
AppendMenu(sysmenu, MenuFlags.MF_STRING, &H1FFF, "Hello")
then, in order to be able to capture the user click on your new menu item, you have to implement this function which will capture all messages, just add it to your form code
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = WM_SYSCOMMAND Then
If m.WParam.ToInt32 = &H1FFF Then
' your menu item is clicked, call a function here
End If
End If
MyBase.WndProc(m)
End Sub