Platform Invoke Services

  • October 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Platform Invoke Services as PDF for free.

More details

  • Words: 5,034
  • Pages: 17
Table of Contents 1. Platform Invoke Services............................................................................. .........2 A closure look of Invoke....................................................................................... .2 2. How to Use PINVOKE................................................................................... .........3 Example Program Prototype.............................................................................. ...3 Example Invoking Program................................................................................ ...5 3. Data Marshaling............................................................................................. ........7 4. DLLImportAttribute Class............................................................................ .........7 5. Marshaling COBOL Data............................................................................ ...........7 Marshaling .NET Data Types from Managed Code to Managed Code..............8 Marshaling Managed Code to Unmanaged Code...............................................8 6. Encoding of COBOL-Specific Data Types...........................................................9 Explanation of the code:.....................................................................................10 7. Suppressing Encoding Conversion of COBOL-Specific Data Types.............10 Explanation of the code:.....................................................................................11 8. Exceptions for COBOL-Specific Data Types.....................................................11 9. Example of Managed COBOL Calling Unmanaged COBOL.............................11 The Unmanaged Code.........................................................................................11 The Program Prototype.......................................................................................12 The Managed Code............................................................................... ...............13 10. Calling NetCOBOL for Windows Programs.....................................................14 Outline.................................................................................................................. .14 Program Prototypes for JMPCINT2/JMPCINT3.................................................15 11. Execution Environments and Environment Variables...................................16 12. Calling NetCOBOL for Windows Programs from Web Applications.............17

1

1. Platform Invoke Services When you work within the .NET environment, the system can pick up information about any code that you invoke and the data types used, and can manage the relationship between your code and the invoked code. However, when you invoke code outside the .NET environment (unmanaged code) the .NET system no longer has access to the code and data information. .NET provides platform invocation services (PINVOKE) to allow the calling of non-.NET programs and API’s from CLR assemblies. To use PINVOKE you create a program prototype (a program with no procedure code) which defines all the information to the .NET system that it needs to invoke the unmanaged code, such as the name of the .DLL file containing the code and the types of data used in the parameters. You then call the program prototype and the platform invoke services translates that into a call to the unmanaged code. A closure look of Invoke

Platform invoke relies on metadata to locate exported functions and marshal their arguments at run time. The following illustration shows this process. A platform invoke call to an unmanaged DLL function

When platform invoke calls an unmanaged function, it performs the following sequence of actions: 

Locates the DLL containing the function.

2

Loads the DLL into memory. Locates the address of the function in memory and pushes its arguments onto the stack, marshaling data as required. Note Locating and loading the DLL, and locating the address of the function in memory occur only on the first call to the function.  Transfers control to the unmanaged function. Platform invoke throws exceptions generated by the unmanaged function to the managed caller.  

2. How to Use PINVOKE To use PINVOKE you first define a program prototype with a custom attribute of the DLLImportAttribute class (you can use the Program Prototype Definition Wizard to help you do this). Then, when you want to call the function defined in this program prototype you call the program prototype rather than the function. This is best explained by showing you an example. Example Program Prototype

Below is a sample program prototype that defines the interface to the native Windows API function MessageBox which is contained in USER32.DLL. MessageBox creates a dialog box and displays the text message provide to it as a parameter. See the notes below for explanations of the code.

PROGRAM-ID. MESSAGEBOX AS "MessageBox" IS PROTOTYPE CUSTOM-ATTRIBUTE IS PINVOKE. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES.

(1)

CUSTOM-ATTRIBUTE PINVOKE CLASS DLLIMPORT USING "USER32.DLL" PROPERTY P-CALLINGCONVENTION IS STDCALL OF E-CALLINGCONVENTION PROPERTY CHARSET IS ANSI OF E-CHARSET. REPOSITORY. CLASS DLLIMPORT AS "System.Runtime.InteropServices.DllImportAttribute" ENUM E-CALLINGCONVENTION AS "System.Runtime.InteropServices.CallingConvention" PROPERTY P-CALLINGCONVENTION AS "CallingConvention" PROPERTY STDCALL AS "StdCall" PROPERTY CHARSET AS "CharSet" ENUM E-CHARSET AS "System.Runtime.InteropServices.CharSet" PROPERTY ANSI AS "Ansi" CLASS SYS-STRING AS "System.String". DATA DIVISION. LINKAGE SECTION. 01 HWND USAGE BINARY-LONG. 01 MESSAGE-TEXT OBJECT REFERENCE SYS-STRING. 01 CAPTION-TEXT OBJECT REFERENCE SYS-STRING. 01 MSGBOX-TYPE USAGE BINARY-LONG. PROCEDURE DIVISION USING BY VALUE HWND MESSAGE-TEXT

