サンプル:Dialog System ダイアログに代わる Windows Forms

Customer + .NET WinForm サンプルの CustomerWinForm.sln は、Dialog System の元の Customer サンプルを取得し、Orders ダイアログをより最新式の Windows Forms Orders フォームに置き換えます。メインの Customer アプリケーションはほとんど未変更のままであるため、アプリケーションはネイティブ コードのメイン プログラムになり、.NET コードを COM オブジェクトとして呼び出して Windows Forms Orders フォームを処理します。

次の図の左側には、ネイティブ コードと、スクリーンセットを処理する Dialog System があります。.NET COBOL コードは、新しい Windows Forms フォームとともに右側にあります。この図は、.NET COBOL コードを含んだ COM 呼び出し可能ラッパー (COM Callable Wrapper; CCW) のインターフェイスとなるネイティブ コード、および渡されて返されるデータ ブロックも示しています。

上図の数字は、プロセス内の重要なポイントを示しています。次では、これらの重要なポイントについて説明します。

  1. ネイティブ COBOL アプリケーションは、従来の方法で Dialog System を呼び出します。
  2. 新しい余分なダイアログを呼び出す代わりに、ダイアログは RETC を使用して制御をアプリケーションに返します。
  3. ネイティブ COBOL は、標準のネイティブ OO COBOL 構文を使用して IFormsFactory .NET COBOL コード インターフェイスを起動します。

    データ ブロックのコピーは、使用する .NET クラス用に作成されます。

    エントリ ポイントはネイティブ コードで作成され、.NET COBOL コードからコールバックできる状態になります。

  4. .NET COBOL コード ライブラリの OrderFormsLibrary は、COM 相互運用に登録されます。これにより、COM 呼び出し可能ラッパー (COM Callable Wrapper; CCW) が確実に作成されます。COM 呼び出し可能ラッパーは、使用するネイティブ コードのインターフェイスを提供します。
  5. .NET COBOL コード クラスの FormsFactory は、フォームをモーダル ダイアログとしてインスタンス化して表示し、必要に応じてイベントを処理します。
  6. .NET COBOL コードは、以前にネイティブ COBOL で作成されたエントリ ポイントにコールバックし、更新されたデータ ブロックへのポインターを渡します。

ネイティブ COBOL での Customer プロジェクト

ソリューションには、ネイティブ プロジェクトと .NET COBOL プロジェクトという 2 つのプロジェクトがあります。ネイティブ プロジェクトの Customer には、元のソースのほかに、別の方法で Orders ダイアログを処理するための変更、および .NET COBOL コード プロジェクトと対話するための変更が含まれています。

Customer ネイティブ プロジェクトの内容は次のとおりです。

  • プロパティ - これらのプロパティは、プロジェクトのビルド方法を定義します。たとえば、プロジェクトは Customer という Windows アプリケーションとしてビルドされ、.\bin\x86\Debug に格納されます。
  • Customer.cbl - 元のソース コードと、新しい Windows Forms Orders フォームを処理する .NET COBOL プロジェクトと対話するための追加コード。
  • Customer.cpb - Dialog System データ ブロックから生成された元の未変更のコピーブック。
  • Customer.gs - 元のスクリーンセット。現在、このスクリーンセットは、Orders ダイアログの表示ではなく customer.cbl に制御を返すことによって Orders ボタンを処理します。
  • Cust.ism - 変更されていない元のデータ ファイル。

ネイティブ プロジェクトをデバッグするには、Customer プロジェクトがスタートアップ プロジェクトとして設定されていることを確認します。これを行うには、プロジェクトを右クリックして [Set as StartUp Project] をクリックします。

.NET COBOL での OrderFormsLibrary プロジェクト

.NET COBOL プロジェクトの OrderFormsLibrary には、新しい Windows Forms Orders フォームが含まれています。また、.NET COBOL コードとネイティブ コード間で顧客データ ブロックを渡すためのサポート コードも含まれています。このマネージ プロジェクトは、COM オブジェクトとして登録および公開されます。

