VBプログラマのための 基礎からわかるASP徹底活用・構築技法

Windows WebSiteの、もうひとつのアプローチ
Apache+VB

ApacheとVBで汎用度の高いサイトを構築する



初音 玲 HATSUNE, Akira



Windows 2000になり、IISのバージョンが5.0になった。しかし、単にバージョンが上がっただけではない。IIS4.0まではInternet Information Server 4.0だったが、IIS5.0からはInternet Information Services 5.0になったのだ。つまり、IIS4.0までとは、まったくの別製品に仕上がっている。他のWebサーバー製品と同じ機能が実装され、ある意味、やっとIIS独自機能を評価する土台が整ったと言えるだろう。しかし、これだけ新しい技術だけに、安定するまでに多少時間がかかるかもしれないし、セキュリティホールなどの調査なども必要だろう。また、IIS5.0はWindowsとの密接度が上がっているが、システム要件によっては、それが逆にユーザー認証などで使い勝手が悪いことになるかもしれない。そういった時の選択肢を増やすためにも、IIS以外のWindows Webサーバーの構築法について考えてみたいと思う。

Apache知っていますか?

 Apacheは、NCSA httpdを母体にして、1995年からApache Software Foundationにより共同開発されているフリーソフトのWebサーバーソフトウェアだ。その処理能力や技術力の高さから、インターネット上でもっとも人気の高いWebサーバーであると言えるだろう。また、Windows NTにIISが付属しているように、FreeBSDやLinuxなど、現在注目されているUNIX処理系にはApacheが付属している。このことからわかるように、元々はUNIX上で稼働するソフトウェアだったが、Apache 1.3からは、Windows NT、Windows 95/98版の配布もはじまった。もちろん、ApacheにPerlなどのスクリプト言語を組み合わせてWebSiteを構築すれば、UNIX版とWindows版でCGI(Common Gateway Interface)を含めたリソースの相互利用が可能だ。また、アクセスしてきた利用者からは、URLなどの情報から、それがWindows版なのかUNIX版なのかは、判断がつかないだろう。Windows上で稼働していることがわかると、Windows上のセキュリティホールを攻撃するという輩もいるので、UNIX版と区別がつきにくいというのは重要な事だと思う。
 なお今回は、Apache Software FoundationのWebSite(図1)より、執筆時点での最新版であるApache 1.3.9を入手し、使用している。入手元のURLは、http://www.apache.org/dist/binaries/win32/だ。

図1:http://www.apache.org/
図1

Apache for Win32をインストールする前に

 Apacheを使うには、TCP/IPがインストールされていなければならない。インターネットと接続できるようになっていれば、そのパソコンは、間違いなくTCP/IPがインストールされているが、モデムにもLANにも接続されていなかったり、Windowsネットワークとしてしか使っていなかったときには、TCP/IPがインストールされていない可能性がある。コントロールパネルのネットワークアイコンをダブルクリックして、TCP/IPがインストールされているかを確認してほしい。もし、後からTCP/IPをインストールしたときには、Service Packの再適用も忘れずに行なうことが大切だ。Windowsの媒体CD-ROMやService PackのCD-ROMを探し出して手元に置いてから、作業を開始してほしい。なお、一部のプレインストールマシンでは、CD-ROMイメージがハードディスクにコピーされているものもあるので、そういったときは、Service PackのCD-ROMだけ手元にあればよい。

Apache for Win32をインストールする

 ApacheをWindows NT 4.0(SP3以降)、Windows 95/98にインストールするには、WebSiteからダウンロードしたapache_1_3_9_win32.exeをダブルクリックして起動するだけでよい。注意する点としては、[Choose Destination Location]画面(図2)で、Apacheのインストール先として、空白や日本語などの2バイト文字系を含まないフォルダ名を指定するくらいだろう。あとは[Yes]ボタンや[Next]ボタンをクリックしてゆくだけで、実行ファイルだけではなく、C言語のソースコードまで手に入る(図3)。
 なお、アンインストールは、コントロールパネルの[アプリケーションの追加と削除]で安全に行なうことができる。

図2:Choose Destination Location
図2

図3:インストールコンポーネント
図3

Apache for Win32を起動する

 Apache for Win32をWindows NTで使用するときは、NTのサービスとして登録可能だ。方法は、[スタート]から[プログラム]を選択し、[Apache Webサーバー]-[Apache Install As Service]メニューをクリックするだけでよい。そうすれば、コントロールパネルの[サービス]でサービスとしての起動方法などを指定できるようになる。
 サービスとして起動しないときや、Windows 95/98では、DOSプロンプトから、インストール先のapache.exeを実行する。

Apache/1.3.9 (Win32) running...
と表示されれば、無事起動したことになる。
 このとき、リスト1のようなメッセージが表示されるときもあると思う。

