JRA-VANをNode.jsで使ってみた

# はじめに
競馬のJRAは、**JRA-VAN**という名前の、レースや馬の情報など様々なデータを有償で提供するサービスがあります。🐎

https://jra-van.jp/

料金体系がやや複雑ですが、ざっくり言うと、データの取得と、それを使ったローカルアプリが使えます。ローカルアプリとは、JRAの外の人たちが作ったアプリです。それもまた有償だったり無料だったり。登録制でJRAが配っているという形。

私は、データの分析や自分で可視化することに興味があるので、**JRA-VAN データラボ**というサービスを選択。（まだ無料期間なのでお金は払っていませんが、契約予定です。）いくつかのアプリも使えるはずです。

この記事は、**JRA-VANで提供されるデータ取得モジュールを、Node.jsで使った**というお話です。

なおVisual Basicと、C++（MFC）のサンプルコードは、公式HPからダウンロードできます。あ、あとAccessのテーブル定義だけの空っぽのファイルも。サンプルコードや仕様書などのドキュメントは無料で見られるので、興味のあるかたはどうぞ。

https://jra-van.jp/dlb/sdv/

ライブラリの関数の仕様や、使い方の話はここでは書きません。（どの言語でも一緒だし、ドキュメントがあるから）

:::note info
2025/1/25追記
32bit版と64bit版のNodeは共存可能と書いていましたが、通常のインストーラーによると不可能でした。後から入れるときの処理で、前のものをアンインストールしてました。パスもきれいにお掃除。

