Visual Studio

2017年12月 9日 (土)

せめて作業場所節の名前だけでも覚えて帰ってください

Fujitsu Advent Calendar 2017の9日目です。 このエントリは個人の立場で書いております。 なのですが、 せっかくなので業務で開発に携わっているモノを紹介しようと思います。 説明上、特定の製品の話も出てきますが、宣伝の意図はありません。 くどいですが、このエントリは個人的に書いているもので、所属企業の見解を示すものではありません。

さて、 私が開発に携わっているモノとは、COBOL処理系です。 富士通が何かやっているというニュースが出ると、 「どうせCOBOLだろw」みたいな反応が出たりもする、 みんな大好きCOBOLです。

古代の遺物扱いされることも多いのですが、 コードはまだまだあるところには大量にあります。 ぜひ「知っている言語」の一つに入れておいてもらって、 どこかのシステムの奥底からCOBOLコードが出てきた際には、 げえっと叫んで封印するのではなく、 読んで、場合によっては手直しなどしてもらえればなと思います。

ざっと以下の内容。

おかんについて

まずは、わたしらのおかんを紹介したいと思います。 Grace Hopperです。 COBOLの母です。 おかんがCOBOL作りました。

米海軍准将でした。 けっこう偉大な人なので、 その名を付けた駆逐艦があります。

つまり、うちのおかんはリアル艦娘です。 ハイパー婆さんなのに駆逐艦。 「バブみ」などという言葉ではとうてい表現できない歴史の深みです。 ひげのおっさんとかではなく駆逐艦が作った言語と聞くと、 ちょっとやってみようという気になりませんでしょうか。 なりませんか。そうですか。

Grace Hopperといえば、 コンピューターにおける「バグ」語源にからんだり、 「許可を求めるより後で謝った方が簡単だ」という名言を残したり、 逆回りの時計を壁に掛けていたり、 色々エピソードのある人です。 生きていれば、本日101歳の誕生日です🎂。 1992年に亡くなっていますが、 女性技術者の集まりである"Grace Hopper Celebration"に名を冠されていたり、 去年はアメリカ合衆国大統領自由勲章が授与されたり、 今なお存在感があります。

プログラムのスタイルについて

そんなおかんが開発したCOBOLはLispやFortranと並んで最古参の高級言語のひとつです。 記号をあまり使わず、 英語の文章のようにプログラムを記述していくスタイルです。 試しに簡単なプログラムを書いてみましょう。

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MAIN.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WK.
         02 FILLER  PIC X(6) VALUE "Hello".
         02 WK-NAME PIC X(5).
         02 FILLER  PIC X    VALUE "!".
       PROCEDURE DIVISION.
           DISPLAY "? " WITH NO ADVANCING.
           ACCEPT WK-NAME.
           DISPLAY WK.
       END PROGRAM MAIN.

"PROCEDURE DIVISION."と書かれてある行より後ろがプログラム本体になります(『手続き部』)。 この部分はとても文章的です。 まあ、文章的な代わり、 ちょっと複雑な処理を書こうとすると色々書かなきゃならないのが面倒になってくるのですが。 その「本文」の流れに邪魔になる様々な定義はほかの部分、

  • IDENTIFICATION DIVISION(『見出し部』)
  • ENVIRONMENT DIVISION(『環境部』、このプログラムでは省略されている)
  • DATA DIVISION(『データ部』)

に記述するようになっています。

このプログラムは、 例えば"World"を入力すると、"Hello World!"と返すものです。 以下のような感じ。

? World
Hello World!

このユーザー入力を受け取る部分は "ACCEPT WK-NAME."と書かれている行になります。 「"WK-NAME"という『変数』に入力を受け取る」という意味の文です。 この『変数』"WK-NAME"の定義はデータ部にあります。

       WORKING-STORAGE SECTION.
       01 WK.
         02 FILLER  PIC X(6) VALUE "Hello".
         02 WK-NAME PIC X(5).
         02 FILLER  PIC X    VALUE "!".

"PIC X(5)"は、 まあ、ざっくり言えば、「5文字分の領域」という意味になります。 なので、富士通のNetCOBOLの場合、 5文字分入力されるまで入力を待ち続けます。 3文字入力してEnterを押してもあと2文字入力されるまで律義に待ち続けます。 ちなみに、5文字以上を入力すると、余った入力は切り捨てられます。

で、プログラムの最後では、"WK-NAME"を含む"WK"全体を一気に出力しています。

           DISPLAY WK.

今時の感覚からすると、 文字列とかは可変長の「文字列オブジェクト」で扱ってほしいし、 テキストのフォーマットももっと動的に処理すれば使い勝手良いだろうに、 とか思うのです。 まあ、複雑な文字列編集を行うための文とかもあるのですが、 この程度ならばこんなふうに書いてしまうことが多い。

このあたりのデータの取り扱いの感覚が今時の言語との大きな違いではないかと思います。 何と言いますか、発想が帳票的です。 上のデータ定義、 そう思ってみると、 帳票的なイメージがしてきませんか? "WK"という一塊のデータ(レコードと言います)を定義し、 その内部の各欄に6文字、5文字、1文字というサイズを割り当てています ("FILLER"は「名前?名前などどうでもよい」という意味)。

