SAP Connector for Microsoft .NET（NCo）をWindows PowerShellから実行してSAPを操作してみる

本記事は [SAPアドベントカレンダー 2021](https://note.com/chillsap/n/ne6fe8141a5c9) 8日目の記事となります。また同時に[BeeXアドベントカレンダー 2021](https://qiita.com/advent-calendar/2021/beex)の8日目でもあります。

本記事では`SAP Connector for Microsoft .NET（NCo）`を`Windows PowerShell`から実行して、SAPシステムを操作してみます。

なお本記事で操作するのは[SAP ABAP Platform 1909, Developer Edition（dockerhub）をopenSUSE Leap 15.3（EC2）で起動してみる](https://qiita.com/SAITO_Keita/items/d826c66d0853282f4b9c)で構築している、`SAP ABAP Platform 1909, Developer Edition`となります。

`SAP ABAP Platform 1909, Developer Edition`についてはPユーザのみで構築はできますが、本記事で利用する`SAP Connector for Microsoft .NET`についてはライブラリがPユーザでは取得できないため、権限のあるSユーザが必須となります。

## ドキュメント

- [SAP Connector](https://support.sap.com/ja/product/connectors.html)
- [Community Wiki ABAP Connectivity Home](https://wiki.scn.sap.com/wiki/display/ABAPConn)

## SAP Connector for Microsoft .NETとは？

`SAP Connector for Microsoft .NET`とは`.NET Framework`を用いたSAPとの接続用コネクタになり、`NCo`と表記されます。

この接続コネクタを利用することにより、.NetアプリケーションからBAPI（Business Application Programming Interface）・汎用モジュール（リモート対応）を実行することができます。

2021年12現在、`NCo 3.0`が提供されています。

### サポート期限について

`.NET Framework`を背景にしているところで引っかる方がいるかと思いますが、サポート期限について。

[Support](https://support.sap.com/ja/product/connectors/msnet.html)のページだったり、[856863 - SAP NCo Release and Support Strategy](https://launchpad.support.sap.com/#/notes/856863)のノートに記載があり。

2021年12月現在、`NCo 3.0`のサポート期限は、`For the .NET 4.0 variant support will be offered until December 31, 2022.` と記載されています。

`NCO 3.0`については以前は`Maintenance and support for the Microsoft .NET 4.0 runtime variant of NCo 3.0 will end on December 31, 2020`と記載されていたのが伸びていたりしていますが、`.NET 4.0`である点は変わらず。

個人的には`.NET（旧称.NET Core）`対応されれば……とは思いますが、`.NET Framework 4`で延命され続けてるように見えるので、SAPとしては積極的には対応されてない？　とは個人的に感じます。

`.NET Framework`自体のサポート期限もありますし、ここらへん本格的にシステムで利用される場合は、他にもJava用のコネクタとして`SAP Java Connector（SAP JCo）`や`SAP NetWeaver RFC SDK`なども用意されているため、それぞれのサポート期限等の状況を見つつ選択するのがよさそうです。

## Windows PowerShell

`Windows PowerShell`は`.NET Framework`を基盤としており、`.NET Framework`を利用できるため今回は`Windows PowerShell`から`SAP Connector for Microsoft .NET`を実行してみます。

## 今回利用する環境

- Windows 10 21H1
- Windows PowerShell 5.1.19041.1320
- SAP Connector for Microsoft .NET 3.0.24.0 for Windows 64bit (x64)（Compiled with .NET Framework 4.0）
- SAP ABAP Platform 1909, Developer Edition

2021年12月現在、`NCo 3.0`が提供されていますが、ダウンロードをみると下記2つがそれぞれ提供されています。

- Compiled with .NET Framework 2.0, final patch level
- Compiled with .NET Framework 4.0

`Nco 3.0`で`.NET Framework 2.0`と`.NET Framework 4.0`とバージョン番号が色々と出てくるので、所見だとちょっと紛らわしいかと思います。

本記事では.NET Framework 4.0を選択します。

## SAP Connector for Microsoft .NET3.0のサポート期間

`NCo`とはのところで少し触れましたが、サポート期限について下記にノートがあるためこちらを参照して下さい。

[SAPnote:856863 SAP NCo Release and Support Strategy](https://launchpad.support.sap.com/#/notes/856863)

過去、`NCo 3.0`のサポート期限をみていると切れそうになると伸びてを繰り返していて、新しいバージョン……　とは少し思ってしまいますが。

## ライブラリのダウンロード

今回は`SAP Connector for Microsoft .NET 3.0.24.0 for Windows 64bit (x64)（Compiled with .NET Framework 4.0）`を利用します。

- [Downloads and Documentation](https://support.sap.com/en/product/connectors/msnet.html)

にリンクがあるのでこちらからダウンロードします。（S-Userが必要です）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/868db730-f423-1c6a-6d73-13f9a64c2828.png)

なおサイトには下記のようにコーディングの参考になるプログラミングガイドやサンプルコードも置いてあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/a3f3f696-fc0c-fba5-a9f8-d2b6e1838401.png)

- dotnet_connector_30_programming_guide.pdf
- dotnet_connector_30_tutorial.zip <-サンプルコードの詰め合わせ(言語はVBとC#)
- dotnet_connector_30_overview.pdf

## SAP .NET Connector 3.0のドキュメント（chmファイル）について。

今回ダンロードしたファイル、`sapnco30dotnet40P_24-20007348.zip`の中にchmファイルが入っており、これがドキュメントになります。（`NCo30APIDocumentation.chm`）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b359d79b-6bd7-c0fc-e478-fe48fc943352.png)

### ダウンロードファイルのブロック設定

ダウンロードしてきたchmファイルを実行しても内容が表示されないケースがあります。
これは"ファイルのプロパティ"からセキュリティのチェックをつけない場合、内容が表示されないので注意して下さい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/bebf34ca-8d0c-c5a5-026b-5be9aa90b141.png)

## ライブラリのインストール

▶インストーラ（MSIファイル）を実行する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/63e84da3-8e8c-7ecc-4eec-27fcfa8ad4fa.png)

▶Nextを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/ec30411a-7138-9ad7-88a7-74d6d5a78c57.png)

▶インストール先を選択してNextを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/1b19e8ae-e7ea-1367-5dd5-7d3661c8b629.png)

▶今回は展開されたdllがほしいだけなので、Noneを選択してNextを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b6122c01-97e5-b36e-c963-9a1d4a817501.png)

