- PR -

null区切りされた文字配列について

投稿者投稿内容
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-29 19:13
こんにちは。VB.NETの初心者です。
アンマネージコード(C++)⇔マネージコード(VB.NET)の
連携で困っています。
内容は、アンマネージコード(C++)で書かれたDLLを呼び出し、
返却された文字配列データを取得したいのですが、
null区切りされている為、String型やStringBuilderで
取得できず困っています。一応、マネージコード側をBYTE
配列にし、1文字ずつEncodingする事で取得はできたのですが、
お粗末な方法な為、どなたか他に良い方法がないものか
教えてください。
ちなみに、アンマネージコード(C++)は変更する事ができません。

<開発言語>
VisualBasic2005

<DLLインターフェース>
TEST_RESULT TEST_APICALL TEST_StrGet(
 //IN :ディレクトリ名 
 char[10] DirName,

 //OUT :0ターミネートしたファイル名リスト(ASCIZ文字列:SJIS)
 char[100] FileList
);

<作成中のプログラムコード>
Public Declare Function TEST_StrGet Lib "test.dll" ( _
 ByVal DirName As String, _
 ByVal FileList As StringBuilder, _
) As Integer

Public Function TEST( _
 ByVal strDirName As String, _
 ByVal strFileList() As String, _
) As Integer

 Dim i, rc As Integer
 Dim DirName As String = "C:\"
 Dim FileList As New StringBuilder(100)

 rc = TEST_StrGet(DirName, FileList)

 For i = 0 to FileList.length
  ★StringBuilderはnullターミネイトされてしまう為、「FileList(0)」
   しか返却されない
  strFileList = FileList(i)
 Next

  ・
  ・
  ・

End Function
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2005-08-29 20:07
こんにちは、じゃんぬ です。

こちらで話が進んでいるようです。
情報を共有するためにも、代わりにフィードバックしておきます。
http://dobon.net/cgi-bin/vbbbs/cbbs.cgi?mode=al2&namber=12417

クロスサイトポストをする時は最初に断りを入れておくと、
こういうこともなくなると思います。

[ シグネチャを削除 ]

[ メッセージ編集済み 編集者: じゃんぬねっと 編集日時 2006-09-19 14:46 ]
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-29 20:26
マナーを良く知らず失礼な事をしてしまい、
申し訳ありませんでした。
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2005-08-29 22:13
引用:

じゃんぬねっとさんの書き込み (2005-08-29 20:07) より:

こちらで話が進んでいるようです。
情報を共有するためにも、代わりにフィードバックしておきます。



