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

■34090 / 親記事)  参照dllへの文字列引数をutf8で渡したい。
  
□投稿者/ ukots 一般人(1回)-(2019/01/12(Sat) 11:01:07)
  • アイコン環境/言語:[VB.NET on Windows7] 
    分類:[.NET] 

    vbnetに第三者のdllを参照しようとしているが、渡すべき引数の一つがUTF8の文字列変数(String)という仕様となっています。vbnet上では文字列変数はすべてUNICODEに限定されているため、どうしてもUNICODEの文字列を渡すことになり、正常に作動しません(文字化けなど)。なにか良い解決策があればご意見ください。
マルチポストを報告
違反を報告
引用返信 削除キー/
■34091 / ResNo.1)  Re[1]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ Hongliang 大御所(549回)-(2019/01/12(Sat) 11:05:22)
  • アイコン確認ですが、そのDLLというのは、Visual Studioの「参照の追加」で参照するものでしょうか。
    それとも、Declare(あるいはDllImport)で呼び出す関数を定義するものでしょうか。
違反を報告
引用返信 削除キー/
■34092 / ResNo.2)  Re[2]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(2回)-(2019/01/12(Sat) 13:25:04)
  • アイコンNo34091に返信(Hongliangさんの記事)
    確認ですが、そのDLLというのは、Visual Studioの「参照の追加」で参照するものでしょうか。
    それとも、Declare(あるいはDllImport)で呼び出す関数を定義するものでしょうか。
    返事ーーー後者です。Declare.........(ByVAl abc As Strings, .........) As Stringのように。

違反を報告
引用返信 削除キー/
■34093 / ResNo.3)  Re[3]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ Hongliang 大御所(550回)-(2019/01/12(Sat) 17:48:36)
  • アイコン> 返事ーーー後者です。Declare.........(ByVAl abc As Strings, .........) As Stringのように。

    であれば、abc As Stringではなくabc As Byte()と宣言して、バイト配列で渡すのが良さそうです。
    渡すバイト配列は以下のように作成します。

    ' str1と言う変数に渡すべき文字列が入ってるとする。
    Dim bytes As Byte() = Encoding.UTF8.GetBytes(str1 & ChrW(0))

    // 返値がStringなのはメモリリークしそうで怖いなぁ。
違反を報告
引用返信 削除キー/
■34095 / ResNo.4)  Re[4]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(5回)-(2019/01/13(Sun) 10:29:14)
  • アイコンNo34093に返信(Hongliangさんの記事)
    // 返値がStringなのはメモリリークしそうで怖いなぁ。

    解決策が見えたかな:
    DllImportを使用し
    文字コードをCharSet.Unicodeとし、
    関数の引数をXYZ(ByVAl abc() As Byte, .........) As Stringとし、
    Dim abc() As Byte = System.Text.Encoding.UTF8.GetBytes(目的の文字列 + ChrW(0))として懸案の第三者Dllを動かし、
    返値XYZを一旦dim temp As String =System.Text.Encoding.Unicode.GetBytes(XYZ)とし、
    それを更にDim result As String System.Text.Encoding.UTF8.GetString(temp)としたら関数の返値(utf8)がresult中にUicodeとして文字化けせずに見えました。

    これで問題ないか更に検証してみます。


違反を報告
引用返信 削除キー/
■34097 / ResNo.5)  Re[5]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(6回)-(2019/01/15(Tue) 15:40:08)
  • アイコンNo34095に返信(ukotsさんの記事)
    > ■No34093に返信(Hongliangさんの記事)
    > // 返値がStringなのはメモリリークしそうで怖いなぁ。
    >
    > これで問題ないか更に検証してみます。

    前記のように、Encoding繰り返し偶然にも文字列が化けずに読めたのですが、文字数に矛盾があり、想定していた返値の文字列以外にゴミのようなものが数或は数十バイト付いてきました。したがって、完璧とは行かない結果となりました。


違反を報告
引用返信 削除キー/
■34099 / ResNo.6)  Re[1]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ 魔界の仮面弁士 大御所(1189回)-(2019/01/15(Tue) 16:48:04)
  • アイコンNo34095に返信(ukotsさんの記事)
    > 関数の引数をXYZ(ByVAl abc() As Byte, .........) As Stringとし、

    「第三者のdll」が戻り値として返却する文字列のメモリは
    DLL 側でどのように確保されているのでしょうか。
    また、戻り値の文字列エンコードは何になっているのでしょうか。


    戻り値が ANSI あるいは UTF-16 の場合には、戻り値の As String に
    対して MarshalAs 属性を付与することで受けられる可能性が
    あるのですが、その時、どのような宣言になるべきかは
    DLL 側の実装に依存しますので、DLL 提供元に問い合わせてみてください。

    たとえば DLL 側が SysAllocString している場合は
    UnmanagedType.BStr あるいは UnmanagedType.AnsiBStr を、
    CoTaskMemAlloc している場合は
    UnmanagedType.LPWStr あるいは UnmanagedType.LPStr を、
    MarshalAs することで対処できる可能性があります。

    あるいは戻り値として、As IntPtr が求められるケースもあるでしょう。
    仮に、戻り値のエンコードが UTF-8 な BSTR だとしたら、
    As String では正しく受けられないはずですので、
    As IntPtr で受け取った上で VB 側でデコードし、
    使用後に Marshal.FreeBSTR メソッドで解放するなどの
    追加手順が必要になるかもしれません。(こちらでは検証できませんが)