リスト1:Apacheからのメッセージ
APACHE.EXE: cannot determine local host name.
Use the ServerName directive to set it manually.

これは、TCP/IPの設定でDNSというインターネット用の名前検索の仕組みを登録していないためにWebサーバーの名前が決定できないという意味だ。これは、ApacheだけではなくIISを使ったときも、それどころかメールサーバーなど、あらゆるサーバーをインターネットに公開するためには、上位プロバイダ(ISP)と常時接続して、そのISPのDNSに自分のWebサーバーのIPアドレスと名前を登録しなければならないからだ。もちろん、この名前は勝手に決定してよいものではなく、JPNIC(http://www.nic.ad.jp/index-j.html)により、自社や個人に割り当てられたドメイン名を使わなければならない(ホスト名は自ドメイン名内で唯一ならば自由に設定可能)。リスト1のメッセージが表示されたとき、将来的にインターネットに公開するのであれば、自マシンのTCP/IPの設定で、DNS名を指定(図4)してからApacheを起動することだ。そうしておけば、そのホスト名とドメイン名をURLに指定することで図5のような初期ページを表示することができる。もし、www.〜のようにホスト名とは別の名前をWebサーバーに付けたいときは、インストールしたフォルダ配下のconf/httpd.confの中にServerNameを指定すればよい(リスト2)。もちろんそのときは、上位のDNSには、そのServerNameへの設定値を登録することになる。

図4:DNSの設定
図4

図5:Apacheの初期表示
図5

リスト2:サーバーネームの設定
ServerName www.balthasal.nerv.go.jp

 自社内に閉じた環境で稼働させるのであれば、NT ServerのWINSを利用して、名前からIPアドレスを引けるように環境を整えて、マシン名をServerNameに指定すればよいだろう。たとえば、http://baltasal/ というような感じだ。WINSを導入していなかったり、NT Serverがないような環境では、hostsファイルにWebサーバーを稼働させているマシンの名前とIPアドレスを指定したものをクライアント側に設定すればよい。
 自マシン上での稼働を確認するだけであれば、TCP/IPの設定で「DNSを使う」に設定する必要はない。conf/httpd.confのServerNameに好きな名前を指定からApacheを起動して、URLとしてhttp://localhost/ を指定すればよい。

Apache for Win32を停止する

 NTのサービスとして起動しているときは、コントロールパネルの[サービス]からApacheを選択して[停止]ボタンをクリックする。または、NTをシャットダウンすれば、Apacheも正常に終了処理を行なってから停止する。
 サービスとして起動していないときやWindows 95/98では、実行しているDOSプロンプトを終了させず、もうひとつDOSプロンプトを開き、インストール先フォルダに移動後、

Apache -k shutdown
を入力して、終了処理を行なわせる。もし、起動しているDOSプロンプトを終了させると、終了処理が行なわれないために、次回起動時にリスト3のメッセージが表示されるが、とりあえず心配する必要はない。

リスト3:終了処理が終わらないと、次回表示されるメッセージ
[Wed Mar 01 21:15:54 2000] [warn] pid file d:/apache/logs/httpd.pid overwritten
-- Unclean shutdown of previous Apache run?

Apache for Win32を設定する

 UNIXの世界で鍛え上げられてきたApacheは、設定値を保存するのにレジストリを使わない。決まった名前のテキストファイルに決まった形のパラメータを記述するだけでよい。それはつまりGUIな設定ツールなど必要なく、使い慣れたテキストエディタだけで設定変更ができるという、開発者にとっては最良の手段が提供されているということに他ならない。
 Apacheの設定ファイルは、インストールフォルダ配下のconf/httpd.confファイルだ。この設定ファイルを変更して、Apacheを再起動すればよい。再起動は、停止と起動でも可能だが、DOSプロンプトから、

Apache -k restart
でも可能だ。
 conf/httpd.confファイルは、大きく3つのセクションから構成されている。
 また、設定方法は、指示子とコンテナ指示子がある。指示子の形式は、

指示子 設定値

であり、コンテナ指示子の形式は、

<コンテナ指示子 指示対象>
    指示子 設定値
       :
</コンテナ指示子>
だ。コンテナ指示子は、設定する対象が複数存在するようなときに、その対象を指定するときに存在する。

Global Environment

 Global Enviromentセクションは、サーバープロセス全体に関する設定を記述する。ほとんどがデフォルト値のままで構わない。

'Main' server configuration

 Apacheでは仮想ホストといって、ひとつのApacheで複数のWebSiteを管理することができる。'Main' server configurationセクションは、この仮想ホストごとに設定できる項目のデフォルト値を設定する。もちろん、仮想ホストを設定しないときは、Apacheの設定そのものといってもよい。このセクションには重要な設定が数多くあり、そのすべてを説明するのが難しいくらい、設定項目も多岐に渡るので、今回は、その中から代表的な指示子を取り上げた。その他の指示子については、httpd.confファイルの中のコメントを参考にしてほしい。
  1. 1.【ServerName】
    サーバーの名前を指定する。
  2. 2.【DocumentRoot】
    DocumentRoor指示子には、Webに公開するルートディレクトリの実フォルダ位置を指定する。デフォルトでは、インストールフォルダ配下のhtdocsフォルダが指定されている。他のフォルダを指定したいときは、設定値として、たとえば、

    DocumentRoot "d:/data/www/pub/htdocs"
    
    のように、そのフォルダを絶対パスで指定する。なお、フォルダを指定するときの区切り記号は、“\”ではなく、UNIXと同じように“/”だ。
  3. 【Directoryコンテナ指示子】
    Directoryコンテナ指示子は、指定したディレクトリおよびその配下のサブディレクトリすべてに対する設定を記述する。DocumentRoot指示子の設定値を変更したときは、そのディレクトリを対象としたDirectoryコンテナ指示子の指示対象部分の記述を変更する。ディレクトリの指定には、実フォルダを絶対パスで指定することもできるし、Webルートディレクトリからの絶対パスも指定することができるので、Global Environmentセクションには、

    <Directory />
       :
    </Directory>
    
    <Directory "D:\Apache\htdocs">
       :
    </Directory>
    
    の2つのDocumentRootに対応するコンテナ指示子がある。このように同じ対象を指定した設定が複数ある場合、後ろに記述された指示子で設定が行なわれる(サーバー指示子が省略されているときは、すでに指定されている設定値を継承する)。よって、Directoryコンテナ指示子のようにサブフォルダも含むようなものの場合は、サブフォルダへの個別設定は、その上位フォルダの設定よりも後ろに記述しないと有効にならない。
  4. 【Options指示子】
    ASPにおける実行権のようなものを指定できる。このOptions指示子により、CGIが実行できるディレクトリを限定する。これは悪意のあるCGIプログラムのアップロードと実行の影響範囲を限定するためだ。代表的なオプションは表1の通り。セキュリティを考慮すると“/(Webルートディレクトリ)”には、

    Options None
    
    を指定して、CGIディレクトリのみ、

    Options ExecCGI
    
    と指定するのがいいだろう。

    表1:Options指示子の設定値
    オプション 備考
    Noneすべてのオプションを無効にする
    Allすべてのオプションを有効にする
    Indexesディレクトリリストをクライアント側に送信する
    Includesサーバー側インクルードを有効にする
    IncludesNOEXEC#execと#include以外のサーバー側インクルードのみ有効にする
    ExecCGICGIスクリプトの実行を有効にする

  5. 【AllowOverride指示子】
    Apacheは設定ファイルだけではなく、「.htAccess」ファイルを作成し、それを配置することにより、その配置先のディレクトリの設定を変更可能だ。これは、個人用Webディレクトリの設定をその利用者に変更可能とするようなときに便利だ。AllowOverride指示子は、「.htAccess」ファイルにより、どのような指示子を変更可能(オーバーライド)させるかを指定する(表2)。

    表2:AllowOverride指示子の設定値
    設定値 備考
    None.htAccessによる設定を無視する
    All.htAccessの設定をすべて有効にする
    AuthConfig.htAccessの認証指示子を有効にする
    FileInfo.htAccessのドキュメントの種類を管理する指示子を有効にする
    Indexes.htAccessのインデックス表示指示子を有効にする
    Limit.htAccessのアクセス管理指示子を有効にする
    Options.htAccessのOptions指示子を有効にする

  6. 【アクセス管理指示子】
    ディレクトリに対するアクセス制限を指定することができる。これは、認証とは別に、ホスト名やIPアドレスを限定して公開・非公開を制限するときに使う。たとえば、

    Allow from .hogehoge.co.jp
    
    のように指定すれば、hogehoge.co.jp内のマシンからのアクセスだけを許可する事ができる。支店からのみアクセス可能なディレクトリを作るようなときには、支店をfk.hogehoge.co.jpやos.hogehoge.co.jpのようにサブドメインにまとめておけば、Allow指示子ひとつでOKだ)。逆に、

    Deny from hogehoge.co.jp
    
    とすれば、特定の会社からのアクセスを拒否することもできる。ところで、

    Allow from hogehoge.co.jp
    Deny from All
    
    のように矛盾した記述はどう扱われるのだろうか。これは、Apacheの設定ファイルの評価基準に従えば、Allowを評価してからDenyを評価することになり、どこからもアクセスを拒否することになる。このようにAllowとDenyの順番は密接に結びついているので、特別にOrder指示子が存在する。

    Order Deny,Allow
    
    と指定しておけば、Deny→Allowの順に評価され、hogehoge.co.jp以外からのアクセスを禁止することができる。
  7. 【UserDir指示子】
    UserDir指示子は、個人用Webディレクトリを指定する指示子だ。たとえば、

    UserDir "D:/Apache/user/
    
    と指定しておけば、

    http://localhost/~mypage/
    
    のように入力したとき、Apacheは、

    D:\Apache\user\mypage\index.html
    
    のファイルをWebブラウザに返却する(図6)。

    図6:UserDir指示子の効能
    図6

  8. 【DirectoryIndex指示子】
    URLでディレクトリ名を指定したときにWebブラウザに返却するファイル名を指定する。通常、index.htmlを指定する。
  9. 【AccessFileName指示子】
    アクセス制限ファイルの名前を指定する。通常は、.htaccessだ。
  10. 【Filesコンテナ指示子】
    Filesコンテナ指示子は、特定のファイルに対するアクセス制限を指定する。アクセス制限の方法は、Directoryコンテナ指示子で指定できるアクセス管理指示子を使う。たとえば、.htaccessへのアクセスを制限するには、

    <Files .htaccess>
        Order Allow,Deny
        Deny from All
    </Files>
    
    と指定する。もちろんこのように指定してもApache自体はこの.htaccessの設定を使う事ができる。
  11. 【ErrorLog指示子】
    エラーログファイルの格納場所を指定する。
  12. 【Alias指示子】
    ルートディレクトリとCGIディレクトリ以外のWebディレクトリを実フォルダにマッピングする。
  13. 【ScriptAlias指示子】
    CGIプログラムが格納されている実フォルダをWebディレクトリにマッピングする。

