HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2009
0811
Nikohsp3.2のバグ?12解決


Niko

リンク

2009/8/11(Tue) 23:43:38|NO.27010

#module
#deffunc test var p1
repeat strlen(p1)
a=peek(p1,cnt)+cnt
if a\256=0{
a=64
}
poke p1,cnt,a
wait 0
loop
return
#global

string={"テスト1テスト1テスト1
     テスト2テスト2テスト2
     テスト3テスト3テスト3"}
string="テスト4テスト4テスト4テスト4テスト4テスト4"
test string
mes string
stop

このスクリプトをhsp3.2で実行すると1行目は問題ないのですが、
2行目に「テスト3テスト3テスト3」と表示されます。
なぜでしょうか?

hsp3.1では正常に1行目のみ表示されます。



この記事に返信する


shinkun

リンク

2009/8/12(Wed) 00:00:01|NO.27012

> hsp3.1では正常に1行目のみ表示されます。

hsp 3.1 で動作検証してみましたが、こんなのが表示されました。

デ・㌫・杵皇春臣砺斌慾亘宀捏氛�r」・{ァ肩zォ鹿Ρ伐
えーっと…、これで正常なのでしょうか?

ちょっとこのスクリプトだけでは何をしたいのかが分からないです。(暗号化?)
どういう結果を期待しているのか、説明していただけるとお力になれるかもしれません。



shinkun

リンク

2009/8/12(Wed) 00:17:06|NO.27013

今、hsp 3.2 をインストールして検証してみました。

デ・㌫・杵皇春臣砺斌慾亘宀捏氛�r」・{ァ肩zォ鹿Ρ伐
で正常なんですね。
それで、hsp 3.2 では

デ・㌫・杵皇春臣砺斌慾亘宀捏氛�r」・{ァ肩zォ鹿Ρ伐 テスト3テスト3テスト3
のように表示されると。


string="テスト4テスト4テスト4テスト4テスト4テスト4"
これを

string="テスト4テスト4テスト4テスト4テスト4"
に変更してみるとこの現象がなくなったので、ヌル文字関係が原因
なのかなと思ったのですが、(test ルーチンでどういうわけかヌル
文字を上書きしてしまったため「テスト3テスト3テスト3」が現れたのか?)
string をファイルに書き出して見たところ、この「テスト3テスト3テスト3」
が綺麗さっぱり消えていました。

一体なんなのでしょう…。



Niko

リンク

2009/8/12(Wed) 00:30:48|NO.27014

shinkunさん、ご回答ありがとうございます。
わざわざ検証のためにhsp3.2のインストールまでしていただいたようで、
本当にありがとうございます。

ここで示しているスクリプトは問題が発生している部分を抜き出した
ものです。ですので、このスクリプト自体には意味はありません。

もとのスクリプトはhsp3.1で利用していた暗号化のためのモジュールです。
なぜhsp3.2で動作しないのか知りたくて書き込みをしました。

stringの内容をmesboxで表示しても、
「テスト3テスト3テスト3」は消えてなくなるんですよね~。

不思議です。



ANTARES

リンク

2009/8/12(Wed) 02:23:49|NO.27016

bsave "test.txt",string,strlen(string)+100
mes strlen(string)
としてみると、長さは48バイト
ダンプしてみると「94 B0 82 00」となっていたので、
82が全角上位バイト、00が全角下位バイトと見なされ、
終端コードが終端コードと見なされなくなったのでしょう。

 バイナリデータを文字列として処理することに無理があるので、
これ自体をバグというのは違うでしょう。
同じ原因でバグというべき問題が発生する可能性はありますが、
実際にそういう現象が見つかってからでも遅くはないでしょう。

 どうしてもこういう現象が起こらないようにしたければ、
暗号化した結果をbase64等でテキスト化するといいでしょう。



Niko

リンク

2009/8/12(Wed) 22:24:35|NO.27027

ANTARESさんご回答ありがとうございます。

理由はわかったのですが、
hsp3.1では使用できていたモジュールが
hsp3.2では使えなくなるのは残念です。



KA

リンク

2009/8/12(Wed) 22:46:14|NO.27028

>>理由はわかったのですが、
>>hsp3.1では使用できていたモジュールが
>>hsp3.2では使えなくなるのは残念です。

 最初から書いてね。



ANTARES

リンク

2009/8/13(Thu) 04:05:13|NO.27030

 「暗号化したものを画面表示できない」ことが
「動かない」ということなのですか?

 poke buf,strlen(buf)+1,0
とかやれば、余分なものが表示されることは
なくなると思います。
バッファオーバーフローに注意。



ANTARES

リンク

2009/8/13(Thu) 04:15:09|NO.27032

 あるいは
string={"テスト1テスト1テスト1
     テスト2テスト2テスト2
     テスト3テスト3テスト3"}

string="テスト4テスト4テスト4テスト4テスト4テスト4"
の間で「sdim string,1000」とか。
バッファオーバーフローの心配がないので、こっちの方がいいです。



shinkun

リンク

2009/8/13(Thu) 08:07:39|NO.27034

> バイナリデータを文字列として処理することに無理があるので、 by ANTARES さん

よくよく考えれば仰る通りです。
文字列に文字列操作以外の処理を行っているので、すでにそれは文字列ではない、
バイナリデータだ、文字列を受け取る命令や関数には絶対に渡してはならない、
何かしたい時は全部自前で処理しなければならない、という扱いをするべきですね。
「文字列」を表示する mes 命令を使って表示しようとした事自体、間違っていたという事ですね。


-------------------------- 以下、余談 -------------------------------

私も ANTARES さんが考えるように、ヌル文字の直前に 2 byte 文字の
1 byte 目となるデータが入ってしまった事が、この問題が発生した
原因のひとつではないかと思っています。
しかし、今回の件ではこれだけでは説明が付かない事が起きているのもまた事実です。

以下は、Niko さんのスクリプトにおける test ルーチン実行後の string を、
ANTARES さんのスクリプトによる方法でダンプした物です。

バイナリデータ 対応する文字 83 66 85 5B 87 6C 88 5A 8B 6E 8D 63 8F 74 90 62 デ・㌫・杵皇春臣 93 76 95 6B 97 7C 98 6A 9B 7E 9D 73 9F 84 A0 72 砺斌慾亘宀捏氛.r A3 86 A5 7B A7 8C A8 7A AB 8E AD 83 AF 94 B0 82 」・{ァ肩zォ鹿Ρ伐. 00 82 51 83 65 83 58 83 67 82 51 0D 0A 81 40 81 .2テスト2.. 40 81 40 20 81 40 83 65 83 58 83 67 82 52 83 65 テスト3テ 83 58 83 67 82 52 83 65 83 58 83 67 82 52 00 スト3テスト3.
48 byte 目、最初のヌル文字 (0x00) の前に、shift-jis における 2 byte 文字
の 1 byte 目である 0x82 が来ています。これによって、ヌル文字が 1 文字の
全角日本語の一部とみなされてしまった、というのは十分考えられる事です。
しかし、このヌル文字をそう解釈したのであれば、その直後の「2テスト2」も
画面に出力されなければならないはずです。
ところが、実際にはそうではなく、「2テスト2」の後の改行 (0x0d 0x0a) 以降しか
出力には現れていません。これは一体どういう事なのでしょう?

試しに string に格納されていた改行を取っ払って、代わりに ' ' を 2 byte 入れてみた所、
なんと正常に表示されました!!
(' ' を代わりに入れたのは、test ルーチンが文字列長で生成データに影響を与えるからです。)
ですから、今回の件にはこの改行も一枚噛んでいる可能性があります。

今はどうして「2テスト2」が消えてしまうのかは分かりませんが、とりあえずご報告を。

なお、ANTARES さんの「ヌル文字が 2 byte 文字に解析されちゃうなら、その次の byte を
ヌル文字にしちゃえ~作戦」は、うまく動作しませんでした。
string のバッファのヌル文字以降の領域すべてをゼロクリアしても同様です。
string 自体にはもう「テスト3」の欠片も残っていないのに「テスト3」が出力されるという事は、
これはどうやら、HSP のメモリ管理の仕組みも理解しないと完全な原因の特定は出来そうに無いですね。

まぁ、こういう底なし沼みたいなドロドロした部分に足を踏み入れたくないなら、
素直に文字列でない物を mes なんかで表示しないって事になりますね。

さらに余談で、私は上記のレスで、string をファイルに書き出した場合
「テスト3」が消えたと述べましたが、その理由について。
私はそのとき notesave を使って書き出したのですが、これは内部的に

repeat c = peek(src,cnt) ; src は書き込むデータを保持した変数 if (c == 0): break poke dst,cnt,c ; dst はファイルと思ってください loop
こんな感じの処理が行われているからだと思います。
これなら 1 byte 文字だろうが 2 byte 文字だろうが関係なく処理されます。
Niko さんの mesbox についても検証してみましたが、あれはあれでさらに余計な現象が
起きているので、他の要因がある事になります。うへぇ~…

ホントにドロドロしてますね。
触らぬ神にたたり無しという言葉が身に染みて分かります。



FUJI

リンク

2009/8/13(Thu) 12:49:15|NO.27036

こういうことですね。


a = "a??" poke a, 1, 0x81 poke a, 2, 0x0 b = " \nfoo" memset b, 0, 64 // bに代入するときの右辺の結果の文字列の内容が mem_pval[HSPVAR_FLAG_STR].pt に残ったまま、 // "a\x82\x00" が上書きされる // mem_pval[HSPVAR_FLAG_STR].pt の中身は "a\x82\x00\nfoo\x00..." となる // そして strsp_get が \x00 を無視して先まで読み進めてしまう mes a

1行ずつ分ける処理の部分では後続バイトが NUL を無視していますが、それぞれの行を表示するときには strlen を使って文字数を図りそこまでの文字列しか表示しません。
これが、先行バイト + NUL 文字から改行までの文字列が表示されない理由です。

ちなみに、 HSP 3.1 と結果が違うのは、3.1 では strsp_get でマルチバイト文字の処理をしていなかったからです。
(正確にはしていたつもりが、char * で比較していたので先行バイトかどうかのチェックが常に false になっていました)

ソースとしての該当部分は以下のあたりです。
hsp3/win32gui/hsp3gr_win32gui.cpp cmdfunc_extcmd() 内の「case 0x0f: // mes,print」以下
hsp3/win32gui/hspwnd_win.cpp Bmscr::Print()
hsp3/win32gui/supio_win.cpp strsp_get()



shinkun

リンク

2009/8/15(Sat) 13:54:31|NO.27068

成程。今回の現象は、HSP 内部の仕組みのいくつかの要因が重なり合った結果起こった事なのですね。
FUJI さんのヒントを元に私が考察した結果を(余計なお世話だとは思いますが)残しておきます。

1.マルチバイト文字の判定の有無
ヌル文字のコード 0x00 は、shift-jis コード体系において、有効な文字コードとしては
利用されていないみたいですね。(他のどんな文字コード体系でもそのような可能性は高い。)
なので、2 byte 文字の 2 byte 目にも 0x00 が来る事はありません。
だから、0x81 0x00 という文字を生成してしまった事が、今回の件が起こった根本的要因となります。
「2 byte 文字の 2 byte 目に 0x00 が来る事はない」、「0x00 は文字列の終端という意味だけ
でしか利用されない」のを前提としているため、マルチバイト文字の判定においても、
1 byte 目が 2 byte 文字を表すコードだったら、それは 2 byte 文字と判断し、
2 byte 目が 2 byte 文字として有効かどうか確認する事はないようです。
ですから、無効な文字であるはずの 0x81 0x00 は、
この判定において有効な 2 byte 文字と判断されてしまいます。
なお、文字列のコピーや strlen においては、同じ理由によって、マルチバイト文字の判定をする必要が無く、
0x00 が来るまでコピーしたりカウントするだけとなっています。

2.mem_pval 配列
推測ですが、mem_pval 配列は演算結果を格納するテンポラリ変数なのでしょうか?
x = "a" + "b"
と記述した場合、まず始めに "a" が mem_pval[HSPVAR_FLAG_STR].pt にロードされ、
続いて "b" が結合演算され、mem_pval[HSPVAR_FLAG_STR].pt に追加される…と。
そして、演算が終了した後、これが x (正しくは x.pt か。)にコピーされる。
(こういう実装なら、式の演算結果のデータ型が、式中の一番左のデータ型になる
という HSP の仕様も納得行きます。)

3.mes 命令の実装
mes 命令は FUJI さんが示してくださったソースを見る限り、文字列を一行毎に抜き出す処理と
その一行を表示する処理で構成され、ヌル文字 0x00 で終了する最終行を出力した時点で終わる
みたいですね。
先の処理では、マルチバイト文字の判定がありますが、後の処理ではそれはなく、
strlen でカウントした分の文字しか出力しません。

以上の事を踏まえて、スクリプトを読んでいくと、次のような動作として読み取れます。

a = "a??" ; poke a, 1, 0x81 ; ここで問題の原因 0x61 0x81 0x00 0x00 [a.NN] が生成される。 poke a, 2, 0x0 ; ※ 表示出来ない文字は "." で、ヌル文字は "N" で表してます。 b = " nnn\nfoo" ; まずは右辺の演算が行われる。 ; mem_pval[HSPVAR_FLAG_STR].pt に " nnn\nfoo" をコピー。演算終了。 ; b にそれをコピー。 memset b, 0, 64 ; b の領域に 0 をコピーするだけなので、mem_pval 等に影響はない。 ; この時点で、b には [NNNNNNNNNNNNNNNNNNNN...] が格納されており、 ; mem_pval[HSPVAR_FLAG_STR].pt には " nnn\nfoo" が残されたまま。 mes a ; mes 命令の第一パラメータも「mes "" + x」のように式を記述出来るので、 ; まず a が mem_pval[HSPVAR_FLAG_STR].pt に「コピー」される。 ; a には 0x61 0x81 0x00 0x00 [a.NN] が格納されているが、 ; 1. で述べた通り、コピーでは 0x00 が来るまでが対象となるので、 ; 実際にコピーされるのは 0x61 0x81 0x00 [a.N] だけ。 ; mem_pval[HSPVAR_FLAG_STR].pt には、以前の内容にこれを上書きした ; [a.Nnnn\nfooN] が書き込まれている事になる。 ; これが、次の一行毎に抜き出す処理に渡される。 ; ここでは、マルチバイト文字の判定が有るので、[.N] は 2 byte 文字と ; 判定され、[a.Nnnn] が取り出される。 ; これが出力処理に渡されるが、ここでは strlen でカウントした分だけ ; の文字を出力するので、[a.] までしか出力しない。 ; 同じ様に残りの [foo] が抜き出され、[foo] が出力される。
という事で、スクリプトの実行結果と一致します。
上記スクリプトは、さんのスクリプトとはデータ長が異なるだけで、要点
(2 byte 文字の 2 byte 目に 0x00、改行有り、mes で表示)はいっしょです。

不可解に思われた動作も、こうやって調べていくと全然不可解ではありませんね。
いや~、スッキリスッキリ。FUJI さん、ご助言ありがとうございます。
開発の立場の方からのヒントがないと手も足も出ませんでした。
半分、あきらめモードでした…。(^^;



Niko

リンク

2009/8/16(Sun) 18:38:53|NO.27120

shinkunさん、そしてご回答いただいたみなさん
ありがとうございました。



ONION software Copyright 1997-2025(c) All rights reserved.