(2) (3) (4) (5) (6) (7) (8) (9) (9) (7) (9) (10)

(11) (11) (11) (11)

3

CAPTION-TEXT MSGBOX-TYPE. END PROGRAM MessageBox.

(12)

Explanation of the code:

1. The PROGRAM-ID paragraph contains important elements: The program name MessageBox. This will be used in two ways: first it is the name that will be called when the managed code wants to call the unmanaged code; second it also defines the entry point within the unmanaged DLL. It defines the entry point because the EntryPoint property is not defined for the DllImportAttribute custom attribute. Using this default behavior (of the program name as the entry point name) is convenient as your calling code will look very similar to the code that would call the unmanaged function directly. If you wanted to specify the program's entry point by using the EntryPoint field of the DllImportAttribute, you would code the following phrase in the CUSTOM-ATTRIBUTE clause in the SPECIAL-NAMES paragraph: PROPERTY EntryPoint IS "<entry point name>" The IS PROTOTYPE phrase. This phrase indicates that this is a program prototype that will define details for an unmanaged subprogram. The CUSTOM-ATTRIBUTE phrase. This gives the name of the custom-attribute defined in the CUSTOM-ATTRIBUTE clause of the SPECIAL-NAMES paragraph. It has been given the name "PINVOKE" because the custom attribute provides the information required by PINVOKE but it doesn't have to have that name. 2. The CUSTOM-ATTRIBUTE clause of the SPECIAL-NAMES paragraph. This starts the definition of the values we are going to specify for the DllImportAttribute fields. The name, PINVOKE, needs to match the name used in the PROGRAM-ID paragraph. 3. The CLASS phrase defines the class of the custom-attribute, in this case the DllImportAttribute class. The name "DLLIMPORT" is an internal name for the class, which is mapped to the external name in the REPOSITORY paragraph. The USING phrase defines the parameter required by the attribute class constructor. For the DllImportAttribute class this is the name of the DLL file containing the target program. (See DllImportAttribute Constructor .) 4. The PROPERTY P-CallingConvention phrase shows the definition of one of the fields of the DllImportAttribute class - the CallingConvention field. This particular field is an enumeration. Its value is set by giving the name of the desired element (StdCall) within the enumeration. For this property we illustrate the use of an internal name (PCallingConvention) that is different from the external name (CallingConvention). As a consequence notice that the PROPERTY specifier in the REPOSITORY paragraph has to give the external name in its AS phrase.

4

5. The PROPERTY CharSet phrase shows a similar definition of another of the DllImportAttribute fields - CharSet. For this property we have chosen to use an internal name that is the same as the external name (CharSet). Consequently, the PROPERTY specifier in the REPOSITORY paragraph does not have to have an AS phrase. Whether you use an internal name that is the same as or different from the external name is a matter of personal preference. Note that neither of these properties need to be specified as we are setting them to the default values for these fields. 6. The CLASS DLLIMPORT specifier in the REPOSITORY paragraph lets the compiler know that "DLLIMPORT" is the name of a class, and provides the external name of the class. The external name is given near the top of the description of the DllImportAttribute class in the .NET Framework Class Library documentation. 7. The ENUM specifiers in the REPOSITORY paragraph give the external names of the internal names of the two enumerations used to assign values to the DllImportAttribute fields. The external names are created by combining the enumeration name with the namespace in which they are defined. You find the namespace towards the bottom of the topic that defines the enumeration - for example see CallingConvention Enumeration. 8. The P-CallingConvention PROPERTY specifier tells the compiler that this is a PROPERTY and gives the external name for the property. 9. The three PROPERTY specifiers for StdCall, CharSet and Ansi simply declare these names as properties. Because we have used the external names as the internal names there is no need to add AS phrases. 10. We use the system String class for the string parameters to the function so need to declare it in the REPOSITORY paragraph. 11. All the parameters have .NET equivalent types, so we use those types in declaring the parameters. By doing this we can leave the .NET system to marshal the data. See the Marshaling COBOL Data topic for details of what to do when this is not the case. 12. Finally the PROCEDURE DIVISION header declares the parameter order and the manner in which the parameters should be passed - in this case BY VALUE. Example Invoking Program

