Sign in to follow this  
Followers 0
Bennet

IBM Websphere MQ - SAP RFC

3 posts in this topic

#1 ·  Posted (edited)

I'm actually really proud of myself today. I'm always keen on using AutoIT wherever I can, as I've been developing in it since the age of 14 and whenever I meet any new technical staff or developers at the different clients I visit it always gets a mention and a demonstration. So we had a minor problem today at one of our clients, whereby they had an existing Windows Service implemented with a config file but the source-code was missing, which limited the ability of our company to enhance the interface in any way.

Now considering the fact that I was paid to develop this I will refrain from divulging any company names or "secrets" as, of course, there are clauses in my contract that state that my work belongs to them. I am however a SAP ABAP developer by trade so anything that falls outside of the defined limits of this trade I can technically share, but regardless I'll keep the information limited.

So the requirement was to pull data from IBM Websphere, write it out to a file and post it into SAP through a custom built RFC. It turned out to be much less complicated than I imagined and through the research on these forums, I had discovered that similar things had been attempted but that nothing concrete had been written for AutoIT specifically (The MQ part, not the SAP part). So without further adieu I present my thinking.

I started out with the IBM Websphere dll using DllOpen and DllCall. After a few frustrating attempts at creating COM objects through the DLL Call. I finally discovered that there was another dll that I could register using regsrv32. It was called MQAX200.dll

Once this dll was registered on the system I could access the following piece of code;

EnvSet("MQSERVER", $iniServer) ;MQ Server Environment Variable
$MQSess = ObjCreate("MQAX200.MQSession")
$QMgr = ObjCreate("MQAX200.MQQueueManager")
$QMgr = $MQSess.AccessQueueManager($iniQM) ;Queue Manager
ConsoleWrite("Connected" & @CRLF)

After this was complete and I successfully tested my connection I added the following to read from MQ;

$Queue = $QMgr.AccessQueue($iniQueue, 2) ;Queue (2=MQOO_INPUT, 16=MQOO_OUTPUT)
While 1
$GetMsg = $MQSess.AccessMessage
$GetOptions = $MQSess.AccessGetMessageOptions
$Queue.Get($GetMsg, $GetOptions)
If $Queue.ReasonCode = 2033 Then ;When there aren't any new messages sleep for 2 seconds.
Sleep(2000)
Else
#EndRegion Accessing MQ
#Region Write File
$MsgData = $GetMsg.MessageData
....

I then wrote this out to a file and attempted my SAP connection using the following code;

Dim $LoggedIn = False
$oConnection = $LogonControl.NewConnection
$oConnection.System = $System
$oConnection.ApplicationServer = $AppServer
$oConnection.SystemNumber = $SysNumb

$oConnection.User = $User
$oConnection.Password = $Password
$oConnection.Client = $Client
$oConnection.Language = $Language
$LoggedIn = $oConnection.Logon(0, True)
Return $LoggedIn

which resulted in failure as the SAP COM object wasn't registered on the system. After attempting to register the correct object without installing SAP GUI, I ended up with a slightly different solution for an executable supplied by SAP called startrfc.exe;