▶Nextを選択
![](image/sapnco.install.step005.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b2dfe02e-c7bc-95af-9ee3-e4b6e0c1e45d.png)

▶Closeを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/8a9314ea-2bba-33f7-9283-bf81a60375a7.png)


▶インストールが完了すると、インストール時に選択したフォルダに下記のdllがインストールされるのでこれらを使ってコーディングを行う。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/cc2dfa45-8899-57c4-dfae-0d67e5a81f6c.png)

- libicudecnumber.dll
- rscp4n.dll
- sapnco.dll
- sapnco_utils.dll

4つのdllがありますが、基本的に利用するのは`sapnco.dll`と`sapnco_utils.dll`の2つになります。
他2つのdll(libicudecnumber.dll,rscp4n.dll)については、下記の場合のみ必要となってくるようです。(SAP .NET Connector 3.0 Programming Guideより)

>ABAP type DECFLOAT, your project will need the libicudecnumber.dll, and only if you
>use the “UseSAPCodepages” parameter, your project will need the rscp4n.dll. In that
>case make sure they can be found in the PATH environment variable.

## 基本的にな進め方について

基本的にな進め方については

- 1.chmファイルのドキュメントを読み、お目当ての操作を探す（ログイン/RFC実行/etcetc）
- 2.TRCD:BAPIやRFCで実行できる汎用モジュールをTRCD:SE37で探して、適宜コーディング

なおRFC出来る汎用モジュールは下記のように処理タイプの所がRemote-Enable Moduleになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/1cfb44fa-9499-539a-3024-63023d468040.png)

ちなみに`RFC_CALL_TRANSACTION_USING`はBDCテーブルを渡すとバッチインプットを動かす汎用モジュール。

## ライブラリを読み込んでSAPに接続してみる

ここではインストールディレクトリに配置された`sapnco.dll`と`sapnco_utils.dll`を`c:\temp\NCo`にコピーしたものをAdd-Typeコマンドレットで読み込み利用します。

今回、ログインに利用するユーザとして`RFCTEST`というダイアログユーザを作成し、権限`SAP_ALL`をつけた物を利用しています。

RFCで実行できる汎用モジュールを制限したりと、権限構成とかの話もありますが、本記事では対象にしていません。
ここでは`SAP_ALL`をつけたユーザを利用しています。

```PowerShell:NCoでダイアログユーザRFCTESTでログイン確認
# c:\temp\NCo\に配置したdllを読み込む
Add-Type -path "C:\temp\NCo\sapnco.dll"
Add-Type -path "C:\temp\NCo\sapnco_utils.dll"

# SAP.Middleware.Connector.RfcConfigParametersを生成してSAPのログオンパラメータを設定

# RfcConfigParametersを生成
[SAP.Middleware.Connector.RfcConfigParameters] $rfcParam = New-Object 

# RfcConfigParametersに接続情報を設定
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::name, "TESTLOGON")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemID, "A4H")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::AppServerHost, "10.10.10.182")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemNumber, "00")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Client, "001")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::User, "RFCTEST")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Password, "パスワード")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SAPRouter, "")
$rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Language, "EN")

# RfcDestinationに接続設定を渡す
[SAP.Middleware.Connector.RfcDestination] $rfcDest = [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($rfcParam)

# RfcDestinationのpingメソッドは汎用モジュールRFC_PINGを実行して接続確認を行うメソッド。
# Invokes a remote call to RFC_PING in order to test whether the physical connection to the backend system is still ok.
$rfcDest.ping()
```