Below is a simple .NET COBOL program that makes use of the above program. CLASS-ID. HELLO. ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY. CLASS SYS-STRING AS "System.String" PROGRAM MESSAGEBOX AS "MessageBox". STATIC. PROCEDURE DIVISION. METHOD-ID. MAIN. DATA DIVISION. WORKING-STORAGE SECTION. 01 HANDLE USAGE BINARY-LONG.

(1)

}

5

01 MESSAGE-TEXT OBJECT REFERENCE SYS-STRING. 01 CAPTION-TEXT OBJECT REFERENCE SYS-STRING. 01 MSGBOX-TYPE USAGE BINARY-LONG. PROCEDURE DIVISION. MOVE 0 TO HANDLE SET MESSAGE-TEXT TO "Hello world!" SET CAPTION-TEXT TO "Calling Unmanaged Code" MOVE 0 TO MSGBOX-TYPE CALL MESSAGEBOX USING HANDLE MESSAGE-TEXT CAPTION-TEXT MSGBOX-TYPE . END METHOD MAIN. END STATIC. END CLASS HELLO.

} } }

(2)

(3)

Explanation of the code: 1. When using a program prototype you need to declare it in the REPOSITORY paragraph using a PROGRAM specifier. As we are using the program prototype's external name (MessageBox) as the internal name we don't need to have an AS phrase. 2. The parameters are defined using the .NET data types that correspond to the types used in the unmanaged code. 3. The CALL to the function is very similar to the CALL that we would have made were we coding this outside the .NET Framework. There we would probably have coded "MessageBox" as a literal, but because we are now calling through the program prototype, declared in the REPOSITORY paragraph, MessageBox is specified as a COBOL name.

Debugging Applications that Use PINVOKE Services 

When you debug applications that use PINVOKE you need to be aware that you cannot use the Visual Studio debugger and the NetCOBOL for Windows debugger at the same time. Attempting to do this will cause unpredicatable behavior.



Debugging applications with managed and unmanaged code requires that you either execute the managed code (i.e. without debugging) and debug the unmanaged code, or debug the managed code and execute (without debugging) the unmanaged code.



You can debug unmanaged code written with NetCOBOL for Windows by setting the @CBR_ATTACH_TOOL environment variable in the runtime environment information file for the NetCOBOL for Windows code. The steps for debugging the unmanaged code are:



In the COBOL Project Manager (NetCOBOL for Windows v7), make sure the dll is built for debugging. Project>Option>Build for Debugging should be checked. Create a COBOL85.cbr file, adding @CBR_ATTACH_TOOL=TEST . Rebuild the native dll project.

 

6



     

Copy the .cbr, .dll, and .svd files into the output directory for the managed application. The output directory for the managed application defaults to bin\debug for debug builds and bin\release for release builds and can be configured in the properties page for the project (select Project>Properties from the menu bar in Visual Studio, navigate to the Configuration Properties folder, select the Build item and enter your desired output path in the "Output Path" property). Open the managed application from Visual Studio. Select Debug>Start Without Debugging. When the native (unmanaged) code is invoked, it will break into the debugger. Switch to the "Source" tab on the "Start Debugging" dialog. Specify the path to the folder containing the unmanaged COBOL source files. Click OK, and you can start debugging your unmanaged code. To debug the managed code and not invoke the NetCOBOL for Windows debugger simply remove the @CBR_ATTACH_TOOL environment variable from the COBOL85.cbr file.

3. Data Marshaling One of the functions of PINVOKE is to marshal the data. This means that it ensures that data is passed to and from the unmanaged code in a valid format, doing any conversions required to take the data between their types within the .NET system and the types outside the .NET system. The CLR handles the marshaling of .NET data types. COBOL-specific data items are marshaled by the COBOLDataMarshaler. Some of the information you provide to PINVOKE in your program prototype concerns the marshaling of the data.

4. DLLImportAttribute Class The DLLImportAttribute class is used to define a custom attribute for the program prototype. The constructor and members of this class provide information required by PINVOKE. How to use the DLLImportAttribute class is probably best learnt by studying the examples below and in the Marshaling COBOL Data topic. The parameter for the DLLImportAttribute constructor is the name of the .DLL that contains the code to be invoked. In NetCOBOL for .NET this parameter is provided as a literal in the USING phrase of the CUSTOM-ATTRIBUTE clause. The members of the DLLImportAttribute class provide other information to PINVOKE such as the calling convention to be used and the character encoding to be used for marshaling string objects.

