〜 ネットワーク 〜
サーバーからリソースをダウンロード (要WININET.DLL)

HSP3からHSPInet.DLLが標準で同梱されており、
このDLLの命令を使うことでインターネット上のデータを取り込むことが出来ます。
便利なものですが、作成した実行ファイルと共にDLLを同梱する必要があるので、
様々な理由により「なんとかDLLナシで実現できないか」と思う人たちの為の参考Tips。
HSPInet.DLLは、Windows標準搭載のWinInet.DLLのラッパーDLLなので、
WinInet.DLLのAPI関数を直接呼び出すことで、
HSPInet.DLLを同梱することなく実現させることが可能となります。

今回は指定したURLをオープンし、アドレス先のデータをパソコンへファイルとして取り込みます。
URLをオープンするAPI関数はInternetOpenUrlで、第1引数から順番に
「インターネットハンドル」「開くURL」「ヘッダ」「ヘッダサイズ」
「オープン種別」「コールバック関数に渡すアプリケーション定義値」を指定します。
インターネットハンドルについてはコチラを参照してください。
開くURLは当然のこと、文字列ではなく、文字列を格納した変数のポインタを指定します。
ヘッダはHTTPサーバーにリファラー等の付加情報を送れますが、なければNULL(=0)としてください。
オープン種別は以下のフラグ定数を指定します。
INTERNET_FLAG_SECURE0x00800000SSLやPCTを使用する
INTERNET_FLAG_DONT_CACHE0x04000000ローカル及びゲートウェイにデータをキャッシュしない
INTERNET_FLAG_EXISTING_CONNECT0x20000000可能な限り既存のサーバとの接続を再利用
INTERNET_FLAG_RAW_DATA0x40000000生のデータを返す
INTERNET_FLAG_RELOAD0x80000000キャッシュを無視して常にサーバからデータを取得
コールバックする値については0を指定しましょう。 指定URLに接続ができると、インターネットハンドルとは別のハンドルが返りますので、 ローカルに取り込むAPI関数InternetReadFileの第1引数から順番に 「上記のハンドル」「データの受取先変数ポインタ」「一度に読み込む最大サイズ」 「読込サイズの受取先変数ポインタ」と指定します。 取得自体に失敗すれば戻り値に0、成功すれば1が返り、第3引数の最大サイズを限度に、 第2引数の変数にデータがセットされ、第4引数の変数に取得したサイズがセットされます。 もし、取得する最大サイズよりもデータサイズが大きいために一度に読み込めなかった場合、 再度InternetReadFileを実行することで、続きから取得する最大サイズ分まで読み込むことが出来ます。 この関数はオフセットを指定しない(読込位置をセットしない)ので、 関数をループで複数回実行すれば全データを取得することが出来ます。 データを取得し終えると、最終的に第4引数の取得サイズ変数に0が返ります。 一度に読み込むサイズは、数値を直に指定しても良いのですが、 一度に読み込めるサイズを取得できる別のAPI関数InternetQueryDataAvailableを使用すると 最適なサイズで効率よく取得できるわけですから使う方が良いでしょう。 InternetQueryDataAvailableは第1引数から順番に「オープンしたURLのハンドル」 「取得サイズ格納変数のポインタ」「フラグ(=0)」「コールバック関数に渡すアプリケーション定義値」です。 実際の使用方法は下記のサンプルを参考にしてみてください。 指定するURLはHTMLドキュメントだけでなく、 画像や音楽等のメディア、アーカイブでも取得できるわけですが、 テキストではないバイナリデータを読み込む際はNULLが挟まっていることが多いので、 strlen関数やstrmid関数等の高レベルな文字列操作命令は使用できません。 面倒ですが、サンプルのように低レベルなpeek関数やpoke命令を使って処理するようにしましょう。 尚、peek関数やpoke命令を使うということは、HSP3以降でも変数の自動拡張機能は使用できません。 バッファオーバーフローとならないよう、データを受け取る変数サイズは大きめに確保してください。 サービス開始後にサーバーと接続して、変数を介さずにそのままファイルとしてダウンロードするには、 モード(アスキー/バイナリー)を指定して保存できるコチラを使うのも手です。 コレならば変数を通してファイル保存するわけではないので、上記のバッファ問題は起こりません。

inetinit
[パラメータなし]インターネットサービスを開始するだけである為、パラメータは必要ない。

