SAP window hidden while obtaining data - vb.net

I am writing a script / program to log into SAP then just grab a few bits of data from a employee then close SAP.
This I can do already but am trying to make a fancy GUI just for that instead of clicking many windows in SAP to get it...mainly because im lazy cant be bothered clicking a million things... Some call this innovation i think :)
I have been researching BackGroundWorker in vb.net but wouldnt this still load the window and just keep the form active and responsive while running the program?
I dont have 'admin' rights (can create and modify user accounts su01,pa30 etc) to the server etc so cant log into the server\database...hence running script to obtain result..
Does anyone know how Ican log SAP and have it hidden while running ?

You can check this post about how to hide an external application window.
http://www.vbforums.com/showthread.php?669210-RESOLVED-Hiding-Window-of-external-process
In this example, guys trying to start calc.exe in hidden mode.
First off all insert at the beggining of your progect
Imports System.Runtime.InteropServices
Then you have to enum flags for ShowWindow (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx
Private Enum ShowWindowCommand As Integer
Hide = 0
Show = 5
Minimize = 6
Restore = 9
End Enum
Set a specified window's show state
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As ShowWindowCommand) As Boolean
End Function
Determine whether the specified window handle identifies an existing window
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function IsWindow(ByVal hWnd As IntPtr) As Boolean
End Function
Determines whether a specified window is minimized.
Private Declare Auto Function IsIconic Lib "user32.dll" (ByVal hwnd As IntPtr) As Boolean
variable to save window handle (you can choise your own name, but rename in other part of code)
Private calc_hWnd As IntPtr
launch windows calculator (in your case saplogon.exe) minimized and hidden, while loading form
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim test As New Process
Try
' file to launch
test.StartInfo.FileName = "calc.exe" ' note: full path not needed for windows calc.
' Note: This next line has no effect on WinXP "calc.exe" and some other apps like FireFox.
'test.StartInfo.WindowStyle = ProcessWindowStyle.Minimized
' start the app
test.Start()
' wait until app is in idle state
test.WaitForInputIdle(-1)
' get app main window handle
Dim tmp_hWnd As IntPtr = test.MainWindowHandle
' make sure handle is valid (non zero)
' try up to 10 times within one second
' do a re-try loop that runs for a second or two
For i As Integer = 1 To 10
tmp_hWnd = test.MainWindowHandle
If Not tmp_hWnd.Equals(IntPtr.Zero) Then Exit For ' got handle so exit loop
Threading.Thread.Sleep(100) ' wait 100ms
Next '- try again
If Not tmp_hWnd.Equals(IntPtr.Zero) Then
' use ShowWindow to change app window state (minimize and hide it).
ShowWindow(tmp_hWnd, ShowWindowCommand.Minimize)
ShowWindow(tmp_hWnd, ShowWindowCommand.Hide)
' save handle for later use.
calc_hWnd = tmp_hWnd
Else
' no window handle?
MessageBox.Show("Unable to get a window handle!")
End If
Catch ex As Exception
' error !
MessageBox.Show(ex.Message)
End Try
End Sub
on exit restore/unhide app if found running.
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
' is our variable set to non-zero?
If Not calc_hWnd.Equals(IntPtr.Zero) Then
' is app window found?
If IsWindow(calc_hWnd) = True Then
' if app is minimized then restore it
If IsIconic(calc_hWnd) Then
ShowWindow(calc_hWnd, ShowWindowCommand.Restore)
End If
' make sure window is seen incase it was hidden.
ShowWindow(calc_hWnd, ShowWindowCommand.Show)
End If
End If
End Sub
But you can write another code in and kill saplogon.exe process.
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
For Each p As Process In System.Diagnostics.Process.GetProcessesByName("saplogon.exe")
Try
p.Kill()
' possibly with a timeout
p.WaitForExit()
' process has already exited - might be able to let this one go
Catch ex As Exception
MessageBox.Show(ex.toString)
End Try
Next
End Sub

Related

How to make vb sendkeys.sendwait execute after a dialog open