5. Marshaling COBOL Data Marshaling is the process of ensuring that data is sent over environment boundaries in an appropriate manner. The boundaries may be between programs written in different programming languages, or between managed and unmanaged code, or both.

7

Marshaling .NET Data Types from Managed Code to Managed Code In the .NET environment, because the .NET data types are common across all .NET languages, there is no need to be concerned with the requirement to marshal data as long as you use the proper .NET data types when crossing language boundaries. NetCOBOL for .NET provides a high level of automated marshaling. This process is transparent when calling managed code written in any other .NET language. The reason for this transparency is the fact that the executables created by all .NET compilers include metadata that contains detailed data descriptions of the data types and interfaces used by each executable. This means that the runtime has all of the information it needs to figure out how to organize and/or convert and pass data across language boundaries. Marshaling Managed Code to Unmanaged Code

A requirement exists in some applications, however, to step outside of the Common Language Runtime (CLR) environment to call non-.NET applications. In .NET terminology, .NET programs that run under the CLR are known as managed code , while non-.NET executables (e.g. many C++ programs and prior versions of COBOL applications), are known as unmanaged code. When calling from managed code to unmanaged code and passing/receiving parameters, these parameters must be marshaled to ensure they are in the proper format and that they flow properly. The runtime needs a mechanism to help it determine what type of data is expected to be passed to or returned by the unmanaged program(s) (a non-.NET Windows function, for example), and how that data should be passed. This is because unmanaged code does not contain within it definitions of the data types, their formats, and the interface(s) to be used. The marshaling for interacting with COM applications is handled differently from marshaling when using Platform Invoke Services (PINVOKE) as .NET is able to provide tools to communicate parameter data types between the .NET and COM systems. When using PINVOKE the need for communicating parameter data types to the .NET system is accomplished by using program prototype definitions. The concept of prototypes has been around for many years in other languages such as C++ (where it is often referred to as a function prototype). In COBOL, a program prototype is actually a special format of a standalone program that is typically a COBOL program containing only interface definition information. You must also make use of the Platform Invoke Services (Pinvoke) to call unmanaged code.

Marshaling with COBOL-Specific Data Types If you are passing COBOL-specific data types, that is non-.NET data types, then the marshaling is performed by the COBOLDataMarshaler. This handles most COBOL data

8

types apart from those listed below, and provides the options to select the encoding of the data and to suppress encoding conversion. The COBOLDataMarshaler cannot marshal the following data items:     

Object references of native COBOL classes Pointer items Returning items (returning .NET data items are OK) Items that contain redefined alphabetic, alphanumeric or national items Items that contain subitems that: contain alphabetic, alphanumeric or national items, and have an OCCURS DEPENDING ON clause

6. Encoding of COBOL-Specific Data Types COBOL specific data items are marshaled in ACP (ANSI Code Page) encoding by default. If you want to marshal a COBOL specific item in Unicode encoding, you must specify the Fujitsu.COBOL.InteropServices.Win32.RuntimeEncodingAttribute custom attribute. This uses the RuntimeEncodingMode enumeration. This enumeration can take one of two values: ACP With this setting, alphabetic, alphanumeric and national items are marshaled as current ACP (ANSI Code Page) encoding. Use it with native COBOL programs compiled with the RCS(ASCII) option. Unicode With this setting, alphabetic, alphanumeric and national items are marshaled as Unicode encoding. Use it with native COBOL programs compiled with the RCS(UCS2) option. Use of the custom attribute and enumeration are best explained by showing you an example. The interface details are provided in the program prototype (see Calling Unmanaged Code from COBOL Using Platform Invoke Services for a full explanation of the use of the program prototype):

