Microsoft.PointOfService のプログラムを作ってみる

ショコラ
ショコラ

Microsoft.PointOfService のプログラムを作ってみる

Microsoft.PointOfService のプログラムを作ってみます。
はまりポイント多いです。
・Microsoft.PointOfServiceを使用するには
 ・Windows フォームアプリケーション(.NET Framework)でプロジェクトを作成する。
 ・PosForDotNet-1.14.1.msi をインストールする。
 ・.NET Framework 4.8.1 を使用する。
 ・対象プラットフォームに「32ビットを選ぶ」を選択する。
 ・Microsoft.PointOfService.dll と Microsoft.PointOfService.WMI.dll を参照に追加する必要がある。

もっさん先輩
もっさん先輩

▼PosForDotNet-1.14.1.msi をインストールします。
 https://www.microsoft.com/en-us/download/details.aspx?id=55758

▼「.NET Framework 4.8.1」を選択する。(その前に、Visual Studio Installer で .NET Framework 4.6.2.-4.7.1 開発ツール と .NET Framework 4.8.1 開発ツールのインストールが必要です。)

▼「32ビットを選ぶ」

▼「WebSocketServer」を使うには、NuGet で「Fleck」のパッケージをインストールする。

▼WebSocketのクライアントの画面です。
この「自動釣銭機から現金を出す」ボタンを押すと、別ホストで動いているWindowsのアプリにWebSocketで通信して、Windowsのアプリ経由で自動釣銭機からお金がでるというものです。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.1/dist/jquery.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.0/dist/semantic.min.css">
  <script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.9.0/dist/semantic.min.js"></script>
<script>
$(function() {
  $('#genkin-btn').on('click',function(){
    try {
      const protocol = $('input[name=protocol]:checked').val()
      const host = $('#host').val()
      const port = $('#port').val()
      const connect = `${protocol}://${host}:${port}`
      addLog( connect )
      const ws = new WebSocket( connect )

      // 接続
      ws.onopen = () => {
        addLog(`接続成功`)
        ws.send( $("#price").val() )
        ws.close()
      }

      // メッセージを受信
      ws.onmessage = ( message ) => {
        addLog(`受信:${message}`)
      }

      // 切断
      ws.onclose = ( event ) => {
        addLog(`切断:コード:${event.code}, 理由:${event.reason}`)
      }

      // エラー
      ws.onerror = ( event ) => {
        addLog(`エラー:コード:${event.code}, 理由:${event.reason}`)
      }
    } catch(e) {
      addLog(`エラー${e}`)
    }
  })

  $('.ui.radio.checkbox').checkbox()
})

function addLog(message) {
  const log = $('#log')
  const current = log.val() // 現在の内容を取得
  log.val(current + (current ? "\n" : "") + message) // 改行して追加
  log.scrollTop(log[0].scrollHeight) // スクロールを一番下へ
}
</script>
</head>
<body>
<div class="ui raised very padded text container segment" style="margin-top:1em">
  <h2 class="ui header">
    <i class="cash register icon"></i>
    <div class="content">
      レジ
    </div>
  </h2>
  <form class="ui form">
    <div class="inline fields">
      <label for="fruit">プロトコル</label>
      <div class="field">
        <div class="ui radio checkbox">
          <input type="radio" name="protocol" value="ws" checked>
          <label>ws</label>
        </div>
      </div>
      <div class="field">
        <div class="ui radio checkbox">
          <input type="radio" name="protocol" value="wss">
          <label>wss</label>
        </div>
      </div>
    </div>
    <div class="field">
      <label>送信先ホスト</label>
      <input type="text" id="host" placeholder="192.168.1.11" value="192.168.1.11">
    </div>
    <div class="field">
      <label>送信先ポート</label>
      <input type="text" id="port" placeholder="8181" value="8181">
    </div>
    <div class="field">
      <label>金額</label>
      <input type="number" id="price" placeholder="¥10,000">
    </div>
    <button type="button" class="positive ui button" id="genkin-btn">
      自動釣銭機から現金をだす
    </button>
    <div class="field">
      <label>ログ</label>
      <textarea id="log"></textarea>
    </div>
  </form>
</div>
</body>
</html>

ws.html は https で表示されるので、wss に対応させなければならないのです。
ようするに、サーバー証明書を作成してブラウザにも自己証明書を設定する必要があります。(面倒)

次にわかったことは、
どうもWindowsのアプリで使用するサーバー証明書は、IP直指定の証明書が使えないようです。
証明書を作成する際に、IP直指定からホスト名にして作成したところ、通信に成功しました。←ココでドはまりしました
また、iPad での使用(hosts を書き換えられない)を想定しているのでここも調べました。
どうも、SAN (Subject Alternative Name) に IP を設定すればできるようだと分かりました。

①設定ファイルを作成して、

# server.cnf
[ req ]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no

[ req_distinguished_name ]
CN = 192.168.1.11

[ v3_req ]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[ alt_names ]
IP.1 = 192.168.1.11

②証明書を作成する。

# 秘密鍵作成
openssl genrsa -out server.key 2048

# 証明書作成
openssl req -new -x509 -key server.key -out server.crt -days 3650 -config server.cnf -extensions v3_req

# PFX に変換 (Fleck / .NET 用)
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt
# 秘密鍵作成
openssl genrsa -out server.key 2048

# 証明書作成
openssl req -new -x509 -key server.key -out server.crt -days 3650 -config server.cnf -extensions v3_req

# PFX に変換 (Fleck / .NET 用)
openssl pkcs12 -export -out server.pfx -inkey server.key -in server.crt

▼VisualC#のソースです。
このポイントは7行目の Tls12 を使うところです。無いとエラーになります。ここもめちゃくちゃはまりました。

var certificate = new X509Certificate2(@"C:\server.pfx", "123456");

// WSS サーバーを作成
var server = new WebSocketServer("wss://192.168.1.11:8181")
{
    Certificate = certificate,
    EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12
};

server.Start(socket =>
{
    socket.OnOpen = () => Console.WriteLine($"接続: {socket.ConnectionInfo.ClientIpAddress}");
    socket.OnClose = () => Console.WriteLine($"切断: {socket.ConnectionInfo.ClientIpAddress}");
    socket.OnMessage = message =>
    {
        Console.WriteLine($"受信: {message}");
        socket.Send("受け取りました: " + message);
    };
});

以上

Scroll to Top