特集 VIVA! Servers
Visual Basic
クライアントアプリケーションから、
Transactional ASPを利用したWebアプリケーション開発
秋月 巌 AKIZUKI, Iwao
秋月 巌ソリューション事務所
| メリッサの衝撃 |
|---|
この号が店頭に並ぶ頃にはすでに古い話題になってしまっていると思うが、メリッサの衝撃がコンピュータユーザーを恐怖に陥れた。メリッサ自身は悪意も少ないし、予防法も難しくない。ウィルスチェックなしに送信されてきた添付ファイルを開かないような習慣をつけさえすればよい。また、類似品を含めてワクチンソフトを開発することも簡単だ。該当のOffice文書にWindows APIが使われていないことを確認すればよい。もちろん、私はメリッサを受け取らなかったし、そのコードを見たことがあるわけではないが、Windows APIを使わずにあのような処理を行なうことはできないはずである。
メリッサが問題だったのは、その感染力である。もし、あの感染力で、もっと害のあるウィルスがデビューしていたら、それこそ惨事にいたっていただろう。新聞などにはメリッサの被害として過負荷によるサーバーのダウンが報じられていたが、サーバーダウンはメリッサが感染するプロセスで発生した問題であり、メリッサに記述されたソースコード自体が与えた被害ではない。サーバーがもっと丈夫だったら、あるいはサーバーあたりのメールの数がもっと少なかったら、サーバーはダウンしなかったかもしれない。
その感染力はクライアントサーバーアーキテクチャによってもたらされた
その強力な感染力は、メリッサがクライアントサーバー(C/S)型のウィルスであったことに原因している。もちろん、メリッサはクライアントアプリケーションであり、サーバーはSMTPサーバーである。動作している間のメリッサは、完全にSMTPメールクライアントとして動作する。このメールクライアントが一般のメールクライアントと異なるのは、一種類のメッセージしか送出できない点である。もちろん、そのメッセージには添付ファイルとして自分自身が含まれている。
コミニュケーションスタイルとしてのC/S型システムの特徴は、誰かと連絡をとる際に必ず代理人を経由するということである。この代理人がサーバーであり、結果としてサーバーに情報が集中する。このメリットはサーバーが情報を統括することで利用者間の情報あるいはリソースの共有が可能だということであり、デメリットとしては負荷の集中とサーバーの重要性が極度に高まることである。皆が代理人を経由して通信していると、代理人が病気で寝込んだ場合に誰とも連絡がとれなくなってしまうのである。
|
クライアントサーバーと データベース |
|---|
C/Sデータベースシステムは、C/Sシステムとデータベースシステムを組み合わせたものである。しかし、この二者は必ずしも一体ではない。たとえば、Webデータベースシステムを考えてみれば、それは明らかである。Webデータベースシステムは完全なC/Sシステムとして動作するが、データベースシステムはデータベースアクセスを行ない、C/Sとしての動作はWebサーバーとWebクライアントに委ねられている。
メールにも同様のことがいえる。一般にメールの配信はSMTP/POPサーバーとSMTP/POPクライアントによって行なわれている。しかし、Yahoo!やHotMailのようなメール配信サービスはPOPサーバーを使わずに、Webサーバー、つまり、HTTPサーバーによって行なわれている。
WebサーバーはWebブラウザ以外からもアクセスが可能
このようにWebサーバーはサーバー側に何かしらのアプリケーションを配置することで、汎用的なC/Sシステムとして動作する可能性をもつ。つまり、メールサーバーにもデータベースサーバーにもファイルサーバーにもなるのである。
しかし、それではクライアントにWebブラウザを使わなければいけないと考えるかもしれない。それをVisual Basicアプリケーションクライアントで実現するのが、INETコントロールのHTTPサポートである。今回はこのコントロールをVisual Basicアプリケーションで利用し、Microsoft SQL ServerにIenternet Information Server経由でアクセスする手法を紹介する。
INETコントロールを使わなくても、WinSockコントロールを使ったりWinSock APIを使用して同じことを実現することもできる。今回、解説するサンプルはDDJ日本版の3月号で使ったものを移植したものだが、そこではWinSockコントロールを使っていたのである。ちなみにそのサンプルはVisual Basicアプリケーションではなく、Visual Basicで開発したActiveXコントロールとして実装されており、しかも、そのコントロールはWebブラウザ内で起動していた。
CookieをサポートするINETコントロール
INETコントロールのWinSockコントロールに対して高水準に実装されているので、ユーザーはHTTPプロトコルについて理解する必要もないし、Cookieなどの実装も自動化される。サンプルではActive Server Pagesのセッション維持機能を利用しているのでWinSockコントロールで同様の処理を実装するには、Cookieを開発者がサポートする必要がある。Active Server Pagesのセッションは内部的にCookieを利用しているからである。
Webサーバーへのリクエスト
INETコントロールでHTTPリクエストを要求する方法は簡単である。INETコントロールのExecuteメソッドにURLを指定すればよい。サンプルアプリケーションのボタンのClickイベントプロシージャに記述された次のコードが、HTTP要求を実行している。
Inet1.Execute sParam, "GET"引数sParamには、取得するページのURLが保持されている。取得される結果は、Webブラウザの「ソースを表示」で確認できるものと同である。Webブラウザのように表示するためには、そのための処理をする必要がある。もちろん、今回はブラウザで表示するためではなく、データを受信するためだけに使用するのでその必要はない。
| サンプルプログラムの構成 |
|---|
図1はサンプルプログラムの実行画面と構成を示している。サンプルプログラムはクライアント用のVisual Basicフォームと、サーバー用の2つのActive Server Pagesファィルによって構成されている。Active Server Pagesファイルはデータの取得用と更新用の2つが用意されており、処理に応じてクライアントプログラムが使い分ける。
サーバー側の処理にActive Server Pagesを使用
サーバー側での処理にはActive Server Pagesを使用している。これはもちろん、他のCGI開発方法を使用することも可能である。また、Visual Basic 6.0のWebClassを使用することもできる。ただ、今後もWebアプリケーション開発を続けるならば、WebClassよりもActive Server Pagesの方が多くの点で優れていることを実感することになるだろう。以前にも本誌特集で書いたことがあるが、WebClassのActive Server Pagesに対するメリットは、コードを隠蔽するためにActiveXコンポーネントを使用した場合のデバッグ性にとどまる。
それ以上にActive Server Pagesの何よりのメリットは、Microsoftのプロダクト構成の中で、WebClasがVisual Basicユーザーのためのマイナーな開発手法であるのに対し、Active Server PagesはWindows DNAの中心的な構成要素であるということだ。将来性の差は最終的に大きな差となる。Active Server Pagesがなくなり、WebClassだけが残るということは、構造的にいってありえないからである。
性能を追求するならば、ISAPIエクステンションを利用するのがもっとも有利である。実はここで紹介するのと同じものをISAPIですでに実装したものが存在する。Remote Data Service(RDS)と呼ばれるこのテクノロジーは以前、Advansed Data Connectorと呼ばれていた。専門誌等で大きく扱われることはないが、現在、Microsoftが提供するテクノロジーのもっとも優れたものである。アーキテクチャは完璧であり、実装も以前よりもはるかに進歩し、実用の領域に達している。
ただ、Webアプリケーション用のActiveXコンポーネントとして位置付けられているため、Internet Explorerでしか使用することができない。そのため、メジャープレーヤーとしての地位が与えられていないのだが、このテクノロジーは、本稿のサンプルと同様にVisual Basicアプリケーションから利用することができる。
図1:サンプルプログラムの構成
|
サンプルのインストールと 実行 |
|---|
付録CD-ROMの\Serverフォルダを、IISによってWebで公開されているフォルダにコピーする。これでサーバー側のActive Server Pagesが使用できる。IIS4.0の場合には、これだけでよいのだが、それ以前のバージョンのIISを使用している場合、Active Server Pagesモジュールのインストールと実行権限の設定を行なう必要がある。
次に\ClientフォルダにあるClentVB.vbpをVisual Basicでロードする。サンプルはVisual Basic 6.0で作成されているが、プロジェクトファイルの“Retained=0”の一行を削除すれば、Visual Basic 5.0でも実行できる。
プログラムを実行したら、URLフォルダ名項目にServerフォルダのURLパスを指定する。たとえば、デフォルトのホームURLがC:\inetpub\wwwrootで、その直下に\Serverフォルダをコピーし、ホスト名がhostnameの場合、この項目に指定する内容は次のようになる。
hostname/ ServerASPまた、IISが稼動しているサーバーマシンにMicrosoft SQL Serverがpubsサンプルデータベースを含めてインストールされサービスが稼動している必要がある。IISとMicrosoft SQL Serverが別の場合や、ログイン権限をデフォルト以外に設定している場合はプログラムの内容を変更する必要があり、その方法については後述する。Microsoft SQL Serverは最新のMicrosoft SQL Server 7.0で作成しているが、6.5でも動作するはずである。
| トランザクションの管理 |
|---|
このサンプルプログラムでは、Active Server PagesのTransactional ASPを使用することで、トランザクションを制御している。そのため、サーバー側ではDTCサービスが実行されている必要がある。データの編集後に表示されるメッセージは、トランザクションの成功と失敗を検知して表示を変更している。ここでは単純なアップデート処理だけなので、失敗することはまずないが、もっと複雑なトランザクションが発生するときに応用すれば効果は大きい。
HTTPリクエストでASPファイルを指定する(クライアントVBアプリケーション)
[データを取得]ボタンのクリック時に実行されるイベントプロシージャで実行されている処理がリスト1である。
複雑に見えるかもしれないが、単にひとつの引数を組み立て、INETコントロールのExecuteメソッドの引数に渡しているだけである。
「URLフォルダ名」項目に“hostname/serverASP”と指定した場合、引数sParamは最終的にリスト2-(1)のようになる。
Executeメソッドの2番目の引数に“GET”を指定し、最初の引数にこの内容を指定するということは、Webサーバーにとっては、リスト2-(2)のようなHTMLリンクがクリックされたのと等しい。
ちなみに、このリンクがクリックされたのが図2である。データベースにアクセスしたActive Server Pagesが結果をテキストとして送り返しているのがわかる。レコードの区切りには改行コードを使用しているので、ソースでは改行されているがHTMLでは反映されていない。
引数の内容を見てみよう。まず最初にaspselect.aspのURLが、[URLフォルダ名]項目に入力された内容を元に結合される。続いて記述されているのが、aspselect.aspに渡される引数である(リスト2-(3))。この引数は2つある。最初の引数Providerは、OLE DBのプロパイダを指定している。
ここではOLE DBプロパイダのタイプにMicrosoft SQL Server、ユーザーIDとして“sa”、使用するデータベースに“pubsデータベース”、データベースサーバーマシン名に“hostname”を指定している。だから別のユーザー名でMicrosoft SQL Serverにアクセスするためには、このコードを変更する必要がある。また、データベースサーバーマシン名にhostnameが指定しされているのは、URLに指定したホスト名を切り出して流用しているからである。そのため、IISのホスト名とMicrosoft SQL Serverのマシンが異なる場合は、この部分を書きかえる必要がある。
スペースの代わりにプラス(+)記号が使われていることに注意してほしい。これはURLにスペースを使うときのHTTPの規則である。もっとも、INETコントロールを使う限りは、ここがスペースのままでも、正しく実行される。
もうひとつの引数SQLでは、実行するSQL文を指定している。
SQL=SELECT+au_id,au_lname,address,city+FROM+authorsここではauthorsテーブルから4つの項目と全レコードを指定して値を取得する。
リスト1:[データを取得]ボタンによって実行される処理
sServer = Mid(txtServerName, 1, _ InStr(1, txtServerName, "/") - 1) sParam = "http://" & txtServerName & "/aspselect.asp" sParam = sParam _ & "?CONPARAM=Provider=SQLOLEDB.1;" _ "User+ID=sa;Initial+Catalog=pubs;" _ "Data+Source=" & sServer sParam = sParam _ & "&SQL=SELECT+au_id,au_lname,address,city+FROM+authors" Inet1.Execute sParam, "GET" |
リスト2
@ http://hostname/serverASP/aspselect.asp?CONPARAM=Provider=SQLOLEDB.1;User+ID=sa;Initial+Catalog=pubs;Data+Source=santiago&SQL=SELECT+au_id,au_lname,address,city+FROM+authors A <A HREF=http://hostname/serverAsp/aspselect.asp?CONPARAM=Provider=SQLOLEDB.1;User+ID=sa;Initial+Catalog=pubs;Data+Source= hostname&SQL=SELECT+au_id,au_lname,address,city+FROM+authors> リンク </A> B Provider=SQLOLEDB.1;User+ID=sa;Initial+Catalog=pubs;Data+Source=hostname |
図2:aspselect.aspの実行結果をブラウザで取得
| Active Server Pagesによるデータアクセスと返信(aspselect.asp) |
|---|
記事リスト4はデータベースにアクセスし、クライアントの結果を送り返すActive Server Pagesファイル(aspselect.asp)の全ソースリストである。
冒頭で、先ほど送信した引数を変数に代入している。
conparam = Request("CONPARAM")
sqlparam = Request("SQL")
Requestオブジェクトは、HTTPリクエストに含まれる引数を取得するのに使用するActive Server Pagesのオブジェクトである。このオブジェクトはWebClassでも使用することができる。
Set dbConnection = _
Server.CreateObject("ADODB.Connection")
dbConnection.Open conparam
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sqlparam, dbConnection, 1, 3
ConnectionオブジェクトのOpenメソッドの引数に、クライアント側で作成した接続文字列を利用している。ポイントは最後の行、RecordsetオブジェクトのOpenメソッドの実行時に、結果セットを更新可能なレコードセットとして作成していることである。ここで作成したRecordsetオブジェクトは、更新用のActive Server Pagesファイルでも再利用され、そこでRecordsetを使用した更新処理が実行される。
Response.Write("DataStart")
Response.Write(Chr(13) & Chr(10))
以下の処理はレコードセットの内容を抽出し、項目と項目の間にタブ記号“Chr(9)”、レコードとレコードの間に改行コード“Chr(13) & Chr(10)”を挿入し、クライアントに返信している。
Do While Not rs.EOF
FieldCount = rs.Fields.Count
i = 0
Do While i < FieldCount
Response.Write(rs.Fields(i) & Chr(9))
i = i + 1
Loop
Response.Write(Chr(13) & Chr(10))
rs.MoveNext
Loop
クライアントに結果を返すためには、Active Server PagesモジュールのResponseオブジェクトを使用する。ネストしたループ内でレコードセット内の全項目のデータとすべてのレコードを出力している。最終レコードの処理が終了するとループを脱出し、処理は次に移る。
Set Session("rs") = rs
ここで格納したオブジェクトやデータを、別のページから呼び出すことで擬似的にページ間でセッションが継続しているように扱うことができる。もちろん、別のページからこのオブジェクトを参照できるのは、このオブジェクトを格納したユーザーに限られる。この場合の「同じユーザー」とは、同じマシンのブラウザから一度もブラウザを閉じることなく再度アクセスしたユーザー、という定義である。このサンプルの場合でいえば、同一のINETコントロールから再ロードすることなくアクセスしたユーザー、ということになる。
Response.Write("DataEnd")
Response.Write(Chr(13) & Chr(10))
|
返信結果の取得 (クライアントVBアプリケーション) |
|---|
Active Server Pagesファイルが返信した結果を処理するのは、クライアントに配置されたINETコントロールのStateChangedイベントプロシージャである。Webサーバーが返信する結果を受信終了したとき、INETコントロールはStateChangedイベントプロシージャの引数Stateに定数icResponseCompletedの値を設定する。
以下の条件指定により続くコードはStateの内容が12の場合にのみ実行される。
Select Case State
Case icResponseCompleted
受信したすべてのデータを変数strDataに格納しているのが、次のコードである。
Do While Not bDone
vtData = Inet1.GetChunk(1024, icString)
strData = strData & vtData
If Len(vtData) = 0 Then
bDone = True
End If
Loop
INETコントロールのGetChunkメソッドを使ってバッファの内容を取得し、それを文字列型の変数strDataに追加している。バッファの内容が空になった時点でループを終了する。If InStr(strData, "DataStart") > 0 Then MakeGridData strDataここで条件を指定しているのは、このイベントプロシージャが、更新用のActive Server Pagesファイルの結果を取得したときにも使用されるからである。
| テキストボックスにカレントレコードの内容を表示する(クライアントVBアプリケーション) |
|---|
フレキシブルグリッドコントロールに最初がデータに表示されたときや、同コントロールがクリックされたときに、テキストボックスにデータを表示しているのが、次のプロシージャである。
Private Sub flgList_Click()
Dim i As Integer
For i = 0 To 3
flgList.Col = i
txtField(i).Text = flgList.Text
Next
End Sub
このフレキシブルグリッドコントロールのClickイベントプロシージャでは、クリックされた行のデータを先頭から順次テキストボックスに代入する。テキストボックスに表示された内容はデータの編集が可能になる。
|
更新内容の送信 (クライアントVBアプリケーション) |
|---|
編集されたテキストボックスの内容をデータベースに反映させるためには[更新]ボタンをクリックする必要がある。 次の条件文では、ユーザー定義関数DataCheckを使用してデータがユーザーによって変更されたか調べている。変更されていない場合、サーバーへのデータ送信は行なわない。
If Not DataCheck Then Exit SubINETコントロールのExecuteメソッドの引数を合成する次の部分では、更新用のActive Server Pagesファイルaspupdate.aspのURLを指定している。
sParam = "http://" & txtServerName & "/aspupdate.asp"以下のコードはテキストボックスの内容をaspupdate.aspの引数として指定している。
sParam = sParam & "?FIELD1=" & txtField(0).Text sParam = sParam & "&FIELD2=" & txtField(1).Text sParam = sParam & "&FIELD3=" & txtField(2).Text sParam = sParam & "&FIELD4=" & txtField(3).TextWebサーバーにリクエストを実行しているのが、次の1行である。
Inet1.Execute sParam, "GET"合成した変数sParamとHTTPオペレーションである“GET”をExecuteメソッドの引数として指定している。
| Active Server Pagesによるデータ更新(aspupdate.asp) |
|---|
[更新]ボタンがクリックされたときに呼び出されるaspupdate.asp(リスト3)について解説する。このファイルの先頭に記述されている次の1行によって、このActive Server PagesファイルがTransactional ASPとして有効になる。
<%@TRANSACTION=Required%>Transactional ASPはMicrosoft Transaction Serverを利用して、Active Server Pagesのトランザクション処理を可能にする。
<% Set rs = Session("rs")
ここではオブジェクトのポインタを取得しているので、前のActive Server Pagesファイルで、このRecordsetオブジェクトを扱ったときの状態がそのまま引き継がれる。
rs.MoveFirst
Do While Not rs.EOF
If rs.Fields(0) = Request("FIELD1") Then
更新するレコードが見つかったら、各項目にRequestオブジェクトが切り出した値を代入する。この値はクライアントが送信したユーザーの入力内容である。
rs.Fields(1) = Request("FIELD2")
rs.Fields(2) = Request("FIELD3")
rs.Fields(3) = Request("FIELD4")
rs.Update
Recordsetオブジェクトに受信した値を入力した後、Updateメソッドを実行することで更新を実行する。ObjectContext.SetCompleteトランザクションの終了後、その結果に応じてOnTransactionCommitイベントかOnTransactionAbortイベントが発生する。それぞれのイベントに対応するプロシージャが次の2つのプロシージャである。
Sub OnTransactionCommit Response.Write "Update Complete" End Sub Sub OnTransactionAbort Response.Write "Update Abort" End Subここでは発生するイベントに応じて返信する内容を変更している。この内容はクライアント側での処理に関連するので重要である。
|
トランザクションの結果を受信する (クライアントVBアプリケーション) |
|---|
aspselect.aspの返信を受信するとき同様、aspupdate.aspの結果を受信するのもINETコントロールのStateChangedイベントプロシージャである。
バッファから受信内容を取り出した後、次の条件がFalseになるため、Else以降の処理が実行される。
If InStr(strData, "DataStart") > 0 Then
| (省略)
Else
Else以降はネストしたIf文が用意されている。この条件文でトランザクションの成功時と失敗時の処理を分岐している。If strData = "Update Complete" Then For i = 0 To 3 flgList.Col = i flgList.Text = txtField(i).Text Next End If最後に受信したメッセージをメッセージボックスで表示し、処理を終了する。
MsgBox strDataここでは単一のトランザクションしか実行していないが、複数の更新を同時におこなうようなケースでは、それらがセットでロールバックされ、データの整合性が確保される。
| まとめ |
|---|
今回のサンプルプログラムでは、私が実際の開発に用いない方法をいくつか使用している。
まず、Active Server PagesのSessionオブジェクトである。このオブジェクトを使用するにはいくつかの問題がある。まず、サーバー側のリソースが無駄使いになりがちだというのもそのひとつだが、Sessionオブジェクトが次回のアクセス時に有効か無効かはタイムアウトに依存することになる。このサンプルでは、タイムアウトが発生した場合のエラーハンドラーは記述していないので、その場合、当然、エラーが発生する。
Active Server Pagesを使ってステートを維持する場合、私はCookieかURLのクエリーストリング、あるいはフォーム内でHIDDEN属性をもつ<INPUT>タグを使用する。
次にRecordsetオブジェクトを使った更新である。私は普段、Recordsetオブジェクトを読取専用で作成し、更新や追加する場合には、それに対応するSQL文を作成して実行する。それは単にRecordsetオブジェクトの更新処理の最適性を信じていないからにほかならない。それは今回のようにRecordsetオブジェクトをページをまたいで使用する場合にはなおさらである。
最後にTransactional ASPを使用したトランザクション管理である。私だったら、MTSのような複雑なレイヤーを介したトランザクションコントロールより、データベースが提供するトランザクション管理メソッドを直接利用する。それはDTC、MTSといったMicrosoftが提唱するメカニズムに対する不信感をもっているからである。
今回、紹介した手法は概してMicrosoftのWindows DNAアーキテクチャに準じたものであるということができる。しかし、それが最善の方法だというわけではないことは覚えていた方がいい。
Microsoftが提供する製品には優れたものも少なくない。しかし、それもMicrosoftが推薦するように使用すると、あまり調子よく動作しない。しかし、Microsoftを責めてはいけない。Microsoftが提唱するのは、コンピュータとはユーザーの責任において使用するものだということなのである。そして、その主張は間違いなく真理である。
この真理が普及すれば、プログラムを開発提供をしている読者の方々も、エンドユーザーから非難されることがなくなるのである。しかし、もちろんそんなことになるわけはないので、責められるのは開発者だということになる。Microsoftの提案通りにシステムを作ったのがいけないのである。
リスト3:クライアントVBアプリケーションの全ソースコード
Private Sub cmdSelect_Click()
Dim sParam As String
Dim sServer As String
sServer = Mid(txtServerName, 1, InStr(1, txtServerName, "/") - 1)
sParam = "http://" & txtServerName & "/aspselect.asp"
sParam = sParam _
& "?CONPARAM=Provider=SQLOLEDB.1;User+ID=sa;Initial+Catalog=pubs;Data+Source=" _
& sServer
sParam = sParam & "&SQL=SELECT+au_id,au_lname,address,city+FROM+authors"
Inet1.Execute sParam, "GET"
End Sub
Private Sub cmdUpdate_Click()
Dim sParam As String
Dim sServer As String
If Not DataCheck Then Exit Sub
sParam = "http://" & txtServerName & "/aspupdate.asp"
sParam = sParam & "?FIELD1=" & txtField(0).Text
sParam = sParam & "&FIELD2=" & txtField(1).Text
sParam = sParam & "&FIELD3=" & txtField(2).Text
sParam = sParam & "&FIELD4=" & txtField(3).Text
Inet1.Execute sParam, "GET"
End Sub
Private Function DataCheck() As Boolean
Dim i As Integer
DataCheck = False
For i = 0 To 3
flgList.Col = i
If txtField(i).Text <> flgList.Text Then
DataCheck = True
Exit Function
End If
Next
End Function
Private Sub flgList_Click()
Dim i As Integer
For i = 0 To 3
flgList.Col = i
txtField(i).Text = flgList.Text
Next
End Sub
Private Sub Form_Load()
flgList.ColWidth(0) = 1300
flgList.ColWidth(1) = 1300
flgList.ColWidth(2) = 1800
flgList.ColWidth(3) = 1500
flgList.Row = 0
flgList.Col = 0
flgList.Text = "au_id"
flgList.Col = 1
flgList.Text = "au_lname"
flgList.Col = 2
flgList.Text = "address"
flgList.Col = 3
flgList.Text = "city"
End Sub
Private Sub Inet1_StateChanged(ByVal State As Integer)
Dim vtData As Variant 'データを入れる変数。
Dim strData As String
Dim bDone As Boolean
Dim i As Integer
strData = ""
bDone = False
Select Case State
Case icResponseCompleted '12
Do While Not bDone
' チャンクを取得します
vtData = Inet1.GetChunk(1024, icString)
strData = strData & vtData
If Len(vtData) = 0 Then
bDone = True
End If
Loop
If InStr(strData, "DataStart") > 0 Then
MakeGridData strData
Else
If strData = "Update Complete" Then
For i = 0 To 3
flgList.Col = i
flgList.Text = txtField(i).Text
Next
End If
MsgBox strData
End If
End Select
End Sub
Private Sub MakeGridData(strParam As String)
Dim strRow As String
Dim strField As String
Dim lngCurPointer As Long
Dim CurRecordLen As Integer
Dim intFieldStartPos As Integer
Dim intFieldNextPos As Integer
Dim i As Integer
Dim j As Integer
Dim CPOS As String
Dim CPOSlen As Integer
CPOS = Chr(9)
CPOSlen = Len(CPOS)
j = 0
' フィールド情報の先頭に移動
lngCurPointer = InStr(strParam, "DataStart")
Do While True
j = j + 1
' 処理する行の先頭ポインタを取得
lngCurPointer = InStr(lngCurPointer, strParam, vbCrLf) + 2
' 処理する行の長さを取得
CurRecordLen = InStr(lngCurPointer, strParam, vbCrLf) - lngCurPointer
' 処理する行を取得
strRow = Mid(strParam, lngCurPointer, CurRecordLen)
' フィールド情報の最後ならば、ループを終了
If Left(strRow, 7) = "DataEnd" Then
Exit Do
End If
intFieldNextPos = 1
flgList.Rows = j + 1
flgList.Row = j
For i = 1 To 4
intFieldStartPos = intFieldNextPos
' 区切り記号が見つかったら、左側がフィールドの値
intFieldNextPos = InStr(intFieldStartPos, strRow, CPOS) + CPOSlen
If intFieldNextPos <> 0 + CPOSlen Then
' 値を strField 変数に割り当てます。
strField = Mid(strRow, intFieldStartPos, intFieldNextPos - intFieldStartPos - CPOSlen) 'Left(strRow, intPos - 1)
Else
' 区切り記号が見つからなければ、最後のフィールドです
strField = Mid(strRow, intFieldStartPos, Len(strField) - CPOSlen + 1)
End If
flgList.Col = i - 1
flgList.Text = strField
Next
Loop
flgList.Row = 1
flgList.Col = 0
flgList_Click
flgList.SetFocus
End Sub
|
リスト4:データ取得用Active Server Pagesファイルの全ソースコード(aspselect.asp)
<%
conparam = Request("CONPARAM")
sqlparam = Request("SQL")
Set dbConnection = Server.CreateObject("ADODB.Connection")
dbConnection.Open conparam
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sqlparam, dbConnection, 1, 3
Response.Write("DataStart")
Response.Write(Chr(13) & Chr(10))
Do While Not rs.EOF
FieldCount = rs.Fields.Count
i = 0
Do While i < FieldCount
Response.Write(rs.Fields(i) & Chr(9))
i = i + 1
Loop
Response.Write(Chr(13) & Chr(10))
rs.MoveNext
Loop
Set Session("rs") = rs
Response.Write("DataEnd")
Response.Write(Chr(13) & Chr(10))
%>
|
リスト5:データ更新用Active Server Pagesファイルの全ソースコード(aspupdate.asp)
<%@TRANSACTION=Required%>
<% Set rs = Session("rs")
rs.MoveFirst
Do While Not rs.EOF
If rs.Fields(0) = Request("FIELD1") Then
rs.Fields(1) = Request("FIELD2")
rs.Fields(2) = Request("FIELD3")
rs.Fields(3) = Request("FIELD4")
rs.Update
ObjectContext.SetComplete
Exit Do
End If
rs.MoveNext
Loop
Sub OnTransactionCommit
Response.Write "Update Complete"
End Sub
Sub OnTransactionAbort
Response.Write "Update Abort"
End Sub
%>
|