それは構造体のようなものではないのか? 構造体の先祖と言えるかもしれませんが、 今となっては「構造体」で期待するものとかなり違うんじゃないかなあ。 COBOLのレコードは参照が含まれることがほとんどないので、 本当に固定長のべたな領域を区切っていることになります。 領域に余白ができたら自動的に空白を詰めたりします。 ファイルの読み書きもそのレコードをそのままべたに読み書きする感じ。

今の眼からすると、 COBOLはそういった帳票的なデータを加工したり入出力したりするための言語とイメージすると、 分かりやすいんじゃないでしょうか。

「事務作業なんて要するに何かの帳票を読んで別の帳票を作ることだろう」と割り切ったとでも言いますか。 英語の文章的な記述法といい、 おかんは技術者ではない一般の人でも事務作業を自動化できるようなモノを作りたかったのではないかと思います。 今で言えば、Excel的なものでしょうかねえ。 ただ、事務作業向きといっても、 やろうと思えばかなりのことができてしまいます。 それが行き過ぎて、神Excelならぬ神COBOLみたいなコードも大量にあったりします。 適材適所は大事。

ともあれ、 COBOLのデータの取り扱いは今時の言語のデータの取り扱いとは発想がかなり違っています。 この違いが、十進演算と並んで、COBOLの置き換えが結構難しい技術面での原因ではないかと思います。 興味があれば、 COBOLの表の添字付けについても調べてみてください。 COBOLの表は多くの言語での配列に相当するものですが、 多段になっている場合の添字付け方法が今時の言語とまったく異なっています。

ちなみに、 このデータ部の変数を定義する部分(WORKING-STORAGE SECTION)、 日本語では「作業場所節」と言います。 変数は「場所」なのです。 いかにもです。 ここに定義したデータは、C#やJavaなどで言うところのstatic的に割り付けられます。 つまり、もう一回このプログラムを呼び出すと、 "WK-NAME"には前回のデータが残ってます。 いったんプログラムがロードされてしまえば、 メモリ不足やStackOverflowが発生しにくい安心安全設計。 なお、2002年規格以降は、 ローカルなデータも定義できるようになっています。 ローカルなデータは局所記憶節(LOCAL-STORAGE SECTION)という場所に書きます。

いにしえの記法について

上のプログラム、左の余白が目につきます。 実は、この余白(行の先頭6カラム)は行番号領域です。 ですが、今となっては空白にしておくのが吉です。 下手に自動ナンバリングとかしてしまうと、 行を追加/削除した場合に以降の行番号がずれて、 ソース管理上そいつら全部が変更された扱いになってしまいます。

また、7カラム目は標識領域で、 ここに「*」が記述されていたら、その行はコメントです。

こんな調子で、昔のCOBOLのソースにはカラムに意味があります。 古典的な書き方では、73カラム以降はコメント(見出し領域)です。 下手にソースをUTF-8化すると、非ASCII文字を含む行で本文のカラムが増えて、 コード末尾が勝手にコメントになってしまうという事故が起きたりします。 その他、「この要素はこの領域から書き始めないとダメ」みたいな規則もあり、 解析処理を書く際にカラムの考慮が極めてだるいです。 文字コードの問題がからむとなおさら。

なんでこんな書き方をするのか。 今でこそみんなPCを持っていて、 エディタでプログラムを書くわけですが、 昔は一人一台端末を占有しつづけるなどできませんでした。 じゃあ、どうやってプログラムを書いていたかといえば、 こんな紙に書いていました。

コーディング用紙

コーディングシートというやつですね。 この紙に書いたプログラムをコンピューター室に持っていって入力したり、 さらに古くはパンチカードを打ってそれを読み込ませたりしていたそうです。 私自身は経験ないけど。

COBOLの古い書式は、このような昔のやり方に合うようにできていたのでしょう。 今では、カラムを意識しない「自由形式」という書法でも書けるのですが、 デフォルトは互換性のためにこの古い「固定形式」ですし、 なにより大量にある既存のソースが古い形式で書かれています。 カラムの呪縛からはなかなか逃げられない。

現状など

さすがに今となっては、世の中の最前線の課題の解決を期待されるようなことはあまりない、と思います。 が、COBOLの特徴は無限のCOBOLプログラムを内包する世界を創り出してしまっていることにあります。 Unlimited COBOL Worksです。 皆さんも日常生活で間接的にCOBOLプログラムを利用しているはずです。 銀行や自治体のシステムとか、表はWebで操作したとしても、 その最深部ではCOBOLプログラムが動いていたりします。 ちょっと古いのですが、2010年頃、 世界で2400億行のCOBOLコードが稼働しているという話がありました。 現在もまだまだコードが書かれています。 こいつらを無事に動かし続けなければなりません。 そのため、 新しいOSや環境で動くようにしたり、 連携できるようにしたり、 やることはまだまだあります。

で、新しいシステムと連携できるようにしたりすると、 「またCOBOLかよw」などと言われたりするわけですね。 数年前にHadoop対応をしたときも「なんでまた」みたいなコメントがありました。 あれ、ビッグデータをやりたいわけではなく(やってもらってもかまわないのですが)、 既存のCOBOLバッチを分割して並行に走らせたいんだけど、 それを独自の基盤を作るのではなく、 よく知られている基盤の上でやろうという話です。 別に流行りだからと何も考えずにやっているわけではないのです。 いや、何も考えずに流行りものをやってみたいとか、思ったりもしていますけど。

