DOBON.NETプログラミング道掲示板

■34104 / ResNo.10)  Re[5]: 参照dllへの文字列引数をutf8で渡したい。
  
□投稿者/ 魔界の仮面弁士 大御所(1191回)-(2019/01/17(Thu) 12:42:37)
  • アイコンNo34103に返信(ukotsさんの記事)
    > UTF8のバッファーをUnicodeにEncodingして、末尾の0hを消去して実用に供する予定です。

    あれ? lstrlenA は「0h の直前までのバイト数」を返すので、
    取得したバッファー内に 0h が含まれることは無いはずなのですが…。

    たとえば UTF-8 文字列のバイナリ表現が
     { &HE5, &HBC, &H81, &HE5, &HA3, &HAB,  &H0  , &HCE, &HB2 }
    だったとしたら、戻り値は 7 では なく 6 ですし、
     { &H0  , &HE5, &HBC, &H81, &HE5, &HA3, &HAB, HCE, &HB2 }
    だった場合、戻り値は 1 ではなく 0 となります。


    もしかして、
    > (3) 受信バッファとして、その長さの Byte 型 1 次元配列を確保する
    > (4) 確保したバッファに、Marshal.Copy( IntPtr, Byte(), Integer, Integer) メソッドで複写
    の手順で、サイズ指定をミスっていたりはしませんか?


    > PS: NLPIR_FileProcess(FileName,FileName,1) As Doubleについては、

    この API ですよね。第 3 引数は Optional ByVal bPOStagged As Integer = 1 かな。

    Double NLPIR_FileProcess(
     const char *sSourceFilename,
     const char *sResultFilename,
     int bPOStagged = 1);


    この場合の入力引数も、先の戻り値と同様、「Encoding.UTF8 を使って自前で変換する」だけです。

    現状の引数宣言が、
     <MarshalAs(UnmanagedType.LPStr)> ByVal sSourceFilename As String
    になっているのだとしたら、それを
     ByVal sSourceFilename As Byte()
    もしくは
     ByVal sSourceFilename As IntPtr
    にしてみてください。


    As Byte() で渡す場合は、 No34093 で Hongliang さんが書かれたコードそのままです。
     Dim bytes As Byte() = Encoding.UTF8.GetBytes("Source.txt" & vbNullChar)


    As IntPtr で渡す場合は、
     Dim ptrSrc As IntPtr = Marshal.StringToHGlobalUTF8( "Source.txt" & vbNullChar )
     Dim ptrDst As IntPtr = Marshal.StringToHGlobalUTF8( "Result.txt" & vbNullChar )
     Dim ret As Double = NLPIR_FileProcess( ptrSrc, ptrDst, 1 )
     Marshal.FreeHGlobal( ptrDst )
     Marshal.FreeHGlobal( ptrSrc )
    としたいところですが……残念ながら StringToHGlobalUTF8 なんていうメソッドは無いので、こんな感じで。
     Dim bytes As Byte() = Encoding.UTF8.GetBytes("Source.txt" & vbNullChar)
     Dim ptrSrc As IntPtr = Marshal.AllocHGlobal( bytes.Length )
     Marshal.Copy(bytes, 0, ptrSrc, bytes.Length)
     Dim ptrDst As IntPtr
     '中略:ptrDst も ptrSrc と同様の手順で、AllocHGlobal + Copy で UTF-8 バッファを作成
     Dim ret As Double = NLPIR_FileProcess( ptrSrc, ptrDst, 1 )
     Marshal.FreeHGlobal( ptrDst )
     Marshal.FreeHGlobal( ptrSrc )
違反を報告
引用返信 削除キー/
■34105 / ResNo.11)  Re[6]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(9回)-(2019/01/17(Thu) 17:03:14)
  • アイコンNo34104に返信(魔界の仮面弁士さんの記事)

    Dim intptr As Integer = NLPIR_ParagraphProcess(ybyt, 1)
    Dim plen As Integer = lstrlenA(intptr)
    Dim bf(plen - 1) As Byte
    Marshal.Copy(intptr, bf, 0, bf.Length)
    Dim pstr As String = System.Text.Encoding.UTF8.GetString(bf)
    これで0hは付加されませんでした。

    Double NLPIR_FileProcess(
     const char *sSourceFilename,
     const char *sResultFilename,
     int bPOStagged = 1);については、

    Dim srcStr As String = "d:\data\漢字\temp.txt"
    Dim dstStr As String = "d:\data\漢字\result.txt"

    Dim bytSrc As Byte() = Encoding.UTF8.GetBytes(srcStr & vbNullChar)
    Dim ptrSrc As IntPtr = Marshal.AllocHGlobal(bytSrc.Length)
    Marshal.Copy(bytSrc, 0, ptrSrc, bytSrc.Length)

    Dim bytDst As Byte() = Encoding.UTF8.GetBytes(dstStr & vbNullChar)
    Dim ptrDst As IntPtr = Marshal.AllocHGlobal(bytDst.Length)
    Marshal.Copy(bytDst, 0, ptrDst, bytDst.Length)

    Dim ret As Double = NLPIR_FileProcess(ptrSrc, ptrDst, 1)
    では機能しません。

    上記でディレクトリの漢字を削除したPathでは問題なくresult.txt作成され、内容も期待したものが入っていました。つまり、
    Dim srcStr As String = "d:\data\temp.txt"
    Dim dstStr As String = "d:\data\result.txt"

    NLPIR_FileProcessにutf8のファイルパスを渡せても、NLPIR_FileProcessからutf8のままでWindowsのファイルシステムにアクセスしてもファイルを探せないのだろうと素人的に推測しています。

    以上。