PROGRAM-ID. NATIVE-METHOD AS "Native-Method" PROTOTYPE CUSTOM-ATTRIBUTE IS DLL-IMPORT RUNTIME-ENCODING. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. CUSTOM-ATTRIBUTE DLL-IMPORT CLASS DLL-IMPORT-ATTRIBUTE USING ... CUSTOM-ATTRIBUTE RUNTIME-ENCODING CLASS RUNTIME-ENCODING-ATTRIBUTE USING E-UNICODE OF RUNTIME-ENCODING-MODE. REPOSITORY. CLASS DLL-IMPORT-ATTRIBUTE AS "System.Runtime.InteropServices.DllImportAttribute" CLASS RUNTIME-ENCODING-ATTRIBUTE AS "Fujitsu.COBOL.InteropServices.Win32.RuntimeEncodingAttribute" PROPERTY E-UNICODE AS "Unicode" ENUM RUNTIME-ENCODING-MODE AS "Fujitsu.COBOL.InteropServices.Win32.RuntimeEncodingMode" ...

(1)

(2) (3) (4) (5) (6) (7) (8) (9)

9

... END PROGRAM NATIVE-METHOD.

Explanation of the code: 1. The PROGRAM-ID paragraph defines this code as a program prototype and declares two custom attributes: DLL-IMPORT required by the Platform Invoke Services, and RUNTIME-ENCODING required by the COBOLDataMarshaler if you want to specify unicode encoding. 2. The CUSTOM-ATTRIBUTE clause is required for the DLL-IMPORT custom attribute. 3. The RUNTIME-ENCODING custom attribute needs to be defined in the CUSTOMATTRIBUTE clause of the SPECIAL-NAMES paragraph. 4. The first detail of the RUNTIME-ENCODING custom attribute is its class. Typically you will use an internal name for the class and map it to the fully qualified class name in the REPOSITORY paragraph. See line 7. 5. The second, and only other, detail of the RUNTIME-ENCODING custom attribute is to set the parameter required by the attribute constructor. This is simple a value of the RuntimeEncodingMode enumeration. E-UNICODE and RUNTIME-ENCODING-MODE are internal names for the value and enumeration respectively. They are mapped to the external names in lines 8 and 9 in the REPOSITORY paragraph. 6. The class DLL-IMPORT-ATTRIBUTE is declared and mapped to its external name. 7. The class RUNTIME-ENCODING-ATTRIBUTE is declared and mapped to its external name. 8. The property E-UNICODE is declared and mapped to its external name. 9. The enumeration RUNTIME-ENCODING-MODE is declared and mapped to its external name.

7. Suppressing Encoding Conversion of COBOL-Specific Data Types When you are using COBOL specific data marshaling you also have the option to suppress the encoding conversion, for example if you have binary data in a PIC X(16) item. To do this you specify the SupressEncodingConverstionAttribute on the items for which you want to suppress encoding conversion. Note that this attribute can only be specified for 01 or 77 level items and it applies to the whole record. Again this is best explained by showing you how to use it in the program prototype: PROGRAM-ID. NATIVE-METHOD AS "Native-Method" PROTOTYPE CUSTOM-ATTRIBUTE IS DLL-IMPORT. ENVIRONMENT DIVISION.

10

CONFIGURATION SECTION. SPECIAL-NAMES. CUSTOM-ATTRIBUTE DLL-IMPORT CLASS DLL-IMPORT-ATTRIBUTE USING ... CUSTOM-ATTRIBUTE SUPPRESS-CONVERSION CLASS SUPPRESS-CONVERSION-ATTRIBUTE. REPOSITORY. CLASS DLL-IMPORT-ATTRIBUTE AS "System.Runtime.InteropServices.DllImportAttribute" CLASS SUPPRESS-CONVERSION-ATTRIBUTE AS "Fujitsu.COBOL.InteropServices.Win32.SuppressEncodingConversionAttribute". DATA DIVISION. LINKAGE SECTION. 01 PARAM1 CUSTOM-ATTRIBUTE IS SUPPRESS-CONVERSION. 02 ITEM1 PIC X(16). 02 ITEM2 PIC X(32). PROCEDURE DIVISION USING PARAM1. END PROGRAM NATIVE-METHOD.

(1)

(2)

(3)

Explanation of the code: 1. Suppression uses a custom attribute which is defined in a CUSTOM-ATTRIBUTE clause in the SPECIAL-NAMES paragraph. The attribute is simply defined by giving the class name. SUPPRESS-CONVERSION-ATTRIBUTE is an internal name that is mapped to the external name in the REPOSITORY paragraph at line 2. This attribute class takes no constructor parameters and has no field values that need to be set. 2. The SUPPRESS-CONVERSION-ATTRIBUTE class is declared in the REPOSITORY paragraph and mapped to the external name. 3. The custom attribute is then assigned to the parameter whose conversion should be suppressed by using the CUSTOM-ATTRIBUTE clause on the data item. Conversion is suppressed for all data items in the PARAM1 record.

