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

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

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

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

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

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

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

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

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

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

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

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

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

マネージ COBOL での OrderFormsLibrary プロジェクト

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

OrderFormsLibrary マネージ プロジェクトの内容は次のとおりです。

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

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

ネイティブ COBOL での Customer.cbl

Customer.cbl には、ビジネス ロジック用および Dialog System スクリーンセットとの対話用の元のコードが含まれています。Customer.cbl には、マネージ 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. マネージ コードでネイティブ コードを呼び出す際に使用できるプロシージャ ポインターを設定します。

      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 ダイアログは表示されなくなります。

マネージ 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() メソッドを定義します。これは、ネイティブ コードからマネージ コードへのエントリ ポイントの定義です。

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

マネージ 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()

マネージ COBOL での OrderForm.cbl [Design]

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

マネージ 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] ボタン クリック イベントを処理します。

マネージ COBOL での OrderForm.Designer.cbl

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