This document explains how to develop your own components. It also gives guidelines for developing client programs and deployment. In addition to this document, you should study the sample component as well as the sample VB program and the sample ASP and HTML files. The samples were written against the 31H version of the IDES demo system so that you can easily run them.
Last updated: 29 Sep 1999
Setting up your development environment.
On the Development machine (NT or Win9x), you will need the following:
The ADO and RDS documentation is installed via the NT Option Pack 3 using the custom install option. The ADO and RDS documentation will be installed in \program files\common files\system\ado\docs & \program files\common files\system\msadc\docs respectively.
On the client machines for client side scripting and or for VB clients, you will need IE4.0 or greater.
On the client machines for server side scripting, you will need a browser of any make.
On the production machine (the COM connector machine), you need the following software
BAPIs and/or function modules in R/3
There are two ways to view the function definitions within R/3. The first is to use the BOR (Transaction swo1) which allows you to navigates through applications areas down to the business objects and their associated methods (se37).

Or if you know the name of the function that implement the object method, or if you know a function module which exposes the functionality which you need, you can go directly via the function library transaction (se37).

The transaction se37 lists the function's parameters (grouped into imports and exports) when your select the function's Import/export parameters interface and double click the Display button.

It is important to be aware that table parameters are listed separately on the screen following the export and import parameters. All non-table parameters will be either simple ABAP datatypes or structures. In the above example the Export parameter 'RETURN' is a structure. The following information from se37 will be needed when constructing the ATL object methods:
Testing the BAPI function: In order to write either the VB or script program that will be used to test your COM implementation, you will need to know which parameter values are appropriate given the configuration and data of the target system and client as well as the authorization profile of your user account. Use the Sngl. Test functionality off of se37 to confirm these values.

Start a new project using VC with the ATL COM wizard.
Once the project is created:
Add the sapmtx.h header in the stdafx.h file:
| extern CComModule _Module; #include <atlcom.h> #include <sapmtx.h> // addition |
Add the following a the end of the stdafx.cpp file
| #define SAPMTX_GLOBAL #define SAPMTX_DEFINE_GUIDS #include <sapmtx.h> #define RFC_DEFINE_TYPE #define RFC_DEFINE_STRUCTURE |
Creating an ATL MTX Object with the object wizard
With the ATL object wizard (Insert -> New ATL Object....), create a new ATL object of type 'MS Transaction Server'. Under the MTX Tab, choose a dual interface. You do not need IObjectControl or Connection Points.
In the object .idl file, replace IDispatch with ISapConnector and import the sapconn.idl
The code generated by the ATL wizard looks like:
| import
"oaidl.idl"; import "ocidl.idl"; [ object, uuid(F135B0AE-367C-11D1-94D8-00609789DA9E), dual, helpstring("IRFCSampObj Interface"), pointer_default(unique) ] interface IRFCSampObj : IDispatch { |
Change it to:
| import "oaidl.idl"; import "ocidl.idl"; import "sapconn.idl"; //<-- add this line [ object, uuid(F135B0AE-367C-11D1-94D8-00609789DA9E), dual, helpstring("IRFCSampObj Interface"), pointer_default(unique) ] interface IRFCSampObj : ISAPConnector // NOT IDispatch { |
In the objects header, replace the ATL base classes and template with CSapConnector.
The code generated by the ATL wizard looks like:
| class
ATL_NO_VTABLE CRFCSampObj : public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CRFCSampObj, &CLSID_RFCSampObj>, public IDispatchImpl<IRFCSampObj, &IID_IRFCSampObj, &LIBID_RFCSAMPLib> { |
Change it to:
| class ATL_NO_VTABLE CRFCSampObj : public CSapConnector<CRFCSampObj, &CLSID_RFCSampObj, IRFCSampObj, &IID_IRFCSampObj, &LIBID_RFCSAMPLib> { |
In the ATL interface map do the following changes:
| BEGIN_COM_MAP(CRFCSampObj) COM_INTERFACE_ENTRY(IRFCSampObj) COM_INTERFACE_ENTRY2(IDispatch, IRFCSampObj) // change COM_INTERFACE_ALL_SAP_ENTRIES // addition END_COM_MAP() |
Change the code generated by the wizard to control the registration of the object:
| //DECLARE_REGISTRY_RESOURCEID(IDR_RFCSAMPOBJ)
// remove DECLARE_REGISTRY_SAP_RESOURCEID(IDR_RFCSAMPOBJ) // addition |
Add the registration map to the object cpp file. The BOR_CLASSNAME will be the name of the class as it appears in the R/3 Business Object Repository -- the name can also be a user defined name as in our example where we chose the name 'Foo'. The map should be inserted prior to any method definitions.
| BEGIN_SAP_REGISTRY_MAP(
CRFCSampObj ) SAP_REGISTRY_ENTRY( BOR_CLASSNAME, Foo ) END_SAP_REGISTRY_MAP() |
You will define a method for each BAPI function that you wish to expose on the object's interface. In declaring the method you will need to follow these conventions:
Input simple datatypes are defined as follows (the Variant Type is used in the method implementation)::
ABAP DataType |
Method DataType |
Variant Type |
| C (Character) | BSTR | VT_BTSR |
| I (Integer) | LONG | VT_I4 |
| F (Float) | DOUBLE | VT_R8 |
| D (Date) | DATE | VT_DATE |
| T (Time) | DATE | VT_DATE |
| P (BCD Packed, Currency, Decimal,Quantity) | DECIMAL CY |
VT_DECIMAL VT_CY |
| N (Numc) | LONG, DECIMAL or BSTR |
VT_I4 VT_DECIMAL VT_BTSR |
| X (Binary and Raw) | BSTR | VT_BTSR |
Example:
Consider the BAPI function, BAPI_SALESORDER_GETLIST. This function module has five import parameters, one export parameters and a table that exports data. The parameters, their ABAP data type and method data types are listed below (from se37):
Parameter Name |
Import/Export |
ABAP DataType |
Method Data Type |
| CUSTOMER_NUMBER | Import | C (char) | BTSR |
| SALES_ORGANIZATION | Import | C (char) | BSTR |
| MATERIAL | Import (optional) | C (char) | BSTR |
| DOCUMENT_DATE | Import (optional) | D (date) | DATE |
| DOCUMENT_DATE_TO | Import (optional) | D (date) | DATE |
| PURCHASE_NUMBER | Import (optional) | C (char) | BSTR |
| RETURN | Export | Structure | VARIANT * |
| SALES_ORDERS | Export | Table | VARIANT * |
A couple of notes:
To add a method to an interface go to the Class View in the VS
project and right click on the Interface node of your class.
Select Add Method. In the dialog box choose a
user friendly name for the method name (in our example we use
GetCustomerOrders). Then add the parameter declarations using
friendly parameter names. The following is the parameter
declarations for BAPI_SALESORDER_GETLIST:
[in] BSTR CustNum,
[in] BSTR SalesOrg,
[in,optional] VARIANT Material,
[in,optional] VARIANT DocDate,
[in,optional] VARIANT DocDateTo,
[in,optional] VARIANT PONum,
[in,out] VARIANT* pBapiReturn,
[in,out] VARIANT* pSalesOrders
***After the wizard has done its magic AND if
you change your mind about the method signature (how you declare
the parameters), you must update the IDL file, the object header
file and the object implementation file by hand.
Important Additional Note: For each input table type it is recommended that an additional method be declared that simply returns an empty table crafted as an ADO Recordset. This Recordset can then be easily used to add items. The alternative is to use the RDS Advanced Data factory to define a disconnected Recordset. An example of using this latter approach can be found in the VB sample client for crafting up the OrderPartners input table on the OrderCreate method invocation.
Tables, structures and special types
For each table and structure a header file must be generated using the genh utility provided with the RFCSDK. If tables or structures share the same Reference Structure, only one instance is required. These headers should be included in the project directory. The following syntax is used when calling genh:
| genh ASHOST=<R/3
application server> SYSNR=<R/3 application server system number> CLIENT=<R/3 client> USER=<R/3 user id> PASSWD=<R/3 user password> LANG=<one of E, D, F ...> <Reference Name of the Table or Structure (NOT the parameter name)> > <header file name>.h Example to create header for Sales_Orders (BAPI_SALESORDER_GETLIST): genh
ASHOST=myserver SYSNR=00 CLIENT=800 USER=Gilbert |
In the StdAfx.cpp file include the header files after the following line:
#define RFC_DEFINE_STRUCTURE
The header files also need to be included in the object cpp file. The includes should be preceded by the following line:
#define RFC_DEFINE_TYPE
Considerations for special ABAP scalar types
Certain ABAP data types, for which there exists no unique C++ data type, must be declared explicitly using CAbapScalarType. This type is used in the method implementation. The following data types are covered by this:
RFCTYPE_DATE
RFCTYPE_TIME
RFCTYPE_FLOAT
RFCTYPE_BCD
RFCTYPE_NUMFor ABAP date, time and float at the C level these are all represented by double; consequently, it is necessary to explicitly tell the template the RFCTYPE of the value. For RFCTYPE_BCD and RFCTYPE_NUM, it is necessary to give the field length (and in the case of BCD the precision)
The BCD type (Binary Coded Decimal) is often used for accouting values. In the SAP data dictionary, this types is sometime called DEC, CURR or QUAN. For BCD fields, the length in bytes of the field and the number to the right of the decimal point are required. The length in bytes is ("total number of digits" + 1) / 2. where "total number of digits" contains the digits on the right of the decimal point. An easy way to obtain the proper length and decimal values is to run the genh.exe tool against the structure or the table which is referenced by the parameter in se37.
Syntax:
CAbapScalarType <user defined type name> (RFCTYPE_xxx ...);
Examples:
CAbapScalarType DateType( RFCTYPE_DATE );
CAbapScalarType Numc5Type( RFCTYPE_NUM, 5 );
CAbapScalarType BCDType8_2( RFCTYPE_BCD,8, 2 );
We employ the following programming framework for the method implementation:
| Example using the
BAPI_GL_ACC_GETLIST function: try { CallFunction( L"BAPI_GL_ACC_GETLIST" ); Exporting( L"COMPANYCODE", CompanyCode ); Exporting( L"LANGUAGE", Language ); ImportingStructure( L"RETURN", &BAPIRETURN_Type, pBapiReturn); ImportingTable( L"ACCOUNT_LIST", &BAPI3006_1_Type, pAccountList); EndCallFunction(); // Enable pooling at the method invocation level for 3.x systems // This presumes that the RFC method is stateless SetComplete(); } catch( HRESULT hr ) { return hr; } return S_OK; |
Catching the HRESULT and returning it is the only error handling that needs to be done on the C++ side. See Handling Errors in Client Program for the client side handling of runtime error.
The class CSapConnector template class provides the following methods for the method implementation:
CallFunction(...)
EndCallFunction()
SetComplete();
Exporting(...)
Importing(...)
ExportingStructure(...)
ImportingStructure(...)
ExportingTable(...)
ImportingTable(...)
Data(...)
The syntax for each is described below:
CallFunction( )
|
EndCallFunction()
Function: executes the associated R/3 function
Syntax:
EndCallFunction()
Example:
EndCallFunction();
SetComplete()
|
Exporting(...)
|
Importing(...)
|
ExportingStructure(...)
|
ImportingStructure(...)
|
ExportingTable(...)
|
ImportingTable(...)
|
Data(...)
|
Compile and register the object. If you are going to test the object locally (i.e., with both the calling and called program on the same machine), then you do not need to install the DLL into a MTS package. This is the recommended approach when you are just starting out. Use VB as a test program. When you are confident that the object runs as expected and you have confirmed how to drive it with an automation client, then register it in MTS and use ASP server side scripting or HTML client side scripting.
Installing the sample object,TOP
Logon and destination handling
When a method invocation comes into the object, the object requests a connection from the RFC connections which are pooled by MTS. (The connections are pooled in MTS even if the object is not installed in MTS.)
The pooled connection obtains logon information from the registry and through a dedicated session object at run time. You may configure several destination entries in the registry. This destination contains the SAP logon information: system name, message server, system number, client and optionally user, password, language and nocleanup. If at the time of the method invocation no destination has been set, the default destination named NONE is used. If the used destination is missing language, user or password then the method call will fail.
You can configure the registry with your favorite registry editor such as regedit.
\HKEY_LOCAL_MACHINE
\SOFTWARE
\SAP
\MTS
\Destination: NONE="TYPE=3
ASHOST=fostcity SYSNR=00 CLIENT=800 USER=muster...
.... PASSWD=ides LANG=E nocleanup=1"
IDES="TYPE=3 ASHOST=fostcity SYSNR=00 CLIENT=800"
Alternatively you may use the DHTML pages default.htm which is included with the RFCSDK. This requires the installation of CCADMIN.dll (also included in the RFCSDK). For local administration, simply register the dll. For remote administration, install the dll into an MTS package and set the appropriate security and role.
In a production environment, you will generally not want to have a R/3 user and password in the registry. So that, in this case, the registry will contain the system name, system number, client and pooling option. The client program will pass the user name and password at runtime. This make it currently difficult to use RDS on HTTP for client side scripting.
A note on the pooling option: A destination value can be set to improve performance (pooling of RFC connections) against R/3 3.x systems. Setting nocleanup=1, assumes that the no state will be preserved between object instantiations in the R/3 system.
You should study the included sample program in the RFCSamp.VB directory which runs against the RFCSamp object provided in source form. If you have installed and registered RFCSamp on your local machine, there is no need to install it in MTS. If you are accessing RFCSamp remotely, it must be installed as a component in an MTS package and this package must be exported. A product of exporting the package is a clients subdirectory containing an exe. Execute this on your development machine and it will do all the required DCOM configuration to remotely access RFCSamp.
Project setting:
In order to get strong type checking and editor help, Reference the following objects:
Programming steps:
Create R/3 business object
Set destination and/or user, password, language
[optional]
Execute input table methods to retrieve empty input recordsets
[optional]
Set input parameter values [optional]
Call the object method
Use ADO to process any output recordsets [optional]
See Error Handling for information about how errors are handled by the client program.
Known issues with MTS 2.0
There is a bug in MTS that prevents inherited interfaces from being correctly handled when dual interfaces are used such as when you create the object via a Dim bo as [new] myBusinessObject. The interface for setting destination and logon information is an inherited interface. There are two possible workarounds. The first is to use IDispatch only such as to create the object using Dim bo as Object and Set bo = CreateObject("myBusinessObject.1"). This approach, however, eliminates the strong type checking and intelli-sense help. The other alternative is to Dim bo as myBusinessObject for regular use, and then Dim boRef As ISAPConnector for changing destination parameters. The following code illustrates the second approach.
See alsoDim bo As New MyBusinessObject
Dim boRef As ISAPConnector
Set boRef = bo
boRef.Destination = "test"
Set boRef = Nothing
Build and install the object, TOP
Create an ASP or an HTML test page
You should study the included sample asp pages in the RFCSamp.IE directory which runs against the RFCSamp object provided in source form.
Do not forget to register the object in an MTS package.
You may script in the browser, this is client side scripting. Use RDS to launch the object on the Internet Server machine. RDS will marshall input and output tables across the HTTP link. Using client side scripting, you may bound the Recordset used to pass tables parameters directly to an active control or to the the DHTML tables. If you use this approach, then on the internet server you must make sure that the business object has been marked safe for scripting and has been registered as launchable by RDS. The client must also have the RDS libraries installed -- these are installed with IE 4.0 or greater.
The following script will mark an object as safe for scripting (example uses an object with the CLSID {F135B0AF-367C-11D1-94D8-00609789DA9E}:
REGEDIT4
[HKEY_CLASSES_ROOT\CLSID\{F135B0AF-367C-11D1-94D8-00609789DA9E}\Implemented Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}]
[HKEY_CLASSES_ROOT\CLSID\{F135B0AF-367C-11D1-94D8-00609789DA9E}\Implemented Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}]
The following script will register an object as launchable by RDS (example uses the object with the ProgID RFCSampObj.RFCSampObj.1):
REGEDIT4
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\Parameters\ADCLaunch\RFCSampObj.RFCSampObj.1]
You may also script the server, this is server side scripting. You loose the neat data bound control features from client side scripts but your application is then less dependant of the browser capabilities.
Build and install the object,Installing the sample object, Set up the component in MTS, TOP
Handling errors in client program
If an RFC error occurs or if the function modules throws an
ABAP exception, the connector will set the last error for use
with ISupportErrorInfo.
In VB, you will want to check Err.Number. If Err.Number is
non zero the an RFC error or an ABAP exception has been throwed.
You will want to check Err.Description to get a string describing
the error. In order to be able to do this, you need to
state
On Error
Resume Next
before the first method invocation.
If a BAPI base method returns an error, you need (in addition to the prior check) to check the Type field of the BAPIRETURN structure returned by the BAPI method.
Setting up a package for the first time:
1) Start the Microsoft Management Console (or the Microsoft transaction Server Explorer).
2) Select Microsoft transaction Server
3) Select Computers -> My Computer -> Packages Installed
4) Right click -> New -> Package
5) Create an empty package (unless, of course, you have a pre-built package to install)
6) Select <your package> -> Components
7) Right click -> New -> Components
8) Install new component
Recommended next steps:
9) Select the package
10) Right click -> Export... this creates the following files:
- a *.pak file which can be used to re-install the package as a pre-buil component on this or another MTS server
- a *.exe client installation program which does the DCOM configuration
Registry entries for logon, TOP
The following sample code running against a 31H IDES R/3 System is provided:
To setup the sample, you must create an MTS package by installing the pre-built package. If this fails, just create an empty package and install the dll "...\rfcsdk\ccsamp\RFCSamp.Exp\RFCSamp.dll" in it. See Set up the component in MTS
To use the provided ASP pages, you must create a virtual root
named "RFCSample" in IIS and point it at
...\rfcsdk\ccsamp\RFCSamp.IE
Allow scripting and directory browser in this directory.
In order to use the sample demonstrating RDS for the client
side scripting, you must mark the object as ADC launch and
safe for scripting. For this, you just have to double click
the RFCSamp.reg file provided in ...\rfcsdk\ccsamp\RFCSamp.Exp.
Further more, you must configure a NONE destination which
contains system, users and password!(Logon and destination
handling).
©SAP AG 1997 - 1999