8. Exceptions for COBOL-Specific Data Types COBOL-specific marshaling can generate the following exceptions:  

If there is a character which cannot be converted within the marshaled data, a Fujitsu.COBOL.InteropServices.Win32.InvalidCharException is thrown. If the converted data length is larger than the data item length, a Fujitsu.COBOL.InteropServices.Win32.extOverflowException is thrown

9. Example of Managed COBOL Calling Unmanaged COBOL In this section we provide all three parts of the application required when managed COBOL code calls unmanaged COBOL CODE: the unmanaged code, the program prototype used to define the interface to the unmanaged code, and the managed code. The Unmanaged Code Suppose the following COBOL program that has been compiled and linked into an unmanaged code .DLL file (using Fujitsu COBOL version 6.1, for example):

11

IDENTIFICATION DIVISION. PROGRAM-ID. UNMANAGEDCODE. ENVIRONMENT DIVISION. DATA DIVISION. WORKING-STORAGE SECTION. 01 SUB1 PIC 99 VALUE 0. 01 TEMP PIC 9(3). LINKAGE SECTION. 01 TEST-DATA. 03 TEST-STRINGS PIC X(25) OCCURS 10 TIMES. 03 TEST-COMP PIC S9(9) COMP. 03 TEST-COMP3 PIC S9(9) COMP-3. 03 TEST-COMP5 PIC S9(9) COMP-5. 03 TEST-STRING PIC X(25). PROCEDURE DIVISION USING TEST-DATA. DISPLAY "In Unmanaged Code" DISPLAY "Test-String is: ", TEST-STRING MOVE 1 TO TEMP PERFORM 10 TIMES DISPLAY TEST-STRINGS(TEMP) ADD 1 TO TEMP END-PERFORM DISPLAY "TEST-COMP is: ", TEST-COMP DISPLAY "TEST-COMP3 is: ", TEST-COMP3 DISPLAY "TEST-COMP5 is: ", TEST-COMP5 DISPLAY "Exiting Unmanaged Code" EXIT PROGRAM. END PROGRAM UNMANAGEDCODE.

The Program Prototype In order to call the above unmanaged code from a .NET managed code program, a program prototype has to be created that defines the interface and data types used in the LINKAGE SECTION. This prototype will also have to identify the specific .DLL to be called and specify the PInvoke service classes and properties to be referenced. An example of such a prototype is given below: PROGRAM-ID. UNMANAGEDCODE AS "UNMANAGEDCODE" IS PROTOTYPE CUSTOM-ATTRIBUTE IS PINVOKE. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. CUSTOM-ATTRIBUTE PINVOKE CLASS DLLIMPORT USING "UNMANAGEDCODE.DLL" PROPERTY CHARSET IS ANSI OF E-CHARSET. REPOSITORY. CLASS DLLIMPORT AS "System.Runtime.InteropServices.DllImportAttribute" PROPERTY CHARSET AS "CharSet" ENUM E-CHARSET AS "System.Runtime.InteropServices.CharSet" PROPERTY ANSI AS "Ansi" CLASS SYS-STRING AS "System.String". DATA DIVISION. LINKAGE SECTION. 01 TEST-DATA. 03 TEST-STRINGS PIC X(25) OCCURS 10 TIMES. 03 TEST-COMP PIC S9(9) COMP. 03 TEST-COMP3 PIC S9(9) COMP-3. 03 TEST-COMP5 PIC S9(9) COMP-5. 03 TEST-STRING PIC X(25). PROCEDURE DIVISION USING TEST-DATA. END PROGRAM UNMANAGEDCODE.

12

The above program prototype defines the interface and data types to be used by the unmanaged code program (“UNMANAGEDCODE”) listed above it and additionally specifies the use of the PInvoke services to call unmanaged code. Notice the following points: The Program ID of the program prototype matches the Program ID of the unmanaged code program it is associated with. The program prototype above declares the name of the actual .DLL to be called in the CUSTOM-ATTRIBUTE definition. It also declares PInvoke related classes and properties to be utilized. The Procedure Division is empty – all that is needed is the LINKAGE SECTION and PROCEDURE DIVISION USING.. definition to define the data types and interface expected. And note: The DLLIMPORT custom attribute property CharSet only affects the encoding of string objects and the unmanaged function name. It is included in the above example to demonstrate how custom attribute properties can be set. With the exception of knowing which classes and properties of PInvoke services you need to specify, a program prototype is typically a pretty simple and straightforward piece of code.