ハードや環境が変わっても同じ機能を動かし続けることができる、 というのがソフトウェアに期待される役割のひとつだとすれば、 それをしぶとく勤め続けております。 まだ当分、COBOLは使い続けられることでしょう。 COBOLで「先の規格」といえば応仁の頃に制定されたやつのことですが、 現行は2014年版で、 その次に向けても動きが始まっています。

今時の機能をCOBOLで書いてみる

せっかくなので、COBOLでもう少し書いてみましょう。 富士通の.NET向けCOBOL(NetCOBOL for .NET)を使ってみます。 実は、最古参の高級言語であるところのCOBOLは.NETプラットフォームにおいても最古参のサードパーティ言語だったりします。 2000年の.NET Framework発表直後のPDC 2000で、 ゲイツ先生に「わしも昔はCOBOL書いたがな」と紹介されてデモしたりしてます。 Channel 9でビデオを見れます。 COBOLの紹介は1:27:30頃から。今から見ると実に素朴。

ここで、 .NETとCOBOLのデータ型のマッピングなどを説明して、 「COBOLプログラムを.NETの一般的なデータ型で呼び出せるようにラップしてライブラリ化すれば、任意の.NET言語から利用可能になります」 とか述べればそれなりにお役に立つのでしょうが、 それはマニュアルにも書いてあるし、 せっかくのアドベントカレンダーですので、 ふつうCOBOLで書かないような処理を書いてみましょう。

async処理をCOBOLで書いてみます。

お題としては、 以下のような非同期でWeb上のテキストファイルを取ってくるメソッドがライブラリとして提供されているとして、

public class Downloader: IDisposable {
    private HttpClient httpClient = new HttpClient();

    public async Task<string> DownloadText(string uri) {
        string text;
        using (HttpResponseMessage response = await this.httpClient.GetAsync(uri)) {
            if (!response.IsSuccessStatusCode) {
                throw new Exception($"Error: {response.StatusCode}");
            }
            text = await response.Content.ReadAsStringAsync();
        }

        // 以下の行を入れてわざと待たせるようにすると、
        // 非同期に動作していることがGUI上から分かりやすい。
        await Task.Run(() => { Thread.Sleep(3000); });
        return text;
    }
    ...
}

以下のようなイベント処理をもつWindows FormをCOBOLで書いてみます。

private Downloader downloader = new Downloader();
...
private async void button_Click(object sender, EventArgs e) {
    // 処理中はボタンを無効化し、二度押しを防ぐ。
    this.button.Enabled = false;
    try {
        string message;
        try {
            string text = await this.downloader.DownloadText("http://www.msftncsi.com/ncsi.txt");
            message = $"[{DateTime.Now}] {text}";
        } catch (Exception exception) {
            message = exception.Message;
        }
        this.label.Text = message;
    } finally {
        this.button.Enabled = true;
    }
}

テキストのダウンロードは非同期処理なのですが、 その後、ラベルのテキストを更新してボタンを有効化する処理があります。 awaitを記述することで、 そのあたりがすっきり記述できています。 C#のasync/await機能ですね。

実行して、ウィンドウのボタンを押すと、こんな結果になります。

サンプルのGUI

Visual Studio 2017向けのNetCOBOL for .NETは(国内向けには)まだ出ていないので、 Visual Studio 2015 + NetCOBOL for .NET V7で書きます。

NetCOBOL for .NETはWindows Formsをサポートしています。 NetCOBOLのWindows Formsプロジェクトテンプレートからそのままプロジェクトを起こせます。 なぜWPFでないかと言えば、 残念ながらNetCOBOL for .NETでは諸般の理由によりWPFベースのGUIの作成をサポートしていないからです。 だって死にそうな思いで無理くりCodeDOM実装したのに、WPFではまったく別のコード生成インターフェイス使いよるし。 まあ、今となってはどっちみちRoslyn以前のものだけど。

で、Windows Formsを使うとして、 このasync付きのイベント処理をどうやってCOBOLで書くか。 当然、現在のCOBOLにはasync/await構文とかありません。 が、幸いなことに、.NETの機能の多くは素のクラスを使って記述可能です。 genericsを最後に構文に手を入れる必要があった覚えがありません。 LINQだろうがasyncだろうが、素のクラスとメソッド呼び出しだけで、がんばれば書けます。 がんばれば。

というわけで、 上のイベント処理を素のクラスとメソッドを使った処理に展開します。

  • 非同期処理をTask<string>オブジェクトを使う形で記述します。 非同期処理完了後の後続処理の実行はTask<string>.ContinueWith()メソッドを使ってスケジュールします。 C#のasync/awaitの展開では、ContinueWith()を単純に使わずに内部クラスを生成して色々処理をしています。 おそらくawaitが複数ある場合や例外処理をうまく処理するためでしょう。 が、今回の処理程度であれば、ContinueWith()で十分でしょう。
  • GUIはGUIスレッド上で操作しなければならないことに注意します。 C#のasync/awaitの展開ではよしなに計らってくれますが、 自力で展開する場合は自分で実装する必要があります。 ここでは、FormInvoke()メソッドを使います。
  • COBOLでラムダは書けないので、独立したメソッドに処理を記述します。 クロージャーが必要な場合は、そのためのクラスを新たに作らなければなりませんが、 幸いこのケースではそこまで必要ありません。 このForm内にメソッドを定義するだけで十分です。