発言番号が違うようですので(^^;

http://dobon.net/cgi-bin/vbbbs/cbbs.cgi?mode=al2&namber=12417
_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2005-08-29 23:05
諸農です。

VB2005で開発されているんですね。

引用:

null区切りされている為、String型やStringBuilderで
取得できず困っています。一応、マネージコード側をBYTE



取得そのものは出来ているのではないでしょうか?
VB2005だと、デバッグ実行中にIDE上で変数上にマウスカーソルを移動させて確認できると思うのですが、デバッグでの確認は出来ませんでしたか?

引用:

配列にし、1文字ずつEncodingする事で取得はできたのですが、



文字列全体を一度にやっても問題ないと思いますが。

引用:

コード:
 For i = 0 to FileList.length
  ★StringBuilderはnullターミネイトされてしまう為、「FileList(0)」
   しか返却されない
  strFileList = FileList(i)
 Next





VBは詳しくないので、このコードの意味を取り違っていたらごめんなさい。
FileList変数には単語単位でnull区切りされた文字列が入っていて、
それを分割して取得したい、ということでしょうか?
ようするに文字列の配列をnullを使って区切っている、ということでしょうか。

DLLインタフェースではないですが、ファイルデータにバイナリエディタでnull区切りのデータを作成しておいて、プログラムコードでは取得したバイト配列データを文字列に変換した上で、その文字列に含まれるnullで文字列の配列に分割するサンプルです。
申し訳ないですが、C#です。(VS2005です)

コード:
// 使ったデータのダンプは次の通り(先頭からnullです(^^;)
// 00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA

    byte[] bStr;
    using (FileStream fs = new FileStream(C_FileName, FileMode.Open))
    {
        bStr = new byte[fs.Length];
        fs.Read(bStr, 0, bStr.Length);
    }
    string str = Encoding.GetEncoding("SHIFT_JIS").GetString(bStr);
    string[] strs = str.Split(new char[] { char.MinValue });
    label1.Text = "";
    foreach (string s in strs) label1.Text += s + Environment.NewLine;



最初のファイルストリームは、DLLインタフェースの代わりに文字データの中にnullが入ったデータを取得してます。
次にバイト配列を一度に全部変換してstring変数に格納し、Splitメソッドでnull部分で分割してstring配列に格納しています。
ですので、DLLインタフェースでStringBuilderで受け取ったとしても、予定しているnullで区切られた文字列の配列全部のデータが格納されているのであれば、ToString()の後にSplitすれば分割できると言うことになると思います。

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/
Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2005-08-30 00:02
あんまり書き込む気がしなかったけれど、向こうで十分なヒントが出てたのに解決してなかったので。

引用:

Jubeiさんの書き込み (2005-08-29 23:05) より:
引用:

null区切りされている為、String型やStringBuilderで
取得できず困っています。一応、マネージコード側をBYTE



取得そのものは出来ているのではないでしょうか?



アンマネージドとの相互運用においてStringBuilderを使用すると、アンマネージドから文字を受け取るときに自動的にNULL文字を検索しそこまでしか格納しません。
ですからこの場合においては使用できません。

そうなると後は使えそうなのが、Byte配列と、Stringです。
Byte配列については、まあ今までに十分出てますよね。

さてStringです。
Stringは変更不可でP/Inokeで値を受け取ることはできない、その場合はStringBuilderを使え、と言うのが定説だと思います。
ですが、VB.NETのDeclare構文においては、ByVal/As Stringで宣言した場合、内部ではByRef扱いになります。そして同時にMarshalAs属性がUnmanagedType.VBByRefStrで宣言されている状態になります。
この状態では、普通に文字列を渡すことができ、そして返値として文字列を受け取ることが可能になります。
ただし受け取る場合は、事前に受け取る文字数分以上、何らかの文字で埋めておかなければ、余った分は切り捨てられます。受け取る文字数より多く文字を埋めていた場合、その文字は残ります。
//ちなみにDllImport属性でもref/ByRefで宣言しUnmanagedType.VBByRefStrを追加すれば同じ動作になります。

問題なのは文字コードのマーシャリングでしょう。
Declare宣言で何故かみんな自動的につけてる(ようなイメージがある)Auto句ですが、これには歴とした意味がありますし漫然とつけて良いものではありません。
Unicode版の関数が扱える環境において、呼び出す関数をUnicode版の関数にし、関数とUnicode文字列でやりとりするという作用があります。
つまりNT系においては、関数にはUnicodeでエンコードされたバイト列が渡され、関数から受け取ったバイト列は自動的にUnicodeとして解釈されデコードされると言うことです。
ではプラットフォームに関わらずAnsiとして文字列をやりとりしたい場合はどうするか。
……まあそのままですね。
taa
常連さん
会議室デビュー日: 2005/08/29
投稿数: 44
投稿日時: 2005-08-30 13:32
引用:

Jubeiさんの書き込み (2005-08-29 23:05) より:
諸農です。

VB2005で開発されているんですね。

引用:

null区切りされている為、String型やStringBuilderで
取得できず困っています。一応、マネージコード側をBYTE



取得そのものは出来ているのではないでしょうか?
VB2005だと、デバッグ実行中にIDE上で変数上にマウスカーソルを移動させて確認できると思うのですが、デバッグでの確認は出来ませんでしたか?

引用:

配列にし、1文字ずつEncodingする事で取得はできたのですが、



文字列全体を一度にやっても問題ないと思いますが。

引用:

コード:

 For i = 0 to FileList.length
  ★StringBuilderはnullターミネイトされてしまう為、「FileList(0)」
   しか返却されない
  strFileList = FileList(i)
 Next





VBは詳しくないので、このコードの意味を取り違っていたらごめんなさい。
FileList変数には単語単位でnull区切りされた文字列が入っていて、
それを分割して取得したい、ということでしょうか?
ようするに文字列の配列をnullを使って区切っている、ということでしょうか。

DLLインタフェースではないですが、ファイルデータにバイナリエディタでnull区切りのデータを作成しておいて、プログラムコードでは取得したバイト配列データを文字列に変換した上で、その文字列に含まれるnullで文字列の配列に分割するサンプルです。
申し訳ないですが、C#です。(VS2005です)

コード:

// 使ったデータのダンプは次の通り(先頭からnullです(^^
// 00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA

byte[] bStr;
using (FileStream fs = new FileStream(C_FileName, FileMode.Open))
{
bStr = new byte[fs.Length];
fs.Read(bStr, 0, bStr.Length);
}
string str = Encoding.GetEncoding("SHIFT_JIS").GetString(bStr);
string[] strs = str.Split(new char[] { char.MinValue });
label1.Text = "";
foreach (string s in strs) label1.Text += s + Environment.NewLine;



最初のファイルストリームは、DLLインタフェースの代わりに文字データの中にnullが入ったデータを取得してます。
次にバイト配列を一度に全部変換してstring変数に格納し、Splitメソッドでnull部分で分割してstring配列に格納しています。
ですので、DLLインタフェースでStringBuilderで受け取ったとしても、予定しているnullで区切られた文字列の配列全部のデータが格納されているのであれば、ToString()の後にSplitすれば分割できると言うことになると思います。





VBに直してサンプルコードを試してみましたが、
やはり、null切りされているとstring変数へ格納
できませんでした。

<VBに直したコード>
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

'使ったデータのダンプは次の通り(先頭からnullです(^^
'00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA 00 93 FA 96 7B 8C EA

Dim bytArray(20) As Byte

bytArray(0) = &H0
bytArray(1) = &H93
bytArray(2) = &HFA
bytArray(3) = &H96
bytArray(4) = &H7B
bytArray(5) = &H8C
bytArray(6) = &HEA
bytArray(7) = &H0
bytArray( = &H93
bytArray(9) = &HFA
bytArray(10) = &H96
bytArray(11) = &H7B
bytArray(12) = &H8C
bytArray(13) = &HEA
bytArray(14) = &H93
bytArray(15) = &HFA
bytArray(16) = &H96
bytArray(17) = &H7B
bytArray(18) = &H8C
bytArray(19) = &HEA

Dim str As String = Encoding.GetEncoding("SHIFT_JIS").GetString(bytArray)

TextBox1.AppendText(str)
    ★nullデータを代入しないと「日本語」と表示されるが、
     nullデータを代入すると先頭がnullの為、何も表示されない。

End Sub
End Class




[ メッセージ編集済み 編集者: taa 編集日時 2005-08-30 13:37 ]
Jubei
ぬし
会議室デビュー日: 2002/03/02
投稿数: 830
お住まい・勤務地: 関西
投稿日時: 2005-08-30 14:17
引用:

コード:
        Dim str As String = Encoding.GetEncoding("SHIFT_JIS").GetString(bytArray)
        TextBox1.AppendText(str)
    ★nullデータを代入しないと「日本語」と表示されるが、
     nullデータを代入すると先頭がnullの為、何も表示されない。





私のコードではSplit使ってnull文字を無くしているんですけど。。(^_^;)

_________________
諸農和岳
Powered by Turbo Delphi & Microsoft Visual Studio 2005

十兵衛@わんくま同盟
http://blogs.wankuma.com/jubei/

スキルアップ/キャリアアップ(JOB@IT)