Wednesday, June 5, 2013

Compiling the SAP NW RFC SDK C/C++ samples on windows XP with visual studio 2005


Hi Folks,
This is a heavy one. I was away for many months, so you deserve a special treat now.

Installing and recompiling the sap nw rfc sdk samples.

In on sentence: a nightmare. I was a c programmer in the past so i definitely have an understanding what it is about, also in the windows environment. And i interfaced several tool to my own c/c++ dll's in order to acces our custom hardware. And based on that experience i can say only one thing: this is the worst experience i had with such an interfacing tool/library.

First Step: understand what it is about. On sdn you find three articles and a nice pdf  "NW_RFC_SDK_Guide.pdf"
"Improve Communication Between Your C_C++ Applications and%20SAP Systems with SAP NetWeaver RFC SDK*"
Just google them and get an idea of the library and interface they expose.
Second step: get the NW RFC SDK. on service.sap.com there are several notes. After searching and searching and searching you get your way through to the file "NWRFC_15-20004566.SAR"

Note: i unzipped it with some difficulty because the version of sapcar i downloaded from service.sap.com did not work. I had to download an older one ( always from the sap service site) and voilà it worked. Now i put the pdf files and the rfc sdk in this file for your pleasure. Please check the sap site for updated versions.

Third step: i unzipped everything to d:\nwrfcsdk
It looks like this


Fourth step: check whether the binaries in the bin directory work.

In order to do so i went on to the sap system. I read the documentation about RFC sdk and about sap gateways. I soon realized that, not having access the the externa sap gateway configuration in our system landscape, i could have not done any serious test installing an RFC server on an application server. So i fell back on the frontend. I decided that from then on the aim of my life would have been to start a calculator ( calc.exe) on my pc running an abap report and without COM and OCX stuff in between.

Step 5: copy calc.exe from the windows folder to the sapgui folder: on my pc C:\Programs\SAP\FrontEnd\SAPgui
Step 6: copy rfcexec.exe from the bin directory to the sapgui directory. It is not clean copying the files around, but not knowing the environment the SAPGui sees when it runs the rfcexec program i prefered to do so.
Step 7: create an RFC in SM59 that point to the server program on my frontend.



Step 7: test that it works ( remember we not have yet recompiled anything !!)
Push the connection test button. The console application should start and appear in a nice black window. like this.

The test should return something like this


 Now that it works if i recompile rfcexec from its source and i just replace the program in the sapgui directory with my recompiled versione it should work.
Step 8: understand how to build that things. I read note 1056696. Following the indication of the readme.txt I actually would have expected some step by step instuctions not really scattered across the universe as they actually are. But at least companies pay B$ for that...
here is the saptechno link.
Being on windows 32bit here is what i have to do
  • Windows (x86, 32 bit)
                       cl -DBCDASM -nologo -Od -Ob1 -fp:strict -Gy -GF -EHs -Z7 -W3 -Wp64 -D_X86_ -DWIN32 -DSAPwithUNICODE -DUNICODE -D_UNICODE -MD -D_AFXDLL -FR -J -RTC1 -D_CRT_NON_CONFORMING_SWPRINTFS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DSAPonNT -c /EHc- /TP .cpp
                       link -nologo /NXCOMPAT -STACK:0x800000  ole32.lib rpcrt4.lib oleaut32.lib oledb.lib uuid.lib kernel32.lib advapi32.lib user32.lib gdi32.lib winspool.lib ws2_32.lib Iphlpapi.lib netapi32.lib comdlg32.lib shell32.lib dbghelp.lib version.lib mpr.lib secur32.lib -OPT:REF -LARGEADDRESSAWARE -subsystem:console -out:.exe .obj sapnwrfc.lib libsapucum.lib [and possibly sapdecfICUlib.lib] 
  • Windows
                       Microsoft Visual Studio 2005 SP1
                       When using Microsoft Visual Studio 2008, please note the following limitation: the functions from libsapucum.dll (sapuc.h) cannot be used with VS 2008. Instead of these functions use the standard Microsoft w-functions (wchar.h). (On Windows the datatype "SAP_UC" is equivalent to "wchar_t".) For example, instead of printfU() use wprintf(), instead of getcU() use _getws().

So i think: great. I'll need the old compiler versions then.

