VBA Change the text color in MsgBox - excel

I want to change the font color from MsgBox
To understand what I want, I chose this exemple:
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim results As String
a = InputBox("Enter your first value:")
b = InputBox("Enter your second value:")
c = InputBox("Enter your third value:")
d = a - b + c
If d = 0 Then
results = "Correct"
MsgBox "Your results is: " & results
Else
results = "Incorrect"
MsgBox " Your Results is: " & results
End If
The "Correct" text I want to be in green when it appears in MsgBox; the "Incorrect" text I want to be in red when it appears in MsgBox
I hope what I have requested is possible.

As Ralph suggests, it'd be better to display your message in a UserForm where you'd have easy control over the text characteristics.
However, it is possible to change the colour of your MessageBox text, using the system color API's. As the MessageBox is a Window, you can alter the colour parameters of it (not just text, but various others too).
You'd want to ensure that you reset the original values immediately afterwards of course otherwise all of your windows will display in the modified colours.
The below code will automatically detect 32-bit and 64-bit systems and should work on both equally well:
Option Explicit
#If Win64 Then
Private Declare PtrSafe Function GetSysColor Lib "user32" _
(ByVal nIndex As Long) As Long
Private Declare PtrSafe Function SetSysColors Lib "user32" _
(ByVal nChanges As Long, lpSysColor As Long, lpColorValues As Long) As Long
#Else
Private Declare Function GetSysColor Lib "user32" _
(ByVal nIndex As Long) As Long
Private Declare Function SetSysColors Lib "user32" _
(ByVal nChanges As Long, lpSysColor As Long, lpColorValues As Long) As Long
#End If
Private Const COLOR_WINDOWTEXT As Long = 8
Private Const CHANGE_INDEX As Long = 1
Public Sub RunMe()
Dim defaultColour As Long
'Store the default system colour
defaultColour = GetSysColor(COLOR_WINDOWTEXT)
'Set system colour to red
SetSysColors CHANGE_INDEX, COLOR_WINDOWTEXT, vbRed
MsgBox "Incorrect", , "Your result is..."
'Set system colour to green
SetSysColors CHANGE_INDEX, COLOR_WINDOWTEXT, vbGreen
MsgBox "Correct", , "Your result is..."
'Restore default value
SetSysColors CHANGE_INDEX, COLOR_WINDOWTEXT, defaultColour
End Sub

Related

Excel Vba Print Userform and save Userform as pdf