いきなりCOBOLで書いても分かりにくいでしょうから、 まずC#で書きます。 以下のようなコードをこの後COBOLで書きます。

private Downloader downloader = new Downloader();
...
private void button_Click(object sender, EventArgs e) {
    this.button.Enabled = false;
    try {
        this.downloader.DownloadText("http://www.msftncsi.com/ncsi.txt").ContinueWith(
            new Action<Task<string>>(this.ContinuedProc)
        );
    } catch {
        FinallyProc();
        throw;
    }
}

private void FinallyProc() {
    this.button.Enabled = true;
}

private void ContinuedProcBody(Task<string> task) {
    try {
        string message;
        if (task.Exception == null) {
            message = $"[{DateTime.Now}] {task.Result}";
        } else {
            message = task.Exception.Message;
        }
        this.label.Text = message;
    } finally {
        FinallyProc();
    }
}

private void ContinuedProc(Task<string> task) {
    // GUIスレッド上で処理を行う。
    this.Invoke(new Action<Task<string>>(this.ContinuedProcBody), task);
}

ではCOBOLで書きます。 こういう場合以外なかなか使ってもらえないオブジェクト指向構文(2002年規格で導入)が炸裂します。 全体を載せると長くなるので、イベント処理関連の部分のみ掲載します。 クラス名の定義とかクラス定義の環境部にあるので、ここだけだと記述が完結しないけど、 まあ雰囲気を見てもらえれば。 ソース全体はGitHubに置いています。

一つおことわりを。 以下のソースでは、 メソッドの『ローカル変数』をWORKING-STORAGE SECTIONに書いています。 規格的にはここはLOCAL-STORAGE SECTIONであるべきですが、 NetCOBOLでは、歴史的な事情により、 メソッドの『ローカル変数』はWORKING-STORAGE SECTIONに書きます。

では。

       METHOD-ID. button_Click PRIVATE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WK-CONTINUED-PROC OBJECT REFERENCE DEL-ACTION-TASK.
       01 WK-TASK OBJECT REFERENCE CLASS-TASK-STRING.
       LINKAGE SECTION.
       01 sender OBJECT REFERENCE CLASS-OBJECT.
       01 e OBJECT REFERENCE CLASS-EVENTARGS.
       PROCEDURE DIVISION USING BY VALUE sender e.
      *    前準備
      *    複雑な型パラメータをもつ型によるメソッドオーバーロードの解決に問題があるので、
      *    ここではAction<Task<string>>ではなく、Action<Task>引数で
      *    Task.ContinueWith()を呼び出す。
           INVOKE DEL-ACTION-TASK "NEW" USING SELF N"CONTINUED-PROC" RETURNING WK-CONTINUED-PROC.
      
      *    ボタンを無効化する。
           SET PROP-ENABLED OF button TO B"0".
           TRY
               SET WK-TASK TO WK-DOWNLOADER::"DownloadText" (N"http://www.msftncsi.com/ncsi.txt")
               INVOKE WK-TASK "ContinueWith" USING BY VALUE WK-CONTINUED-PROC
           CATCH
               INVOKE SELF "FINALLY-PROC"
               RAISE
           END-TRY.
       END METHOD button_Click.
      
      *button_Clickで必ず行うべき後処理
       METHOD-ID. FINALLY-PROC PRIVATE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WK-BUTTON OBJECT REFERENCE CLASS-BUTTON. 
       PROCEDURE DIVISION.
      * ボタンを有効化する。
           SET WK-BUTTON TO PROP-BUTTON OF SELF.
           SET PROP-ENABLED OF WK-BUTTON TO B"1".
       END METHOD FINALLY-PROC.
      
      *ダウンロード終了後に行いたい処理の本体
       METHOD-ID. CONTINUED-PROC-BODY PRIVATE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WK-TASK OBJECT REFERENCE CLASS-TASK-STRING.
       01 WK-EXCEPTION OBJECT REFERENCE CLASS-EXCEPTION.
       01 WK-MESSAGE OBJECT REFERENCE CLASS-STRING.
       01 WK-TEXT OBJECT REFERENCE CLASS-STRING. 
       01 WK-NOW OBJECT REFERENCE CLASS-DATETIME.
       LINKAGE SECTION.
       01 PARAM-TASK OBJECT REFERENCE CLASS-TASK.
       PROCEDURE DIVISION USING BY VALUE PARAM-TASK.
      *    タスクをTask<string>型にキャストする。
      *    実際のタスクの型はTask<string>だが、インターフェイス上はTaskにしている。
      *    button_Clickメソッドの手続き部のコメントを参照。
           SET WK-TASK TO PARAM-TASK AS CLASS-TASK-STRING.
      
           TRY
      *      ラベルのテキストを更新する。
             SET WK-EXCEPTION TO PROP-EXCEPTION OF WK-TASK
             IF WK-EXCEPTION = NULL THEN
               SET WK-TEXT TO PROP-RESULT OF WK-TASK
               SET WK-NOW TO PROP-NOW OF CLASS-DATETIME
               SET WK-MESSAGE TO CLASS-STRING::"Format" ("[{0}] {1}" WK-NOW WK-TEXT)
             ELSE
               SET WK-MESSAGE TO PROP-MESSAGE OF WK-EXCEPTION
             END-IF
             SET PROP-TEXT OF label1 TO WK-MESSAGE
           FINALLY
      *      ボタンを有効化する。
             INVOKE SELF "FINALLY-PROC"
           END-TRY.
       END METHOD CONTINUED-PROC-BODY.
       
      *「ダウンロード終了後に行いたい処理」をGUIスレッド上で実行するメソッド
       METHOD-ID. CONTINUED-PROC PRIVATE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 WK-PROC OBJECT REFERENCE DEL-ACTION-TASK.
       LINKAGE SECTION.
       01 PARAM-TASK OBJECT REFERENCE CLASS-TASK.
       PROCEDURE DIVISION USING BY VALUE PARAM-TASK.
      *    GUIスレッド上で処理を行う。
           INVOKE DEL-ACTION-TASK "NEW" USING SELF N"CONTINUED-PROC-BODY" RETURNING WK-PROC.
           INVOKE SELF "Invoke" USING WK-PROC PARAM-TASK.
       END METHOD CONTINUED-PROC.