Step 9: retrieve an obsolete compiler version. I asked a friend of mine to help me. He found the DVD somehow. Some might say you can look it  up as a torrent, mount the iso with deamon tools and install it. But it is not me ;-)
So i installed the Visual studio 2005.

Step 10: install SP1. This step is fundamental. I've have spent two days figuring out why my exe did not work. Then i thought of the missing SP. I installed it and voilà i could recompile it and it worked. You can get SP1 on the microsoft site.

Step 11: create a project in visual studio.One could have created a little makefile to recompile it. I was lazy and requested the Nice devstudio gui to do the work for me.
I create a new project Win 32 console "from existing code"

 The i ask for an "external build system"






The next screen i leave empty. I'll fill it later
Now from the project properties i enter the commands to compile. (This is really brute force, don't do that at home. Create a makefile instead. :) )
In the properties in the nmake command line
i enter the following
cl -DBCDASM -nologo -Od -Ob1 -fp:strict -Gy -GF -EHs -Z7 -W3 -Wp64 -D_X86_ -DWIN32 -DSAPwithUNICODE -DUNICODE -D_UNICODE -MD -D_AFXDLL -FR -J -RTC1 -D_CRT_NON_CONFORMING_SWPRINTFS -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DSAPonNT -c /EHc- /TP rfcexec.cpp
   link -nologo /NXCOMPAT -STACK:0x800000  ole32.lib rpcrt4.lib oleaut32.lib oledb.lib uuid.lib kernel32.lib advapi32.lib user32.lib gdi32.lib winspool.lib ws2_32.lib Iphlpapi.lib netapi32.lib comdlg32.lib shell32.lib dbghelp.lib version.lib mpr.lib secur32.lib -OPT:REF -LARGEADDRESSAWARE -subsystem:console -out:rfcexec.exe rfcexec.obj ..\lib\sapnwrfc.lib ..\lib\libsapucum.lib










 Note that i coded the lib paths relative to the curretn path. I did not want to mess around with setting the paths correctly. i will take time later to make it via vcvars32 or similar. ../lib is fine for now.

It now looks like this



Step 12: I save and i compile all. (F7 or menu build-build all)
Step 13: I realize i have no include paths. The quick and dirty solution is to hard code the path like in the next picture. Again don't...


Now it compiles.
Step 13: copy the exe file and the manifest file into the sapgui directory


 Without the manifest it does not load the CRT (C run time) dll's so it is definitely needed.

Step 14:I run the RFC test and i see that it is loaded by the sap gui as before happened with the rfcexec.exe provided as a compiled binary. I need no pic. you trust me don't you? :)

Step 15: comment the autorization check in order to run anything not only forn SAPL... program. The standard has a restriction the allows only programs to call rfcexec if it comes from SAPLEDI7
I don't want that. so i by pass it in the coding as shown below.



 Step 16: create a report to call the RFC. Yes our aim is to call an rfc in sap and get an rfc server on our frontend programmed in C to be called. Here is the report.



 Step 17: run the report. I set the parameters as shown.
 

 Step 18: press Run and get the result: Yes!!! Yes finally SAP can run a calcolator!!!!



Step 19: The trace file lands in the "Mydocuments/SAP directory" I had to do a search all drives to get it.

***** Rfcexec trace file opened at Wed Jun 05 15:59:32 2013 My real own compiled version!!! NW RFC SDK Version: 720 Patch 15 ***** Current working directory: D:\xxxxxxx\My Documents\SAP Using default mode. Allowing connections only from Report * and System: * ---------- RfcExecServer created successfully Received call for: RFC_REMOTE_EXEC User: xxxxxxxx SysID: xxx Client: 100 Program: calc.exe Calling Report: ZTESTZ Number of arguments: 0 Process started successfully Communication Failure: connection closed without message (CM_NO_DATA_RECEIVED) RfcExecServer is shutting down ***** Rfcexec trace file closed at Wed Jun 05 15:59:32 2013
Conclusions: it is a real mess. Specially figuring out how to create the project, compile it. Leave alone that there is no debugging support. I read in some note that debugging information must be left out of the exec in order for it to run. No information is given on the initial handshake between the RFC library and the server EXE. So if the exe does not start at all and does not write anything in the trace file your are ON YOUR OWN.
I don't understand: given the fact that the note states several platforms and development systems as "supported", why do they not provide a makefile for all platforms...and detailed instructions.

Thursday, February 23, 2012

Buffer problems with HR_READ_INFOTYPE HR_INFOTYPE_OPERATION and the TBUFF


Hi Folks,
i had an enquiry today. the problem is a report that makes some 20000 HR_INFOTYPE_OPERATION function calls on different pernr/persons.
The run time grows exponentially  with the number of pernr processed.

The reason for this is the buffering scheme in the HR_INFOTYPE_OPERATION FUNCTION
Calling the HR_PSBUFFER_INITIALIZE before processing each person leads to a linear run time.
The reason is that with each person processed the PS buffer table grows by several lines.

In SAP HR there are several buffers with this tricky behaviour:
  1. TBUFF: the buffer of the PCLx (PCL1,PCL2...PCL4) tables. The import export macros use a form that buffers the data when reading and writing. It can be cleared with the RP-INIT-BUFFER macro (Table TRMAC)
  2. PS Buffer: already mentioned above int the HR_IT_OPERATION VERSION
  3. PS Buffer HR_READ_INFOTYPE the same buffer as above, (ususally the tables are common parts) can be cleared with HR_INITIALIZE_BUFFER
  4. PNP Buffer the so called PRELP Buffer. This one you cannot clear, and usually it does not create problems, because it is designed to work with huge amounts of data.
One important and interesting thing is that you must know what buffer you  are using (even if indirectly with standard function calls).
So in a PNP program you should not call functions that read the infotypes with the PS buffer or with another buffer. Because you would break the buffer, causing time wasting reads in the best case, and in the worst case reading fake information from the database leading to data corruption.

So in a PNP program you should always call the "read-infotype form in program SAPDBPNP" Possibly with the TRMAC Macro RP-READ-INFOTYPE
While in  masterdata (pa30 and the like) you should use the PS buffer functions and forms. just like     PERFORM READ_INFOTYPE(SAPFP50P) USING...

So watch your buffers, and ...have a happy run time!
Happy hacking!


-- The TRMAC RP-READ-INFOTYPE macro

RP-READ-INFOTYPE    001    ****************************************
RP-READ-INFOTYPE    002    * RP-READ-INFOTYPE                     *
RP-READ-INFOTYPE    003    ****************************************
RP-READ-INFOTYPE    004    * PARAMETERS:                          *
RP-READ-INFOTYPE    005    *   PERNR      REQUESTED PERSONALNUMBER*
RP-READ-INFOTYPE    006    *   INFTY      REQUESTED INFOTYPNUMBER *
RP-READ-INFOTYPE    007    *   INFTY-TABLE OUTPUT-TABLE LIKE PNNNN*
RP-READ-INFOTYPE    008    *   BEGDA      REQUESTED INTERVAL-BEGIN*
RP-READ-INFOTYPE    009    *   ENDDA      REQUESTED INTERVAL-END  *
RP-READ-INFOTYPE    010    ****************************************
RP-READ-INFOTYPE    011    *SET DEBUGGING INTERRUPT ON            *
RP-READ-INFOTYPE    012    PERFORM READ-INFOTYPE(SAPDBPNP)        *
RP-READ-INFOTYPE    013    TABLES &3                      *
RP-READ-INFOTYPE    014    USING  &1 '&2' &4 &5
RP-READ-INFOTYPE    015    *SET DEBUGGING INTERRUPT OFF           *


--- The RP-INIT-BUFFER macro coding from table TRMAC

RP-INIT-BUFFER    001    ****************************************
RP-INIT-BUFFER    002    * RP: initialisierung puffer-tabellen  *
RP-INIT-BUFFER    003    ****************************************
RP-INIT-BUFFER    004    CLEAR:   BUFFER_DIR
RP-INIT-BUFFER    005    REFRESH: BUFFER_DIR
RP-INIT-BUFFER    006    CLEAR:   TBUFF
RP-INIT-BUFFER    007    REFRESH: TBUFF
RP-INIT-BUFFER    008    CLEAR:   BEFORE_IMAGE_PCLX
RP-INIT-BUFFER    009    REFRESH: BEFORE_IMAGE_PCLX
RP-INIT-BUFFER    010    CLEAR:   DEL_PCLX_TAB
RP-INIT-BUFFER    011    REFRESH: DEL_PCLX_TAB
RP-INIT-BUFFER    012    CLEAR: PAYR_BUFFER
RP-INIT-BUFFER    013    REFRESH: PAYR_BUFFER

Wednesday, February 15, 2012

Off topic: how time evaluation (SAP RPTIME00) sets the retrocalculation date for payroll

One insteresting question today: 
How does the TM RPTIME00 time evaluation engine sets the retrocalculation date RRDAT on the IT0003 to trigger a payroll recalculation?

the answer is simple, but as always the thingy is tricky
In form SET_PAYROLL_RECALCULATION. if the retro switch for TM is set the the evaluation period of TM is checked, and a routine checks whether for each recalculated day a change has occurred in the ZL table compared to the existing ZL of the previous calculation.
the oldes change date in ZL i writte to RRDAT of IT0003

the enabling of the retro switch "SW_FUNC-RLG" happens through the statement
CHECK      RPR 
in the Time evaluation schema


Here the routine.
*---------------------------------------------------------------------*
*       FORM SET_PAYROLL_RECALCULATION                              *
*---------------------------------------------------------------------*
*$*$ Bestimmt RR-Datum für L&G von Sicht des RPTIME00
*$*$ Lücke: Falls RR-Datum von L&G schon für die Vorperiode sitzt wird
*$*$     nicht die RPTIME-spezfische Rückrechnungserkennung durchlaufen.
*       Vorgehen:
*       1. Ist die aktuelle Periode von L&G schon abgerechnet?
*          Wenn nein: stop.
*       2. Liegt das RR-Datum-LG vor Beginn der aktuellen Periode?
*          Wenn ja: stop.
*       3. Bestimme den Tag, an dem sich Daten fuer L&G geaendert
*          haben. (FORM CHECK_RECALCULATION)
*$*$ Punkt 5 wird nun in EXPORT_PERSON überprüft, da die RR-Relevanz für
*$*$ L&G auch dann für das Setzen des Matchcode W gebraucht wird, wenn
*$*$ schon ein RR-Datum für L&G gesetzt ist.
**      5. Ist das RR-Datum-LG kleinergleich dem RR-Datum-ZE?
**         Wenn ja: stop.
*       6. Es muss eine RR fuer L&G gesetzt werden.
*---------------------------------------------------------------------*
FORM SET_PAYROLL_RECALCULATION.
  DATA: SET_RECALC_DATE TYPE D.

  CHECK SW_FUNC-RLG EQ '1'.

* Check ob L&G den Monat schon abgerechnet hat:
  CHECK P0003-ABRDT GE ACT_PERIOD-BEGDA.

* Check ob L&G schon eine RR sitzen hat, die vor Beginn der Per. ist:
  CHECK P0003-RRDAT IS INITIAL OR P0003-RRDAT GE PER-BEGDA.

* Ueberpruefen, ob RR zu setzen ist: This one compars the ZL with the original ZL
  PERFORM CHECK_RECALCULATION USING SET_RECALC_DATE.
* nur wenn Datum kleiner als P0003-rrdat
  CHECK SET_RECALC_DATE NE HIGH-DATE.

* This check is now done in EXPORT_PERSON
* check p0003-rrdat gt set_recalc_date or p0003-rrdat is initial.
                                       "XAYP30K136788

* -- RR ist von Sicht des RPTIME00 zu setzen:
* P0003-RRDAT = SET_RECALC_DATE.                             "YLLK63068
* MODIFY P0003 INDEX 1.                                      "YLLK63068
  IF SET_P0003-RRDAT IS INITIAL                             "YLLK63068
      OR SET_RECALC_DATE LT SET_P0003-RRDAT.                "YLLK63068
    SET_P0003-RRDAT = SET_RECALC_DATE.                      "YLLK63068
  ENDIF.                                                    "YLLK63068
ENDFORM.                               "OF SET_RECALCULATION.

Interestingly enough function check RLG has no coding but is handled in a very special way that puzzles me every time i have to look it up. So this time i write it down, so hopefully it will be the last time i have to look it up.

form RPTASI00_FILL_VARIABLES_FROM_F filles the "SW_FUNC" fields dynamically (with assigns)
the table FIELDS that containes information on what field to set is saved on the cluster with the generated TM schema:

  perform clear_variables_from_fields.                     "note757356
  perform get_schema(rpuscgt0)
                    using vschema '1'
                          print sw-schema_update
                          space space
                          returncode.
  read table as-source with key schem = vschema.            "PH4K008260
  if returncode = 0.                                        "NOTE202647
    perform fill_variables_from_fields.                     "PH4K008260
  endif.     

And guess what: the fields table is filled in the generation of the TM schema and written with the generated schema directly to cluster PS.
Here a screen shot.




HTH
happy hacking
cheers

Thursday, February 2, 2012

Mastering the SAP PAL (HR_PAL_): Icons in the PAL application Log

If you want an icon to show up in a PAL log it is sufficient to create a text column (for example in your DDIC structure or in your field catalog). I used a TEXT30 data element.
Then you write one of the codes you find below  (the ones with @xx@ to the text field.
The result is in the picture below




This is really trivial, but useful to know!
Have fun

----- From the type pool "icon"
 
 
ICON_2 ICON_DUMMY                     '@00@'."  PlaceholderIcon
ICON_2 ICON_CHECKED                   '@01@'."  Checked; OK
ICON_2 ICON_INCOMPLETE                '@02@'."  Incomplete
ICON_2 ICON_FAILURE                   '@03@'."  Failed
ICON_2 ICON_POSITIVE                  '@04@'."  Positive
ICON_2 ICON_NEGATIVE                  '@05@'."  Negative
ICON_2 ICON_LOCKED                    '@06@'."  Locked
ICON_2 ICON_UNLOCKED                  '@07@'."  Free; unlock
ICON_4 ICON_GREEN_LIGHT               '@08@'."  Green light; positive
ICON_4 ICON_YELLOW_LIGHT              '@09@'."  Yellow light; neutral
ICON_4 ICON_RED_LIGHT                 '@0A@'."  Red light; negative
ICON_2 ICON_TOTAL_LEFT                '@0B@'."  Extreme left; first ...
ICON_2 ICON_TOTAL_RIGHT               '@0C@'."  Extreme right; last ...
ICON_2 ICON_COLUMN_LEFT               '@0D@'."  Column left; previous ..
ICON_2 ICON_COLUMN_RIGHT              '@0E@'."  Column right; next ...
ICON_2 ICON_PAGE_RIGHT                '@0F@'."  Page right
ICON_2 ICON_PAGE_LEFT                 '@0G@'."  Page left
ICON_2 ICON_PREVIOUS_VALUE            '@0H@'."  Previous value; next ent
ICON_2 ICON_NEXT_VALUE                '@0I@'."  Next value; previous ent
ICON_2 ICON_ANNOTATION                '@0J@'."  Note; remark
ICON_2 ICON_CREATE_NOTE               '@0K@'."  Create note
ICON_2 ICON_DISPLAY_NOTE              '@0L@'."  Display note
ICON_2 ICON_CALCULATION               '@0M@'."  Costing
ICON_2 ICON_GRAPHICS                  '@0N@'."  Graphics
ICON_2 ICON_CREATE_TEXT               '@0O@'."  Create text
ICON_2 ICON_DISPLAY_TEXT              '@0P@'."  Display text
ICON_2 ICON_CHANGE_TEXT               '@0Q@'."  Text
ICON_2 ICON_VARIANTS                  '@0R@'."  Variants
ICON_2 ICON_INFORMATION               '@0S@'."  Information
ICON_2 ICON_ADDRESS                   '@0T@'."  Address
ICON_2 ICON_VIEWER_OPTICAL_ARCHIVE    '@0U@'."  Optical archive viewer
ICON_2 ICON_OKAY                      '@0V@'."  OK; Continue; Choose
ICON_2 ICON_CANCEL                    '@0W@'."  Cancel
ICON_2 ICON_PRINT                     '@0X@'."  Print
ICON_2 ICON_CREATE                    '@0Y@'."  Create
ICON_2 ICON_CHANGE                    '@0Z@'."  Change
ICON_2 ICON_DISPLAY                   '@10@'."  Display
ICON_2 ICON_DELETE                    '@11@'."  Delete
ICON_2 ICON_TEST                      '@12@'."  Test
ICON_2 ICON_SEARCH                    '@13@'."  Find
ICON_2 ICON_COPY_OBJECT               '@14@'."  Copy