OrderFormsLibrary .NET COBOL プロジェクトの内容は次のとおりです。

  • プロパティ - これらのプロパティは、プロジェクトのビルド方法を定義します。プロジェクトは COM オブジェクトとして登録されることに注意してください。その結果、プロジェクトは COM オブジェクトとして公開され、ネイティブ プロジェクトはそのプロジェクトにアクセスできるようになります。[Project > Properties > COBOL > Advanced] を参照してください。
  • リファレンス - 必要なすべての .NET クラス。
  • Calendar.cbl - データ グリッドで使用するカレンダー エディター列を実装します。
  • Customer.cpb へのリンク - ネイティブ COBOL プロジェクトと .NET COBOL プロジェクトでは、同じコピーブックが使用および認識されます。コピーブック自体はネイティブ プロジェクト内に残り、そのコピーブックへのリンクが .NET COBOL プロジェクト内に追加されます。
  • FormsFactory.cbl - CreateOrderForm() メソッドを実装し、コールバック関数のデリゲートを作成します。このデリゲートは、顧客データ ブロックを確保するために使用されます。
  • IFormsFactory.cbl - CreateOrderForm() メソッドを定義します。このメソッドは、ネイティブ コードから .NET COBOL コードへのエントリ ポイントを提供します。
  • OrderForm.cbl [Design] - これは、デザイナーで描画されるオーダー フォームです。
  • OrderForm.cbl [Code] - これにはフォームを処理するコードが含まれています。ユーザーは、これを編集して独自のコードを追加できます。

.NET COBOL プロジェクトをデバッグするには、OrderFormsLibrary プロジェクトがスタートアップ プロジェクトとして設定されていることを確認します。

ネイティブ COBOL での Customer.cbl

Customer.cbl には、ビジネス ロジック用および Dialog System スクリーンセットとの対話用の元のコードが含まれています。Customer.cbl には、.NET COBOL プロジェクトと対話するための追加コードと Windows Forms Order フォームを表示するための追加コードも含まれています。Customer.cbl に含まれている追加コードは、次の処理を行います。

  1. $SET を使用して次のコンパイラ指令を設定します。

      $SET ans85 mfoo ooctrl(+P) case
    • ANS85 は以前と同じ設定のままです。
    • MFOO は、Micro Focus ネイティブ OO 構文のサポートを有効にします。
    • OOCTRL(+P) は、ランタイム システムで COBOL データ型を COM データ型にマッピングすることを可能にします。
    • CASE は、外部シンボル (呼び出し先のプログラムのプログラム ID や名前など) が大文字に変換されないようにします。
  2. CLASS-CONTROL パラグラフで COM クラスの OrderFormsLibrary.FormsFactory を OO COBOL クラス名の OrderFormFact にマッピングします。このパラグラフでは、COM クラスは Micro Focus ネイティブ OO クラス ライブラリの $OLE$ という COM ドメイン内にあります。

      class-control.  
      OrderFormFact is class"$OLE$OrderFormsLibrary.FormsFactory".
  3. フラグを評価して Orders ボタンが押されたどうかを判断し、それに応じて応答を行います。

      WHEN customer-orders-flg-true
      PERFORM Show-Orders-Form
  4. customer.gs に元々存在していた Orders ダイアログの Windows Forms バージョンを実装する COM オブジェクトのインスタンス (新しい .NET オーダー フォーム ライブラリ) を作成します。

      invoke OrderFormFact "New" Returning formsLibrary
  5. .NET コードでネイティブ コードを呼び出す際に使用できる手続きポインターを設定します。

      set  pptr  to  entry  "Customer_Callback"
  6. コールバックで使用されるデータ ブロックへのポインターを取得します。

      Customer-Callback SECTION.
          entry "Customer_Callback" stdcall.
          exit program returning address of CUSTOMER-DATA-BLOCK.
    
  7. 新しい Orders フォームを起動して表示します。

      invoke formsLibrary "CreateOrderForm" using
          by value pptr-val

Customer.GS (Dialog System スクリーンセット)

スクリーンセットの customer.gs は、元の [Orders] ダイアログを表示しなくなっていますが、代わりにネイティブ COBOL プログラムに制御を渡します。[Main-Window] ダイアログ内の [Orders] ボタンに対する命令は、次のように変更されています。

  SET-FLAG ORDERS-FLG(1)
  RETC
  * REFRESH-OBJECT DIALOG-BOX
  * SET-FOCUS DIALOG-BOX

上記の命令により、ORDERS-FLG は Orders ボタンが押されたことを示すように設定されます。そのため、ネイティブ COBOL プログラムはフラグを評価し、それに応じて応答を行うことができます。制御は、ネイティブ COBOL プログラムに返されます。余分な行はコメントアウトされるため、古い Orders ダイアログは表示されなくなります。

.NET COBOL での IFormsFactory.cbl