inetend
[パラメータなし]インターネットハンドルを解放するだけの為、パラメータは必要ない。
尚、終了時に自動的に呼び出されるので命令を実行する必要はない。

inetget 受取変数, URL
受取変数取得したデータの受取先変数を指定する。
尚、statには受け取ったデータサイズが返る。
URL取得するデータのアドレスを指定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
	ll_libload wininet, "wininet.dll"
	ll_getproc InternetOpen, "InternetOpenA", wininet
	ll_getproc InternetOpenUrl, "InternetOpenUrlA", wininet
	ll_getproc InternetQueryDataAvailable, "InternetQueryDataAvailable", wininet
	ll_getproc InternetReadFile, "InternetReadFile", wininet
	ll_getproc InternetCloseHandle, "InternetCloseHandle", wininet

#module
#deffunc inetinit
	mref stt, 64
	prm = 0, 0, 0, 0, 0
	agent = "hspbc"
	ll_getptr agent : ll_ret prm
	ll_callfunc prm, 5, InternetOpen@
	ll_ret hinet : stt = hinet
	return

#deffunc inetend onexit
	if hinet : ll_callfunc hinet, 1, InternetCloseHandle@
	return

#deffunc inetget val, str
	mref data, 24 : mref url, 33
	prm = hinet, 0, 0, 0, 0x80000000, 0
	ll_getptr url : ll_ret prm.1
	ll_callfunc prm, 6, InternetOpenUrl@
	ll_ret hfile
	ll_getptr i : ll_ret i.1
	prm = hfile,i.1
	ll_callfunc prm, 2, InternetQueryDataAvailable@
	ll_ret i
	if i = 0 : return
	sdim s, i.1
	i = 0
	prm = hfile, 0, i.1
	ll_getptr s : ll_ret prm.1
	ll_getptr i.2 : ll_ret prm.3
	repeat
		ll_callfunc prm, 4, InternetReadFile@
		if i.2 = 0 : break
		repeat i.2
			peek i.3, s, cnt
			poke data, i + cnt, i.3
		loop
		i += i.2
	loop
	ll_callfunc hfile, 1, InternetCloseHandle@
	return
#global

	sdim buf, 10000 // 受け取る変数は大きめに確保する
	inetinit
	inetget buf, "http://www.rinku.zaq.ne.jp/ultimate/"
	mesbox buf, winx, winy, 1
	stop

inetinit
[パラメータなし]インターネットサービスを開始するだけである為、パラメータは必要ない。

inetend
[パラメータなし]インターネットハンドルを解放するだけの為、パラメータは必要ない。
尚、終了時に自動的に呼び出されるので命令を実行する必要はない。

inetget 受取変数, URL
受取変数取得したデータの受取先変数を指定する。
尚、statには受け取ったデータサイズが返る。
URL取得するデータのアドレスを指定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#uselib "wininet.dll"
#cfunc	global InternetOpen "InternetOpenA" sptr, int, int, int, int
#cfunc	global InternetOpenUrl "InternetOpenUrlA" int, sptr, int, int, int, int
#cfunc	global InternetQueryDataAvailable "InternetQueryDataAvailable" int, var, int, int
#func	global InternetReadFile "InternetReadFile" int, var, int, var
#func	global InternetCloseHandle "InternetCloseHandle" int

#module
#deffunc inetinit
	hinet = InternetOpen("hspbc")
	return hinet

#deffunc inetget var data, str url, local i, local s
	if hinet = 0 : inetinit
	hfile = InternetOpenUrl(hinet, url, , , 0x80000000, 0)
	if InternetQueryDataAvailable(hfile, i.1) = 0 : return 0
	sdim s, i.1 + 1
	repeat
		InternetReadFile hfile, s, i.1, i.2
		if i.2 = 0 : break
		repeat i.2 : poke data, i + cnt, peek(s, cnt) : loop
		i += i.2
	loop
	InternetCloseHandle hfile
	return i

#deffunc inetend onexit
	if hinet : InternetCloseHandle hinet
	return
#global

	sdim buf, 16000 // 受け取る変数は大きめに確保する
	inetinit
	inetget buf, "http://www.rinku.zaq.ne.jp/ultimate/img/banner.jpg"
	size = stat
	dialog "jpg", 17
	if stat {
		bsave refstr, buf, size
		dialog "保存しました"
	}
	end