対策として、nvmを使って回避しました。その方法は、[こちらの記事](https://qiita.com/yo16/items/15ce6dd5721f12045d8d)をご参照ください。🍰
:::

## この記事の目新しさ
いくつかの条件が重なり、ニッチでチャレンジングな内容になってます。

### Node
データを取得するためには、Active Xコントロールが提供されていて、それ経由でないと取得できません。Visual Basic、MFCのC++、Accessという顔ぶれでなんとなくわかると思いますが、Microsoftどっぷり。

でも一方で、可視化とかUIのあれこれをしたいとき、今どきはもうNode、Reactが中心でしょ（個人の感想です）ということで、**Nodeでチャレンジ😤**してみました。

取得だけは妥協してC++とかでDBへ入れ、UIはNode、という選択肢もあると言えばある。頑固者か。

### 32bit版
提供されているActive Xコントロール（dll）は、**32bitアプリ😢**です。今どきのWindowsはみんな64bit版を使ってるので、32bit版は普通には使えないです。MacユーザーやLinux系はハナから対象外です。

なお、64bit化は何年も前から要望があるようですが、スルーされているようです。（どういう体制？）

### 日本語圏のみ
JRAは日本競馬の情報なので、ライブラリを使うのはほぼ日本人。関数の使い方などがわからないとき、英語圏での掲示板などに情報がありません。

そもそも、JRA-VANで開発をやろうという人が少ないので、日本語圏の情報だって、ほぼないんですが。


# 対応内容
## 1. 32bit版Node
まず32bitの件をクリアします。（本当は、**32bit版DLLしか提供されてない！** ということには、結構あとで気づきます）。

Node.jsの公式HPのダウンロードから、32bit版（x86アーキテクチャ版）のNodeをダウンロードしてインストールします。私は、2025年1月12日時点の最新バージョン `node-v22.13.0-x86.msi` を使いました。

**普段使う64bit版との共存ができて**、64bit版を入れた環境でも、自然に32bit版もインストールできます。

:::note warn
2025/1/26 訂正：「普段使う64bit版との共存ができて」の部分

少なくとも通常のインストーラーでは共存できないようです。対策は、[こちらの記事](https://qiita.com/yo16/items/15ce6dd5721f12045d8d)をご参照ください。🍰
:::

ですが、**インストーラーが環境変数のPATHをいじる**ので、そこだけ注意。少なくとも私の環境では、ユーザー環境変数のPATHに、32bit版のインストール先が追加されていました。今回以外の開発では32bit版は使いたくないので、そのパスは消して、使うときにいちいち、`$env:Path = "C:\Program Files (x86)\nodejs32;$env:Path"`と設定する運用にしました。（インストールフォルダを、`nodejs`から`nodejs32`に変えてあります）

これで、32bit版のNodeを動かす環境ができました。

### 先に64bit版で`npm install winax`をしてしまった方へ
私がそうです。

その場合は、`winax`のコンパイルが64bit版でされているので、32bit版のNodeから実行すると、なんか変なエラーが出ます。（語彙）

一度`winax`をアンインストールして、改めて32bit版のNodeでインストールすると解消されます。

## 2. `winax`のインストール
`winax`というモジュールは、Node環境でActive Xを使えるモジュールです。

https://www.npmjs.com/package/winax

これをインストールしようとして問題が生じました。

### 2.1. Pythonがないエラー
これは私だけの話かもしれませんが、`npm install winax`でインストールすると、**Pythonがない**というエラーが起きました。winaxのインストール時に、コードをPythonを使って何かしているようです。

私はローカルのグローバル環境にPythonを置いていないので、`Python`と打っても動きません。（親切なOSさんが、Microsoft Storeを開いて紹介してくれます😇）

私のPythonの環境は`conda`を使っていて、condaプロンプトから、`conda info -e`とか`conda activate`とかしてます。

今回、`npm install`する環境で、`python`が動かすために、いつも使っているPower Shellを、スタートメニューのcondaのプロンプトのようにしないといけないです。

```powershell
> C:\Users\{名前}\anaconda3\shell\condabin\conda-hook.ps1
> conda activate 'C:\Users\{名前}\anaconda3'
> conda activate {環境名}
```

`{名前}`はOSログインユーザー名、`{環境名}`はcondaの環境名です。

これで、condaプロンプトではおなじみの`(base)`が出てきて、Pythonが使えます。

### 2.2. C++コンパイル環境がないエラー
引き続き、`npm install winax`で、**C++のコンパイル環境がない**というエラーが起きました。winaxのインストール時に、C++のコードをコンパイルしているようです。

これは今まで機会がなかったから入れていなかったけど、インストールしました。

https://visualstudio.microsoft.com/ja/visual-cpp-build-tools/

（インストール後は、エクスプローラーのコンテキストメニューに、"Visual Studioで開く"が出てくるようになったので、なんとか消したい）

これで、`winax`はインストールでき、JavaScriptから、Active Xのライブラリは呼び出す環境は整いました。

## 3. Active Xのインストール（不要かも）
DLLを、使用するWindowsへCOMコンポーネントライブラリとして登録する必要があります。

この部分はもしかしたら、提供されるライブラリのインストーラによって、行われているような気がしますが、手動でやってしまったので、その話。

### 3.1. インストール
1. 管理者としてPower Shellを起動
1. `regsvr32 "C:\Windows\SysWOW64\JVDTLAB\JVDTLab.dll"`

成功すると、「C:\Windows\SysWOW64\JVDTLAB\JVDTLab.dll の DllRegisterServer は成功しました。」のメッセージが出ます。

### 3.2. 32bit版にインストールされているか確認
1. 下記のファイルを作る
    ```VBScript: check.vbs
    Set obj = CreateObject("JVDTLab.JVLink.1")
    If Not obj Is Nothing Then
      MsgBox "COMオブジェクトの作成に成功しました。"
    Else
      MsgBox "COMオブジェクトの作成に失敗しました。"
    End If
    ```
1. コマンドプロンプトで、`C:\Windows\SysWOW64\cscript.exe test.vbs` で実行（32bit版のcscriptを動かすことが重要）
    OKなら、「COMオブジェクトの作成に成功しました。」のメッセージが出ます。

`"JVDTLab.JVLink.1"`ってなんじゃー！😡という声が聞こえますが、レジストリを見たり、dumpbinを見たり(Active Xは見られない)、いろいろして得た情報だけど、ソースは忘れました。すみません。わかる方、コメント欄で教えてください。

### 3.3. なぜ手動でやってしまったか
正しくインストールされると、「32bit版のCOMコンポーネントの一覧にはあるが、64bit版にはない」という状態になりますが、64bit版で確認して「インストールされていない」と判断していたので、試行錯誤の上、手動でやってしまいました。

先に、上の[32bit版にインストールされている確認](###32bit版にインストールされているか確認)をやればよかったです。🙈


## 4. winaxの使い方と、出力引数
基本はこうです。
```javascript
// import
import winax from "winax";

// 登録されたCOMコンポーネントを、名前を指定して使うよという宣言
jvlink = new winax.Object("JVDTLab.JVLink");

// JRA-VANの関数
const result = jvlink.JVInit("UNKNOWN");
```
`"JVDTLab.JVLink"`ってなんじゃー！`.1`どこやったー！？😡という声が聞こえますが、これも忘れました。すみません。

まぁこれだけならいいんですが、JavaScriptとC系の関数の引数の違いとして、**出力引数**があり、今回使うJRA-VANのライブラリにもあります。

例えばこれ。JVOpenという関数の定義書です。~~読みづらいです。~~ `readcount`、`downloadcount`、`lastfiletimestamp`が出力引数です。

> Long JVOpen(String型 dataspec, String型 fromtime, Long型 option, Long
型 readcount , Long 型 downloadcount , String 型
lastfiletimestamp);

出力引数には、変数のポインタを渡すというあれです。正確には Long*型とかString*型かな。仕様書上の書き方がわからないけど。

`winax.Variant`クラスを使って、それを渡すことでやり取りできます。`Variant`クラスには、初期値と**型**を指定し、次のような形になります。

```TypeScript
const out_readCount = new winax.Variant(0, "pint32" as any);
```

・・・・・`as any`、気になりますよね。

`winax`の`index.d.ts`では、第二引数の文字列はいくつかの候補の文字列から選ぶことになっているのですが、`pint32`という型はありません。なので`as any`で強引に突っ込んでます。

裏技的な方法のようですが、githubのREADME.mdにこうあります。

> byref or use prefix '**p**' to indicate reference to the current type
（byrefか、型の参照を示すために頭に'**p**'をつけろ）

なので`p`をつけるのは正当な方法で、`index.d.ts`の不具合ですかね。（`int32`で呼び出すとしれっと動くが出力値は得られず、`byref`で呼び出すと型が違うというエラーが出ます。）この解を見つけたのは奇跡。

文字列の場合は`"pstring" as any"`、などになります。

https://github.com/durs/node-activex


## 5. ライブラリの関数の仕様や、使い方
[はじめに](#はじめに) でも書いたように、関数の使い方などはどの言語でも一緒なので、詳しくは書かないけど、ちょっとだけ。

JRA-VANのデータ取得ライブラリは、**C言語っぽい手続き型プログラミング**なライブラリです。なので、なつかしさとめんどくささを感じられます。いろいろあって、関数に切り出すのが難しく、ダラダラなります。（語彙力）


# おわりに


技術的なハードルがいくつもあり、それらは全部乗り越えたので、あとは作るだけ。実は、新しいもの好きの私は「未知のハードルを越えるのが好きで、作るのはそうでもない説」が浮上🤔 この記事を書いて、一休みしています。🍰

チャットエージェントで色々解決できる時代にはなったけど、ガラパゴス的なニッチな領域は対応が難しく、エンジニアの勘と経験で乗り越える機会はまだあるなぁと思いました。

ニッチな部分に引っ掛かった、後世の誰かのお役に立てれば幸いです。

ではよきWin32ライフを！