C#のFormが106行(Form1.cs: 38行、Form1.Designer.cs: 68行)なのに対し、 COBOLのForm1.cobは1126行です。 いまどき行数で生産性を計っている方々、 お喜びください。 生産性10倍以上です。

まあ、COBOLのFormにはデザイナが自動生成する特殊コメント等が大量にありまして(しかも事情があってちょっと冗長)、 単純集計だと極端に行数が増えます。 なので、button_Click処理に限定しましょう。 それでも、C#: 16行に対し、COBOL: 83行です。 COBOLのスタイルだとどうしても記述量は増えます。

やはり、言語は適材適所に使っていただいて、 適切な言語で記述された今時のシステムから伝家のCOBOLプログラムを呼び出し、 末永く利用していただければと思います。

謝辞

古来、 COBOL仕様扱う文書はCODASYLへの謝辞を掲載していました。 謝辞を掲載することで、 COBOL仕様書の内容やアイデアを自由に利用することが許可されていました。 せっかくですので、 古式ゆかしく、 CODASYLへの謝辞で終わりたいと思います。 CODASYLはもうないけど。

以下、1984年版の謝辞(「CODASYL COBOL JOURNAL OF DEVELOPMENT 1984」から)。

【謝辞】

COBOLは産業界の言語であり、特定の団体や組織の所有物ではない。 CODASYL COBOL委員会又は仕様変更の提案者は、 このプログラミングシステムと言語の正確性や機能について、 いかなる保証も与えない。 さらに、それに関する責任も負わない。

次に示す著作権表示付きの資料の著作者及び著作権者は、 これら全体又は一部分をCOBOLの原仕様書中に利用することを許可した。 この許可は、COBOL原仕様書をプログラミングマニュアルや類似の刊行物に複製したり、 利用したりする場合にまで拡張される。

  • FLOW-MATIC(Sperry Rand Corporationの商標), Programming for Univac(R) Ⅰ and Ⅱ, Data Automation Systems, Sperry Rand Corporation 著作権表示1958年, 1959年
  • IBM Commercial Translator Form No.F28-8013, IBM 著作権表示1959年
  • FACT, DSI 27A5260-2760, Minneapolis-Honeywell 著作権表示1960年

今後ともよろしくお願い申し上げます。

2017年3月 7日 (火)

OAナガシマのおっさんの話(Visual Studio 20周年記念)

Visual Studioは今年で20周年だそうで、 #MyVSStory などというハッシュタグもできていました。

おおっと思ったので、こんなことを書きました。

で、OAナガシマのおっさんのことをちょっと思い出したので、書き残しておこうかなと思います。 実はそんなによく知らないし、 人柄を表すようなエピソードとかも無いんだけど、 大昔、おっさんを店頭で見かけていた頃の話。

OAナガシマとは

私が沼津に来たのは90年代始めの頃です。 地元の人に「沼津のパソコンショップならここ」と教えてもらったのがOAナガシマでした。 「OAナガシマ」という店名からして、 OA事務機器屋が出自なのでしょう。 (そういえば、当時DOS/V雑誌に怪しげな広告を載せていた「大西ジム」も高砂の商店街の文房具屋(ジム = 事務)でした。何回か行ったことある。今検索したら、まだ元気でやっているらしい)

その頃の店は沢田のバイパス沿いにあり、まあ、いわゆるDOS/Vショップでした。 世界的には、ちょうどPC/AT互換機上でWindowsがブレイクした頃です。 日本ではNECやら富士通やら東芝やらが独自規格のパソコンを出しており、 それらは結局PC/AT互換(いわゆる「DOS/V機」)になっていくのですが、 その時点ではDOS/Vはまだ立ち上がりかけの頃でした。 Windowsが盛り上がっており、海外で面白そうな機器やソフトが出るのに、 日本は独自規格のせいでなかなか利用できない。 Windows 3.1とか、USで発売されてから国内発売されるまで1年以上かかったなあ。 各社版の対応に手間取っている、とかの噂でした(本当かどうかは知らない)。 「鎖国状態」とか言われたりしてましたね。今ならガラパゴスか。 お好きな方はPC/AT互換機を個人輸入したりしてましたが、 そういう「DOS/V」世界の窓口がDOS/Vショップでした。 秋葉原ならたくさん店もあるんでしょうが、 地方なもんで、 近所にOAナガシマがあるのはとてもありがたかった。

