Anda di halaman 1dari 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. (1)
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.

CUSTOM-ATTRIBUTE PINVOKE (2)


CLASS DLLIMPORT USING "USER32.DLL" (3)
PROPERTY P-CALLINGCONVENTION IS STDCALL OF E-CALLINGCONVENTION (4)
PROPERTY CHARSET IS ANSI OF E-CHARSET. (5)

REPOSITORY.
CLASS DLLIMPORT AS "System.Runtime.InteropServices.DllImportAttribute" (6)
ENUM E-CALLINGCONVENTION
AS "System.Runtime.InteropServices.CallingConvention" (7)
PROPERTY P-CALLINGCONVENTION AS "CallingConvention" (8)
PROPERTY STDCALL AS "StdCall" (9)
PROPERTY CHARSET AS "CharSet" (9)
ENUM E-CHARSET AS "System.Runtime.InteropServices.CharSet" (7)
PROPERTY ANSI AS "Ansi" (9)
CLASS SYS-STRING AS "System.String". (10)
DATA DIVISION.

LINKAGE SECTION.
01 HWND USAGE BINARY-LONG. (11)
01 MESSAGE-TEXT OBJECT REFERENCE SYS-STRING. (11)
01 CAPTION-TEXT OBJECT REFERENCE SYS-STRING. (11)
01 MSGBOX-TYPE USAGE BINARY-LONG. (11)
PROCEDURE DIVISION USING BY VALUE HWND MESSAGE-TEXT

3
CAPTION-TEXT MSGBOX-TYPE. (12)
END PROGRAM MessageBox.

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 (P-
CallingConvention) 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". (1)
STATIC.
PROCEDURE DIVISION.
METHOD-ID. MAIN.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 HANDLE USAGE BINARY-LONG. }

5
01 MESSAGE-TEXT OBJECT REFERENCE SYS-STRING. } (2)
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 (3)
CAPTION-TEXT MSGBOX-TYPE
.
END METHOD MAIN.
END STATIC.
END CLASS HELLO.

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. (1)
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SPECIAL-NAMES.
CUSTOM-ATTRIBUTE DLL-IMPORT (2)
CLASS DLL-IMPORT-ATTRIBUTE USING ...
CUSTOM-ATTRIBUTE RUNTIME-ENCODING (3)
CLASS RUNTIME-ENCODING-ATTRIBUTE (4)
USING E-UNICODE OF RUNTIME-ENCODING-MODE. (5)
REPOSITORY.
CLASS DLL-IMPORT-ATTRIBUTE (6)
AS "System.Runtime.InteropServices.DllImportAttribute"
CLASS RUNTIME-ENCODING-ATTRIBUTE (7)
AS "Fujitsu.COBOL.InteropServices.Win32.RuntimeEncodingAttribute"
PROPERTY E-UNICODE AS "Unicode" (8)
ENUM RUNTIME-ENCODING-MODE (9)
AS "Fujitsu.COBOL.InteropServices.Win32.RuntimeEncodingMode"
...

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 CUSTOM-


ATTRIBUTE 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 (1)
CLASS SUPPRESS-CONVERSION-ATTRIBUTE.
REPOSITORY.
CLASS DLL-IMPORT-ATTRIBUTE
AS "System.Runtime.InteropServices.DllImportAttribute"
CLASS SUPPRESS-CONVERSION-ATTRIBUTE (2)
AS "Fujitsu.COBOL.InteropServices.Win32.SuppressEncodingConversionAttribute".
DATA DIVISION.
LINKAGE SECTION.
01 PARAM1 CUSTOM-ATTRIBUTE IS SUPPRESS-CONVERSION. (3)
02 ITEM1 PIC X(16).
02 ITEM2 PIC X(32).
PROCEDURE DIVISION USING PARAM1.
END PROGRAM NATIVE-METHOD.

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 ACP-
OF 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

Anda mungkin juga menyukai