The Customer + .NET WinForm sample CustomerWinForm.sln takes the original Customer sample from Dialog System and replaces the Orders dialog with a more modern Windows Forms Orders form. The main Customer application remains largely unchanged, so that the application becomes a native code main program calling managed code as a COM object to handle the Windows Forms Orders form.
The following diagram shows the native code on the left, with Dialog System handling the screenset. The managed code is on the right, with the new Windows Forms form. The image also shows the native code interfacing with a COM Callable Wrapper (CCW) containing the managed code, and shows the data block being passed and returned.
The numbers in the above diagram highlight some key points in the process and are explained below:
A copy of the data block is created for the managed classes to use.
An entry point is created in the native code, ready for calling back from the managed code.
The solution has two projects: one native and one managed. The native project, Customer, contains the original sources with some changes to handle the Orders dialog differently and to interact with the managed code project.
The native project, Customer, contains:
To debug the native project, make sure the Customer project is set as the startup project. To do this, right-click the project and click Set as StartUp Project.
The managed project, OrderFormsLibrary, contains the new Windows Forms Orders form. It also contains the supporting code to pass the customer data block between managed and native code. It is registered and exposed as a COM object.
The managed project, OrderFormsLibrary, contains:
To debug the managed project, make sure the OrderFormsLibrary project is set as the startup project.
Customer.cbl, contains the original code for the business logic and for interaction with the Dialog System screenset. Customer.cbl also contains some additional code to interact with the managed COBOL project and to display the Windows Forms Order form. Customer.cbl contains additional code to do the following:
Set the following Compiler directives, using $SET:
$SET ans85 mfoo ooctrl(+P) case
Map the COM class, OrderFormsLibrary.FormsFactory, to an OO COBOL class name, OrderFormFact , in the CLASS-CONTROL paragraph, where the class is in the COM domain, $OLE$, of the Micro Focus native OO class library:
class-control. OrderFormFact is class"$OLE$OrderFormsLibrary.FormsFactory".
Evaluate the flag to determine if the Orders button has been pressed and respond accordingly:
WHEN customer-orders-flg-true PERFORM Show-Orders-Form
Create an instance of the COM object, the new .NET order forms library, which implements a Windows Forms version of the Orders dialog that originally existed in customer.gs:
invoke OrderFormFact "New" Returning formsLibrary
Set a procedure pointer that the managed code can use to call the native code:
set pptr to entry "Customer_Callback"
Get a pointer to the data block, to be used in the callback:
Customer-Callback SECTION. entry "Customer_Callback" stdcall. exit program returning address of CUSTOMER-DATA-BLOCK.
Invoke and display the new Orders form:
invoke formsLibrary "CreateOrderForm" using by value pptr-val
The screenset, customer.gs no longer displays the original Orders dialog, but instead it passes control back to the native COBOL program. The instructions for the Orders button in the Main-Window dialog have changed as follows:
SET-FLAG ORDERS-FLG(1) RETC * REFRESH-OBJECT DIALOG-BOX * SET-FOCUS DIALOG-BOX
The instructions now set the ORDERS-FLG to indicate that the Orders button has been pressed, so that the native COBOL program can evaluate the flag and respond appropriately. Control is returned to the native COBOL program. The redundant lines are commented out, so that the old Orders dialog is no longer displayed.
IFormsFactory.cbl defines an interface to create a Windows Forms Order form. IFormsFactory.cbl has code to do the following:
Declare the interface using the InteropServices classes from the .NET Framework. In addition, establish the COM object support for late binding, by specifying ComInterfaceType:: InterfaceIsDual. Alternatively, you could specify ComInterfaceType::InterfaceIsIDispatch (but not ComInterfaceType::InterfaceIsIUnknown):
interface-id OrderFormsLibrary.IFormsFactory attribute System.Runtime.InteropServices.InterfaceType (type System.Runtime.InteropServices.ComInterfaceType::InterfaceIsDual)
Note, the 'Register For COM Interop' property exposes everything as a dual interface, by default.
Define the CreateOrderForm() method. This is the definition of the entry point into managed code from native code:
method-id CreateOrderForm. procedure division using by value callback as binary-long. end method.
FormsFactory.cbl creates an instance of the Orders form, when the native program asks for it. FormsFactory.cbl has code to do the following:
Declare the class, which implements the IFormsFactory interface:
class-id OrderFormsLibrary.FormsFactory implements type OrderFormsLibrary.IFormsFactory.
Expose the class to COM, by making it visible:
attribute ComVisible(true)
Specify our interface IFormsFactory as the default interface to expose to COM:
attribute ComDefaultInterface (type of OrderFormsLibrary.IFormsFactory)
Get a delegate for the callback function and use it to get hold of the customer data block. The GetDelegateForFunctionPointer() converts an unmanaged function pointer to a delegate:
set pptr to new System.IntPtr(callback) set custCallback to type System.Runtime.InteropServices.Marshal::GetDelegateForFunctionPointer (pptr, type of CustomerCallback) as type CustomerCallback
Declare the delegate for the callback function:
delegate-id CustomerCallback. procedure division returning pCustomerDataBlock as type IntPtr. end delegate.
Create the Orders form and show it as a modal dialog box:
set form to new OrderFormsLibrary.OrderForm(custCallback) invoke form::ShowDialog()
The order form is drawn with the form designer. See the Windows Forms tutorials.
OrderForm.cbl [Code] is a partial class, also called OrderFormsLibrary.OrderForm and this contains the code to handle the Windows Forms Orders form.
OrderForm.cbl [Code] has the following methods:
Call back to native code to retrieve customer data block. We store the pointer in _pCustomerDataBlock.:
set _pCustomerDataBlock to callback::Invoke()
Unpack the data block to a copy in local storage, which can then be accessed in the same way as in native code. The Marshal::Copy() method copies the pointer _pCustomerDataBlock into pData:
invoke type Marshal::Copy (_pCustomerDataBlock, pData, 0, length of CUSTOMER-DATA-BLOCK) set CUSTOMER-DATA-BLOCK to pData
Initialize the Orders form using the orders information in the CUSTOMER-DATA-BLOCK, with a PERFORM block and statements such as:
perform varying row thru OrdersGridView::Rows move CUSTOMER-ORD-NO(array-ind) to row::Cells::get_Item("OrderNo")::Value move CUSTOMER-ORD-DATE(array-ind) to dt1 ...
Handle the form closing event and move the locally held Dialog System data block back to the caller. The Marshal::Copy() method copies the pointer pData into _pCustomerDataBlock:
set pData to CUSTOMER-DATA-BLOCK invoke type Marshal::Copy (pData, 0, _pCustomerDataBlock, LENGTH OF CUSTOMER-DATA-BLOCK)
Handle the OK button click event.
Handle the Delete button click event.