なんか、DR-DOS買ったこととか思い出した。 冷やかしのつもりで店番してたおっさんに声をかけたら、 勢いに乗せられて気づいたら買ってて、 しばらくDR-DOS使ってたよ。

さて、時は1992年

Windowsはまだ16-bitの3.xの時代。 ちょうどアメリカでVisual Basic 1.0(これがまたちょっとした衝撃だった)が出た翌年。 私は「某言語でVisual Basicみたいのを作ろう」というプロジェクトに配属されました。 当時のWindows開発環境は、コマンドラインベースのMicrosoft C 6.0 + Windows SDK。 デバッガ(Code View)はこんな感じ。 (Microsoftのフォーラムの質問にCode Viewの画面イメージがあったので引用)

Code Viewの画面

せっかくWindowsがGUIを導入したのに、開発環境はキャラクタベースです。 ついでに言うと、会社支給のパソコンはFMRという独自規格。 それに富士通が出しているFMR用のDOSとWindowsとWindows SDKを入れて頑張るわけです。 (ちなみに、富士通の「DOS/V機」であるFMVは翌年秋の発売でした)

そんな中、 このプロジェクトが調査用に購入していたソフトウェアの中にQuick C for Windowsがあって、 これを動かしてみて私は衝撃を受けました。 フルGUIだし、「統合」されていて、ビルドからデバッグまでその上ですべてできる。 Windowsが普及し始めたころで、当時はWebとかもまだ広がってないためググるわけにもいかず、 Petzold本とかを頼りにプログラムを書いて色々挙動を確認するんですが、 この「ちょっと書いて動かしてデバッガで状況を見る」ということがGUI上で一目瞭然にストレスなくがんがん回せる。 生産性、という言葉では気が済まない、なにかパラダイムシフトのようなものが頭の中で起こりました。 目から鱗。

巧みな操作で計算機リソースを効率的に使う、 というのがスマートだと思っていました(まわりの達人的な人はほとんど共用ワークステーションとかにぶら下がってたし)。 しかし、考えたらこれは「パーソナル」なコンピュータなので、 CPUを自分の都合のためにぶん回すのは大正義である、 いや、自分が仕事を遂行するためにぶんぶんぶん回すべきである。 能天気にカーソルキー押しっぱなしてスクロールするのはCPUを無駄に使う頭悪そうなやり方だけど、 それで簡単に目的の場所に行けるならいいじゃないか。 「パーソナル」用機械を好きに使ってさっさと仕事しろよ、ということに思い至ったのでした。

人によってはVisual Basicが目から鱗だったりするようですが、 私はQuick C for Windowsだったなあ(いやまあ、Visual Basicも凄かったけど)。

ということで、 同じく調査用に海外から購入してたGateway 2000のマシン(鉄の筐体、純白の4DX2-66V)を占有し、 DOS/VとWindowsを入れ、Quick Cでごりごりプログラムを書いていました。

そんなこんなしているうちに

アメリカでVisual C++ 1.0が出ました。 「むっちゃ欲しい」と思うわけです。 IDEも進化しているみたいだし、CもC++になっているし。 でも日本では発売していない。

で、とある日曜日にOAナガシマに行ったら、 なんということでしょう。 置いてあるんですね。 でかい箱がでーんと置いてある。 おっさん、どこからモノを引っ張ってきたんだと思いました。 しかも、US価格から想定した値段よりかなり安い。 バッタもんじゃなかろうかとも疑ってまじまじと見てみましたが、どう見ても本物。 結局買いました。 両手でないと持てない大きさで、重かった。 開けてみると、中にはみっしりとWindows APIのマニュアルが入ってた。

当時の写真を探したら、箱が写っているものが一枚ありました。 あまりに部屋が乱雑で全景を出すのはアレなので、一部切り出しますが、箱の奥行をお察しください。

Visual C++の箱

探してみたら、フロッピーディスクまだありましたよ。

Visual C++のインストールフロッピーディスク

安かった理由は謎です。 仕入れ経路で誰か何か間違えてたか。 実はアップグレード版か何かだったりして。

ともかく、 Visual C++というアイテムを手に入れた私は絶好調でぶいぶいいわせながら開発したのでした。 当時まだ私物ソフトの使用禁止とかルール化される前でしたね。 まあ、仕上げには会社設備のMSCでコンパイルするようにはしてたけど。

その後

Visual C++はVisual Basicと統合されてVisual Studioになりました。

