none
文字⓪ が C#からC++のDLLの関数へ引き渡せません。 RRS feed

  • 質問

  • "D:\\abc⓪_0001.png" を渡そうとしていますが、
    "D:\\abc?_0001.png" となります。 
    ①~⑨は大丈夫などですが、⓪を渡すにはどうしたら良いですか?

    C#
               string FullPath;
                FullPath = "D:\\abc⓪_0001.png";
                Tes.StrTest(FullPath);
    C++
    __declspec( dllexport) void StrTest(char FullPath[])
    {
    }


    また、C++関数内で、
    char str[_MAX_PATH];
    strcpy_s(str, _MAX_PATH, "D:\\abc⓪_0001.png");
    としても、str は"D:\\abc?_0001.png" となります。 


    VS2013 でマルチバイトでコンパイルしています。
    C++は MFCのDLLですが、グローバル関数で、ほぼCの範囲でコーディングしています。





    mnicksashimisan

    2018年12月17日 1:52

回答

すべての返信

  • はマルチバイト(Shift-JIS)には存在しない文字ですので表現不可能です。ワイド文字(Unicode)を使用する必要があります。
    2018年12月17日 1:55
  • 基本的には、今後のことを考えて佐祐理さんのご助言の通りパスの文字列を Unicode で処理するようにしたほうが良いと思いますが、DLL 側の修正が困難な場合、もしかするとショートファイル名で渡すことでも対応が可能かもしれません。

    using System;
    using System.Windows.Forms;
    
    namespace WindowsFormsApp1
    {
        public partial class Form1 : Form
        {
            [System.Runtime.InteropServices.DllImport("kernel32.dll",
                CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
            private static extern uint GetShortPathName(
                [System.Runtime.InteropServices.MarshalAs(
            System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        string lpszLongPath,
                [System.Runtime.InteropServices.MarshalAs(
            System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        System.Text.StringBuilder lpszShortPath,
                uint cchBuffer);
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                string FullPath = "D:\\abc⓪_0001.png";
                System.Text.StringBuilder sb = new System.Text.StringBuilder(1024);
                if (GetShortPathName(FullPath, sb, (uint)sb.Capacity) != 0)
                {
                    Tes.StrTest(sb.ToString());
                }
            }
        }
    }

    参考サイト:http://yokuarunanika.blogspot.com/2016/06/unicodedll.html

    • 編集済み kenjinoteMVP 2018年12月17日 3:52 CharSet.Auto⇒CharSet.Unicode
    2018年12月17日 3:32
  • 補足ですが、マルチバイトコードが問題となったための回避策なので、CharSet.AutoよりもCharSet.Unicodeを明示的に選択するべきです。
    2018年12月17日 3:41
  • ご指摘ありがとうございます。修正いたします。
    2018年12月17日 3:44
  • ワイド文字(Unicode)を使用する必要があるということで了解しました。
    ありがとうございました。

    ショートファイル名で渡すべく、ご案内のコードをそのまま書きましたが、
    下記のエラーになります。
    初回例外が 0x00007FFC6AE43247 (clr.dll) で発生しました (abcCS.exe 内): 0xC00000FD: Stack overflow (パラメーター: 0x0000000000000001, 0x00000043E8A05000)。
    共通言語ランタイムは、この例外で停止できません。一般的な原因は、正しくない COM 相互運用機能のマーシャリングやメモリの破損などです。さらに調べるには、ネイティブのみのデバッグを使用します。



    mnicksashimisan

    2018年12月17日 5:41
  • 下記のエラーになります。

    C++ 側はマルチバイト文字のままなのですよね。

    ご案内のコードをそのまま書きましたが、下記のエラーになります。

    Stack Overlow とのことなので、関数呼び出しのネストが深すぎているということはないでしょうか。.NET の例外ではない場合には、DLL(C++)側の問題も疑われるので、どの行を実行したときにエラーが発生するのかを特定してみてください。

    もしも DLL の StrTest に渡す前に止まってしまうようであれば、宣言部に間違いが無いか再確認の上、そもそも変換が正しく行われているかどうかも確認してみてください。

    変換が行われていないようであれば、コマンド プロンプトを管理者モードで開き、ショートファイル名が有効なドライブかどうかを確認してみてください。

    • fsutil behavior query disable8dot3
    • fsutil behavior query disable8dot3 D:

    最近の環境だと、システムドライブ(C:)はショートファイル名が有効で、データドライブ(D:)はロングファイルネームのみに設定されていることがあります。

    これが『無効』に設定されていた場合、GetShortPathName 案は利用できません。

    string fullPath = @"D:\abc⓪_0001.png";
    var sb = new System.Text.StringBuilder(1024);
    // 下記により、「D:\ABC_00~1.PNG」などに変換されることが期待される string shortPath = sb.ToString(0, (int)GetShortPathName(FullPath, sb, (uint)sb.Capacity));

    ショートファイル名が無効な場合、マルチバイト文字でアクセス可能なハードリンク名を MKLINK で付与してから受け渡すという方法もあります。

    • MKLINK /H "D:\TEMP.PNG" "D:\abc⓪_0001.png"

    2018年12月17日 8:21
  • C++ 側はマルチバイト文字のままです。

    現象は D: の時のものです。
    Tes.StrTest(sb.ToString()); の所までは正しく文字列は来ています。

    C++ 側の中身は何もない状態です。
    C++ 側の関数が全く呼び出されていないようです。

    if (GetShortPathName(FullPath, sb, (uint)sb.Capacity) != 0)
    の所とC++側にブレークポイントを置くと、C++側ではとまりません。
    F5で進むと、if (GetShortPathName(FullPath, sb, (uint)sb.Capacity) != 0)
    の所で止まり、その繰り返しになります。
    それで、Stack Overlow になってしまうようです。

    ドライブを C: にすると、
    if (GetShortPathName(FullPath, sb, (uint)sb.Capacity) != 0)
    if 文の中に入らず、Tes.StrTest(sb.ToString()); は実行されず、
    そのまま終わってしまいます。

    他の部分は意味が良く分かりませんで試せませんでした。



    mnicksashimisan

    2018年12月18日 2:24
  • プロジェクトのデバッグ設定でネイティブデバッグを有効化しないことには、.NETのみのデバッグとなり、ネイティブ部分の設定は無視されます。正しく設定されていますでしょうか?
    2018年12月18日 2:35
  • ネイティブデバッグは有効になっています。
    私が最初に提示したコーディングではC++の関数に入って行きます。


    mnicksashimisan

    2018年12月18日 5:19