Tutorial: Creating Windows Forms in COBOL

Overview

This tutorial shows how to create a Windows form and how to make this interact with an existing COBOL program. The form is generated in managed COBOL. The form calls an intermediary program to map .NET data types onto COBOL data types. The intermediary program then calls the existing COBOL program to perform the business logic.

The solution has three programs:

  • Windows form. The Microsoft form designer enables you to create a Windows form in any .NET language, such as C# and COBOL. This tutorial shows how to generate these forms in .NET COBOL.
  • Existing COBOL program, the Book program. This program simulates a pre-existing COBOL program, which contains the business logic and which remains unchanged. This program is supplied and this tutorial adds it to the solution so that it is recompiled as managed code and exposed as a class that can be called by .NET managed code without needing any changes to the source.
  • Intermediary COBOL class, the BookWrapper program. This program acts as an interface between the client form and the pre-existing COBOL program. This intermediary class interfaces with the .NET client form using objects, and it interfaces with the existing COBOL using the standard COBOL PIC X data types. This intermediary class is supplied and this tutorial adds it to the solution.

Prerequisites

As a prerequisite step for this tutorial, you need to build the BookWrapper and LegacyBook projects that are part of the WinBook sample as follows:

  1. Start the Visual COBOL Samples Browser. If you need instructions, see To start the Samples Browser.
  2. Click Windows Forms in the left pane and then Win Book.
  3. Click Open sample in Visual Studio.

    This loads the WinBook solution in Solution Explorer. Notice that this solution consists of three projects - BookWrapper, LegacyBook and WinFormBook. WinFormBook is emboldened which indicates it is the startup project of the solution - that is the project that runs when you start the debugger.

  4. In Solution Explorer, right-click WinBook solution and choose Build Solution.

The build creates two binaries, BookWrapper.dll and LegacyBook.dll, in the WinFormBook\bin\Debug subfolder of the WinBook solution. You can close the solution.

Start Painting the Form

In this section, you create a Windows form and then you paint the form. You can also examine the code generated.

  1. Create a Windows Forms project for .NET Framework 4.5 or later:
    1. In Visual Studio, click File > New > Project.
    2. In the Create a new project dialog box, select COBOL from the Language drop-down list.
    3. Click Windows Forms Application (.NET Framework).
    4. Enter WinFormBook as the project name.
    5. In the Location field, click Browse and navigate to the Tutorials folder.
    6. In the Solution name field, type WinBook.
    7. In the Framework field, choose the appropriate .NET Framework version from the drop-down list.
      Note: Must be .NET Framework 4.5 or higher.
    8. Check Place solution and project in the same directory.
    9. Click Create.

    Visual Studio creates the project and automatically opens the form in the designer.

  2. Paint four labels, three text boxes and one button on the form using the properties in the table below.
    Note: To add controls to the form, click on Toolbox and expand All Windows Forms. You can drag and drop the required controls on to the form. Edit the properties of the controls, being careful not to double-click any of the controls. Double-clicking creates an event for the control, which you do not want to do till the next step. Edit the properties so they have the following values:
    Control Name Property Text Property
    Label label1 Stock Number
    TextBox textBoxStockNo (the case is significant) (blank)
    Label label2 Title
    TextBox textBoxTitle (the case is significant) (blank)
    Label label3 Price
    Label label4 (the case is significant) (blank)
    TextBox textBoxPrice (the case is significant) (blank)
    Button button1 (the case is significant) Read

    To display the properties, click the label and then view the Properties pane. You can then scroll to the relevant property and edit it.

  3. Create a click event for the Read button. Do this by double-clicking the button, which creates an event called button1_Click, and then displays the code that is generated for the form.
  4. Notice that the code for the button1_Click method does not do anything yet; the procedure division is empty. You can scroll through and review the rest of the generated code.
  5. You can build the form, by clicking Build > Build WinFormBook. You should have no errors.

For more information on the generated code, see the tutorial Tutorial: Developing .NET COBOL Applications.

Handle the Click Events

