My SAP-GUI has Scripting installed and Scripting is enabled.
Like in this screenshot:
In this Introduction to SAP GUI Scripting in "Step 2: Setup your SAP System" you need to call RZ11.
I don't have permissions to call RZ11.
Is there a way to detect this (sapgui/user_scripting on or off) via a script?
At the moment I use below code, but the list of connections is always empty:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
sapgui = win32com.client.GetObject("SAPGUI")
system = query.get('system')
client = query.get('mandant')
session = False
application = sapgui.GetScriptingEngine
seen = []
for i_conn in range(0, application.Connections.Count):
seen.append('i_conn=%s session_count=%s' % (i_conn, application.Connections.Item(i_conn).Sessions.Count))
for i_sess in range(0, application.Connections.Item(i_conn).Sessions.Count):
session_info = application.Connections.Item(i_conn).Sessions.Item(i_sess).Info
system_of_session = session_info.SystemName
client_of_session = session_info.Client
if system_of_session == system and client_of_session == client:
connection = application.Connections.Item(i_conn).Children(i_sess)
session = connection.Children(i_sess)
break
seen.append('system=%s client=%s' % (system_of_session, client_of_session))
if session:
break
else:
info_popup('You are not logged into system %s in Client %s! Seen:\n%s' % (
system, client, '\n'.join(seen)))
return
When you don't have sufficient priviledges in sap, the fact that you can't connect is a pretty good indication that the user does not have scripting enable (assuming the user has a active sap session running), other wise you could just test with 'session.findById("wnd[0]/usr/tblSAPLCMDITCTRL_3500").getAbsoluteRow(3).selected = true' and check for errors.
Also, I suggest you factor in "SAPGUISERVER' in your sapgui = win32com.client.GetObject("SAPGUI") connection if "SAPGUI" fails.
As I know sapgui/user_scripting is a system-level = application level setting but not a user-level. So, if you have no permissions to run RZ11 tcode then you have no opportunity or permissions to read applicaton server settings and, of course, no permissions to change it. You have to contact your basis administrator to verify this application settings with him.
You see, SAP limited scripting abilities due to possible vulnerability, that's why scripting support should be turned on both on client side and on application server side.
If you have access to interrogate the registery you could write a cutom function to check SAPGUI is installed and flagged e.g:
Public Sub CheckKey()
Const cRegKey As String = "HKEY_CURRENT_USER\Software\SAP\SAPGUI Front\SAP Frontend Server\Security\UserScripting"
If CheckSAPGUI(cRegKey) Then
MsgBox "User has SAPGUI installed and initialized", vbOKOnly Or vbInformation, Application.Name
Else
MsgBox "User does not have SAPGUI installed", vbOKOnly Or vbCritical, Application.Name
End If
End Sub
Public Function CheckSAPGUI(RegKey As String) As Boolean
Dim rtn As Variant
On Error Resume Next
rtn = vbNullString
With CreateObject("wscript.shell")
rtn = .RegRead(RegKey)
End With
If Len(rtn) = 0 Then
CheckSAPGUI = False
ElseIf Val(rtn) <> 1 Then
CheckSAPGUI = False
Else
CheckSAPGUI = True
End If
On Error GoTo 0
End Function
You should be able to modify the MsgBox comments to better suit how you want to interact with your end user
Related
I have an Access database application that I've split into a Frontend and Backend. The backend sits on a shared network drive that all users can access. My issue is that when a user launches the frontend of this app, and they don't have a connection to the backend because the shared drive may not have been mounted locally, the initial form to be displayed when the app is launched, doesn't open and leaves the user questioning what's going on. I already have code to check if the backend is connected although for some reason, when it isn't connected the intro screen form never is displayed and the access app just sits there
Private Sub Form_Open(Cancel As Integer)
Dim strBackEndPath, LResult As String
Dim i, j, lenPath As Integer
'initialize variable status to 0
Me.BEDB_Status = 0
'define what to check in backend database
strBackEndPath = CurrentDb.TableDefs("VersionInfo-Available").Connect
' Now remove the datebase & password prefix
j = InStrRev(strBackEndPath, "=") + 1
strBackEndPath = Mid(strBackEndPath, j)
'Checking access to Backend database files...
Me.MessageText = "Checking access to Backend database files..."
On Error Resume Next
LResult = Dir(strBackEndPath)
'Set status to Length of LResult
Me.BEDB_Status = Len(LResult)
'Check length of BEDB_Status, if greater than 0, backend is connected. If 0, backend is not connected.
If Me.BEDB_Status > 0 Then
'length is greater than 0 so continue opening the app
DoCmd.OpenForm "IntroScreen"
Else
'length is 0 so backend is not connected. Alert user and quit the app
Me.MessageText = "The database isn't currently accessible. Program will now exit. Please ask the support team for assistance"
DoCmd.Quit acQuitSaveNone
End If
End Sub
It may be simpler and much faster to attempt opening one of the linked tables and ignore the error:
Public Function IsLinkedTable(ByVal TableName As String) As Boolean
Dim LinkOk As Boolean
On Error Resume Next
LinkOk = (DCount("*", TableName) >= 0)
IsLinkedTable = LinkOk
End Function
And do use the OnLoad event of the form as this allows the form to open.
Implemented Gustav's suggestion to use the On Load event rather than the On Open event along with his IsLinkedTable function and it's working great.
Thanks Gustav.
I have 2 ALM's (11 and 12.5), with the below code, i am able to connect to ALM11, but it says invalid user name and password for 12.5 ALM, (user credentials are correct)
Function ALM_Connection(ByRef TDConnection, server, user, pwd)
On Error Resume Next
Dim fileArray
almLogin.connectionMessage.ForeColor = vbBlue
Set TDConnection = CreateObject("tdconnection")
sUserName = user
sPassword = pwd
If Trim(sUserName) = "" Or Trim(sPassword) = "" Then
almLogin.connectionMessage.ForeColor = vbRed
almLogin.connectionMessage.Caption = "Please Enter the data and click on 'Authenticate'"
ALM_Connection = False
Exit Function
End If
almLogin.connectionMessage.Caption = " Please Wait.. "
Application.Wait (Now + TimeValue("0:00:02"))
TDConnection.InitConnectionEx server
TDConnection.Login sUserName, sPassword
If (TDConnection.loggedin <> True) Then
almLogin.connectionMessage.ForeColor = vbRed
almLogin.connectionMessage.Caption = "Invalid UserName or Password"
ALM_Connection = False
Exit Function
Else
almLogin.connectionMessage.Caption = "Logged In Successfully"
ALM_Connection = True
End If
End Function
any help is appreciated
I generally use the below syntax to create the OTA object
Set TDconnection = CreateObject("tdapiole80.tdconnection")
However, Since the connection and authentication are enclosed inside "On Error Resume Next", the error message may not actually reflect the real cause.
So, this is what I would do to debug.
Remove the On Error Resume Next and check whether the code can create a TDConnection Object
Installing the correct version of HP ALM Addin.
Console out "err.description" to understand the real cause
As I can see from your code it throws message "Invalid UserName or Password" in any case when TDConnection.loggedin property is not True, but it can be because of other reason, e.g. wrong OTAClient.dll library (they are not backward compatible). So in general case you should register your ALM client on the machine (server_url/qcbin -> Tools -> Register client) and then try one more time.
If it doesn't help, you can try to rewrite your code using sample from official documentation http://alm-help.saas.hpe.com/en/12.50/api_refs/ota/topic96.html
This problem is due to the Project unavailable. Also check if you have access to any one of the projects in the v12.5.
Thanks.
Good morning, everybody!
I've been looking for the solution in the last days but I really have not managed to succeed: I am trying to make a VBA code to:
log into SAP,
run some transactions,
export to excel.
But even the "log into SAP" part is not OK!
I tried several codes, the one below OPENS the SAP logon screen, but does not fill in any fields.
In the first attempt, I Used CreateObject("Sapgui.ScriptingCtrl.1"):
Sub Entrar_SAP()
If Not IsObject(SAPguiApp) Then
Set SAPguiApp = CreateObject("Sapgui.ScriptingCtrl.1")
End If
If Not IsObject(Connection) Then
Set Connection = SAPguiApp.OpenConnection("xxxxxxx)", True)
End If
If Not IsObject(session) Then
Set session = Connection.Children(0)
End If
session.findById("wnd[0]/usr/txtRSYST-MANDT").Text = "100"
session.findById("wnd[0]/usr/txtRSYST-BNAME").Text = "user"
session.findById("wnd[0]/usr/pwdRSYST-BCODE").Text = "pass"
session.findById("wnd[0]/usr/txtRSYST-LANGU").Text = "PT"
session.findById("wnd[0]/usr/txtRSYST-LANGU").SetFocus
session.findById("wnd[0]/usr/txtRSYST-LANGU").caretPosition = 2
session.findById("wnd[0]").sendVKey 0
In the second attempt, I tried CreateObject("SAP.Functions"), it showed:
"RFC error received. No RFC authorization for function module RFC PING"
The code is:
'Declaration
Dim objBAPIControl As Object 'Function Control (Collective object)
Dim sapConnection As Object 'Connection object
Set objBAPIControl = CreateObject("SAP.Functions")
Set sapConnection = objBAPIControl.Connection
sapConnection.Client = "xxxxx"
sapConnection.User = "xxxxxx"
sapConnection.Language = "PT"
sapConnection.hostname = "xxxxx"
sapConnection.Password = "xxxxxxxx" 'Fake password
sapConnection.SystemNumber = "4"
sapConnection.System = "xxxxxx)"
sapConnection.Logon
If sapConnection.Logon(1, True) <> True Then
MsgBox "No connection to R/3!"
Exit Sub 'End program
End If
Can someone please help me? Thanks!!
First of all, RFC is a perfectly fine method for interacting with SAP. It's not out of support.
Second, you don't have enough authorization so your code will not work even if you get the syntax right. "RFC error received. No RFC authorization for function module RFC PING". Ask your SAP team to give you access to execute RFCs remotely. Ask for SAP_S_RFCACL.
On a side note, your main object of running some transactions and exporting to Excel is quite easy to do in SAP. Maybe you should just ask your SAP team to do it for you instead of developing it in VBA?
I assume your pulling via an RFC read Table. This Connection will work fine for those.
Dim LogonControl As Object
Dim conn As Object
Dim retcd As Boolean
Set LogonControl = CreateObject("SAP.LogonControl.1")
Set conn = LogonControl.NewConnection
conn.System = "strSystemName"
conn.Client = "100"
conn.Language = "EN"
conn.User = "sUserName"
conn.Password = "strPassword"
retcd = conn.Logon(0, True) 'True = No logon GUI 'False = logon GUI
If retcd <> True Then
MsgBox "Login failed for- " & strSystemName & " -UserName or Password are incorrect, check them and run try again ."
Exit Sub
End If
Set funcControl = CreateObject("SAP.Functions")
funcControl.Connection = conn
From this Point on you can make your RFC call without any issues.
But to be truthful though, Above is almost exactly what you have as your second example. your RFC Error your getting seems like you don't have security settings for SAP to make RFC calls to whatever table your pulling from and not a problem with your login code.
Disclaimer: RFC_READ_TABLE is NOT supported by SAP and is more of a backdoor then a day to day method for pulling data.
Edit1: To Cover the Comments and not turn this into a discussion I will try and Summarize them here.
Firstly
the pop-up: If you want the pop-up for the log in then you need to change this line of code
retcd = conn.Logon(0, True)
to
retcd = conn.Logon(0, False) 'This one DISPLAYS the pop-up
Secondly
The Permissions: RFC_Read_Table uses Very Different Security Settings then a SAP t-Code uses, The technical Difference is difficult to explain but for a rule of thumb, If you cant access the SAP Table (t-Code SE16) you most likely not be able to pull it from RFC Read Table
Thirdly
If your company has Multiple SAP boxes (DEV, production, test) the Systemname would be EXACTLY what shows up on the box selection screen of SAP under name. assuming you were getting an RFC error from your second code block then the box name you used in that code would be the correct one.
You can bypass RFC controls and just go for a normal scripting that imitates a human user and manually introduces username and password. Credit to The Script Man from the SAP forums:
Sub SapLogin()
'Logs onto SAP system
Dim SapGuiApp As Object
Dim oConnection As Object
Dim session As Object
Dim SAPCon As Object, SAPSesi As Object
Dim SAPGUIAuto As Object, SAPApp As Object
Dim system As String
system = "XX" 'SAP system you will log on to like "01. ENGINEERING PRODUCTION [EG1]
If SapGuiApp Is Nothing Then
Set SapGuiApp = CreateObject("Sapgui.ScriptingCtrl.1")
End If
If oConnection Is Nothing Then
Set oConnection = SapGuiApp.OpenConnection(system, True)
End If
If SAPSesi Is Nothing Then
Set SAPSesi = oConnection.Children(0)
End If
Application.DisplayAlerts = FALSE
With SAPSesi
.FindById("wnd[0]/usr/txtRSYST-MANDT").Text = "100"
.FindById("wnd[0]/usr/txtRSYST-BNAME").Text = "USERNAME"
.FindById("wnd[0]/usr/pwdRSYST-BCODE").Text = "PASSWORD"
.FindById("wnd[0]/usr/txtRSYST-LANGU").Text = "EN"
.FindById("wnd[0]").SendVKey 0
'start extraction
.FindById("wnd[0]").Maximize
.FindById("wnd[0]/tbar[0]/okcd").Text = "/TCODEYOUWANTTORUN"
.FindById("wnd[0]").SendVKey 0
'...
'etc
'...
End With
Application.DisplayAlerts = True
MsgBox "After clicking OK, this SAP session is terminated."
End Sub
I am using the below code to identify instance of the application also if we need to check that which user is using this application what will be the code for it?
Function PrevInstance() As Boolean
If Ubound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName)) > 0 Then
Return True
Else
Return False
End If
End Function
My requirement is if same user tries to open the application then it should display pop up message like "application already opened".
Please advice...
abhay
Thanks a lot guys.... solution on my problem as follows:
> Function PrevInstance() As Boolean
> If UBound(Diagnostics.Process.GetProcessesByName(Diagnostics.Process.GetCurrentProcess.ProcessName))
> > 0 Then
> Dim CurUser As Boolean = GetProcessOwner(Diagnostics.Process.GetCurrentProcess.ProcessName)
> Return CurUser
> Else
> Return False
> End If
> End Function
Function GetProcessOwner(ByVal ProcessName As String) As Boolean
Dim boolVal As Boolean
Dim CurUserName As String
Dim CountInstance As Integer
CountInstance = 0
CurUserName = System.Environment.UserName
Dim selectQuery As SelectQuery = New SelectQuery("Select * from Win32_Process Where Name = '" + ProcessName + ".exe' ")
Dim searcher As ManagementObjectSearcher = New ManagementObjectSearcher(selectQuery)
Dim y As System.Management.ManagementObjectCollection
y = searcher.Get
For Each proc As ManagementObject In y
Dim s(1) As String
proc.InvokeMethod("GetOwner", CType(s, Object()))
Dim n As String = proc("Name").ToString()
If n = ProcessName & ".exe" Then
If s(0) = CurUserName Then
CountInstance = CountInstance + 1
If CountInstance > 1 Then
boolVal = True
End If
End If
End If
Next
Return boolVal
End Function
I have called PrevInstance() in my Form_Load() and its working perfectly.
PrevInstance Property
Returns a value indicating whether a previous instance of an application is already running.
Syntax
object.PrevInstance
The object placeholder represents an object expression that evaluates to an object in the Applies To list.
Remarks
You can use this property in a Load event procedure to specify whether a user is already running an instance of an application. Depending on the application, you might want only one instance running in the Microsoft Windows operating environment at a time.
Note Since a computer running Windows NT can support multiple desktops, if you use a component designed to work with distributed COM, it can result in the following scenario:
A client program in a user desktop requests one of the objects the component provides. Because the component is physically located on the same machine, the component is started in the user desktop.
Subsequently, a client program on another computer uses distributed COM to request one of the objects the component provides. A second instance of the component is started, in a system desktop.
There are now two instances of the component running on the same NT computer, in different desktops.
This scenario is not a problem unless the author of the component has placed a test for App.PrevInstance in the startup code for the component to prevent multiple copies of the component from running on the same computer. In this case, the remote component creation will fail.
Send feedback to MSDN.Look here for MSDN Online resources.
Session
The above tells you if it's already running. This tells you what session. An interactive user is always session1 on Vista and later. 0 for XP and earlier.
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set colItems = objWMIService.ExecQuery("Select * From Win32_Process")
For Each objItem in colItems
msgbox objitem.name & " PID=" & objItem.ProcessID & " SessionID=" & objitem.sessionid
Next
PS
The rules for single instance programs is just before you exit you switch windows to the previous instance.
PPS
Due to problems introduced by 32 bit computing previnstance (Win32's one rather than VB's) becomes less meaningful. The common way to do this now is to open and lock a file on startup (Windows also has memory constructs you can use such as mailslots, pipes, etc). If a program can't lock then another is already running.
I am implementing a tool which relies on Bloombergs blpapilib2, which is the Bloomberg API COM Lib 3.5.
Before giving my user access to any refresh-data functionality, I want to make sure that the connection works. My approach so far:
Check wether the library is available and linked. Basically a loop through references does the job.
Open a connection with session.Start() . I was hoping to get an error here, but it won't give me one. Thus, step 3.
Request some data and verify it (make sure its not empty)
Surporisingly, I cannot reliably reproduce getting an empty result. I expected my session relies on a user being looged into the terminal. It seems I was wrong; even if I log out, my request will be handled and return correct data.
I can imagine two scenarios:
some background caching in the bbcom-Server
an alternative authentication method is used
I have two questions:
Q1. What is the best way to make sure a user will be able to download data?
Q2. How can I verify whether a connection has been established successfully and a user is authenticated?
Thanks.
To "cut" the connection, you need to log out and log in on a different machine. If you simply log out the feed is still available using the API.
This is how I test the connection - I think it works fairly well. I have a BloombergWrapper class that handles all the low level stuff of communicating with the API and it has the following functions:
Private pSession As blpapicomLib2.Session
Private pService As blpapicomLib2.Service
Private Sub Class_Initialize()
Dim locStatusBar As Variant
Dim locBbResult As Variant
On Error GoTo error_handler
If Application.StatusBar = False Then locStatusBar = False Else locStatusBar = Application.StatusBar
Application.StatusBar = "Connecting to Bloomberg..."
Set pSession = New blpapicomLib2.session
pSession.Start
pSession.OpenService ("//blp/refdata")
Set pService = pSession.getService("//blp/refdata")
Application.StatusBar = locStatusBar
Exit Sub
error_handler:
If InStr(Err.Description, "timeout") Then
Call MsgBox("A Bloomberg timeout has occured. Make sure you are logged on your terminal.", vbCritical + vbOKOnly, "Bloomberg error...")
End If
If locStatusBar <> "" Then Application.StatusBar = locStatusBar
End Sub
Private Sub Class_Terminate()
pSession.Stop
Set pSession = Nothing
End Sub