Virtual Hosts

 Virtual Hostsセクションは、ひとつのApacheで複数のWebSiteを管理するときに使うセクションで、NameVirtualHost指示子とVirtualHostコンテナ指示子から構成されている。VirtualHostコンテナ指示子の中では、'main' server configrationセクションで指定した値を個別に指定することもできる。

NameVirtualHost IPアドレス:ポート番号

<VirtualHost IPアドレス:ポート番号>
    ServerName   www.hogehoge.co.jp
    ServerAdmin  webmaster@hogehoge.co.jp
    DocumentRoot /www/hogehoge/public/htdocs
</VirtualHost>
 なお、仮想ホストの管理方法には、
  1. 名前ベース
  2. IPベース
  3. ポート番号
の3つがある。
 たとえば、《1》の名前ベースで、www.shoeisha.co.jpとwww.hogehoge.co.jpの2つのWebSiteをひとつのApacheで管理するには、ひとつのNameVirtualHost指示子に対応した2つのVirtualHostコンテナ指示子を記述すればよい。

NameVirtualHost IPアドレス:ポート番号

<VirtualHost IPアドレス:ポート番号>
  ServerName   www.shoeisha.co.jp
  ServerAdmin  webmaster@shoeisha.co.jp
  DocumentRoot /www/shoeisha/public/htdocs