Now you need to add the code for the button click, which needs to call the legacy COBOL program and to populate the form with the information returned.

  1. In code view, find the button1_Click method. Update the code so that it reads as follows:
           method-id  button1_Click final private.
           local-storage section.
           01 input-string string.
           01 my-exception type System.Exception.
           procedure division using by value sender as object e as type System.EventArgs.
               set input-string to textBoxStockNo::Text
               try
                   set my-book to type BookWrapper.Book::Read(input-string)
                   invoke self::PopulateForm(my-book)
               catch my-exception
                   invoke self::DisplayException(my-exception)
               end-try
           end method.
    
  2. Notice the red squigglies underlining some words. These indicate a syntax error from automatic parsing happening in the background. Hover over an underlined word and read the popup that gives information on the error. Notice that the status bar tells you the number of background parsing errors.
  3. Hover over the words error-message and display the popup, which says that it cannot find the PopulateForm method. Add the following methods to your code, after the button1_Click method and before the end class statement.
           method-id  PopulateForm final private.
           procedure division using my-book as type BookWrapper.Book.
               if my-book <> null
                   set textBoxStockNo::Text      to my-book::StockNumber
                   set textBoxTitle::Text        to my-book::Title
                   set textBoxPrice::Text        to type System.Convert::ToString(my-book::RetailPrice)
               else
                   set textBoxStockNo::Text      to "****"
                   set textBoxTitle::Text        to "*************************************"
                   set textBoxPrice::Text        to "****"
               end-if
           end method.
           method-id DisplayException private.
           procedure division using by value ex as type System.Exception.
               set label4::Text to ex::Message
               set my-book to null
               invoke self::PopulateForm(my-book)
           end method.

The remaining errors are all associated with my-book. You can clear them when you have added the legacy code.

Add the Legacy Code and the Wrapper Code

You now need to add the existing COBOL code that contains the business logic, so that the form can use it. This code is supplied in a LegacyBook project containing book.cbl.

In addition, you need some wrapper code to convert the data from .NET types to COBOL types. The Windows form uses .NET data types; these are System.String objects in our case. The book program uses COBOL types such as PIC X and PIC 99V99. The supplied program BookWrapper.cbl does this conversion, and you need to add this to the solution.

The supplied files are in the %PUBLIC%\Documents\Micro Focus\Visual COBOL\Samples folder in the Forms subfolder.

To add book.cbl and BookWrapper.cbl:

  1. Copy the supplied LegacyBook and BookWrapper folders from the Forms folder into your tutorials directory.
  2. Add the BookWrapper project to the solution, as follows:
    1. Right-click the solution in the Solution Explorer.
    2. Click Add > Existing project.
    3. Browse to the BookWrapper folder, which is in the tutorials folder.
    4. Select the project file BookWrapper.cblproj and click Open.
  3. Now add the LegacyBook project in the same way.
  4. Add a reference to the BookWrapper project in the WinFormBook project. To do this:
    1. Right-click the WinFormBook project in the Solution Explorer.
    2. Click Add Reference.
    3. Expand Solution in the left pane, and then click Projects.
    4. Check the BookWrapper and the LegacyBook projects and click OK.

      The projects are now added to the References folder in your solution.

  5. Confirm that when the two supplied projects are built, their built .dll files are stored in the bin directory for the project WinFormBook. For the BookWrapper project and then the LegacyBook project:
    1. In Solution Explorer, double-click the Properties folder for each project.
    2. Click the COBOL tab in the properties.
    3. Confirm that the Output Path is ..\bin\Debug. You might need to adjust this relative path if your BookWrapper or LegacyBook directories are not siblings to the WinFormBook directory. Alternatively you can specify absolute paths.

Provide Access to the Legacy Code

You now need to add some code to the form to call the wrapper code and to clear the last of the red squiggles to do with my-book.

  1. Declare the data item my-book in working storage section of the main class of the form, Form1.cbl , as follows:
           01 my-book   type BookWrapper.Book.
  2. You should now have no parsing errors, now that BookWrapper is declared. If you do have errors, check the supplied demonstration in Forms\ WinBook to see where yours differs.

  3. Set up an environment variable to point to the bookfile.* data files, which are supplied in BookData. To do this:
    1. Right-click the project WinFormBook and click Add > New Item.

      This opens the Add New Item dialog box.

    2. Click COBOL Items > managed in the Installed pane.
    3. Select Application Configuration File.
    4. Click Add.

      This adds an App.config file to the WinFormBook project.

    5. In Solution Explorer, right-click App.config, and then click Edit.

      This opens the Application Settings dialog box.

    6. Enter the following details on the Environment tab:
      • In the Name field, type dd_bookfile, which is the prefix dd_ followed by the name of the data file as declared in the program book.cbl.
      • In the Value field, type the path and filename for bookfile.dat. For example, enter a hardcoded path such as %PUBLIC%\Documents\Micro Focus\Visual COBOL\Samples\BookData\bookfile.dat.

        See Start > All Programs > Micro Focus Visual COBOL > Samples for the exact location of the samples on your machine.

    7. Click Set.
    8. Click OK.
    9. Click Yes in the dialog that appears to reload the App.config file in your solution.