スクリプト中で利用している`RfcConfigParameters`、`RfcDestinationManager`、`RfcDestination`について詳しいことはドキュメントに記載があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/1884e608-0c69-c927-658e-5737a4b97714.png)

### 指定された情報でログオン出来るかチェックするスクリプト

プロンプトからコマンドを実行しましたが、引数としてログオン情報を渡すと接続テストを行うスクリプトのサンプルとしては下記のようになります。
(sapnco.dllとsapnco_utils.dllについてはps1ファイルと同じディレクトに格納して下さい)

```Powershell:Test-SAPLogon.ps1
<#
.SYNOPSIS
    SAPログオンテスト
.DESCRIPTION
    引数で指定したSAPシステムへログオンテストを行う
.PARAMETER systemID
    SAP システムID
.PARAMETER appServerHost
    SAP ホスト名
.PARAMETER systemNumber
    SAP システムNO
.PARAMETER client
    ログオンクライアント
.PARAMETER user
    ログオンユーザ
.PARAMETER sapRouterString
    SAPRouterストリング
.PARAMETER language
    ログオン言語
#>
PARAM(
    [Parameter(Mandatory = $true)][string]$SystemID,
    [Parameter(Mandatory = $true)][string]$AppServerHost,
    [Parameter(Mandatory = $true)][string]$SystemNumber,
    [Parameter(Mandatory = $true)][string]$Client,
    [Parameter(Mandatory = $true)][string]$User,
    [Parameter(Mandatory = $true)][secureString]$Password,
    [Parameter(Mandatory = $true)][AllowEmptyString()][string]$SAPRouterString,
    [Parameter(Mandatory = $false)][string]$Language = "EN"
)

BEGIN {
    # 初期処理
    # secureStringからパスワードを取得
    $plainPassword = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) | ForEach-Object { [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($_) }
    # dll読み込み
    try {
        $sapNcoPath = (Join-Path -Path $PSScriptRoot -ChildPath "sapnco.dll")
        $sapnco_utils = (Join-Path -Path $PSScriptRoot -ChildPath "sapnco_utils.dll")
        Add-Type -Path $sapNcoPath
        Add-Type -Path $sapnco_utils
    }
    catch {
        Write-Error -Message "dll読み込み失敗"
        exit
    }
}

PROCESS {

    # RFCのログオンパラメータオブジェクトに値を設定
    [SAP.Middleware.Connector.RfcConfigParameters] $rfcParam = New-Object SAP.Middleware.Connector.RfcConfigParameters
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::name, "testLogon")
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemID, $SystemID)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::AppServerHost, $AppServerHost)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemNumber, $SystemNumber)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Client, $Client)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::User, $User)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Password, $plainPassword)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SAPRouter, $SAPRouterString)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Language, $Language)

    # RfcDestinationに接続設定を渡す
    [SAP.Middleware.Connector.RfcDestination] $rfcDest = [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($rfcParam)

    # ログオン判定
    try {
        # RfcDestinationのpingメソッドは汎用モジュールRFC_PINGを実行して接続確認を行うメソッド。
        # Invokes a remote call to RFC_PING in order to test whether the physical connection to the backend system is still ok.
        $rfcDest.ping()
        Write-Host "ログオン成功"
    }
    catch {
        Write-Host "ログオン失敗"
    }

    Read-Host -Prompt "enterで終了"
}
```

▶実行結果 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/9f80fda3-8735-f9e0-b1f1-2c82be6a532a.png)

## BAPI(Business Application Program Interfaces)を実行してみる（BAPI_USER_GETLIST）

今回は`BAPI_USER_GETLIST`を実行してみます。

▶BAPI_USER_GETLIST
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/a0cf0b39-b781-f38f-1c70-6017d2748f47.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/d4a79850-7c4b-2fcf-3845-84201d27fa14.png)

▶今回BAPIからの戻り値として取得するテーブル:USERLIST
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/d0def51d-ef4d-e562-d4be-1d00659d6844.png)

▶USERLISTテーブルの構造、BAPIUSNAME
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/6ee17150-7891-4688-fea3-40f064864a86.png)

`NCo`で汎用モジュールを実行する場合は、`SAP.Middleware.Connector.IRfcFunction`を利用します。