一方、 OAナガシマの方は、どんどん店が増えていきました。 本店は沢田から今ある大諏訪に移り、 ZOAという名前で静岡県以外にも店を出すようになりました。 今の正式な社名は「株式会社ZOA」のようですね。 その頃にはもうおっさんを店頭で見ることはなくなりました。 そういえば、Windows 2000の発表時には、 マイクロソフトの発表資料に他のメーカーやショップに並んで、 おっさんのコメントが載っていたなあ。 今検索して探し出したプレスリリースには社名しか出ていないけど、 当時のリリースではこのコメントがおっさんの名前入りで出てて、Webニュース経由で見てのけぞった記憶がある。

そして、一、二年前、 店の営業時間を確認するためにOAナガシマのサイトを見に行って、 間違って「会社概要」を開いてしまったら、 代表者の名前がおっさんじゃなくなっていました。 引退したのかな、とも思ったのですが、 気になったのでIRを見てみたら、 2013年の夏におっさんが亡くなったとの知らせが出ていました。

昔の沢田の店も、 去年更地になり、 ポルシェの店になりました。

その頃(もう四半世紀前)からすると、 PCをめぐる状況も、 OSやソフトや開発環境のあり方もずいぶん変わりました。 一方、その頃作っていたそのソフトは、 幸運にも気に入ってくれた人が結構おり、 バージョンを重ね、 まだ稼働しているものもあります。 ソフトウェアが世に出るのはいろんな人の仕事やら偶然やらが積み重なった結果なのですが、 あのソフトには「おっさんがVisual C++をどっかから引っ張ってきてくれた」という要素も入っているのです。

ということで、 おっさんのご冥福をお祈りします。 と同時に、Visual Studio 20周年おめでとうございます。

2017年1月26日 (木)

認証プロキシをなんとかするためにツールを作っている話

【要約】認証プロキシをなんとかするためにツールを作ってGitHubで公開した。

経緯

社内ネットワークからhttp/httpsで外部に出る際にはプロキシを通さなければならない、 という環境は多いと思います。 こういうプロキシの中にはユーザー名とパスワードを入れないと通してくれないタイプのものがあります。 認証プロキシというやつですね。 これは本当に頭痛のタネです。

さすがにブラウザとかは認証プロキシに対応しているのですが、 これに対応していないソフトウェアも結構あります。 今時いろんなソフトウェアがhttp/httpsで通信していまして、 特に開発系のツールは必要なコンポーネントをhttp経由で自動的に取ってくるみたいな動作をすることが多いのです。 こういうソフトウェアの多くが素の状態では認証プロキシを通らない。 家で試すと特に問題なく動くツールが、 会社環境だと意味不明なエラーで動かなくなったりします。

ここ二三年、 わけのわからないエラーが出るたびに、 どれが引っかかっているのか調べ、 その設定方法を調べ、 ちまちまと設定しては乗り切っていました。 結果、あちこちの設定ファイルに認証プロキシのパスワードが埋め込まれて、 なにかが間違っているような気がするわけですが、 まあ、仕方ないね。

が、最近Visual Studio 2017 RCのアップデートが認証プロキシ環境内で帰ってこなくなり、 対処方法が皆目わからないという事態が発生するに至って、 我慢の限界を越えました。 こんなもん、やっとれるかーーーーーーーー。

というわけで

ツールを作りました。

これはWindowsのデスクトップ上でローカルプロキシとして動作します。 動かすと、現在のユーザーのプロキシ設定を書き換え、 自分をプロキシとして登録し、 ソフトウェアからの通信を中継します。 で、本来の認証プロキシが認証を要求すると、 通信元のソフトウェアに帰ることなく認証情報をつけてリクエストを再送信します。 結果として、 ソフトウェアが普通のプロキシにふつうに対応していれば、 そのまま通信できます。 中継はタスクトレイのアイコンから簡単にON/OFFできます。

これは認証プロキシを回避するものではありません。 認証プロキシに対応していないソフトウェアのhttp/https通信を中継して、 正しく認証プロキシを通すようにするものです。

効能としては以下の通り。

  • 認証プロキシ環境内で認証プロキシ非対応ソフトウェアを利用することができます。
  • 認証プロキシのパスワードをあちこちに書かなくてすみます。 パスワードは暗号化された状態で設定ファイル一箇所にあれば十分です。 (まあ、元々認証プロキシのパスワードはあんまり安全でないやり方でやりとりされますので、気休めではありますが)
  • 仮想化ソフトウェア(Hyper-Vなど)が定義したマシン内ネットワークに対する(通常)プロキシとして利用できます。 これを利用すると、 「プロキシの設定を埋め込んだままのイメージを公開してしまった!」という事故をやらかしても、 パスワードが埋め込まれないため、多少状況はましになります。

GitHub上で公開しています

設定ウィンドウとかまだ実装できていないけど、 とりあえず役立つ程度には動いているので(Visual Studio 2017 RCのアップデートもできたし)、 tu公開します。 モノはGitHub上にあります。 ツール名は「認証プロキシ爆発しろ!(英語名は MAPE: May Authentication Proxy Explode)」です。

ipponshimeji/MAPE (GitHub)

詳細な説明とかは、 以下のページから。

目次 - 認証プロキシ爆発しろ! (GitHub)

手っ取り早くどういうものか知りたい方は、特に以下のページを。

認証プロキシでお困りの方はお試しくださいまし。

ちなみに