The Managed Code

Below is a .NET COBOL program that will call the unmanaged code using the program prototype shown above: PROGRAM-ID. INVOKEUNMANAGED. ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY. PROGRAM UNMANAGEDCODE AS "UNMANAGEDCODE". DATA DIVISION. WORKING-STORAGE SECTION. 01 TEST-DATA. 03 TEST-STRINGS PIC X(25) OCCURS 10 TIMES. 03 TEST-COMP PIC S9(9) COMP. 03 TEST-COMP3 PIC S9(9) COMP-3. 03 TEST-COMP5 PIC S9(9) COMP-5. 03 TEST-STRING PIC X(25). 01 TEMP-STRING. 05 TEMP-1 PIC X(21) VALUE "This is Test String: ". 05 TEMP-2 PIC 9(3) VALUE 0. PROCEDURE DIVISION. MOVE 1 TO TEMP-2 MOVE "FUJITSU COBOL TEST" TO TEST-STRING PERFORM 10 TIMES MOVE TEMP-STRING TO TEST-STRINGS(TEMP-2) ADD 1 TO TEMP-2 END-PERFORM MOVE +123 TO TEST-COMP MOVE +123 TO TEST-COMP3 MOVE -123 TO TEST-COMP5 CALL UNMANAGEDCODE USING TEST-DATA. END PROGRAM INVOKEUNMANAGED.

13

Note the definition of the unmanaged code program to be called in the REPOSITORY, which also matches the name of the program prototype it relates to. To use the program prototype with the above NetCOBOL for .NET program to call the unmanaged code program, you simply compile the program above and the program prototype above it and build them together into the same executable. Below is a sample make file (.mak file) you can use with the Nmake utility to build such an executable (note that program prototype listed above has been saved into a file named “InvokeUnmanagedPT.cob”): INVOKEUNMANAGED.exe: InvokeUnmanagedPT.cob INVOKEUNMANAGED.cob cobolc /main:INVOKEUNMANAGED /out:INVOKEUNMANAGED.exe /wc:NOALPHAL InvokeUnmanagedPT.cob INVOKEUNMANAGED.cob

10. Calling NetCOBOL for Windows Programs Outline

To call a NetCOBOL for Windows program you need to use a program prototype and the Platform Invoke (PINVOKE) services as described in the topic Calling Unmanaged Code from COBOL Using Platform Invoke Services. If you just invoke the NetCOBOL for Windows subprograms, the Windows NetCOBOL execution environment is started and terminated at the beginning and end of each call. If you are making many calls this is very inefficient, and it is not possible to share data between subprograms from one call to the next as illustrated in the diagram below.

14

However, if you use the JMPCINT2 and JMPCINT3 routines provided with NetCOBOL for Windows, you can determine when the Windows COBOL execution environment is started and terminated. The environment need only be started once before your calls are made, and terminated when you have finished making your calls. This is much more efficient, and allows data to be shared between subprograms between calls as illustrated in this diagram:

The JMPCINT2 and JMPCINT3 routines are described further in the "Calling Subprograms" chapter of the NetCOBOL for Windows User's Guide. Relevant topics are: "COBOL Inter-Language Environment" and "Calling COBOL Programs from C Programs". Program Prototypes for JMPCINT2/JMPCINT3 As explained in Calling Unmanaged Code from COBOL Using Platform Invoke Services you need to define program prototypes to call unmanaged code and the JMPCINT2 and JMPCINT3 routines are unmanaged code. The code below shows you what the program prototypes look like for interfacing to these routines and how you would add code to call them to your .NET application. Sample: Program prototypes for JMPCINT2/JMPCINT3 PROGRAM-ID. JMPCINT2 IS PROTOTYPE CUSTOM-ATTRIBUTE IS PINVOKE. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. CUSTOM-ATTRIBUTE PINVOKE CLASS DLLIMPORT USING "F3BIPRCT.dll" PROPERTY P-CALLINGCONVENTION IS CDECL OF E-CALLINGCONVENTION

15