</VirtualHost>

<VirtualHost IPアドレス:ポート番号>
  ServerName   www.hogehoge.co.jp
  ServerAdmin  webmaster@hogehoge.co.jp
  DocumentRoot /www/hogehoge/public/htdocs
</VirtualHost>
 《2》のIPベースでは、1台のマシンに複数のIPアドレスを割り当て、そのIPアドレスごとにWebSiteを割り当てる方法だ。この方法でwww.shoeisha.co.jpとwww.hogehoge.co.jpの2つのWebSiteをひとつのApacheで管理するには、2つのNameVirtualHost指示子それぞれに(つまり2つの)VirtualHostコンテナ識別子を記述すればよい。
 《3》のポート番号による管理は、80番以外のポート番号では、ブラウザ側でIPアドレスを指定する必要があるので、特定の場合を除いて、あまり使われることはない(実際の設定は、IPベースの指定で“:(コロン)”を付けてポート番号を付ければよい)。特別な場合とは、たとえば、ポート番号以外が同一になるのでURLが覚えやすいという理由からか、管理者用の別WebSiteを立ち上げるときなどに使われるときだ。
 Windowsを使っていると「レジストリエディタが存在するならば、レジストリ自体を1ファイルにする必要があるのだろうか」と考え込んでしまうときがある。ソフトをインストールしたら、そのインストール先に、そのソフト特有のレジストリファイルを作成して、その位置をシステム共通のレジストリファイルに設定するだけでよいのではないか。そうすれば、不要な情報によりレジストリファイルが肥大化する必要もないはずだ。テキストファイルとまでは言わないが、レジストリファイルの分散化を考慮してもよい時期なのでないだろうか。