IFormsFactory.cbl は、Windows Forms Order フォームを作成するためのインターフェイスを定義します。IFormsFactory.cbl には、次の処理を行うコードがあります。

  1. .NET Framework の InteropServices クラスを使用して、インターフェイスを宣言します。また、ComInterfaceType:: InterfaceIsDual の指定によって、遅延バインドに対する COM オブジェクト サポートを確立します。別の方法として、ComInterfaceType::InterfaceIsIDispatch (ComInterfaceType::InterfaceIsIUnknown ではない) を指定することもできます。

      interface-id OrderFormsLibrary.IFormsFactory
          attribute System.Runtime.InteropServices.InterfaceType 
            (type
            System.Runtime.InteropServices.ComInterfaceType::InterfaceIsDual)

    デフォルトでは、'Register For COM Interop' プロパティはすべてのものをデュアル インターフェイスとして公開するので注意してください。

  2. CreateOrderForm() メソッドを定義します。これは、ネイティブ コードから .NET COBOL コードへのエントリ ポイントの定義です。

      method-id CreateOrderForm.
      procedure division using by value callback as binary-long.
      end method.

.NET COBOL での FormsFactory.cbl

FormsFactory.cbl は、ネイティブ プログラムからの要求に応じて Orders フォームのインスタンスを作成します。FormsFactory.cbl には、次の処理を行うコードがあります。

  1. IFormsFactory インターフェイスを実装するクラスを宣言します。

      class-id OrderFormsLibrary.FormsFactory implements
          type OrderFormsLibrary.IFormsFactory.
  2. クラスの可視化によって、そのクラスを COM に公開します。

      attribute ComVisible(true)
  3. COM に公開するデフォルト インターフェイスとして IFormsFactory インターフェイスを指定します。

      attribute ComDefaultInterface
          (type of OrderFormsLibrary.IFormsFactory)
  4. アセンブリ レベルでは、デフォルトで ComVisible 属性をオフに設定します。つまり、公開するタイプは明示的に ComVisible(true) として示す必要があります。これにより、使用するクライアントに対してより適切に定義されたタイプのライブラリが提供されます。
      assembly-attributes.
      attribute ComVisible(false).
  5. コールバック関数のデリゲートを取得し、それを使用して顧客データ ブロックを確保します。GetDelegateForFunctionPointer() は、アンマネージ関数ポインターをデリゲートに変換します。

      set  pptr to new System.IntPtr(callback)
      set  custCallback to type  
          System.Runtime.InteropServices.Marshal::GetDelegateForFunctionPointer
          (pptr, type of CustomerCallback) 
          as type  CustomerCallback
  6. コールバック関数のデリゲートを宣言します。

      delegate-id CustomerCallback.
      procedure division returning pCustomerDataBlock as type IntPtr.
      end delegate.
  7. Orders フォームを作成し、それをモーダル ダイアログ ボックスとして表示します。

      set form to new OrderFormsLibrary.OrderForm(custCallback)
      invoke form::ShowDialog()

.NET COBOL での OrderForm.cbl [Design]

オーダー フォームは、フォーム デザイナーで描画されます。Windows Forms チュートリアルを参照してください。

.NET COBOL での OrderForm.cbl [Code]

OrderForm.cbl [Code] は部分クラスであり、OrderFormsLibrary.OrderForm とも呼ばれ、Windows Forms Orders フォームを処理するためのコードを含んでいます。

OrderForm.cbl [Code] には、次のメソッドがあります。

New()

ネイティブ コードにコールバックして、顧客データ ブロックを取得します。ポインターは、_pCustomerDataBlock. に格納します。

  set  _pCustomerDataBlock  to  callback::Invoke()
OrderForm_Load()

データ ブロックをローカル記憶域内のコピーにアンパックします。このコピーには、ネイティブ コードの場合と同じ方法でアクセスできます。Marshal::Copy() メソッドは、_pCustomerDataBlock ポインターを pData にコピーします。

  invoke type Marshal::Copy
  (_pCustomerDataBlock, pData, 0, length  of  CUSTOMER-DATA-BLOCK)
  set CUSTOMER-DATA-BLOCK to pData
PopulateOrder()

次のような PERFORM ブロックおよび文とともに CUSTOMER-DATA-BLOCK 内のオーダー情報を使用して Orders フォームを初期化します。

  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
  ...
OrderForm_FormClosing()

フォーム クローズ イベントを処理し、ローカルに保持された Dialog System データ ブロックを呼び出し元に移動します。Marshal::Copy() メソッドは、pData ポインターを _pCustomerDataBlock にコピーします。

  set pData to CUSTOMER-DATA-BLOCK
  invoke type Marshal::Copy
  (pData, 0, _pCustomerDataBlock, LENGTH OF CUSTOMER-DATA-BLOCK)
OK_Click()

[OK] ボタン クリック イベントを処理します。

Delete_Click()

[Delete] ボタン クリック イベントを処理します。

.NET COBOL での OrderForm.Designer.cbl

これは、フォームを定義する Windows Forms の背後のコードであり、デザインから生成されます。Windows Forms チュートリアルを参照してください。