Find SAP GUI session opened for given SAP system - excel

First I want to check via VBA, before I do some transactions in SAP GUI, if a connection is already open. I am not able to login a second time, so I need to stay in the same connection.
Secondly I want to open another session. The second problem has been solved, if I assume SAP GUI is already open. But I don't know it for sure. So I need to find a way to access the current SAPGUI and Application and Connection, if they exist. If not, the standard code of If Not IsObject(SAPGUI) Then… is fine. But how do I define these variables correctly to check, if they are „filled“ Objects or not?
Thanks for help!

Based on a script by S. Schnell you can use the follwing function to find a free session
Function findGuiSession(ByVal sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
' this will find a free session using the systemnam resp. SID
' and optional one can also supply a transaction to
Dim CollCon As SAPFEWSELib.GuiComponentCollection
Dim CollSes As SAPFEWSELib.GuiComponentCollection
Dim guiCon As SAPFEWSELib.GuiConnection
Dim guiSes As SAPFEWSELib.GuiSession
Dim guiSesInfo As SAPFEWSELib.GuiSessionInfo
Dim i As Long, j As Long
Dim SID As String, transaction As String
'On Error GoTo EH
Dim guiApplication As SAPFEWSELib.guiApplication
Set guiApplication = getGuiApplication
If guiApplication Is Nothing Then
Exit Function
End If
Set CollCon = guiApplication.Connections
If Not IsObject(CollCon) Then
Exit Function
End If
' Loop through all existing connections
For i = 0 To CollCon.Count() - 1
Set guiCon = guiApplication.Children(CLng(i))
If Not IsObject(guiCon) Then
Exit Function
End If
Set CollSes = guiCon.Sessions
If Not IsObject(CollSes) Then
Exit Function
End If
' Now loop through all existing sessions
For j = 0 To CollSes.Count() - 1
Set guiSes = guiCon.Children(CLng(j))
If Not IsObject(guiSes) Then
Exit Function
End If
If guiSes.Busy = vbFalse Then
Set guiSesInfo = guiSes.Info
If guiSesInfo.user = "" Or guiSesInfo.user = "SAPSYS" Then
' Logon Screen - cannot be used
Else
If IsObject(guiSesInfo) Then
SID = guiSesInfo.SystemName()
transaction = guiSesInfo.transaction()
' Take the first one - In case one could also use the transactionaction addtionally
If Len(tCode) = 0 Then
If SID = sapSID Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
Else
If SID = sapSID And transaction = tCode Then
Set findGuiSession = guiSes
'FindSession = True
Exit Function
End If
End If
End If
End If
End If
Next
Next
Exit Function
'EH:
End Function
Function getGuiApplication() As SAPFEWSELib.guiApplication
On Error GoTo EH
Set getGuiApplication = GetObject("SAPGUI").GetScriptingEngine
EH:
End Function
For this code to run you need to add a reference to the SAP library, described here
The following piece of code uses the above function to connect to a system with the name P11, starts the transaction MB52 and downloads the result in a Excel file
Option Explicit
Sub getMB52_data()
Dim guiSes As SAPFEWSELib.GuiSession
Set guiSes = getGuiSession("P11")
If Not guiSes Is Nothing Then
With guiSes
.StartTransaction "MB52"
.FindById("wnd[0]/usr/ctxtMATNR-LOW").Text = "<MATNR_LOW<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtMATNR-HIGH").Text = "<MATNR_HIGH<" ' replace with a material nr
.FindById("wnd[0]/usr/ctxtWERKS-LOW").Text = "<WERKS>" ' replace wiht a plant
.FindById("wnd[0]/tbar[1]/btn[8]").Press
.FindById("wnd[0]/tbar[0]/okcd").Text = "&XXL"
.FindById("wnd[0]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/tbar[0]/btn[0]").Press
.FindById("wnd[1]/usr/ctxtDY_PATH").Text = "<xlPath>" ' Pathname
.FindById("wnd[1]/usr/ctxtDY_FILENAME").Text = "<xlFile>" ' filename
.FindById("wnd[1]/tbar[0]/btn[11]").Press
End With
Else
MsgBox "No free SAP Session", vbOKOnly + vbInformation, "SAP Verbindung"
End If
End Sub
Function getGuiSession(sapSID As String, Optional tCode As String) As SAPFEWSELib.GuiSession
Dim guiApp As SAPFEWSELib.guiApplication
Set guiApp = getGuiApplication
If Not guiApp Is Nothing Then
Set getGuiSession = findGuiSession(sapSID, tCode)
End If
End Function
Additonal remarks: (hopefully answering some questions in the comments)
Gui Connection: A GuiConnection represents the connection between SAP GUI and an application server. Connections can be opened from SAP Logon or from GuiApplication’s openConnection and openConnectionByConnectionString
methods
So, in other words a gui connection is a kind of login to an SAP system. And usually you have more than one SAP system in your organization. If you follow the guidelines you have a DEV, QUAL and PROD for a given system environment. Each of this system is identifid by a SID
What is SID?
SID is a unique identification code for every R/3 installation (SAP system) consisting of a database server & several application servers. SID stands for SAP System Identification. SAPSID — a three-character code such as C11, PRD, E56, etc.)
An SID is unique within the organization. Usually SAP licence only allows a user to login to a productive system only once, i.e. you cannot use the same user on different computers and you cannot even login to a SAP system with the same user on the same computer twice.
Having said that: One might be tempted to use guiApplication.Children(0) instead of looping through all connections as done in findGuiSession. And this will work as long as you can make sure that you are only logged on to one SAP system and it is the right one. But in my experience this is often not the case.
The parameter SID in findGuiSession tells the function which system to look for. As written above SID is unique and therefore identfies the system you want to use.
Using tCode in findGuiSession is optional and just forces the user to have a session in place with a given tCode already started. I use this very seldom.
OpenConnection: If you open a connection with the function OpenConnection("<SYSTEM>") you can, of course, use the returned object in order to work with it. But this only does a logon to the system if you have a kind of single sign on in your organization in place. Otherwise you have to provide logon credentials. I do not use this because I do not want to take care of this. And it also can happen that a password change is requested during the logon and I sure do not want to script this.
Example code
Rem Open a connection in synchronous mode
Set Connection = Application.OpenConnection( "U9C [PUBLIC]", True)
Set Session = Connection.Children(0)
Rem Do something: Either fill out the login screen
Rem or in case of Single-Sign-On start a transaction.
Session.SendCommand( "/nbibs")