違反を報告
引用返信 削除キー/
■34101 / ResNo.7)  Re[2]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(7回)-(2019/01/16(Wed) 11:25:02)
  • アイコンNo34099に返信(魔界の仮面弁士さんの記事)

    魔界の仮面弁士さん、返信有り難うございます。marshal関係のことは私には難し過ぎてgive-upです。今回の問題のdllの仕様は下記のpdfに載っています(NLPIR_ParagraphProcess)。

    https://github.com/NLPIR-team/NLPIR/blob/master/NLPIR%20SDK/NLPIR-ICTCLAS/doc/NLPIR-ICTCLAS%E5%88%86%E8%AF%8D%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91%E6%89%8B%E5%86%8C2017%E7%89%88%20.pdf

    Declare Function NLPIR_ParagraphProcess Lib "NLPIR.dll" (ByVal sParagraph As String, ByVal bPOStagged As Integer) As Stringに関しては返値文字列の必要な部分のみを取り出す処理を追加し、一応使用できる状態になりましたので、自分的にはこれで良しと考えています。

    別件で、この関数のシリーズ中に、NLPIR_FileProcessがあります。これも
    Declare Function NLPIR_FileProcess Lib "NLPIR.dll" (ByVal sSourceFileName As String, ByVal sResultFileName As String, ByVal bPOStagged As Integer) As Doubleという記述でVBNETで使用しようとしています。ただ、ファイルパスはUTF8文字列であり、漢字交じりのパス名を渡すとエラーになります。よって、半角英数のファイルパスの元で動かすようにして使用できるようにしました。

    皆さんいろいろお世話いただき有り難うございます。そろそろこの件をcloseしようかと思います。

違反を報告
引用返信 削除キー/
■34102 / ResNo.8)  Re[3]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ 魔界の仮面弁士 大御所(1190回)-(2019/01/16(Wed) 17:05:20)
  • アイコン2019/01/16(Wed) 17:08:10 編集(投稿者)

    No34101に返信(ukotsさんの記事)
    > 今回の問題のdllの仕様は下記のpdfに載っています

    簡体は読み解けないので、コード部と英語部分だけを
    流し読みしてみただけですが…エンコーディングについては
    最初の NLPIR_Init API で指定するのでしょうか。

    簡体字、繁体字、UTF-8 の 3 種があるようですが(既定では簡体字)、
    残念ながら .NET Framework では Marshal.PtrToStringXXX メソッドの
    UTF-8 版が使えないという罠。
    https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.marshal.ptrtostringutf8


    となるとやはり、System.Text.Encoding を使って
    自前で処理する必要があると思います。


    > NLPIR_ParagraphProcess

    この API ですね。

    const char * NLPIR_ParagraphProcess(const char *sParagraph, int bPOStagged = 1);

    だとすれば、こんな手順でどうでしょう。


    (1) NLPIR_ParagraphProcess API の戻り値を As IntPtr で宣言しなおす

    (2) 戻り値のバッファ長を得るため、kernel32.dll の lstrlenA API を呼び出す
     Private Declare Ansi Function lstrlenA Lib "kernel32" (ByVal p As IntPtr) As Integer

    (3) 受信バッファとして、その長さの Byte 型 1 次元配列を確保する

    (4) 確保したバッファに、Marshal.Copy( IntPtr, Byte(), Integer, Integer) メソッドで複写

    (5) System.Text.Encoding.UTF8.GetString メソッドで文字列に変換する



    なお、受信した文字列バッファの解放処理をどうすべきかは、
    読み解けませんでした。

    NLPIR_Exit を呼び出すとワーキングバッファも解放されるようですが、
    戻り値で返される文字列のメモリ解放もこれですかね…?
違反を報告
引用返信 削除キー/
■34103 / ResNo.9)  Re[4]: 参照dllへの文字列引数をutf8で渡したい。
□投稿者/ ukots 一般人(8回)-(2019/01/17(Thu) 10:16:14)
  • アイコンNo34102に返信(魔界の仮面弁士さんの記事)

    NLPIR_ParagraphProcessの件、ご指示通り行いました。うまく行きそうです。
    UTF8のバッファーをUnicodeにEncodingして、末尾の0hを消去して実用に供する予定です。

    なお、「戻り値で返される文字列のメモリ解放」については特段手立てを設けずともいまのところエラーが出ません。

    魔界の仮面弁士さん、天才だね。有り難うございました。

    PS: NLPIR_FileProcess(FileName,FileName,1) As Doubleについては、NLPIR_FileProcess自体がUTF8でWindows側のファイルにアクセスしようとすることは、そのままではどうにも対処できるものではないのであきらめですね。
    ではこれにて解決済みとします。


違反を報告
引用返信 削除キー/

次のレス10件>

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

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -