Therefor I added a wrapper method in the COM library which delivers as return type a string. This method called run_str. It is very interesting to develop a component which can be used in different scenarios.
This approach uses the COM interface of the SAP GUI for Windows. Without the SAP GUI for Windows this approach can not be used. It is also not possible to use it with background processes.
Let us begin with the wrapper class for the COM library. It contains six public methods and the two most important are:
◈ add_Assembly = Adds references to other dotNET assemblies
◈ run_str = Executes VB.NET or C# code
The run_str method has six parameters
1. Language as string, allowed are CS for C# or VB for VB.NET.
2. Code as string.
3. Instance of the class as string.
4. Method to call as string.
5. Parameters as string, optional, default value empty string. Parameters are always strings, so it is necessary to convert it in the code in the right format.
6.. Separator for the parameters as string, optional, default value comma
The VB.NET or C# source code can be stored as include object inside the SAP system. With the method read_incl_as_string it can be loaded into a string variable.
"-Begin-----------------------------------------------------------------
CLASS z_cl_dotnetrunner DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
"! Loads the dotNETRunner library
"!
"! @parameter rv_result | 1 for success, otherwise 0
METHODS load_lib
RETURNING
VALUE(rv_result) TYPE i .
"! Frees the dotNETRunner library
METHODS free_lib .
"! Executes stored OLE activities
METHODS flush .
"! Adds an assembly
"!
"! @parameter iv_AssemblyName | Name of the Assembly
METHODS add_Assembly
IMPORTING
VALUE(iv_AssemblyName) TYPE string.
"! Executes C# or VB.NET code
"!
"! @parameter iv_Language | CS for CSharp or VB for VB.NET
"! @parameter iv_Code | Code
"! @parameter iv_Instance | Instance
"! @parameter iv_Method | Method
"! @parameter iv_Parameters | Parameters
"! @parameter iv_Separator | Separator of the parameters
"!
"! @parameter rv_result | Value as string
METHODS run_str
IMPORTING
VALUE(iv_Language) TYPE string
VALUE(iv_Code) TYPE string
VALUE(iv_Instance) TYPE string
VALUE(iv_Method) TYPE string
VALUE(iv_Parameters) TYPE string DEFAULT ''
VALUE(iv_Separator) TYPE string DEFAULT ','
RETURNING
VALUE(rv_result) TYPE string.
"! Reads an include as string
"!
"! @parameter iv_incl_name | Name of the include
"!
"! @parameter rv_str_incl | Include as string
METHODS read_incl_as_string
IMPORTING
VALUE(iv_incl_name) TYPE sobj_name
RETURNING
VALUE(rv_str_incl) TYPE string .
PROTECTED SECTION.
PRIVATE SECTION.
METHODS isactivex
EXPORTING ev_result TYPE i.
DATA olib TYPE ole2_object.
ENDCLASS.
CLASS z_cl_dotnetrunner IMPLEMENTATION.
METHOD load_lib."-----------------------------------------------------
DATA rc TYPE i VALUE 0.
rv_result = 0.
CALL METHOD me->isactivex IMPORTING ev_result = rc.
CHECK rc = 1.
CREATE OBJECT olib 'dotNET.Runner'.
CHECK sy-subrc = 0 AND olib-handle <> 0 AND olib-type = 'OLE2'.
rv_result = 1.
ENDMETHOD.
METHOD isactivex."----------------------------------------------------
DATA hasactivex(32) TYPE c.
ev_result = 0.
CALL FUNCTION 'GUI_HAS_OBJECTS'
EXPORTING
object_model = 'ACTX'
IMPORTING
return = hasactivex
EXCEPTIONS
invalid_object_model = 1
OTHERS = 2.
CHECK sy-subrc = 0 AND hasactivex = 'X'.
ev_result = 1.
ENDMETHOD.
METHOD free_lib."-----------------------------------------------------
FREE OBJECT olib.
ENDMETHOD.
METHOD flush."--------------------------------------------------------
CALL METHOD cl_gui_cfw=>flush.
ENDMETHOD.
METHOD add_assembly."-------------------------------------------------
SET PROPERTY OF olib 'Assembly' = iv_assemblyname.
ENDMETHOD.
METHOD run_str."------------------------------------------------------
CALL METHOD OF olib 'run_str' = rv_result
EXPORTING
#1 = iv_language
#2 = iv_code
#3 = iv_instance
#4 = iv_method
#5 = iv_parameters
#6 = iv_separator.
ENDMETHOD.
METHOD read_incl_as_string."------------------------------------------
DATA:
lt_trdir TYPE trdir,
lt_incl TYPE TABLE OF string,
lv_inclline TYPE string,
lv_len_line TYPE i,
lv_retincl TYPE string.
SELECT SINGLE * FROM trdir INTO lt_trdir
WHERE name = iv_incl_name AND subc = 'I' AND appl = space.
CHECK sy-subrc = 0.
READ REPORT iv_incl_name INTO lt_incl.
CHECK sy-subrc = 0.
LOOP AT lt_incl INTO lv_inclline.
IF strlen( lv_inclline ) > 0.
IF lv_inclline+0(1) = '*'.
lv_len_line = strlen( lv_inclline ) - 1.
lv_inclline = lv_inclline+1(lv_len_line).
ENDIF.
ENDIF.
lv_retincl = lv_retincl && lv_inclline &&
cl_abap_char_utilities=>cr_lf.
CLEAR lv_inclline.
ENDLOOP.
rv_str_incl = lv_retincl.
ENDMETHOD.
ENDCLASS.
"-End-------------------------------------------------------------------
Here now to include examples, the first with VB.NET and the second with C# code. The VB.NET code uses Win32 API functions and both uses Windows.Forms.
*Imports System.Windows.Forms
*Imports System.Runtime.InteropServices
*
*Namespace Foo
*
* Public Class Bar
*
* <DllImport("user32.dll", EntryPoint:="MessageBox", SetLastError:=True)> _
* Public Shared Function MBox(ByVal hWnd As Integer, ByVal txt As String, _
* ByVal caption As String, ByVal Typ As Integer) As Integer
* End Function
*
* Public Function SayHelloFunc() As String
* SayHelloFunc = "Hello World from VB.NET"
* End Function
*
* Public Function Say42Func() As Integer
* Say42Func = 42
* End Function
*
* Public Function Say166Func() As Double
* Say166Func = 166.0
* End Function
*
* Public Function Add(val1 As String, val2 As String) As Integer
* Add = CInt(val1) + CInt(val2)
* End Function
*
* Public Function Yepp(val1 As String, val2 As String) As String
* Yepp = val1 & val2
* End Function
*
* Public Sub Yell()
* MessageBox.Show("Hello World with native dotNET", "VB.NET")
* MBox(0, "Hello World with native Win32 call", "user32.dll", 0)
* End Sub
*
* End Class
*
*End Namespace
*using System.Windows.Forms;
*
*namespace Foo {
*
* public class Bar {
*
* public string SayHelloFunc() {
* return "Hello World from CSharp";
* }
*
* public int Say42Func() {
* return 42;
* }
*
* public double Say166Func() {
* return 166.0;
* }
*
* public int add(string v1, string v2) {
* int val1 = System.Convert.ToInt32(v1);
* int val2 = System.Convert.ToInt32(v2);
* int res = val1 + val2;
* return res;
* }
*
* public void yell() {
* MessageBox.Show("Hello World with native dotNET", "C#");
* }
*
* }
*
*}
Here now a report to use the class and the include objects. At first it is necessary to add the assemblies we need and to read the VB.NET from the include. Then each VB.NET method will be called step by step. Then follows the same procedure with the C# code.
"-Begin-----------------------------------------------------------------
REPORT z_dotnetrunner_test.
DATA:
lo_dotNETRunner TYPE REF TO Z_CL_DOTNETRUNNER,
lv_vbcode TYPE string,
lv_cscode TYPE string,
lv_result TYPE string
.
CREATE OBJECT lo_dotNETRunner.
CHECK lo_dotnetrunner->load_lib( ) = 1.
lo_dotnetrunner->add_assembly( iv_assemblyname = 'System.Windows.Forms.dll' ).
lo_dotnetrunner->add_assembly( iv_assemblyname = 'System.Runtime.InteropServices.dll' ).
"-VB.NET code---------------------------------------------------------
lv_vbcode = lo_dotnetrunner->read_incl_as_string('Z_DOTNET_VB_TEST').
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'SayHelloFunc'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Say42Func'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Say166Func'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Add'
iv_parameters = '20,22'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Yepp'
iv_parameters = 'Hello, Stefan'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Yell'
).
"-C# code-------------------------------------------------------------
lv_cscode = lo_dotnetrunner->read_incl_as_string('Z_DOTNET_CSHARP_TEST').
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'SayHelloFunc'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'Say42Func'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'Say166Func'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'add'
iv_parameters = '20,22'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'yell'
).
lo_dotnetrunner->free_lib( ).
"-End-------------------------------------------------------------------
And now the result of the report as a sequence of images. At first pops up the Windows.Forms dialog of the VB.NET call.
This approach uses the COM interface of the SAP GUI for Windows. Without the SAP GUI for Windows this approach can not be used. It is also not possible to use it with background processes.
Let us begin with the wrapper class for the COM library. It contains six public methods and the two most important are:
◈ add_Assembly = Adds references to other dotNET assemblies
◈ run_str = Executes VB.NET or C# code
The run_str method has six parameters
1. Language as string, allowed are CS for C# or VB for VB.NET.
2. Code as string.
3. Instance of the class as string.
4. Method to call as string.
5. Parameters as string, optional, default value empty string. Parameters are always strings, so it is necessary to convert it in the code in the right format.
6.. Separator for the parameters as string, optional, default value comma
The VB.NET or C# source code can be stored as include object inside the SAP system. With the method read_incl_as_string it can be loaded into a string variable.
"-Begin-----------------------------------------------------------------
CLASS z_cl_dotnetrunner DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
"! Loads the dotNETRunner library
"!
"! @parameter rv_result | 1 for success, otherwise 0
METHODS load_lib
RETURNING
VALUE(rv_result) TYPE i .
"! Frees the dotNETRunner library
METHODS free_lib .
"! Executes stored OLE activities
METHODS flush .
"! Adds an assembly
"!
"! @parameter iv_AssemblyName | Name of the Assembly
METHODS add_Assembly
IMPORTING
VALUE(iv_AssemblyName) TYPE string.
"! Executes C# or VB.NET code
"!
"! @parameter iv_Language | CS for CSharp or VB for VB.NET
"! @parameter iv_Code | Code
"! @parameter iv_Instance | Instance
"! @parameter iv_Method | Method
"! @parameter iv_Parameters | Parameters
"! @parameter iv_Separator | Separator of the parameters
"!
"! @parameter rv_result | Value as string
METHODS run_str
IMPORTING
VALUE(iv_Language) TYPE string
VALUE(iv_Code) TYPE string
VALUE(iv_Instance) TYPE string
VALUE(iv_Method) TYPE string
VALUE(iv_Parameters) TYPE string DEFAULT ''
VALUE(iv_Separator) TYPE string DEFAULT ','
RETURNING
VALUE(rv_result) TYPE string.
"! Reads an include as string
"!
"! @parameter iv_incl_name | Name of the include
"!
"! @parameter rv_str_incl | Include as string
METHODS read_incl_as_string
IMPORTING
VALUE(iv_incl_name) TYPE sobj_name
RETURNING
VALUE(rv_str_incl) TYPE string .
PROTECTED SECTION.
PRIVATE SECTION.
METHODS isactivex
EXPORTING ev_result TYPE i.
DATA olib TYPE ole2_object.
ENDCLASS.
CLASS z_cl_dotnetrunner IMPLEMENTATION.
METHOD load_lib."-----------------------------------------------------
DATA rc TYPE i VALUE 0.
rv_result = 0.
CALL METHOD me->isactivex IMPORTING ev_result = rc.
CHECK rc = 1.
CREATE OBJECT olib 'dotNET.Runner'.
CHECK sy-subrc = 0 AND olib-handle <> 0 AND olib-type = 'OLE2'.
rv_result = 1.
ENDMETHOD.
METHOD isactivex."----------------------------------------------------
DATA hasactivex(32) TYPE c.
ev_result = 0.
CALL FUNCTION 'GUI_HAS_OBJECTS'
EXPORTING
object_model = 'ACTX'
IMPORTING
return = hasactivex
EXCEPTIONS
invalid_object_model = 1
OTHERS = 2.
CHECK sy-subrc = 0 AND hasactivex = 'X'.
ev_result = 1.
ENDMETHOD.
METHOD free_lib."-----------------------------------------------------
FREE OBJECT olib.
ENDMETHOD.
METHOD flush."--------------------------------------------------------
CALL METHOD cl_gui_cfw=>flush.
ENDMETHOD.
METHOD add_assembly."-------------------------------------------------
SET PROPERTY OF olib 'Assembly' = iv_assemblyname.
ENDMETHOD.
METHOD run_str."------------------------------------------------------
CALL METHOD OF olib 'run_str' = rv_result
EXPORTING
#1 = iv_language
#2 = iv_code
#3 = iv_instance
#4 = iv_method
#5 = iv_parameters
#6 = iv_separator.
ENDMETHOD.
METHOD read_incl_as_string."------------------------------------------
DATA:
lt_trdir TYPE trdir,
lt_incl TYPE TABLE OF string,
lv_inclline TYPE string,
lv_len_line TYPE i,
lv_retincl TYPE string.
SELECT SINGLE * FROM trdir INTO lt_trdir
WHERE name = iv_incl_name AND subc = 'I' AND appl = space.
CHECK sy-subrc = 0.
READ REPORT iv_incl_name INTO lt_incl.
CHECK sy-subrc = 0.
LOOP AT lt_incl INTO lv_inclline.
IF strlen( lv_inclline ) > 0.
IF lv_inclline+0(1) = '*'.
lv_len_line = strlen( lv_inclline ) - 1.
lv_inclline = lv_inclline+1(lv_len_line).
ENDIF.
ENDIF.
lv_retincl = lv_retincl && lv_inclline &&
cl_abap_char_utilities=>cr_lf.
CLEAR lv_inclline.
ENDLOOP.
rv_str_incl = lv_retincl.
ENDMETHOD.
ENDCLASS.
"-End-------------------------------------------------------------------
Here now to include examples, the first with VB.NET and the second with C# code. The VB.NET code uses Win32 API functions and both uses Windows.Forms.
*Imports System.Windows.Forms
*Imports System.Runtime.InteropServices
*
*Namespace Foo
*
* Public Class Bar
*
* <DllImport("user32.dll", EntryPoint:="MessageBox", SetLastError:=True)> _
* Public Shared Function MBox(ByVal hWnd As Integer, ByVal txt As String, _
* ByVal caption As String, ByVal Typ As Integer) As Integer
* End Function
*
* Public Function SayHelloFunc() As String
* SayHelloFunc = "Hello World from VB.NET"
* End Function
*
* Public Function Say42Func() As Integer
* Say42Func = 42
* End Function
*
* Public Function Say166Func() As Double
* Say166Func = 166.0
* End Function
*
* Public Function Add(val1 As String, val2 As String) As Integer
* Add = CInt(val1) + CInt(val2)
* End Function
*
* Public Function Yepp(val1 As String, val2 As String) As String
* Yepp = val1 & val2
* End Function
*
* Public Sub Yell()
* MessageBox.Show("Hello World with native dotNET", "VB.NET")
* MBox(0, "Hello World with native Win32 call", "user32.dll", 0)
* End Sub
*
* End Class
*
*End Namespace
*using System.Windows.Forms;
*
*namespace Foo {
*
* public class Bar {
*
* public string SayHelloFunc() {
* return "Hello World from CSharp";
* }
*
* public int Say42Func() {
* return 42;
* }
*
* public double Say166Func() {
* return 166.0;
* }
*
* public int add(string v1, string v2) {
* int val1 = System.Convert.ToInt32(v1);
* int val2 = System.Convert.ToInt32(v2);
* int res = val1 + val2;
* return res;
* }
*
* public void yell() {
* MessageBox.Show("Hello World with native dotNET", "C#");
* }
*
* }
*
*}
Here now a report to use the class and the include objects. At first it is necessary to add the assemblies we need and to read the VB.NET from the include. Then each VB.NET method will be called step by step. Then follows the same procedure with the C# code.
"-Begin-----------------------------------------------------------------
REPORT z_dotnetrunner_test.
DATA:
lo_dotNETRunner TYPE REF TO Z_CL_DOTNETRUNNER,
lv_vbcode TYPE string,
lv_cscode TYPE string,
lv_result TYPE string
.
CREATE OBJECT lo_dotNETRunner.
CHECK lo_dotnetrunner->load_lib( ) = 1.
lo_dotnetrunner->add_assembly( iv_assemblyname = 'System.Windows.Forms.dll' ).
lo_dotnetrunner->add_assembly( iv_assemblyname = 'System.Runtime.InteropServices.dll' ).
"-VB.NET code---------------------------------------------------------
lv_vbcode = lo_dotnetrunner->read_incl_as_string('Z_DOTNET_VB_TEST').
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'SayHelloFunc'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Say42Func'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Say166Func'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Add'
iv_parameters = '20,22'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Yepp'
iv_parameters = 'Hello, Stefan'
).
WRITE: / `VB.NET - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'VB'
iv_code = lv_vbcode
iv_instance = 'Foo.Bar'
iv_method = 'Yell'
).
"-C# code-------------------------------------------------------------
lv_cscode = lo_dotnetrunner->read_incl_as_string('Z_DOTNET_CSHARP_TEST').
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'SayHelloFunc'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'Say42Func'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'Say166Func'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'add'
iv_parameters = '20,22'
).
WRITE: / `C# - Return: ` && lv_result.
lv_result = lo_dotnetrunner->run_str(
iv_language = 'CS'
iv_code = lv_cscode
iv_instance = 'Foo.Bar'
iv_method = 'yell'
).
lo_dotnetrunner->free_lib( ).
"-End-------------------------------------------------------------------
And now the result of the report as a sequence of images. At first pops up the Windows.Forms dialog of the VB.NET call.
Then it pops up the Win32 API dialog.
And then the Windows.Forms dialog of the C# code comes up.
Last but not least all the return values.
The same approach as for IRPA can also be applied to ABAP. Great, VB.NET and C# seamlessly in ABAP and also the possiblity to use Win32 API calls and also the possibility to use Windows.Forms.
No comments:
Post a Comment