Related

Dim As Dictionary. Compile Error: User-defined type not defined

Can you please assist with this problem?
Whenever I run this macro, it stops at:
Dim authResult As Dictionary
With an error message of:
Compile error: User-defined type not defined.
I have not used the dictionary type before and I am trying to re-use this code from a sample macro.
The aim of this script is to use excel to make rest calls to a website so that I can download historic data. I am currently stuck at the login section.
Sub Login()
Dim userName As String
Dim password As String
Dim apiKey As String
userName = "username"
password = "password"
apiKey = "key123"
'activityTextbox.Text = ""
'clearData
Dim authResult As Dictionary
Set authResult = restClient.authenticateAccount(userName, password, apiKey)
If Not authResult Is Nothing Then
'appendActivity "Connected"
' Configure Excel to pull streaming updates as often as possible
Application.RTD.ThrottleInterval = 0
' Uncomment for real-time prices - this is very CPU intensive
' Buffer interval defaults to 500ms
'Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "bufferInterval", "0"
' Set manual refresh to true from very remote locations
' Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "manualRefresh", "true"
' This will require manually calling refresh to update lighstreamer subscriptions, i.e.
' Application.WorksheetFunction.RTD "IG.api.excel.RTD.IGApiRTDServer", "", "refresh"
Dim maxPriceRequestsPerSecond As Double
maxPriceRequestsPerSecond = 0 ' all available updates
If restClient.streamingAuthentication(maxPriceRequestsPerSecond) Then
m_loggedIn = True
'populateWatchlists
'populateAccounts
'manualStreamingRefresh
'Else
' appendActivity "Lightstreamer connection failure"
End If
Else
MsgBox "Authentication failed"
End If
End Sub
Thanks in advance.
Cheers,
Joe
Add a reference to Microsoft Scripting Runtime as #YowE3k said:
In the VBA Editor:
Tools -> References
Find Microsoft Scripting Runtime
Check it
Click okay

Determine Process ID with VBA