PROPERTY CHARSET IS ANSI OF E-CHARSET. REPOSITORY. CLASS DLLIMPORT AS "System.Runtime.InteropServices.DllImportAttribute" ENUM E-CALLINGCONVENTION AS "System.Runtime.InteropServices.CallingConvention" PROPERTY P-CALLINGCONVENTION AS "CallingConvention" PROPERTY CDECL AS "Cdecl" PROPERTY CHARSET AS "CharSet" ENUM E-CHARSET AS "System.Runtime.InteropServices.CharSet" PROPERTY ANSI AS "Ansi" . DATA DIVISION. PROCEDURE DIVISION. END PROGRAM JMPCINT2. * PROGRAM-ID. JMPCINT3 IS PROTOTYPE CUSTOM-ATTRIBUTE IS PINVOKE. ENVIRONMENT DIVISION. CONFIGURATION SECTION. SPECIAL-NAMES. CUSTOM-ATTRIBUTE PINVOKE CLASS DLLIMPORT USING "F3BIPRCT.dll" PROPERTY P-CALLINGCONVENTION IS CDECL OF E-CALLINGCONVENTION PROPERTY CHARSET IS ANSI OF E-CHARSET. REPOSITORY. CLASS DLLIMPORT AS "System.Runtime.InteropServices.DllImportAttribute" ENUM E-CALLINGCONVENTION AS "System.Runtime.InteropServices.CallingConvention" PROPERTY P-CALLINGCONVENTION AS "CallingConvention" PROPERTY CDECL AS "Cdecl" PROPERTY CHARSET AS "CharSet" ENUM E-CHARSET AS "System.Runtime.InteropServices.CharSet" PROPERTY ANSI AS "Ansi" . DATA DIVISION. PROCEDURE DIVISION. END PROGRAM JMPCINT3. Sample: Calling JMPCINT2/JMPCINT3 from NetCOBOL for .NET code. : ENVIRONMENT DIVISION. CONFIGURATION SECTION. REPOSITORY. PROGRAM JMPCINT2 PROGRAM JMPCINT3 PROGRAM … *> The name of the called program is defined in here. : . PROCEDURE DIVISION. : CALL JMPCINT2. *> The call of the NetCOBOL for Windows program is described here. CALL JMPCINT3. : END PROGRAM MAIN.

Note You also need to define a program prototype for the NetCOBOL for Windows program to be called.

11. Execution Environments and Environment Variables The execution environments for NetCOBOL for .NET and NetCOBOL for Windows are totally independent, therefore, you need to making settings for both environments.

16

Both environments will likely require a runtime initialization file. It is unlikely that you will be able to share a runtime initialization file between the two environments as environment variables and settings can be different. You can use separate initialization files by using one of the following techniques: Placing the NetCOBOL for Windows and NetCOBOL for .NET applications in separate folders. Specifying the .NET initialization file name on the command line, letting the NetCOBOL for Windows runtime use the default COBOL85.CBR file. Calling the NetCOBOL for Windows subroutine JMPCINTC to specify the name of initialization file that the Windows execution environment should use, letting the NetCOBOL for .NET runtime use the default COBOL85.CBR file. (See "Specifying the Initialization File Name" towards the end of Chapter 5 of the "NetCOBOL for Windows User's Guide for details of this routine.) Note that it is not possible to access environment variables that have been set in the .NET environment from the Windows environment, so if your Windows code needs to access information in .NET environment variables, you will need to pass the information using some other means (for example in a call parameter, or by writing and reading data files).

12. Calling NetCOBOL for Windows Programs from Web Applications You need to be aware of the following when calling NetCOBOL for Windows programs from Web applications (such as WebForms and Web Services): Specify "THREAD(MULTI)" when compiling the program. Ensure that the applications are using, or are communicating using, the same code system (UNICODE/ACP). For example, the Windows application could work in UCS2 by compiling with the directive RCS(UCS2), or the .NET application could convert UCS2 characters before sending them to the Windows application using the UTF8-OF or ACPOF functions. It is not possible to find appropriate points to call both "JMPCINT2" and "JMPCINT3" so it is recommended that you do not use these routines for Web applications.

17

Related Documents

Platform Invoke Services
October 2019 4
Azure Services Platform
November 2019 7
Platform
December 2019 29
Platform
April 2020 18
Platform
October 2019 34
Platform
November 2019 15