Windows版の注意点

 Apache for Windowsは、付属ドキュメントにもベータ版である事が明記されているように、まだまだ開発途上である部分がある。Windows系のベータ版に比べて、コアになる部分は、かなり安定しているが、WindowsとUNIXのプラットフォームの違いなどに起因する部分では不具合があるときもある。たとえば、httpd.confファイルで、srm.confファイルとaccess.confファイルを無効に設定できるが、その設定方法である、

ResourceConfig nul
AccessConfig   nul
という記述をhttpd.confファイルに行なっても、Apache起動時にエラーとなってしまう。こういった細かな障害は存在するので、エラーが発生したら、対処方法としては、たとえば、null.confという名前で空のファイルを作成して、

ResourceConfig conf/null.conf
AccessConfig   conf/null.conf
のように指定する。

Apache for Win32を使う

 Apacheのドキュメントルートは、インストールフォルダ配下のhtdocsフォルダだ。もちろんこのフォルダのままhtmlファイルを配置していってもよいが、オンラインマニュアルなども含まれているので、別フォルダを作成して、そのフォルダをDocumentRoot指示子に指定することをお勧めする。

Apache+VB-CGI

 1999年8月号の特集でASPから実行するCGIのサンプルを作成した(図7)。

図7:CGIアプリケーションの流れ
図7