Situation - I have a macro where I need to send keystrokes to two Firefox windows in order. Unfortunately both windows have the same title. To handle this I have activated the window, sent my keystrokes, then used F6 to load the URL of the second window and then send the keystrokes then use F6 to return it to the original page.
The issue is that loading the webpages is unreliable. Page load speeds vary so much that using a wait command is not consistent or reliable to ensure the keystroke makes it to the second window.
Question -
I've read a scattering of posts that mentioned that app activate will work with Process ID's. Since each window would have its own PID that would be an ideal way to handle 2 windows with the same title. I am unable to find information specifically how to determine the PID of each window with a given name.
You can use something like the following. You'll have to tinker about with the different info available in the Win32_Process class to figure out which window is which. It's also important to keep in mind that one window could mean many processes.
Public Sub getPID()
Dim objServices As Object, objProcessSet As Object, Process As Object
Set objServices = GetObject("winmgmts:\\.\root\CIMV2")
Set objProcessSet = objServices.ExecQuery("SELECT ProcessID, name FROM Win32_Process WHERE name = ""firefox.exe""", , 48)
'you may find more than one processid depending on your search/program
For Each Process In objProcessSet
Debug.Print Process.ProcessID, Process.Name
Next
Set objProcessSet = Nothing
End Sub
Since you'll probably want to explore the options with WMI a bit, you may want to add a Tools>>References to the Microsoft WMI library so you don't have to deal with Dim bla as Object. Then you can add breakpoints and see what's going on in the Locals pane.
After adding the reference:
Public Sub getDetailsByAppName()
Dim objProcessSet As WbemScripting.SWbemObjectSet
Dim objProcess As WbemScripting.SWbemObject
Dim objServices As WbemScripting.SWbemServices
Dim objLocator As WbemScripting.SWbemLocator
'set up wmi for local computer querying
Set objLocator = New WbemScripting.SWbemLocator
Set objServices = objLocator.ConnectServer(".") 'local
'Get all the gory details for a name of a running application
Set objProcessSet = objServices.ExecQuery("SELECT * FROM Win32_Process WHERE name = ""firefox.exe""", , 48)
RecordCount = 1
'Loop through each process returned
For Each objProcess In objProcessSet
'Loop through each property/field
For Each Field In objProcess.Properties_
Debug.Print RecordCount, Field.Name, Field.Value
Next
RecordCount = RecordCount + 1
Next
Set objProcessSet = Nothing
Set objServices = Nothing
Set objLocator = Nothing
End Sub
That will print out every property of every process found for the name 'firefox.exe'.

vb6 trying to get old program working with excel 2013

I have this code from a program that was created to take data and export it into excel it is in vb6 and I dont know much about vb6 I started coding in vb.net could someone tell me why this isnt working with excel 2013 it opens but then closes right away and i am unsure as to why.
Sub GetExcel()
Dim MyExcel As Object ' Variable to hold reference
' to Microsoft Word.
Dim ExcelWasNotRunning As Boolean ' Flag for final release.
' Test to see if there is a copy of Microsoft Excel already running.
10 On Error Resume Next ' Defer error trapping.
' Getobject function called without the first argument returns a
' reference to an instance of the application. If the application isn't
' running, an error occurs.
20 Set MyExcel = GetObject(, "XLMAIN")
30 If Err.Number <> 0 Then ExcelWasNotRunning = True
40 Err.Clear ' Clear Err object in case error occurred.
' Check for Microsoft Excel. If Microsoft Excel is running,
' enter it into the Running Object table.
50 DetectExcel
' Set the object variable to reference the file you want to see.
60 Set MyExcel = GetObject(App.Path & "\test.xls")
' Show Microsoft Word through its Application property. Then
' show the actual window containing the file using the Windows
' collection of the MyWord object reference.
' MyExcel.Application.Visible = True
' MyExcel.document(1).Visible = True
70 MyExcel.Show , f1
'//////////////////////////////////////////////
' Do manipulations of your file here.
'//////////////////////////////////////////////
' ...
' If this copy of Microsoft Excel was not running when you
' started, close it using the Application property's Quit method.
' Note that when you try to quit Microsoft Excel, the
' title bar blinks and a message is displayed asking if you
' want to save any loaded files.
80 If ExcelWasNotRunning = True Then
90 MyExcel.Application.Quit
100 End If
110 Set MyExcel = Nothing ' Release reference to the
' application and spreadsheet.
End Sub
Sub DetectExcel()
' Procedure dectects a running Word and registers it.
Const WM_USER = 1024
Dim hwnd As Long
' If Excel is running this API call returns its handle.
10 hwnd = FindWindow("XLMAIN", 0)
20 If hwnd = 0 Then ' 0 means Word not running.
30 Exit Sub
40 Else
' Word is running so use the SendMessage API
' function to enter it in the Running Object Table.
50 SendMessage hwnd, WM_USER + 18, 0, 0
60 End If
End Sub
even if i can get some direction on how to rewrite this it would be appreciated.
It's been a long time since I've used it, so you may need to address some of the finer points... But:
First and foremost - get rid of the On Error Resume Next it masks whatever happens next.
This is not a situation where you want to have errors ignored. Capture the error, show something useful (not debug information and not details that can be used for hacking) to the user, and resume or return (after cleanup of assigned object variables to avoid memory leaks).
Then step through the code to see what errors you get.
Some of the things that may help:
Change the specification in your GetObject to be "Excel.Application" i.e.
Set MyExcel = GetObject(, "Excel.Application")
Instead of MyExcel.Show which may or may not be supported, use actual Excel objects and their methods and properties, for instance the commented MyExcel.Application.Visible = True works, while I question the MyExcel.document(1).Visible = True.
Look up the Excel object model help for details. And never hardcode the index - obtain the actual reference that you want and use it.
You can still find articles about Excel and VB6 on line. Use your search engine of choice and good luck.
Beware of those who say something is impossible - first see if it works. Some say you can's install VB6 on a 64-bit system - that is not true, but there are some issues getting it to work. Some say you can't interact with 64-bit Office from VB6, but that is not true. Maybe you can't do some things - I haven't done much with it, just enough to know it is possible to do some things.
Consider developing and testing with typed objects in VB6. It can be very helpful, but to make the application version independent, you will need to remove the object typing before final test and deployment.