I'm testing an addin in ArcMap to open an existing file, the program stops after the dialog opened, it seems the SendKeys already executed before the dialog. Your advice is much appreciated.
Dim pCmdItmOpen As ICommandItem 'file open dialog
Dim pUIDopn As New UID
pUIDopn.Value = "{119591DB-0255-11D2-8D20-080009EE4E51}"
pUIDopn.SubType = 2
pCmdItmOpen = mxApp.Document.CommandBars.Find(pUIDopn)
pCmdItmOpen.Execute()
SendKeys.SendWait("C:\TEST.mxd")
SendKeys.SendWait("{TAB 3}")
SendKeys.SendWait("{ENTER}")
I got this solution:
Detecting whether the dialog has been opened or not, if existing, then execute SendKeys. If not, waiting for a moment, maybe 3 sec, then detecting again.
How to:
Detecting whether the dialog has been opened or not
FindWindow FindWindowEx
Use FindWindow to find the dialog
Waiting for a period then execute the next step
Using timers in vb
Use timer to count the waiting time
Reply:
I don't have 50 reputations, so not allowed to post a comment.
To use the FindWindow, you have to use the correct parameter. You could use Spy++ (Visual Studio, Tools/Spy++) to find the parameter of this dialog window.
You could use code below:
Declare:
<DllImport("user32.dll", CharSet:=CharSet.Auto, EntryPoint:="FindWindow")>
Private Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
End Function
Function catchDialog:
Private Sub catchDialog()
Dim hwnd As IntPtr = FindWindow("Class", "Caption")
If hwnd <> IntPtr.Zero Then
yourSendKeyProcess(hwnd)
Else
' dialog not showing, waiting for 3 Sec. then detecting again.
' Use Thread.Sleep() is simple then timer.
Thread.Sleep(3000)
catchDialog()
End if
End Sub
Function yourSendKeyProcess:
Private Sub yourSendKeyProcess(ByVal window as IntPtr)
SetForegroundWindow(window)
SendKeys.SendWait("C:\TEST.mxd")
SendKeys.SendWait("{TAB 3}")
SendKeys.SendWait("{ENTER}")
End Sub
Use Spy++ to find the value of "Caption", and "Class" of the dialog.
The example above, the value of Caption of the window is "Add to Archive"
and the value of Class is "#32770(Dialog)", and the code would be:
Dim hWnd As IntPtr = FindWindow("#32770", "Add to Archive")
But even you could catch the dialog window, I am not sure that your code SendKey could work, so why you use those codes?
SendKeys.SendWait("C:\TEST.mxd")
SendKeys.SendWait("{TAB 3}")
SendKeys.SendWait("{ENTER}")
You want to input a string ("C:\TEST.mxd") in the textbox on the dialog, then press a button on the dialog?
Edit:
Set the form1 always on top but not influence operation on other window:
Add this sub to make the form1 on top in the beginning:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Me.TopMost = True
End Sub
and Function catchDialog:
Private Sub catchDialog()
Dim hwnd As IntPtr = FindWindow("Class", "Caption")
If hwnd <> IntPtr.Zero Then
me.TopMost = false
yourSendKeyProcess(hwnd)
Else
' dialog not showing, waiting for 3 Sec. then detecting again.
' Use Thread.Sleep() is simple then timer.
Thread.Sleep(3000)
catchDialog()
End if
End Sub
and Function yourSendKeyProcess:
Private Sub yourSendKeyProcess(ByVal window as IntPtr)
SetForegroundWindow(window)
SendKeys.SendWait("C:\TEST.mxd")
SendKeys.SendWait("{TAB 3}")
SendKeys.SendWait("{ENTER}")
me.TopMost = true
End Sub
Thanks for your advice. I have tried the code below however the hWnd return 0. It seems the window name parameter "Open" in the FindWindow function not working?
It works in ArcMap vba use "SendKeys" and "Doevents" everything just prefect and easy to debug in ArcMap. Now I'm converting the vba to vb.net and I found it is more difficult than I thought!!
The "SendKeys.SendWait' wait for the dialog to close then proceed. If I use the "SendKeys.Send" the error "SendKeys.Send cannot run inside this application...".
Dim pCmdItmOpen As ICommandItem 'file open dialog
Dim pUIDopn As New UID
pUIDopn.Value = "{119591DB-0255-11D2-8D20-080009EE4E51}"
pUIDopn.SubType = 2
pCmdItmOpen = mxApp.Document.CommandBars.Find(pUIDopn)
pCmdItmOpen.Execute()
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Dim hWnd As IntPtr = FindWindow("Open", Nothing)
MsgBox(hWnd)
If hWnd.Equals(IntPtr.Zero) Then
Return
End If
SetForegroundWindow(hWnd)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SendKeys.SendWait("C:\TEST.mxd")
SendKeys.SendWait("{TAB 3}")
SendKeys.SendWait("{ENTER}")

Listen to key press when the program is in the background