今回は、このサンプルをApacheから呼び出ように変更してゆく。まず、ApacheからVBで作成したCGIを実行できるようにするには、大きく2つのステップがある。
  1. Apacheで標準EXEの実行を設定
     Apache for WindowsでVBの標準EXEを実行するには、CGIの設定を行なえばよい。そのためには、httpd.confファイルの中で、

    ScriptAlias /cgi-bin/ "D:/Apache/cgi-bin/"
    
    <Directory "D:/Apache/cgi-bin">
      AllowOverride None
      Options ExecCGI
    </Directory>
    
    のようにCGIディレクトリを指定する。これで、あとはCGIディレクトリに標準EXEをコピーすればCGIとして実行される。もちろん、httpd.confファイルを変更したら、Apacheの再起動を行なうことを忘れてはいけない。
  2. CGI規約で標準EXEをコーディング
     CGIの入出力は、標準入出力を使うが、Visual Basicは、標準入出力をサポートしていない。そこで、Windows APIを使って、標準入出力を実現する(リスト4)。

    リスト4:サンプルCGI(cgi.bas)
    Option Explicit
    Declare Function GetStdHandle Lib "kernel32" _
      (ByVal nStdHandle As Long) As Long
    Declare Function WriteFile Lib "kernel32" _
                    (ByVal hFile As Long, _
                     lpBuffer As Any, _
                     ByVal nNumberOfBytesToWrite As Long, _
                     lpNumberOfBytesWritten As Long, _
                     ByVal lpOverlapped As Long) As Long
    Declare Function ReadFile Lib "kernel32" _
                    (ByVal hFile As Long, _
                     lpBuffer As Any, _
                     ByVal nNumberOfBytesToRead As Long, _
                     lpNumberOfBytesRead As Long, _
                     lpOverlapped As Any) As Long
    Public Const STD_INPUT_HANDLE As Long = -10&
    Public Const STD_OUTPUT_HANDLE As Long = -11&
    Public Const STD_ERROR_HANDLE As Long = -12&
    Private Sub Main()
      On Error GoTo errMain:
    
    ' printf "HTTP/1.1 200 OK" & vbCrLf                       ' 応答コード部
      printf "Content-type: text/html" & vbCrLf & vbCrLf      ' ヘッダ部
      printf "" & vbCrLf
      printf "Hello, World!" & vbCrLf
      printf "こんにちは、世界!" & vbCrLf
    
    exitMain:
      On Error Resume Next
      printf "" & vbCrLf
      Exit Sub
    
    errMain:
      printf Error$
      Resume exitMain:
    End Sub
    Private Sub printf(ByVal strBuffer As String)
      Dim abytBuffer()            As Byte
      Dim lngOutLen               As Long
      Dim lngBytes                As Long
      Dim lngRet                  As Long
      Dim hStdOut                 As Long
    
    ' VBの内部コードであるUnicodeからSJISに変換してそれをバイナリとして扱う
      abytBuffer = StrConv(strBuffer, vbFromUnicode)
    ' バイト配列の大きさ(いわゆる文字列長)を求める
      lngOutLen = UBound(abytBuffer) - LBound(abytBuffer) + 1
    ' 標準出力ハンドルを取得する
      hStdOut = GetStdHandle(STD_OUTPUT_HANDLE)
    ' 標準出力先へSJISコードのまま出力する
      lngRet = WriteFile(hStdOut, _
                         abytBuffer(0), _
                         lngOutLen, _
                         lngBytes, _
                         0&)
    End Sub
    

     このとき、Visual Basicの内部コードであるUNICODEをそのまま標準出力に渡してはいけない。必ずシフトJIS(SJIS)コードに変換する。このとき、必ずバイト型変数に文字列を格納して、UNICODE⇔SJISの自動変換が発生しないようにする。
     なお、リスト4は、標準EXEが作成できるEditionであれば、Visual Basic 4.0(32bit)以降のどのバージョンでもコーディング可能だ。
     この2つのステップをクリアしていれば、Webブラウザからcgi.exeを指定することができる(図8)。

    図8:Apache→VBの標準EXE-CGI
    図8

     このときのCGIとのやりとりは、httpd.confファイルのScriptLog指示子を指定すれば取得することができる(リスト5)。

    リスト5:ScriptLog指示子でブラウザから起動するcgi.exeを指定する
    %% [Sun Mar 05 19:55:34 2000] GET /cgi-bin/cgi.exe HTTP/1.1
    %% 500 d:/apache/cgi-bin/cgi.exe
    %request
    Accept: */*
    Accept-Encoding: gzip, deflate
    Accept-Language: ja
    Connection: Keep-Alive
    Host: localhost
    User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)
    %response
    HTTP/1.1 200 OK
    %stdout
    Content-type: text/html
    
    <HTML>
    <HEAD><TITLE>Hello, World!</TITLE></HEAD>
    <BODY>こんにちは、世界!</BODY>
    </HTML>
    

    ScriptLog指示子の指定方法は、

    ScriptLog "D:/Apache/log/cgi.log"
    
    だ。もちろん、指定後にはApacheの再起動が必要だ。

環境変数を参照する

 CGIがクライアントから情報を取得する方法のひとつが、環境変数を参照することだ。表3に代表的な環境変数をまとめてみた。この環境変数を参照する方法は、Visual Basicでも標準でサポートしているEnviron関数で簡単に実現できる。URLにパラメータを指定する方法であれば、この方法で十分だ(図9)。

表3:CGI環境変数
変数名 意味
AUTH_TYPE認証タイプ
CONTENT_LENGTHPOSTメソッドで渡されるデータのバイト数
CONTENT_TYPEPOSTメソッドで渡される情報の種類
REMOTE_ADDRクライアントのIPアドレス
REMOTE_HOSTクライアントのホスト名
REMOTE_USERユーザー名
REQUEST_METHOD要求メソッド(POSTやGET)
SCRIPT_NAMEスクリプト名
SERVER_NAMEサーバー名
SERVER_PORT要求を受け取ったポート番号
URL要求されたURL

図9:環境変数を利用する
図9

標準入力から入力する

 環境変数を使う方法でも、ある程度の情報を取得はできるが、本格的にWebブラウザからデータを送信するときは、標準入力を使う方法が必要だ。標準入力に値を渡すためには、URLに直接CGIを指定することはない。Webページのフォームを作成し、methodとしてPOSTを指定して、CGIをSUBMITすることになる(リスト6)。

リスト6:StdIn.html
<html>
<head><title>StdIn Test Driver</title></head>
<body>
<FORM METHOD="POST" ACTION="/cgi-bin/CGIStdIn.exe">
<table>
<tr>
	<td>UserID:</td>
	<td><INPUT NAME="strUID" SIZE=8 MAXLENGTH=8 value="scott"><br></td>
</tr>
<tr>
	<td>Password:</td>
	<td><INPUT NAME="strPass" TYPE="PASSWORD" SIZE=8 MAXLENGTH=8 value="tiger"><br></td>
</tr>
<tr>
	<td>Database:</td>
	<td><INPUT NAME="strHost" SIZE=8 MAXLENGTH=8 value="kawa8"><br></td>
</tr>
<tr>
	<td>SQL:</td>
	<td><TEXTAREA NAME="strSQL" cols=80>SELECT * FROM EMP ORDER BY EMPNO</TEXTAREA><br></td>
</tr>
</table>
<INPUT TYPE="SUBMIT" VALUE="検索">
</form>
</body>
</html>

 一方、標準入力と同じようにVisual Basicでは標準入力もサポートしていないので、CGI側では、Windows APIを使うことになる(リスト7)。

リスト7:標準入力のサポート
Declare Function ReadFile Lib "kernel32" _
                (ByVal hFile As Long, _
                 lpBuffer As Any, _
                 ByVal nNumberOfBytesToRead As Long, _
                 lpNumberOfBytesRead As Long, _
                 lpOverlapped As Any) As Long
Public Const STD_INPUT_HANDLE As Long = -10&

Private Function getchar() As String
  Dim abytBuffer()       As Byte
  Dim lngInLen           As Long
  Dim lngBytes           As Long
  Dim hStdIn             As Long
  Dim lngRet             As Long
  Dim strBuffer          As String

  On Error GoTo errChar:

  lngInLen = Environ("CONTENT_LENGTH")
  If lngInLen > 0 Then
    ReDim abytBuffer(lngInLen)
    strBuffer = String(lngInLen, Chr$(0))
    hStdIn = GetStdHandle(STD_INPUT_HANDLE)
    lngRet = ReadFile(hStdIn, _
                      ByVal strBuffer, _
                      lngInLen, _
                      lngBytes, _
                      ByVal 0&)
    getchar = Left$(strBuffer, lngBytes)
  Else
      getchar = ""
  End If

exitChar:
  On Error Resume Next
  Exit Function

errChar:
  printf Error$ & vbCrLf
  Resume exitChar:
  Resume Next
End Function

また、POSTメソッドの結果を標準入力から入力したとき、シフトJISにもUNICODEにもなっていないので、Visual Basicが処理できる形式に変換(デコード)する必要がある。そこで、入力した値は、リスト8の関数を経由して文字コードを変換している。

リスト8:URLデコード
Private Function strURLDecode(ByVal rstrPost As String) As String
  Dim intindex    As Integer
  Dim intPos      As Integer
  Dim strAsc      As String

' 8bit Decode
  intindex = 1
  strURLDecode = ""
  intPos = InStr(rstrPost, "%")
  Do While intPos > 0
    If intPos > 1 Then
      strURLDecode = strURLDecode & Mid$(rstrPost, _
                                         intindex, _
                                         intPos - 1)
      intindex = intindex + intPos
    Else
        intindex = intindex + 1
    End If
    strAsc = StrConv(Mid$(rstrPost, intindex, 2), _
                     vbUpperCase)
    intindex = intindex + 2
    If strAsc > "7F" Then
    ' 漢字項目なので
      If Mid$(rstrPost, intindex, 1) = "%" Then
        strAsc = Chr(CInt("&H" & strAsc _
                          & Mid$(rstrPost, _
                                 intindex + 1, 2)))
        intindex = intindex + 3
      Else
        strAsc = Chr$(CInt("&H" & strAsc & _
                           Hex(Asc(Mid$(rstrPost, _
                                        intindex, 1)))))
        intindex = intindex + 1
      End If
    Else
      strAsc = Chr$(CInt("&H" & strAsc))
    End If
    strURLDecode = strURLDecode & strAsc
    intPos = InStr(Mid$(rstrPost, intindex), "%")
  Loop
  strURLDecode = strURLDecode & Mid$(rstrPost, _
                                     intindex)
' + Decode
  strAsc = strURLDecode
#If VB6 Then
  strURLDecode = Replace(strAsc, "+", " ", _
                         1, -1, vbTextCompare)
#Else
  intPos = InStr(strAsc, "+")
  Do While intPos > 0
    Mid$(strAsc, intPos, 1) = " "
    intPos = InStr(strAsc, "+")
  Loop
  strURLDecode = strAsc
#End If
End Function

 さて、このようにして作成したサンプルを実行するには、次の手順を取る。
  1. StdIn.htmlファイルをWebルートディレクトリにコピー
  2. CGIStdIn.exeを/cgi-binディレクトリにコピー
  3. StdIn.htmlをURLに指定して、Webブラウザから呼び出す(図10)

    図10:<Form>タグを使ったページ
    図10

  4. [検索]ボタンをクリックして、CGIを起動(図11)

    図11:標準入力を利用する
    図10

Apache+VB-CGI+ActiveX EXE

 CGIを使ったときの弱点と言われているもののひとつは、CGIプログラムの起動に要する時間(起動コスト)の問題だ。それを解消するために、COMコンポーネント(ActiveX EXE)を利用するのもよいだろう(図12)。もちろん、このCOMコンポーネント自体は、CGI用ディレクトリなどのApache関連のフォルダに置く必要はないうえ、間接的にもビジネスロジックをWeb上に公開することがないのでセキュリティ的にも有利だろう。

図12:COMコンポーネントを利用する
図12

 サンプルActEXEでは、実際にはRDBMSとは接続していないが、CGIEXE.exeからみればわからないし、実際のファイルの場所もわからない。また、インターフェイスさえ同じならば、内部処理の変更も気にしなくてよい。これがコンポーネント技術のよいところだ。
 このサンプルを実行するには、次の手順を取る。
  1. ActExe.htmlファイルをWebルートディレクトリにコピー
  2. CGIEXE.exeを/cgi-binディレクトリにコピー
  3. ActExe.htmlをURLに指定して、Webブラウザから呼び出す(図13)

    図13:標準入力を利用する
    図13

  4. [検索]ボタンをクリックして、CGIを起動(図14)

    図14:標準入力を利用する
    図14

    (注意)ActiveX EXEはレジストリに登録する必要がある。一度でもVbpファイルを開き[開始]すれば自動的に登録が完了する。もしくは、一度だけActiveX EXEを/regserverパラメータ付きで起動すればよい。レジストリの登録を抹消したいときは、ActiveX EXEを削除や移動する前に/unregserverパラメータ付きで起動すればよい。
 COMコンポーネント自体は、CGIを意識する必要はないので、COMコンポーネント化されたビジネスロジックを有効利用することが可能である。もちろん、ビジネスロジックを別マシンに配置し、DCOMで通信したり、Microsoft Transaction Server(MTS)コンポーネント化してトランザクションをサポートする事だって可能だ。また、MAPIやCDO(Collaboration Data Object)などを使って、Exchange Server用のWebベースメールリーダなども作成できる。

インスタンシングの設定について

 ActiveX EXEは、Visual Basicで作成したCGIプログラムとは別プロセスで動作する。そのため、どのように別プロセスとして動作するのかを指定できる。それが、インスタンシングの設定だ。
 インスタンシングには大きくわけて、SingleUseインスタンシングとMultiUseインスタンシングがある。今回は利用効率を向上させることが目的なので、ActiveX EXEが呼び出された数だけ別プロセスとして起動されるSingleUseインスタンシングではなく、MulutiUseインスタンシングを選択するのがよい。
 MulitiUseインスタンシングにはさらにいくつかのスレッドモデルが存在する。
  1. オブジェクトごとのスレッド
  2. スレッドプール1スレッド
  3. スレッドプールnスレッド
 オブジェクトごとに別スレッドとして起動する《1》はSingleUseインスタンシングとほぼ同等と考えてよい。問題は、スレッドプールを指定したときだ。
 スレッドプールとは、同時に何個のスレッドを起動できるかを指定したもので、たとえば1スレッドモデルとしたときは、複数のプロセスから同一のActiveX EXEを呼び出してもメモリ上には1スレッドしか生成されない。そのため、ActiveX EXEの処理は並行して行なわれず、呼び出し元の処理はひとつずつ順番に処理される。また、パブリック変数も共通利用することができるので、うまくこの特性を利用すればリアルタイムチャットのようなもののメッセージ交換プロセスとして利用することができる。ただし、ひとつでも処理時間がかかるとシステム全体として、処理待ちの状態に陥ることになるので、1スレッドモデルのActiceX EXEは、短時間で終わる処理のみを提供するように心がけなければならない。
 nスレッドモデルActiveX EXEは、指定した個数までメモリ上にスレッドを生成する。つまり、最大n並列処理が可能になる限り、1スレッドモデルのときよりも性能が向上する可能性がある。ただし、そのひとつひとつは1スレッドモデルと同様に逐次処理やパブリック変数の共通利用などの特性をもっている。テスト時には必ずnスレッドよりも大きい処理を行なわせるか、1スレッドモデルとして十分テストを行なう必要がある。そうしないと実運用で多くのスレッドを処理したときのみスレッドの同時使用(1スレッドモデルの特性で動作)が起こり、予定とは異なった動きをしてしまうことがあるかもしれない。このような現象が発生すれば、再現も難しくシステムの安定稼働を阻害する要因になるだろう。

最後に

 ApacheからVisual Basicを使うのは、Apacheを使っている人からは邪道に見えるだろう。それは、Apacheにはmod_perlを始めとするPerl環境との充実した相性のよさがあるからだ。そして、Perl for Winを使えば、プラットフォームがWindowsかUNIXかといった垣根がますます低くなるからだ。そのような状況があるのに、Visual Basicを使うことで、プラットフォーム依存にすることはないだろうと思われるかもしれない。プラットフォーム依存を厭わなければPerlからCOMコンポーネントを使うことだって可能になっているという事実もある。
 しかし、Microsoftの開発環境の中核製品群でもその中心に位置するVisual BasicとApacheの組み合わせの実現性を認識しておくことは重要なことだ。Webシステムを構築するときの選択肢のひとつとして検討対象に上げておく必要があると思う。

■【動作確認環境】
ThinkPad 240 2609-31J
  Windows 98 SE  4.10.2222A
  IE5 5.00.2614.3500
  Office 2000 Developer
  Visual Basic 6.0 (SP3)

VB Magazine ライブラリ| Visual Basic WorkGroup
int21 ホームページ| PCDN ホームページ

PCDN
Copyright (c) 1998 int21 CorporationAll Rights Reserved.
For questions or comments, please send mail to: pcdn@int21.co.jp