$foo = Run(@ComSpec & " /c " & $RFCExec & " -3 -h " & $AppServer & " -s " & $SysNumber & " -F " & $RFC & " -u " & $User & " -p " & $Password & " -c " & $Client & " -l " & $Language & " -E FNAME=" & $FilePath & " -E PNAME=" & $Program, "", @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD)
While 1
   $line = StdoutRead($foo)
    If @error Then ExitLoop
    ConsoleWrite($line)
    $LogFile = FileOpen(StringReplace($FilePath, "\In\","\Logs\"), 2)
    FileWrite($LogFile, $Line)
    FileClose($LogFile)
    FileMove($FilePath, StringReplace($FilePath, "\In\","\Out\"))
WEnd
While 1
    $line = StderrRead($foo)
    If @error Then ExitLoop
    ConsoleWrite($line)
    $ErrFile = FileOpen(StringReplace($FilePath, "\In\","\Errors\E"), 2)
    FileWrite($ErrFile, $Line)
    FileClose($ErrFile)
  FileMove($FilePath, StringReplace($FilePath, "\In\","\Errors\"))
WEnd

One last piece of code that you should keep in mind if you attempt this so that your program doesn't stop when a COM error is returned (MQ returns an error that indicates there is no data available on the queue as you see with the reason code check earlier);

Global $oErrorHandler = ObjEvent("AutoIt.Error", "_ErrFunc") ;Catch COM errors

In conclusion, I really enjoyed implementing something that'll be used for the foreseeable future at a powerful company and writing it in my favorite language. I hope you find some use in these snippets.

This will be running as a Windows Service from now on using srvany.exe.

Edited by Bennet

Share this post


Link to post
Share on other sites



Hello Bennet,

cool solution. :thumbsup:

What is the content of the variable $RFCExec?

If you like you can try this: http://scn.sap.com/docs/DOC-40480

CCo is a COM-Wrapper around SAP NetWeaver RFC library.

At the moment I try to create an example with AutoIt as SAP server application, hope it will work.

Cheers

Stefan

Share this post


Link to post
Share on other sites

Hey StSchnell,

A very late response to your post. :) $RFCExec contains the path to the startrfc.exe file supplied by SAP.

I ended up changing the solution in any case to rather check for new files using a background job every five minutes.

Kind regards,

Ben

 

Hello Bennet,

cool solution. :thumbsup:

What is the content of the variable $RFCExec?

If you like you can try this: http://scn.sap.com/docs/DOC-40480
CCo is a COM-Wrapper around SAP NetWeaver RFC library.
At the moment I try to create an example with AutoIt as SAP server application, hope it will work.

Cheers
Stefan

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0

  • Similar Content

    • PauloRodrigues
      By PauloRodrigues
      I need to select an item from a SAP combobox, could anyone help me or did something like that? I tried to use some AutoIt functions but without success.
      When i try to identify the combobox with au3Info, this is the return:


      Could anyone help me with this challange?
    • Neutro
      By Neutro
      Hey guys!
      Here are some informations on how to automate AS400 tasks with AutoIT. AS400 are mainframes made by IBM and used mainly in professional workplaces. 
      First you need to launch an IBM Iseries console to the AS400. It looks like this:

      As it is a regular window, you can use the "AutoIT Window Info" tool and functions like "ControlSetText", "ControlClick" to automate the login process.
      Notice that the name of the window (in the top left of the above screenshot) is "Session A". This is because it is the first Iseries window that is opened on the client computer. If you do not close it and open another one, the next one will be named "Session B". The third one "Session C"...
      Once you're logged into the Iseries console interface, the OS400 login window shows up:

      Use this code to create an autoIT object linked to the iseries console:
      global $oOIA = ObjCreate("PCOMM.autECLOIA") $oOIA.SetconnectionByName("A") global $oPS = ObjCreate("PCOMM.autECLPS") $oPS.SetConnectionByName("A") The letter "A" is a reference to the name of the session displayed in the iseries console window, as explained before. Change it to another letter if you have multiples iseries console windows opened at the same time.
      Then there are 3 main functions that you can use to interact with the interface:
      $oOIA.WaitForInputReady() ;waits for the interface to be ready for input. $oPS.SetCursorPos(6, 53) ;put the cursor of the interface to position X = 6, Y = 53 $oPS.SendKeys("hello world[enter]") ;write the text "hello world" where the cursor of the interface is then press the enter/return key $result = $oPS.SearchText("banana") ;search for the text "banana" on the interface screen. Returns "True" if found, "False" if not. The function "WaitForInputReady" is badfully not very reliable. For better results, use the fuction "SearchText" in a while loop to wait for a specific text to appear on the interface if you want to be sure that the interface is ready for input.
      With these 3 functions you can pretty much do anything you would do manually on an Iseries console. Special keys for the "SendKeys" function can be found using the virtual keyboard included in the iseries console software.
      Enjoy
    • StSchnell
      By StSchnell
      Hello community,
      here is my script integration library ScriptX. With this library is it very easy possible to integrate AutoIt inside SAPs programming language ABAP.
      Here an ABAP example:
      "-Begin----------------------------------------------------------------- Program ZSCRIPTX. "-Constants--------------------------------------------------------- Constants CrLf(2) Type c Value %_CR_LF. Constants SW_SHOWNORMAL Type i Value 1. "-Variables--------------------------------------------------------- Data oScriptX Type OLE2_OBJECT. Data Buffer Type String Value ''. Data WorkDir Type String Value ''. Data FileName Type String Value ''. Data rc Type i Value 0. "-Macros------------------------------------------------------------ Define _. Concatenate Buffer &1 CrLf Into Buffer. End-Of-Definition. Define Flush. Call Function 'AC_SYSTEM_FLUSH' Exceptions Others = 1. End-Of-Definition. "-Main-------------------------------------------------------------- Create Object oScriptX 'ScriptX'. If sy-subrc <> 0 Or oScriptX-Handle = 0 Or oScriptX-Type <> 'OLE2'. Call Function 'ZSCRIPTXDLL'. Create Object oScriptX 'ScriptX'. EndIf. If sy-subrc = 0 And oScriptX-Handle > 0 And oScriptX-Type = 'OLE2'. Call Method Of oScriptX 'About'. Flush. Call Function 'ZAUTOIT3EXE'. "-AutoIt Script begin--------------------------------------------------- _ 'MsgBox(0, "AutoIt", "Version " & @AutoItVersion)'. "-AutoIt Script end----------------------------------------------------- Call Method cl_gui_frontend_services=>get_sapgui_workdir Changing SAPWORKDIR = WorkDir Exceptions Others = 1. Concatenate WorkDir '\Version.au3' Into FileName. Flush. Call Method Of oScriptX 'WriteFile' Exporting #1 = FileName #2 = Buffer. Flush. Call Method Of oScriptX 'Shell' = rc Exporting #1 = 'AutoIt3.exe' #2 = 'Version.au3' #3 = SW_SHOWNORMAL #4 = 1. Flush. Free Object oScriptX. EndIf. "-End------------------------------------------------------------------- Good integration.
      Cheers
      Stefan
    • StSchnell
      By StSchnell
      Hello community,
      SAP offers different connectors to develop ABAP compatible components and applications. JCo for Java environments, NCo for dotNET languages and the NetWeaver RFC SDK for C++. But what's up if you work neither with Java or dotNET environments nor with C++?
      Here is another alternative, CCo - the COM Connector for SAP. CCo is a COM library and offers wrappers around all functions of the SAP NetWeaver RFC library. So it is possible to use all functionalities of the SAP NetWeaver RFC library inside any language which is COM-enabled, like AutoIt. With CCo it is easily possible to use the SAP NetWeaver RFC functions inside AutoIt.
      Hint: CCo has at the moment experimental character. Don't use it in production environments.
      Hint: CCo needs SAP RFC SDK, you find it here, SAP passport required.
      You find CCo here: cco.stschnell.de
      You find a few examples how to use CCo here, in particular three examples in AutoIt Script how to call BAPI functions - here BAPI_USER_ * functions.
      Cheers
      Stefan
    • StSchnell
      By StSchnell
      Hello community,

      here is an example code of a server application for an SAP system in AutoIt language.

      My problem is, that this AutoIt example crashs after the first function call of ABAPCall from an ABAP report.

      I try two methods:
      Use DllCallbackGetPtr Use CreateThread and get the address of the thread. But both methods crashs at the same code position, look at the example below.
      Is there any other way to use this kind of multithreading?

      Thanks for hints.

      Cheers
      Stefan


      ;-Begin----------------------------------------------------------------- ;-Directives---------------------------------------------------------- AutoItSetOption("MustDeclareVars", 1) ;-Constants----------------------------------------------------------- Const $RFC_OK = 0 Const $RFC_RETRY = 14 ;-Variables----------------------------------------------------------- Dim $SAP, $ABAPCall, $ptrABAPCall, $hDesc, $conPar[3] Dim $ptrConPar[3], $ConParPtr, $i, $rc, $hCon ; Dim $hThread ;-Function CreateThread----------------------------------------------- Func CreateThread($Handle) Local $Ret $Ret = DllCall("kernel32.dll", "handle", "CreateThread", "ptr", 0, _ "dword", 0, "long", DllCallbackGetPtr($Handle), "ptr", 0, _ "long", 0, "int*", 0) Return $Ret[0] EndFunc ;-Function GetThreadAddr---------------------------------------------- Func GetThreadAddr($hThread) Local $ThreadAddr = DllStructCreate("ptr") DllCall("NTDLL.dll", "long", "NtQueryInformationThread", _ "handle", $hThread, "long", 9, "ptr", _ DllStructGetPtr($ThreadAddr), "ulong", 4, "ulong*", 0) Return Number(DllStructGetData($ThreadAddr, 1)) EndFunc ;-Function OutputDebugString------------------------------------------ Func OutputDebugString($OutputString) Local $strOut = String($OutputString) DllCall("kernel32.dll", "none", "OutputDebugStringW", "wstr", _ $strOut) EndFunc ;-Function ABAPCall--------------------------------------------------- Func ABAPCall() MsgBox(0, "", "Ja") Return $RFC_OK ;-Important hint-------------------------------------------------- ;- ;- Program crashs here, because AutoIt is not able to use multi ;- threading with SAP NetWeaver RFC library ;- ;----------------------------------------------------------------- EndFunc ;-Main---------------------------------------------------------------- $SAP = ObjCreate("COMNWRFC") If IsObj($SAP) Then $ABAPCall = DllCallbackRegister("ABAPCall", "long", "") $hDesc = $SAP.RfcCreateFunctionDesc("ABAPCall") If $hDesc And $ABAPCall Then ;-Define RFC_CONNECTION_PARAMETER structures-------------------- For $i = 0 To 2 $conPar[$i] = DllStructCreate("wchar name[16];wchar value[16]") Next ;-Set RFC_CONNECTION_PARAMETER---------------------------------- DllStructSetData($conPar[0], "name", "program_id") DllStructSetData($conPar[0], "value", "AUTOITSERVER") DllStructSetData($conPar[1], "name", "gwhost") DllStructSetData($conPar[1], "value", "ABAP") DllStructSetData($conPar[2], "name", "gwserv") DllStructSetData($conPar[2], "value", "sapgw00") ;-Switch RFC_CONNECTION_PARAMETER strings to pointers----------- $ptrConPar = DllStructCreate("ptr;ptr;ptr;ptr;ptr;ptr") DllStructSetData($ptrConPar, 1, DllStructGetPtr($conPar[0], "name")) DllStructSetData($ptrConPar, 2, DllStructGetPtr($conPar[0], "value")) DllStructSetData($ptrConPar, 3, DllStructGetPtr($conPar[1], "name")) DllStructSetData($ptrConPar, 4, DllStructGetPtr($conPar[1], "value")) DllStructSetData($ptrConPar, 5, DllStructGetPtr($conPar[2], "name")) DllStructSetData($ptrConPar, 6, DllStructGetPtr($conPar[2], "value")) $ptrABAPCall = Number(DllCallbackGetPtr($ABAPCall)) ;-Alternative----------------------------------------------------------- ; ; $hThread = CreateThread($ABAPCall) ; If $hThread Then ; $ptrABAPCall = GetThreadAddr($hThread) ; If $ptrABAPCall Then ; ;----------------------------------------------------------------------- $rc = $SAP.RfcInstallServerFunction("", $hDesc, $ptrABAPCall) If $rc = $RFC_OK Then $ConParPtr = Number(DllStructGetPtr($ptrConPar)) $hCon = $SAP.RfcRegisterServer($ConParPtr, 3) If $hCon Then While $rc = $RFC_OK Or $rc = $RFC_RETRY $rc = $SAP.RfcListenAndDispatch($hCon, 1) Select Case $rc = $RFC_OK OutputDebugString("RFC_OK") Case $rc = $RFC_RETRY OutputDebugString("RFC_RETRY") EndSelect Sleep(256) Wend EndIf EndIf $SAP.RfcDestroyFunctionDesc($hDesc) DllCallbackFree($ABAPCall) ; DllCall("kernel32.dll", "boolean", "CloseHandle", _ ; "handle", $hThread) ; EndIf ; EndIf EndIf $SAP = 0 EndIf ;-End-------------------------------------------------------------------