I am currently working with a program which is supposed to run in the background but also check if "mod + o" is pressed then do something. But I cannot figure out how a vb.net program can listen to key presses when the program is not Selected / Opened.
You can use P/Invocation to be able to use WinAPI's GetAsyncKeyState() function, then check that in a timer.
<DllImport("user32.dll")> _
Public Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short
End Function
Const KeyDownBit As Integer = &H8000
Private Sub Timer1_Tick(sender As Object, e As System.EventArgs) Handles Timer1.Tick
If (GetAsyncKeyState(Keys.LWin) And KeyDownBit) = KeyDownBit AndAlso (GetAsyncKeyState(Keys.O) And KeyDownBit) = KeyDownBit Then
'Do whatever you want when 'Mod + O' is held down.
End If
End Sub
EDIT:
To make the code only execute one time per key press, you can add a little While-loop to run until either of the buttons are released (add it inside your If-statement):
While GetAsyncKeyState(Keys.LWin) AndAlso GetAsyncKeyState(Keys.O)
End While
This will stop your code from executing more than once while you hold the keys down.
When using this in a Console Application just replace every System.Windows.Forms.Keys and Keys with ConsoleKey, and replace LWin with LeftWindows.

VB.NET Events not Firing

I've received a project from another developer. Long story short, the program pulls several thousand entries from an SQL Database hosted on our internal network. The program displays the entries and allows you to filter them for convenience.
Recently we had an issue in which a table in our SQL Database was cleared (It's normally regenerated each day, but for several days it was blank.) Found the issue and solved it (Made no changes to the VB project) to repopulate the table; but since that point the VB project would no longer fire events.
The program is several thousand lines of code long, so I can not post the entire thing; but I will try my best to give symptoms:
The form object can fire events (Form_Closing, Form_Closed, etc.)
The existing controls (Radio button, buttons, picturebox, data grid view, etc) will not fire any events.
If I add a new control (such as a button), it will not fire events.
If a put a debug breakpoint at the sub that should be fired, it will not break.
Here's an example of a method that should be fired but is not fired:
`Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
MsgBox("GOT IT!")
End Sub`
Here's the Form_Load sub:
<DllImport("user32.dll")> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
InitializeComponent()
Try
DataGridView_Items.RowsDefaultCellStyle.SelectionBackColor = Color.Yellow
DataGridView_Items.RowsDefaultCellStyle.SelectionForeColor = Color.Black
CheckBox_Highlight.DataBindings.Add("Visible", RadioButton_BD, "Checked")
Try
'Populates the DGV
LoadTable()
TableLayoutPanel_BD_Parts.Visible = True
TableLayoutPanel_PF_Parts.Visible = False
'Exits if no data was pulled from the database
If dbDataSet.Rows.Count = 0 Or pfDataSet.Rows.Count = 0 Then Application.ExitThread()
Catch ex As Exception
Using w As StreamWriter = File.AppendText(logFile)
Log("Function Form1_Load says " & ex.Message & " # " & DateTime.Now.ToString("yyyy-MM-dd") & "_" & DateTime.Now.ToString("HH-mm-ss"), w)
End Using
End Try
BackgroundWorker1.RunWorkerAsync()
formLoaded = True
Catch exx As Exception
MsgBox(exx.ToString())
End Try
End Sub
There is a backgroundworker, but it appears to work correctly and exit out.
All the forms can be interacted with; but do not fire events. (I can change the selection of the radio button, click the button, type into text boxes, etc.)
I know this is a little vague, I'm just hoping someone can give suggestions as to things that could cause this that I can look into. I can provide specifics; but I can't copy the entire code here.
Thanks for any help!
A very strange thing in your code is that you call InitializeComponent() from Form_Load.
Usually this method is called in Form constructor so you can remove it from Form_Load.
I made some test on my PC: if you called twice InitializeComponent() you duplicate every controls in the form and their events doesn't fire anymore maybe because you have two controls with the same name.

Check process is running, then switch to it?

I have the below code to check if 'chrome' is running when I click Button1. If not it launches chrome. This works but I dont know the code needed in the If statement to switch to the chrome if its already running. Hopefully this is something very simple i am missing.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Process.GetProcessesByName("chrome").Count > 0 Then
??**SHOW RUNNING APPLICATION**??
Else
Process.Start("C:\Program Files\Google\Chrome\Application\chrome.exe")
End If
End Sub
As I mentioned in my above comment, Chrome starts many instances of itself. Each tab has its own process, so how are you going to tell it which one to switch to?. This come's down to what tab was selected when you minimize the window or it minimizes itself to the task bar. Below should help you out and it's tried and tested. The only issue is, if you open Chrome and have multiple tabs it's fine, but if you create another instance of Chrome it will not show the second instance, it will only bring forward the first instance... If you close the first instance, the second instance of course will come forward.
Public Class Form1
#Region "DLL Imports"
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function SetForegroundWindow(handle As IntPtr) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function ShowWindow(handle As IntPtr, nCmdShow As Integer) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("User32.dll")> _
Private Shared Function IsIconic(handle As IntPtr) As Boolean
End Function
#End Region
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
StartOrShowProcess("chrome")
End Sub
Private Sub StartOrShowProcess(ByVal strProcessName As String)
Try
Dim handle As IntPtr
Dim proc As Process() = Process.GetProcessesByName(strProcessName)
If proc.Count > 0 Then
For Each procP As Process In proc
handle = procP.MainWindowHandle
If handle <> 0 AndAlso IsIconic(handle) Then 'Do we have a handle and is it minimized?
ShowWindow(handle, 9)
SetForegroundWindow(handle)
End If
Next
Else 'Not running or started...
Process.Start(strProcessName)
End If
Catch ex As Exception
'Handle your error...
End Try
End Sub
End Class

How to run a console application without showing the console window

I have written an application with the following sub main:
Public Sub Main()
Dim Value As String() = Environment.GetCommandLineArgs
Dim F As Form
Select Case Value.Last.ToLower
Case "-character"
F = New frmCharacterSheet
Case "-viewer"
F = New frmClient
Case Else
F = New frmCombat
End Select
Application.Run(F)
End Sub
This is because I want to be able to install my app with three different startup modes based on the command line. I did have a form that did this, but this has made error trapping very hard because the main form just reports the error.
This console seems to work well but I don't want the user to see the black console screen at startup.
I have searched for the answer but most solutions are 'switch back to a windows forms application'. I don't want to do this though for the above reason. (I cannot use application.run(f) in a winforms start situation because I get a threading error.
I need to know either how to hide the console window, or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form).
Any help would be appreciated....
Try:
Private Declare Auto Function ShowWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As Boolean
Private Declare Auto Function GetConsoleWindow Lib "kernel32.dll" () As IntPtr
Private Const SW_HIDE As Integer = 0
Sub Main()
Dim hWndConsole As IntPtr
hWndConsole = GetConsoleWindow()
ShowWindow(hWndConsole, SW_HIDE)
'continue your code
End Sub
It has a side effect that the window will be shown and then immediately hidden
valter
"or alternatively how to code a main menu that will launch one of the other three forms (but making them the startup form)."
Start with a standard WinForms Project and use the Application.Startup() event. From there you can check your startup parameters and then dynamically change the Startup form by assigning your desired instance to "My.Application.MainForm". This will cause that form to load as if it was the one originally assigned to the "Startup Form" entry.
Click on Project --> Properties --> Application Tab --> "View Application Events" Button (bottom right; scroll down).
Change the Left dropdown from "(General)" to "(MyApplication Events)".
Change the Right dropdown from "Declarations" to "Startup".
Simplified code:
Namespace My
' The following events are available for MyApplication:
'
' Startup: Raised when the application starts, before the startup form is created.
' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
' UnhandledException: Raised if the application encounters an unhandled exception.
' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
Partial Friend Class MyApplication
Private Sub MyApplication_Startup(sender As Object, e As ApplicationServices.StartupEventArgs) Handles Me.Startup
If True Then
My.Application.MainForm = New Form1 ' <-- pass your desired instance to MainForm
End If
End Sub
End Class
End Namespace
Just go to Project Properties> Application> Application Type> and select Windows Forms Application
At this point your ConsoleApplication turns totally invisible, with no User-Interface.
I just want to add another solution although Idle_Mind has already provided an excellent one. This demonstrates that you can use Application.Run(Form) inside a WinForms app.
Public Class Form1
Private Shared applicationThread As New Threading.Thread(AddressOf Main)
Private Shared Sub Main()
Dim myForm As Form
Dim config = 2 ' if 3, will run Form3
Select Case config
Case 2
myForm = New Form2
Case 3
myForm = New Form3
Case Else
MessageBox.Show("Bad config!")
Exit Sub
End Select
Application.Run(myForm)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
applicationThread.Start()
' immediately dispose Form1 so it's not even shown
Dispose()
End Sub
End Class