Emulate document locking in Lotusscript

In my Lotus Notes Application, when a user clicks an action, the action will call a run-on-server agent that will process the current document. The invoked agents sometimes doesn't run (which I think because of the concurrent agent limit of the server). This is why every 5 minutes there is a maintenance agent that runs to processed documents that are not processed by the invoked agents. The problem is, sometimes, a document is SIMULTANEOUSLY processed by these 2 agents, producing unacceptable results.
Is there a way I can emulate the document locking, such that documents can only be processed by one agent at a time? I don't like to use the native document locking because problems with the business rules might arise. I tried tagging the documents when one of the agents process it, then clears the flag after it is done. But the problem here is that there will still be a chance that the agents get hold of the document reference AT THE SAME TIME (due to the delay of saving the document, maybe).
Please help me. Thanks! :D
Yeah, that is not hard. Create a locking database where you have locking documents. They really only need to contain the UNID of the document being locked.
When your agents start processing a document, check if a locking document exists. If not, create one.
If there is one, either wait or skip the document for now.
After the document is done processing, delete the locking document.
This is trivial. Back when we were still on Notes/Domino 5, I even wrote a simple class to handle document locking in one of my application. The code below is referencing some functions and variables from another script library, but you get the idea. I am sure you can easily modify the code to work for you.
Option Public
Option Declare
Use "Functions.Globals"
Class DocumentLock
Private lockdb As NotesDatabase
Private lockview As NotesView
Private lockdoc As NotesDocument
Private lockservername As String
Private lockdbname As String
Private lnpdoc As NotesDocument ' Document to lock/unlock
Public Sub New(doc As NotesDocument)
me.lockservername = globals.GetValue("LockServer")
me.lockdbname = AppHomeDir + globals.GetValue("LockDBname")
If me.lockdb Is Nothing Then
Set me.lockdb = New NotesDatabase(me.lockservername, me.lockdbname)
End If
Set me.lockview = me.lockdb.GetView("LockedDocs")
Call me.lockview.Refresh()
Set me.lnpdoc = doc
End Sub
Public Sub LockMe()
Set me.lockdoc = New NotesDocument(me.lockdb)
me.lockdoc.Form="Locked"
me.lockdoc.LockUNID=me.lnpdoc.UniversalID
me.lockdoc.LockUser= globalcurrentusername
me.lockdoc.LockTime=Str(Now())
me.lockdoc.ClaimNumber = me.lnpdoc.GetItemValue("ClaimNumber")(0)
me.lockdoc.DocumentForm = me.lnpdoc.GetItemValue("Form")(0)
Call me.lockdoc.Save(True,True)
End Sub
Public Sub UnlockMe()
Call me.lockview.Refresh()
Set me.lockdoc = me.lockview.GetDocumentByKey(me.lnpdoc.UniversalID)
If Not me.lockdoc Is Nothing Then
Call me.lockdoc.Remove(True)
Call me.lockview.Refresh()
End If
End Sub
Public Function IsLocked(flagShowInfo As Boolean) As Boolean
Call lockview.Refresh()
Set me.lockdoc = me.lockview.GetDocumentByKey(me.lnpdoc.UniversalID)
If me.lockdoc Is Nothing Then
me.IsLocked = False
Else
me.IsLocked = True
If flagShowInfo = True Then
MsgBox "Document locked " & locktext & "." & Chr$(13) & "Please wait a while and try again.."
End If
End If
End Function
Public Function LockText() As String
LockText = "by " & LockUserName() & " at " & me.lockdoc.LockTime(0)
End Function
Public Function LockUserName() As String
Dim lockedby As String
lockedby = me.lockdoc.LockUser(0)
If lockedby = globalcurrentusername Then
LockUserName = "you"
Else
LockUserName = lockedby
End If
End Function
End Class