```Powershell:Get-UserList.ps1
<#
.SYNOPSIS
    SAPユーザリスト取得
.DESCRIPTION
    対象システムのユーザ一覧を取得する
.PARAMETER systemID
    SAP システムID
.PARAMETER appServerHost
    SAP ホスト名
.PARAMETER systemNumber
    SAP システムNO
.PARAMETER client
    ログオンクライアント
.PARAMETER user
    ログオンユーザ
.PARAMETER sapRouterString
    SAPRouterストリング
.PARAMETER language
    ログオン言語
#>
PARAM(
    [Parameter(Mandatory = $true)][string]$SystemID,
    [Parameter(Mandatory = $true)][string]$AppServerHost,
    [Parameter(Mandatory = $true)][string]$SystemNumber,
    [Parameter(Mandatory = $true)][string]$Client,
    [Parameter(Mandatory = $true)][string]$User,
    [Parameter(Mandatory = $true)][secureString]$Password,
    [Parameter(Mandatory = $false)][AllowEmptyString()][string]$SAPRouterString,
    [Parameter(Mandatory = $false)][string]$Language = "EN"
)

BEGIN {
    # 初期処理
    # secureStringからパスワードを取得
    $plainPassword = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password) | ForEach-Object { [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($_) }
    # dll読み込み
    try {
        $sapNcoPath = (Join-Path -Path $PSScriptRoot -ChildPath "sapnco.dll")
        $sapnco_utils = (Join-Path -Path $PSScriptRoot -ChildPath "sapnco_utils.dll")
        Add-Type -Path $sapNcoPath
        Add-Type -Path $sapnco_utils
    }
    catch {
        Write-Error -Message "dll読み込み失敗"
        exit
    }
}

PROCESS {

    # RFCのログオンパラメータオブジェクトに値を設定
    [SAP.Middleware.Connector.RfcConfigParameters] $rfcParam = New-Object SAP.Middleware.Connector.RfcConfigParameters
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::name, "getUser")
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemID, $SystemID)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::AppServerHost, $AppServerHost)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SystemNumber, $SystemNumber)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Client, $Client)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::User, $User)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Password, $plainPassword)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::SAPRouter, $SAPRouterString)
    $rfcParam.Add([SAP.Middleware.Connector.RfcConfigParameters]::Language, $Language)

    # SAPログオン
    [SAP.Middleware.Connector.RfcDestination] $rfcDest = [SAP.Middleware.Connector.RfcDestinationManager]::GetDestination($rfcParam)

    # ログオン判定
    try {
        # RfcDestinationのpingメソッドは汎用モジュールRFC_PINGを実行して接続確認を行うメソッド。
        # Invokes a remote call to RFC_PING in order to test whether the physical connection to the backend system is still ok.
        $rfcDest.ping()
        Write-Host "ログオン成功"
    }
    catch {
        Write-Host "ログオン失敗"
        exit
    }

    # BAPI_USER_GETLIST
    [SAP.Middleware.Connector.IRfcFunction] $sapFunc = $rfcDest.Repository.CreateFunction("BAPI_USER_GETLIST")
    # 実行
    $sapFunc.Invoke($rfcDest)

    # 返値取得
    [SAP.Middleware.Connector.IRfcTable] $returnUserList = $sapFunc.GetTable("USERLIST")

    # 情報格納用にArrayListを宣言
    $userArray = New-Object System.Collections.ArrayList
    # 返値から情報を取得しArrayListに格納
    $returnUserList | ForEach-Object {
        $userRow = New-Object PSCustomObject
        # USERNAMEとFULLNAME項目を取得
        $userRow | Add-Member -MemberType NoteProperty -Name "USERNAME" -Value $_.getValue("USERNAME")
        $userRow | Add-Member -MemberType NoteProperty -Name "FULLNAME" -Value $_.getValue("FULLNAME")
        $userArray.add($userRow) > $null
    }

    $userArray | Format-Table

    Read-Host -Prompt "enterで終了"
}
```

▶実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/33ce5bee-68f1-e99b-2f9f-7c077ba2d693.png)

`SAP.Middleware.Connector.IRfcFunction`に`RfcDestination`の`RfcRepository.CreateFunctionメソッド`で実行したい汎用モジュールを設定し、`Invoke`メソッドで実行。

実行結果テーブルから`GetTable`メソッドでテーブルを取得し、あとは`USERNAME`と`FULLNAME`を出力しています。

## 総評

`NCo`について、`.Net Framework`のため`Windows PowerShell`からさっくりと利用することができて、ツールの作成がとても手軽に実現できます。

ただし、2021年現在は`.NET Framework 4`で提供されており先行きがちょっと不透明な部分があるため、本格的なシステム連携に使うには状況をよく見て選択する必要があるかと思います