Run the Application

You can now run the application:

  1. Build the project. Set the form project as the starting project.

    To do this right-click the WinFormBook project in the Solution Explorer and then click Set as StartUp Project.

  2. Click Debug > Start Without Debugging.
  3. Experiment entering information and using the Read button.

    You can read data for stock numbers 1111 and 2222.

Examine the Sample Code

The sample code is available in the Samples folder, with all the Visual Studio and .NET examples, and in the Forms subfolder.

Client Windows Form

The client Windows form Form1.cbl is generated as COBOL. The user enters data into the form and receives the return data there. The client form does the following:

  1. Extracts the input data from text boxes on the form as System.Strings.
  2. Calls BookWrapper passing it the string objects.
  3. Populates the text boxes on the form with the System.Strings.

Book Wrapper Program

The BookWrapper.cbl program acts as an intermediary between the pre-existing COBOL program book.cbl and the Windows form. This enables you to leave the pre-existing COBOL unchanged.

The important point here is that you need to use compatible types when mixing languages. The Windows form stores the data as .NET types and yet the Book program expects data as COBOL types.

The purpose of the BookWrapper program is to map your COBOL PICTUREs to .NET System.Strings. The program receives data from the Windows form as System.Strings, and maps them onto standard COBOL data types before passing them to the pre-existing book program.

The working storage declares the data items in a book record by using a copybook, as follows:

working-storage section.
        copy "book-rec-dotnet.cpy" replacing == (prefix) == by == book ==.
    ...

The copybook book-rec-dotnet.cpy declares the book-details record. It declares book-title and book-stockno as COBOL pictures and also as properties so that Getter/Setter methods can be used to access them. The copybook contains:

    01 (prefix)-details.
        03 (prefix)-text-details.
            05 (prefix)-title  pic x(50) property as "Title".
        ...
        03 (prefix)-stockno pic x(4) property as "StockNumber".

The following get property method gets a pointer to the book-details record:

       property-id BookDetails pointer.
       getter. 
           set property-value to address of book-details
       end property.

The Read method is implemented as follows:

       method-id Read static. 
       local-storage section.
       01 file-status pic xx.
       procedure division using by value stockno-in as string
                          returning      myBook     as type BookWrapper.Book. 

           set myBook to new BookWrapper.Book()
           set myBook::StockNumber to stockno-in

           call "BookLegacy" using by value readRecord
                                   by value myBook::BookDetails
                                   by reference file-status       
            
           invoke self:RaiseExceptionIfError(file-status)
          
       end method.

Where:

  • procedure division using ...

    shows a .NET System.String, stockno-in, being passed in from client form. It also shows an instance of the Book class being returned. BookWrapper.cbl defines a new .NET type, Book, which can be used by programs written in any .NET language.

  • set myBook to new BookWrapper.Book() 

    creates a new instance of the BookWrapper class.

  • set mybook::StockNumber

    takes the data from the .NET System.String (stockno-in) and stores it as the StockNumber property of myBook. This property is declared in the copybook as a picture string, and so the data is stored as a standard COBOL data type in book-stockno. The COBOL Compiler implicitly converts the data from the .NET string into a COBOL usage display item (pic x).

  • call BookLegacy using ...

    calls the legacy program, book.cbl. It passes it the “BookDetails” property of myBook. If you look at the code in BookWrapper.cbl, you can see that what BookDetails does is pass a pointer to the BookRecord structure defined in book-rec-net.cpy. This structure matches the structure in the old book-rec.cpy, so what the legacy Book program sees is a book record being passed in by reference – which is what it expects. Book reads the stock number from this record, reads a record from the indexed file, and then puts the data in the other fields of the record.

  • invoke self::RaiseExceptionIfError(file-status)

    checks the file status returned from reading the file, and raises a .NET exception if there was an error. Exceptions are the standard .NET mechanism for signaling error conditions.

Legacy Book Program

The book.cbl program is a long-standing demonstration program that has been shipped with Micro Focus products for several years. It is written in procedural COBOL. The program reads and writes to an indexed file containing book records.

In this solution, the Book program is recompiled to managed code without any changes. Recompiling the program exposes it as a class and exposes its main entry point as a static method.

The program's Linkage section defines data as standard COBOL types, such as PIC X, which non-COBOL client programs do not handle. These types need to be mapped to .NET compatible types before communication with the client program. This mapping is done by the intermediary program BookWrapper.cbl.