SAP RFC call returns "Error 0" in RETURN parameter from vb

Hi everybody and thanks in advance.
I'm trying to call a SAP BAPI using RFC from vb but I'm having some problem to get the result of the call.
The BAPI "BAPI_GL_ACC_EXISTENCECHECK" (from General Ledger Account module) has two parameters,
COMPANYCODE and GLACCT, and a RETURN parameter.
I wrote this piece of code to make the call and I had no problem to establish the SAP Connection (I use the SAP Logon Control OLE/COM object to do the job), and I tried to make the RFC call.
Also in this case I make the call without problems (it seems, not sure about it ...), because the RFC call returns true and no exception.
However, looking through the objReturn object/parameter, it has a value "Error 0" in it.
I was expecting a complex structure like the BAPIRETURN object in SAP or something similar if the account doesn't exist.
Tried to search with Google and SAP forums but I haven't found a real solution to my problem, so here I am to ask you all if you have some idea to solve this problem (maybe I'm only making a wrong call!!! I'm quite a newbie on SAP integration ...).
BTW, Final Notes: after a lot of RFC_NO_AUTHORIZATION on the SAP side, they gave me a SAP_ALL / S_RFC authorization (sort of, not a SAP expert) and the error RFC_NO_AUTHORIZATION disappeared, but not the Error 0 return
Dim sapConn As Object
Dim objRfcFunc As Object
Dim SAPMandante As String
Dim SAPUtente As String
Dim SAPPassword As String
Dim SAPLingua As String
Dim SAPApplicationServer As String
Dim SAPNumeroSistema As Variant
Dim SAPIDSistema As String
Dim SAPRouter As String
Dim FlagInsertLogin As Integer
Dim FlagLogin As Variant
On Error GoTo ErrorHandler
Set sapConn = CreateObject("SAP.Functions") 'Create ActiveX object
'Silent Logon
SAPMandante = "xxx"
SAPUtente = "yyyy"
SAPPassword = "zzzzzz"
SAPLingua = "IT"
SAPApplicationServer = "www.xxx.com"
SAPNumeroSistema = x
SAPIDSistema = "zzz"
SAPRouter = ""
FlagLogin = SilentLogin(sapConn, SAPMandante, SAPUtente, SAPPassword, SAPLingua, SAPApplicationServer, SAPNumeroSistema, SAPIDSistema, SAPRouter) 'IT WORKS, NO PROBLEM HERE
If FlagLogin = False Then
'Explicit Logon
If sapConn.Connection.logon(0, False) <> True Then
MsgBox "Cannot Log on to SAP", 16, "Query Interrupted"
sapConn.Connection.logoff
Set sapConn = Nothing
InsertCash = False
Exit Sub
End If
End If
'BAPI RFC Call
Set objRfcFunc = sapConn.Add("BAPI_GL_ACC_EXISTENCECHECK")
objRfcFunc.exports("COMPANYCODE") = "C100"
objRfcFunc.exports("GLACCT") = "0000000001" 'Inexistent
Rem *** BAPI CALL ***
If objRfcFunc.Call = False Then
ErrorMsg = objRfcFunc.Exception 'Message collection
MsgBox ErrorMsg, 16, "Errore"
sapConn.Connection.logoff
Exit Sub
else
Dim objReturn As Object
Set objReturn = objRfcFunc.imports("RETURN")
End If
You need to put
Dim objReturn As Object
Set objReturn = objRfcFunc.imports("RETURN")
BEFORE objRfcFunc.Call
i.e. you must state what you're importing from the function before you call it. I usually put it alongside the .exports() lines.
The problem was solved, please take a look at this thread ...
http://sap.ittoolbox.com/groups/technical-functional/sap-dev/sap-rfc-call-returns-error-in-return-parameter-from-vb-before-the-rfc-call-4894968#M4902486

Resources