Windows環境で認証プロキシをなんとかするにはFiddlerを使う技とかもありますが、 以下の理由から独立したツールを作りました。

  • Fiddlerを使う場合、Fiddlerのスクリプトをいじる必要(しかもちょっと複雑)があり、 非開発者な人に「これを使えばなんとかできます」とか言いにくい。
  • 自動構成スクリプトでプロキシが設定されている環境では、 Fiddlerのプロキシ設定書き換えは「次で始まるアドレスにはプロキシを使用しない」を設定してくれない。 認証プロキシが設置されているような環境では自動構成スクリプトを利用していることが多いでしょうから、 これは面倒の元になりがちです。

なお、Fiddler自体は便利なデバッグツールとして利用させていただいております。

【2017年4月11日追記】

上にはこう書きましたが、 「次で始まるアドレスにはプロキシを使用しない」に相当する設定がありました。 すいません。

2016年11月23日 (水)

VS2017RCでローカルのDockerコンテナ上でアプリをデバッグ実行しようとすると"MSB4018"エラーになる問題

覚え書き。

問題点

"Connect(); 2016"のキーノートのデモで、 Visual Studio 2017を使ってASP.NET CoreアプリをローカルのDockerコンテナで動かすというやつ(ビデオでは1時間10分頃から)があります。 それを見て、いっちょやってみようと思うわけです。

ということで、Visual Studio 2017 RCとDocker for Windowsをインストールし、 「ASP.NET Core Webアプリケーション」をテンプレートから作り、 Docker上でデバッグ実行しようとすると、 以下のエラー(MSB4018)が出ます。

"PrepareForLaunch" タスクが予期せずに失敗しました。

Microsoft.DotNet.Docker.CommandLineClientException: Creating network "webapplication12161511896_default" with the default driver

Building webapplication1

Creating webapplication12161511896_webapplication1_1

ERROR: for webapplication1 Cannot create container for service webapplication1: E: drive is not shared. Please share it in Docker for Windows Settings

Encountered errors while bringing up the project..

解決法

Docker for Windowsにドライブ共有の設定を追加します。

Docker for Windowsの"Settings"画面の"Shared Drives"タブを開いて、コンテナがローカルディスクを参照できるようにします。 要するに、 ここに書いてある設定を行う。

私の例の場合、プロジェクトを置いているドライブ(E:ドライブ)とシステムドライブ(C:ドライブ)の両方を共有する必要がありました。

また、セキュリティソフト等でファイヤーウォールが有効になっている場合、 Docker for Windowsのネットワーク上(デフォルトでは 10.0.75.0/24)でWindowsのファイル共有(TCPのポート445)が通るようにしておかなければなりません。 その説明は、ここ。 具体的な設定の仕方は使っているセキュリティソフトによって異なります。

まあ…

Visual Studio Tools for Dockerのドキュメントにはちゃんと説明されているし、 エラーメッセージもちゃんと読めば指示が書いているんですけどね。 キーノートのビデオ見て「いっちょやったろ」とそのままやってみたら、私のようにはまるかなと。

2016年7月20日 (水)

xUnit.netのテストがTest Explorerに出てこない

覚え書き。

xUnit.netを使っていてテストエクスプローラーにテストが出てこない、 というのはたまに起こって毎回原因探しに苦労するんだけど、 今回は条件がはっきりしているので記録しておく。

問題点

通常、 以下の手順でxUnit.netのテストを書いてビルドすると、 Visual Studioの「テスト エクスプローラー」上に記述したテストが自動的にリストアップされるはずだが、 それが出てこない。

  • Visual Studio 2015 Update 3で、
  • C#の「クラス ライブラリ(ポータブル)」プロジェクトを作り、
  • NuGetで以下のコンポーネントを追加し、
    • xunit 2.1.0
    • xunit.runner.visualstudio 2.1.0
  • xUnit.netのテストを記述する
  • プロジェクトをビルドする

発生条件

「クラス ライブラリ(ポータブル)」の「ターゲット」として以下を選択すると発生するっぽい。

  • 以下の二項目のみを選択する。または、
    • .NET Framework 4.6
    • Windows Universal 10.0
  • 以下の三項目のみを選択する。
    • .NET Framework 4.6
    • Windows Universal 10.0
    • ASP.NET Core 1.0

どうも、 ターゲットを上記条件にすると、 プロジェクトがNuGet 3を使う構成になるようです。 そうなった場合に、 テストがテストエクスプローラーに出てこなくなるみたい。 一部の設定がうまく組み込まれないのかねえ。

ちなみに、 ターゲットを「.NET Platform Standard」にしても プロジェクトがNuGet 3構成になるようです。 この場合、xunit 2.1.0を組み込もうとしても、 「Some packages are not compatible with .NETStandard」と文句を言われてそもそも組み込めません。 最新のプレリリースである、"xunit 2.2.0-beta2-build3300"ならば組み込むことができました。 それでもテストエクスプローラーにテストは出ないけど。

回避法

見つけられませんでした。

まあ、当面ターゲットを".NET Framework 4.5" + "Windows 8" + "ASP.NET Core 1.0"ぐらいにしておいて、 様子を見ますかねえ。

今後Portable Class Libraryは.NET Platform Standardベースになっていくだろうし、 プレリリース版で.NET Platform Standardに対応しようとしている感じなので、 待っていればそのうち対応されるんじゃなかろうか。 知らんけど。