I have a project that requires user entry form to be printed, renamed and saved to a specific folder
I've gone through numerous subjects relating to .printform command on this platform and usually the solutions requires to PrintScreen, save as bitmap, paste to new workbook and use Activesheet.ExportAsFixedFormat to set as pdf......
but then
My userforms width is 603,
My userforms height is 875,
The userform requires a vertical scrollbar to view all details
So "SnapScreen" wouldn't go a long way
The current code i have uses .printform command
and asks me to choose the printer i want to print to
and if the selected printer isn't active on my computer, it aborts command
Below is the code
Option Explicit
Private Const HWND_BROADCAST As Long = &HFFFF&
Private Const WM_WININICHANGE As Long = &H1A
Private Declare PtrSafe Sub keybd_event Lib "user32" _
(ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As Long, ByVal dwExtraInfo As LongPtr)
Private Declare PtrSafe Function SendNotifyMessage Lib "user32" Alias "SendNotifyMessageA" ( _
ByVal hwnd As LongPtr, _
ByVal Msg As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Declare PtrSafe Function SetDefaultPrinter Lib "winspool.drv" Alias "SetDefaultPrinterA" ( _
ByVal pszPrinter As String) As Long
Public Sub ChangePrinter(NewPrinter As String)
SetDefaultPrinter NewPrinter
Call SendNotifyMessage(HWND_BROADCAST, _
WM_WININICHANGE, _
0, ByVal "windows")
End Sub
Private Sub btn_Print_Click()
Dim Msg As String
Dim ireply As Integer
ireply = MsgBox("Select Active-Printer", vbQuestion + vbOKCancel)
Select Case ireply
Case vbOK
GoTo line123456
Case vbCancel
Exit Sub
End Select
line123456:
Dim dir as string
Dim OldPrinter As String
Dim NewPrinter As String
OldPrinter = Left(Application.ActivePrinter, InStrRev(Application.ActivePrinter, "on ") - 2)
Application.Dialogs(xlDialogPrinterSetup).Show
NewPrinter = Left(Application.ActivePrinter, InStrRev(Application.ActivePrinter, "on ") - 2)
''folderpath (created pdf to be renamed based on textbox value)
'dir = "C:\Users\user\Documents\Almadina Related\NHIS RELATED" & Me.txt_pNameDefaultclaimsForm.Value & ".pdf"
ChangePrinter NewPrinter
On Error GoTo Endproc
Me.PrintForm
GoTo SkipExit
Endproc:
MsgBox "Sorry, print command aborted - a default printer is not selected"
Unload Me
Exit Sub
SkipExit:
ChangePrinter OldPrinter
End Sub
Kindly Help.
is it possible to print, rename and save USERFORM to a specific folder as pdf
Thank You!

VBA to Close a PDF Document from Excel

I have this lovely little procedure that is supposed to either shut down the window with the Acrobat display or just one document in it. Only the design is of my own making, meaning I don't fully understand the code, but I do know that it works only partially. It will quit Adobe Acrobat in full, regardless of how many documents are displayed but it can't close just one (as the original from which it was transcribed claimed that it could and should).
Private Sub CloseReaderDC(Optional ByVal MailIdx As Integer)
Dim WinId As String
Dim Wnd As LongPtr
If MailIdx Then
WinId = AcrobatWindowID(Mail(MailIdx))
Wnd = FindWindow(vbNullString, WinId)
PostMessage Wnd, WM_CLOSE, 0, ByVal 0&
Else
WinId = AcrobatWindowID
Wnd = FindWindow(WinId, vbNullString)
SendMessage Wnd, WM_CLOSE, 0, ByVal 0&
End If
End Sub
The logic is that the parameter MailIdx identifies a file name which is sufficient to find a top window. If no value is given the app should be shut down. This part works. The other part also works, but only if there is a single document open, in which case not the document is closed but the entire application. I believe this shutdown may be caused by Acrobat Reader itself which doesn't see a reason for staying open with no document to display. I also think that the window handle may not be found if there are several documents because FindWindow finds a top window only and the one I want to close would be the second one. In practice, I tried both, to close the existing before opening another one and after. In the one case the app gets shut down, in the other nothing happens.
I don't know why my tutor uses SendMessage in one case and PostMessage in the other. I also don't know if the window I'm after is a Child Window or how to get a handle on it if it is. Any suggestions?
Edit 11 Jan 2021
I used the following code to test #FaneDuru's solution.
Private Sub Test_CloseReaderDC()
ReDim Mail(2)
Mail(0) = ""
Mail(1) = "File1.PDF"
Mail(2) = "File2.PDF"
CloseReaderDC 1
End Sub
Private Sub CloseReaderDC(Optional ByVal MailIdx As Integer)
' NIC 003 ++ 10 Jan 2021
Dim WinTitle As String
Dim WinCap As String
Dim Wnd As LongPtr
WinTitle = AcrobatWindowID
If MailIdx Then
WinCap = AcrobatWindowID(Mail(MailIdx))
Wnd = FindWindow(vbNullString, WinCap)
Debug.Print Wnd
SendMessage Wnd, WM_CloseClick, 6038, ByVal 0&
Else
Wnd = FindWindow(WinTitle, vbNullString)
Debug.Print Wnd
SendMessage Wnd, WM_CLOSE, 0, ByVal 0&
End If
End Sub
Function AcrobatWindowID(Optional ByVal Wn As String)
' NIC 003 ++ 07 Jan 2021
Dim Fun As Boolean
Fun = CBool(Len(Wn))
If Fun Then Wn = Wn & " - "
AcrobatWindowID = Wn & Split("AcrobatSDIWindow,Adobe Acrobat Reader DC", ",")(Abs(Fun))
End Function
The code worked perfectly for both 1 or 2 files, not closing the app until called with a parameter of 0. But on second try it failed to find the window and therefore took no action.
I started Acrobat and selected the 2 previously opened files from its File>Open menu. File1 was in the first tab, File2 in the second, active. Then I attempted to delete File1 which failed. Then I called the code with 2 as parameter which closed the top file. Thereafter the code found the window for File1 and closed it.
I don't think the apparent rule is followed consistently, however. How the files were opened seems to make a difference. In my project the files are opened by hyperlink, one at a time. My above test therefore is not indicative of how FaneDuru's suggestion will work in my project but it proves that the solution works.
You did not say anything about my comment regarding closing the active document by programmatically pressing the File menu "Close File" control...
This way of closing does not make Acrobat application quitting. It stay open, even if only a document was open in the moment of running the code.
So, test the next code line, please. You need the Acrobat Reader DC handler and the necessary arguments, like following:
Const WM_CloseClick = &H111
SendMessage Wnd, WM_CloseClick, 6038, ByVal 0&
6038 is the 'Close File' File menu control ID.
I could determine it using the next function:
Private Function findControlID(mainWHwnd As LongPtr, ctlNo As Long) As Long
Dim aMenu As LongPtr, sMenu As LongPtr
aMenu = GetMenu(mainWHwnd): Debug.Print "Main menu = " & Hex(aMenu)
sMenu = GetSubMenu(aMenu, 0&): Debug.Print "File menu = " & Hex(sMenu)
mCount = GetMenuItemCount(sMenu): Debug.Print "File menu no of controls: " & mCount 'check if it is 28
findControlID = GetMenuItemID(sMenu, ctlNo - 1) 'Menu controls are counted starting from 0
End Function
The above function was called in this way:
Sub testFindCloseControlID()
Dim Wnd As LongPtr
'Wnd = findWindowByPartialTitle("Adobe Acrobat Reader DC") 'you will obtain it in your way
Debug.Print findControlID(Wnd, 15) '15 means the fiftheenth control of the File menu (0)
End Sub
15 has been obtained counting the horizontal controls separators, too.
In order to find "Adobe Acrobat Reader DC" window handler I used the function mentioned above, but this does not matter too much. You may use your way of determining it...
Please, test the above way and send some comments
Edited:
In order to extract the applications menu(s) captions, I use the next declarations:
Option Explicit
'APIs for identify a window handler
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (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 GetWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal wCmd As Long) As Long
'____________________________________________________
'Menu related APIs
Private Declare PtrSafe Function GetMenu Lib "user32.dll" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GetSubMenu Lib "user32" (ByVal hMenu As LongPtr, ByVal nPos As Long) As LongPtr
Private Declare PtrSafe Function GetMenuItemID Lib "user32" _
(ByVal hMenu As LongPtr, ByVal nPos As Long) As Long
Private Declare PtrSafe Function GetMenuItemCount Lib "user32" (ByVal hMenu As LongPtr) As Long
Private Declare PtrSafe Function GetMenuItemInfo Lib "user32" Alias "GetMenuItemInfoA" (ByVal hMenu As LongPtr, _
ByVal Un As Long, ByVal b As Long, lpMenuItemInfo As MENUITEMINFO) As Long
Private Declare PtrSafe Function GetMenuString Lib "user32" Alias "GetMenuStringA" (ByVal hMenu As LongPtr, _
ByVal wIDItem As Long, ByVal lpString As String, ByVal nMaxCount As Long, ByVal wFlag As Long) As Long
'_____________________________________________________
Private Type MENUITEMINFO
cbSize As Long
fMask As Long
fType As Long
fState As Long
wID As Long
hSubMenu As LongPtr
hbmpChecked As LongPtr
hbmpUnchecked As LongPtr
dwItemData As LongPtr
dwTypeData As String
cch As Long
hbmpItem As LongPtr
End Type
Private Const GW_HWNDNEXT = 2
And the next functions/subs:
To find any window knowing only its partial title:
Sub testFindWindByPartTitle()
Debug.Print findWindowByPartialTitle("Notepad")
End Sub
Private Function findWindowByPartialTitle(ByVal sCaption As String, Optional strSecond As String) As LongPtr
Dim lhWndP As LongPtr
Dim sStr As String
findWindowByPartialTitle = CLngPtr(0)
lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
Do While lhWndP <> 0
sStr = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
GetWindowText lhWndP, sStr, Len(sStr)
If Len(sStr) > 0 Then sStr = left$(sStr, Len(sStr) - 1)
If InStr(1, sStr, sCaption) > 0 And _
IIf(strSecond <> "", InStr(1, sStr, strSecond) > 0, 1 = 1) Then
findWindowByPartialTitle = lhWndP
Exit Do
End If
lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
Loop
End Function
A version of extract the necessary ID by control caption, but it works only for Notepad:
Private Sub TestfindMenuItemsByCaption()
Const NotePApp As String = "Notepad"
Debug.Print findMenuIDByString(NotePApp, "Save") 'it does work
Const pdfApp As String = "Adobe Acrobat Reader DC"
Debug.Print findMenuIDByString(pdfApp, "Close") 'it does not work
End Sub
Private Function findMenuIDByString(pdfApp As String, searchString As String) As Long
Dim mainWHwnd As LongPtr, aMenu As LongPtr, mCount As Long
Dim LookFor As Long, sMenu As LongPtr, sCount As Long
Dim LookSub As Long, sID As Long, sString As String
mainWHwnd = findWindowByPartialTitle(pdfApp)
aMenu = GetMenu(mainWHwnd): Debug.Print "Main menu = " & Hex(aMenu)
sMenu = GetSubMenu(aMenu, 0): Debug.Print "File menu = " & Hex(sMenu)
sCount& = GetMenuItemCount(sMenu)
For LookSub& = 0 To sCount& - 1
sID& = GetMenuItemID(sMenu, LookSub&): Debug.Print "ID = " & sID: 'Stop
sString$ = String$(100, " ")
Call GetMenuString(sMenu, sID&, sString$, 100&, 1&) ' 1&)
Debug.Print sString$ ': Stop
If InStr(LCase(sString$), LCase(searchString$)) Then
findMenuIDByString = sID
Exit Function
End If
Next LookSub&
End Function
And a second version, unfortunately working exactly in the same way. I mean, returning the ID only for Notepad:
Private Sub TestfindMenuItemsByCaptionBis()
Const NotePApp As String = "Notepad"
Debug.Print findMenuItemIDByCaption(NotePApp, "Save")
Const pdfApp As String = "Adobe Acrobat Reader DC"
Debug.Print findMenuItemIDByCaption(pdfApp, "Close")
End Sub
Private Function findMenuItemIDByCaption(strApp As String, strCaption As String)
Dim appHwnd As LongPtr, hMenu As LongPtr, fMenu As LongPtr, i As Long
Dim retval As Long, mii As MENUITEMINFO 'mii receives information about each item
Const WM_SaveClick = &H111, MIIM_STATE = &H1, MIIM_STRING = &H40&, MIIM_ID = &H2&, MIIM_CHECKMARKS = &H8&
Const MIIM_SUBMENU = &H4&, MIIM_TYPE = &H10, MIIM_FTYPE = &H100&, MIIM_DATA = &H20&
appHwnd = findWindowByPartialTitle(strApp)
If appHwnd = 0 Then MsgBox "No application window found...": Exit Function
hMenu = GetMenu(appHwnd) 'application window Menu
fMenu = GetSubMenu(hMenu, 0) 'app window 'File' Submenu
For i = 0 To GetMenuItemCount(fMenu)
With mii
.cbSize = Len(mii)
.fMask = MIIM_STATE Or MIIM_SUBMENU Or MIIM_TYPE
.dwTypeData = space(256)
.cch = 256
retval = GetMenuItemInfo(fMenu, i, 1, mii) '2 = the third menu item
Debug.Print left(.dwTypeData, .cch)
If InStr(left(.dwTypeData, .cch), strCaption) > 0 Then
findMenuItemIDByCaption = GetMenuItemID(fMenu, i): Exit Function
End If
End With
Next i
End Function
I tried all constants as I could find, but not success... If we would find a way, a subroutine could also read the recent files list and activate the needed one, if is not the active one is the necessary one.

Hand Cursor for Label vba excel

I'm developing an application with many controls. I want to change the mouse cursor when it passes over a Label. I took a look in the option but there you have a limited choice and not what I want. I tried also to upload a mouse icon but I faced two difficulties: the first one is finding an icon under license cc0 and the second one is that Excel doesn't accept the format that I found. Can you please help? Thanks in advance
You can use the Windows API to change the cursor appearance. I'm assuming this is in an Excel UserForm, so you can use the MouseMove event to know when the mouse is over the label.
Here's the code that you would add in the code behind in a form.
Option Explicit
'Api Declarations
Private Declare Function GetCursorInfo Lib "user32" (ByRef pci As CursorInfo) As Boolean
Private Declare PtrSafe Function LoadCursor Lib "user32" Alias "LoadCursorA" (ByVal hInstance As Long, ByVal lpCursorName As Long) As Long
Private Declare PtrSafe Function SetCursor Lib "user32" (ByVal hCursor As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'You can use the default cursors in windows
Public Enum CursorTypes
IDC_ARROW = 32512
IDC_IBEAM = 32513
IDC_WAIT = 32514
IDC_CROSS = 32515
IDC_UPARROW = 32516
IDC_SIZE = 32640
IDC_ICON = 32641
IDC_SIZENWSE = 32642
IDC_SIZENESW = 32643
IDC_SIZEWE = 32644
IDC_SIZENS = 32645
IDC_SIZEALL = 32646
IDC_NO = 32648
IDC_HAND = 32649
IDC_APPSTARTING = 32650
End Enum
'Needed for GetCursorInfo
Private Type POINT
X As Long
Y As Long
End Type
'Needed for GetCursorInfo
Private Type CursorInfo
cbSize As Long
flags As Long
hCursor As Long
ptScreenPos As POINT
End Type
'Event that handles knowing when the mouse is over the control
Private Sub Label1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
AddCursor IDC_HAND
End Sub
'To set a cursor
Private Function AddCursor(CursorType As CursorTypes)
If Not IsCursorType(CursorType) Then
SetCursor LoadCursor(0, CursorType)
Sleep 200 ' wait a bit, needed for rendering
End If
End Function
'To determine if the cursor is already set
Private Function IsCursorType(CursorType As CursorTypes) As Boolean
Dim CursorHandle As Long: CursorHandle = LoadCursor(ByVal 0&, CursorType)
Dim Cursor As CursorInfo: Cursor.cbSize = Len(Cursor)
Dim CursorInfo As Boolean: CursorInfo = GetCursorInfo(Cursor)
If Not CursorInfo Then
IsCursorType = False
Exit Function
End If
IsCursorType = (Cursor.hCursor = CursorHandle)
End Function

Determine if application is running with Excel

Goal
Have an Excel file with a "Search" button that opens a custom program. This program is used for researches. If the program is already opened when the user clicks on the button, make it popup and focus on that given program.
Current Situation
Here's the code I'm trying to use to make it work:
Search Button
Private Sub btnSearch_Click()
Dim x As Variant
Dim Path As String
If Not IsAppRunning("Word.Application") Then
Path = "C:\Tmp\MyProgram.exe"
x = Shell(Path, vbNormalFocus)
End If
End Sub
IsAppRunning()
Function IsAppRunning(ByVal sAppName) As Boolean
Dim oApp As Object
On Error Resume Next
Set oApp = GetObject(, sAppName)
If Not oApp Is Nothing Then
Set oApp = Nothing
IsAppRunning = True
End If
End Function
This code will work only when I put "Word.Application" as the executable. If I try to put "MyProgram.Application" the function will never see the program is running. How can I find that "MyProgram.exe" is currently opened?
Further more, I'd need to put the focus on it...
You can check this more directly by getting a list of open processes.
This will search based on the process name, returning true/false as appropriate.
Sub exampleIsProcessRunning()
Debug.Print IsProcessRunning("MyProgram.EXE")
Debug.Print IsProcessRunning("NOT RUNNING.EXE")
End Sub
Function IsProcessRunning(process As String)
Dim objList As Object
Set objList = GetObject("winmgmts:") _
.ExecQuery("select * from win32_process where name='" & process & "'")
IsProcessRunning = objList.Count > 0
End Function
Here's how I brought the search window to front:
Private Const SW_RESTORE = 9
Private Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Private Sub btnSearch_Click()
Dim x As Variant
Dim Path As String
If IsProcessRunning("MyProgram.exe") = False Then
Path = "C:\Tmp\MyProgram.exe"
x = Shell(Path, vbNormalFocus)
Else
Dim THandle As Long
THandle = FindWindow(vbEmpty, "Window / Form Text")
Dim iret As Long
iret = BringWindowToTop(THandle)
Call ShowWindow(THandle, SW_RESTORE)
End If
End Sub
Now if the window was minimized and the user clicks the search button again, the window will simply pop up.
Just want to point out that the Window Text may change when documents are open in the application instance.
For example, I was trying to bring CorelDRAW to focus and everything would work fine so long as there wasn't a document open in Corel, if there was, I would need to pass the complete name to FindWindow() including the open document.
So, instead of just:
FindWindow("CorelDRAW 2020 (64-Bit)")
It would have to be:
FindWindow("CorelDRAW 2020 (64-Bit) - C:\CompletePath\FileName.cdr")
As that is what would be returned from GetWindowText()
Obviously this is an issue as you don't know what document a user will have open in the application, so for anyone else who may be coming here, years later, who may be experiencing the same issue, here's what I did.
Option Explicit
Private Module
Private Const EXE_NAME As String = "CorelDRW.exe"
Private Const WINDOW_TEXT As String = "CorelDRAW 2020" ' This is common with all opened documents
Private Const GW_HWNDNEXT = 2
Private Const SW_RESTORE = 9
Private Declare PtrSafe Function ShowWindow Lib "user32" (ByVal hWnd As Long, ByVal nCmdShow As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As Long, ByVal wCmd As Long) As Long
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Sub FocusIfRunning(parAppName as String, parWindowText as String)
Dim oProcs As Object
Dim lWindowHandle As Long
Dim sWindowText As String
Dim sBuffer As String
' Create WMI object and execute a WQL query statement to find if your application
' is a running process. The query will return an SWbemObjectSet.
Set oProcs = GetObject("winmgmts:").ExecQuery("SELECT * FROM win32_process WHERE " & _
"name = '" & parAppName & "'")
' The Count property of the SWbemObjectSet will be > 0 if there were
' matches to your query.
If oProcs.Count > 0 Then
' Go through all the handles checking if the start of the GetWindowText()
' result matches your WindowText pre-file name.
' GetWindowText() needs a buffer, that's what the Space(255) is.
lWindowHandle = FindWindow(vbEmpty, vbEmpty)
Do While lWindowHandle
sBuffer = Space(255)
sWindowText = Left(sBuffer, GetWindowText(lWindowHandle, sBuffer, 255))
If Mid(sWindowText, 1, Len(parWindowText)) Like parWindowText Then Exit Do
' Get the next handle. Will return 0 when there are no more.
lWindowHandle = GetWindow(lWindowHandle, GW_HWNDNEXT)
Loop
Call ShowWindow(lWindowHandle , SW_RESTORE)
End If
End Sub
Private Sub btnFocusWindow_Click()
Call FocusIfRunning(EXE_NAME, WINDOW_TEXT)
End Sub
Hopefully somebody gets use from this and doesn't have to spend the time on it I did.
Just wanted to say thank you for this solution. Only just started playing around with code and wanted to automate my job a bit. This code will paste current selection in excel sheet into an already open application with as single click. Will make my life so much easier!!
Thanks for sharing
Public Const SW_RESTORE = 9
Public Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
Public Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long
Public Sub updatepart()
'
' updatepart Macro
' copies current selection
' finds and focuses on all ready running Notepad application called Test
' pastes value into Notepad document
' Keyboard Shortcut: Ctrl+u
'
Dim data As Range
Set data = Application.Selection
If data.Count <> 1 Then
MsgBox "Selection is too large"
Exit Sub
End If
Selection.Copy
If IsProcessRunning("Notepad.EXE") = False Then
MsgBox "Notepad is down"
Else
Dim THandle As Long
THandle = FindWindow(vbEmpty, "Test - Notepad")
Dim iret As Long
iret = BringWindowToTop(THandle)
Call ShowWindow(THandle, SW_RESTORE)
End If
waittime (500)
'Call SendKeys("{F7}")
Call SendKeys("^v", True) '{F12}
Call SendKeys("{ENTER}")
End Sub
Function waittime(ByVal milliseconds As Double)
Application.Wait (Now() + milliseconds / 24 / 60 / 60 / 1000)
End Function
Function IsProcessRunning(process As String)
Dim objList As Object
Set objList = GetObject("winmgmts:") _
.ExecQuery("select * from win32_process where name='" & process & "'")
If objList.Count > 0 Then
IsProcessRunning = True
Else
IsProcessRunning = False
End If
End Function

Excel Application.InputBox Position

With the Top and Left arguments for this function is there a Centre screen option, or will it always be a number?
I'm using this instead of a regular inputbox as it handles the cancel event better but it always appears in the bottom right of the screen which is less than helpful :/
There is no center screen option. You'd have to calculate it. But, assuming you are using Excel 2007 or later, there's another issue...
This was news to me, but in googling and testing I see that in Excel 2007 and 2010 Application.Inputbox reverts to its last position, disregarding the Top and Left settings. This problem seems to persist even if a new Inputbox is called from a new worksheet. When I try it in XL 2003 it works correctly, and the Inputbox is placed at the correct left and right coordinates.
You can maybe just drag the Inputbox where you want and then save. Unless somebody drags it later, it will re-open in the same place.
Here's a link to a solution that worked for somebody to bring back the correct behavior, and also addresses centering the inputbox. It does require API calls, so save your work before you try it.
EDIT - Per JMax's comment, here's the code from the link above. It's by a user called KoolSid on the vbforums.com site:
Private Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Long) As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Private Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" (ByVal idHook As Long, _
ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Private Declare Function SetWindowPos Lib "user32" _
(ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _
ByVal x As Long, ByVal y As Long, ByVal cx As Long, _
ByVal cy As Long, ByVal wFlags As Long) As Long
'~~> Handle to the Hook procedure
Private hHook As Long
'~~> Hook type
Private Const WH_CBT = 5
Private Const HCBT_ACTIVATE = 5
'~~> SetWindowPos Flags
Private Const SWP_NOSIZE = &H1 '<~~ Retains the current size
Private Const SWP_NOZORDER = &H4 '<~~ Retains the current Z order
Dim InputboxTop As Long, InputboxLeft As Long
Sub TestInputBox()
Dim stringToFind As String, MiddleRow As Long, MiddleCol As Long
hHook = SetWindowsHookEx(WH_CBT, _
AddressOf MsgBoxHookProc, 0, GetCurrentThreadId)
'~~> Get the center cell (keeping the excel menus in mind)
MiddleRow = ActiveWindow.VisibleRange.Rows.Count / 1.2
'~~> Get the center column
MiddleCol = ActiveWindow.VisibleRange.Columns.Count / 2
InputboxTop = Cells(MiddleRow, MiddleCol).Top
InputboxLeft = Cells(MiddleRow, MiddleCol).Left
'~~> Show the InputBox. I have just used "Sample" Change that...
stringToFind = Application.InputBox("Sample", _
"Sample", "Sample", InputboxLeft, InputboxTop, , , 2)
End Sub
Private Function MsgBoxHookProc(ByVal lMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
If lMsg = HCBT_ACTIVATE Then
'~~> Change position
SetWindowPos wParam, 0, InputboxLeft, InputboxTop, _
0, 0, SWP_NOSIZE + SWP_NOZORDER
'~~> Release the Hook
UnhookWindowsHookEx hHook
End If
MsgBoxHookProc = False
End Function
You can test the regular inputbox to see if cancel was pressed, and it has the extra benifit of always being centered. Just use StrPtr(variable) = 0 to test it. Simple!
Another way to avoid a user hitting OK with nothing typed is to add a default value inside the input box to start with, that way you know that if it returns an empty string, it's most likely due to the cancel button being pressed.
StrPtr will return a 0 if cancel was selected (also returns 0 for vbNullString, btw). Please note that StrPtr work in VB5, VB6, and VBA, but since it's not officially supported, it could be rendered unusuable years down the line. I highly doubt they'd get rid of it but it's worth considering if this is an application you plan to distribute.
Sub CancelTest()
Dim temp As String
temp = InputBox("Enter your name", "Cancel Test")
If StrPtr(temp) = 0 Then
' You pressed cancel
Else
If temp = "" Then
'You pressed OK but entered nothing
Else
'Do your thing
End If
End If
End Sub
Some more info on strptr:
StrPtr(S) returns a pointer to the actual string data currently stored in S. This is what you need when passing the string to Unicode API calls. The pointer you get points to the Datastring field, not the Length prefix field. In COM terminology, StrPtr returns the value of the BSTR pointer. (from the fantastic site: http://www.aivosto.com/vbtips/stringopt2.html)
' assume normal screen else go through GetDeviceCaps(hDCDesk, LOGPIXELSX) etc etc
' 1440 twips / inch pts / pix = 3/4 inch 100 pts
' so twips / pixel = 15
Sub GetRaXy(Ra As Range, X&, Y&) ' in twips
Dim ppz!
ppz = ActiveWindow.Zoom / 75 ' zoom is % so 100 * 3/4 =>75
' only the pixels of rows and columns are zoomed
X = (ActiveWindow.PointsToScreenPixelsX(0) + Ra.Left * ppz) * 15
Y = (ActiveWindow.PointsToScreenPixelsY(0) + Ra.Top * ppz) * 15
End Sub
Function InputRealVal!(Optional RaTAdd$ = "K11")
Dim IStr$, RAt As Range, X&, Y&
Set RAt = Range(RaTAdd)
GetRaXy RAt, X, Y
IStr = InputBox(" Value ", "ENTER The Value ", 25, X, Y)
If StrPtr(IStr) = 0 Then
MsgBox "Cancel Pressed"
Exit Function
End If
If IsNumeric(IStr) Then
InputRealVal = CDec(IStr)
Else
MsgBox "Bad data entry"
Exit Function
End If
End Function

Resources