違反を報告
引用返信 削除キー/
■34106 / ResNo.12)  Re[7]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ 魔界の仮面弁士 大御所(1192回)-(2019/01/17(Thu) 19:41:47)
  • アイコン2019/01/17(Thu) 20:51:04 編集(投稿者)

    No34105に返信(ukotsさんの記事)
    > Dim bf(plen - 1) As Byte

    そうですね。それで OK だと思います。

    丸括弧内は「配列の要素数」ではなく「最大インデックス番号」ですので
    「バッファの長さ - 1」を指定することになります。

    たとえば 4 バイトのバッファなら、
    Dim bf(0 To 3) As Byte な範囲になる換算ですね。


    > Dim srcStr As String = "d:\data\temp.txt"
    > Dim dstStr As String = "d:\data\result.txt"

    結果報告ありがとうございます。

    ASCII なパスを UTF-8 変換したバイナリとして渡すのは OK だったということは
    それは DLL 側の不具合(もしくは制限)である可能性が考えられます。


    おそらくは、ファイル アクセスに用いられている Windows API が
    Wide バージョン(たとえば CreateFileW 関数など)ではなく、
    Ansi バージョン(たとえば CreateFileA 関数など)になっているのでしょう。


    すなわちこれは、ファイルパスに対して
     日本語版 Windows で実行した場合は CP932 (Shift_JIS 相当) の文字列
     繁体字版 Windows で実行した場合は CP950 (Big5 相当) の文字列
     簡体字版 Windows で実行した場合は CP936 (GBK/GB2312 相当) の文字列
    しか渡せないということを意味します。


    つまり、パス引数に対して使うのは Encoding.UTF8 や Encoding.Ascii や Encoding.Unicode ではなく、
     Encoding.Default.GetBytes(srcStr & vbNullChar)
     Encoding.GetEncoding(932).GetBytes(srcStr & vbNullChar)
     Encoding.GetEncoding(936).GetBytes(srcStr & vbNullChar)
     Encoding.GetEncoding(950).GetBytes(srcStr & vbNullChar)
    のいずれかであったものと推察します。
    ※ただし strSrc の中身が OS 既定のコードページで扱える文字のみであることが条件


    そしてもし、Encoding.Default で呼び出せる文字列である場合、
    ANSI バージョンであることを明示するために、API の引数宣言を

    Declare Ansi Function NLPIR_FileProcess Lib "NLPIR" ( _
     <MarshalAs(UnmanagedType.LPStr)> ByVal sSourceFilename As String, _
     <MarshalAs(UnmanagedType.LPStr)> ByVal sResultFileName As String, _
     Optional ByVal bPOStagged As Integer = 1) As Double

    もしくは

    <DllImport("NLPIR.DLL", CharSet:=CharSet.Ansi)> _
    Function NLPIR_FileProcess(
     <MarshalAs(UnmanagedType.LPStr)> ByVal sSourceFilename As String, _
     <MarshalAs(UnmanagedType.LPStr)> ByVal sResultFileName As String, _
     Optional ByVal bPOStagged As Integer = 1) As Double
    End Function

    の形式に修正することでも、ファイルパスを渡すことが出来るのでは無いでしょうか。


    > Dim srcStr As String = "d:\data\漢字\temp.txt"

    上記のように、API 宣言で Ansi 版であることを明示した場合、
    As String な引数に "漢字" という文字列が渡されたときには
      日本語 Windows では 8A, BF, 8E, 9A
      繁体字 Windows では BA, 7E, A6, 72
      簡体字 Windows では 9D, 68, D7, D6
    というバイナリに自動変換されてから DLL に渡される見込みです。
違反を報告
引用返信 削除キー/
■34107 / ResNo.13)  Re[8]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(10回)-(2019/01/18(Fri) 09:21:45)
  • アイコンNo34106に返信(魔界の仮面弁士さんの記事)

    皆様、おかげさまで懸案のdllを使えるようになりました。種々のアドバイス、大変勉強になりました。また、別件あるいは今回の延長線で何かあれば寄稿させていただきます。
    御礼と記事を閉じることの報告まで。

解決み!
違反を報告
引用返信 削除キー/

<前のレス10件

スレッド内ページ移動 / << 0 | 1 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -