none
特定の環境でStrConvの半角から全角への変換が正常に動作しない RRS feed

  • 質問

  • はじめまして。

    初めての質問で色々と不備があるとは思いますが、どうぞ宜しくお願い致します。

    Visual Studio 2008にて、c#でWindowsアプリケーションを開発しています。

    その中で、Wordのbookmarkを利用して帳票を作成している処理があるのですが、

    bookmarkに全角の数字が連番で使われているためMicrosoft.VisualBasic.Strings.StrConvメソッドを利用して

    int型変数を全角に変換して、Word側のbookmarkを検索するための文字列を生成しています。

      int i = 0;

      foreach(...)

      {

        i++;

        string wIdx = Strings.StrConv(i.ToString().PadLeft(2, '0'), VbStrConv.Wide, 0x0411);

        //debug

        string msgdbg2 = string.Format("wIdx=\"{0}\"", wIdx);

        MessageBox.Show(msgdbg2);    // 実行結果   wIdx="01"

        foreach (Word.Bookmark bookmark in oDoc2.Bookmarks)

        {

          strBookmarkName = bookmark.Name;

          if (strBookmarkName == "債券名称和文" + wIdx)

          {

                :

          }

        }

      }

      ※COMオブジェクト解放処理はfinallyで実装

    この処理はビルドされて他のEXEから呼ばれるのですが、

    お客様の特定の端末から実行した時のみ、wIdxが "01" ではなく "0" となってしまいます。(このせいでWord側のbookmarkと一致しない)

    しかも、同じ端末であっても呼び元を変えると正常に動作するのです。

    1) Login.exe -> menu.exe -> ProcA.exe -> StrConv実装.exe <- NG

    2) ダミー.exe -> menu.exe -> ProcA.exe -> StrConv実装.exe <- OK

    ちなみに上記1で正常に動作しない端末は今のところ1台だけで、他の端末では正常に動作しています。

    また、この件の対処についてはWord側のbookmarkの数字を半角にすることで対応する予定ですが、

    特定の端末での不具合となると本番リリース前の検証テストで担保が取れないということで、

    お客様からは原因を特定して欲しいとの要望を受けています。

    どのような要因がStrConvに影響を与えるのか、ご存じの方はご教示いただけないでしょうか。

    お忙しいところ大変恐縮ですが、以上宜しくお願い致します。

    =====

    [開発環境]

     ・Windows7 Professional SP1(日本語)

     ・Visual Studio 2008 SP1(.NET Framework 3.5)

     ・Microsoft Office Professional Plus 2010

    [システム構成]

     ・サーバ:Windows Server 2008 Standard SP1

     ・DBMS:Microsoft SQL Server 2008

     ※サーバ上に全てのEXEファイルを置いてあり、ユーザは各自のクライアントPCからサーバ上のLogin.exeのショートカットをダブルクリックで起動する。

    [事象が発生している端末]

     ・Windows7 Professional SP1(日本語)

     ・Microsoft Office Professional Plus 2010

    2015年12月10日 14:42

回答

  • 互換モードの設定は、その端末のレジストリ上に保存されます。
    ですのでEXEファイルを格納しているサーバー上の設定、あるいは無関係な端末からネットワーク経由で先サーバのEXEファイルを参照したときの設定ではなく、
    今回の問題が発生している端末からネットワークサーバ上のEXEに対して、どのように互換モード設定を行っているのかを確認する必要があります。

    また、念のため調査した事象が発生している端末のWordも互換モードではありませんでした。

    このタイミングで発生端末からの実行ファイル互換モード設定は確認されなかったのでしょうか。
    レジストリからも確認はできますが誤って編集しないようご注意ください。

    HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

    2015年12月11日 14:59
    モデレータ

すべての返信

  • こんにちは。

    少し調べてみたところ過去に以下のようなスレッドがありましたが確認済みでしたでしょうか。
    要は実行ファイルの互換モードが問題になるケースがあるらしいです。

    Strconvの戻り値がおかしくて,とても困っています。

    また、「Strconv 互換モード」などで調べてみるといくつか関連しそうな記事も見当たります。
    ひとつ可能性として確認してみては如何でしょうか。


    2015年12月10日 15:39
    モデレータ
  • Tak1Waさん

    コメントありがとうございます。

    互換モードについては意識しておりませんでした。

    教えて頂いたリンクを参照したところ、起動するアプリケーションの互換モードが影響するようだったので、

    1) Login.exe -> menu.exe -> ProcA.exe -> StrConv実装.exe <- NG

    2) ダミー.exe -> menu.exe -> ProcA.exe -> StrConv実装.exe <- OK

    Login.exe、menyu.exe、ProcA.exe、StrConv実装.exeのそれぞれの互換モードを確認したところ、

    [互換モード]にチェックは入っておりませんでした。

    また、以下

    http://support.microsoft.com/kb/2297924/ja

    を参考にWordの互換モードを「XP SP3」にし、自分の端末でLogin.exeから実行してみましたが、

    事象は再現しませんでした。


    後ほど、事象の発生しているお客様の端末でWordの互換モードを確認してみます。

    2015年12月11日 6:35
  • Strings.StrConvの問題であれば、Wordの互換モードではなくLogin.exeの互換モードを確認・検証を行うべきではないですか?
    VBAだとか、COM上の操作で発生する問題ではないのですよね?.NET上の問題なのかと思ってましたので。

    2015年12月11日 7:05
    モデレータ
  • Strings.StrConvメソッドは内部的にWindows APIのLCMapStringAを呼びます。しかしWindows Vista以降では文字列の並び替えテーブルに変更が加えられているため、LCMapStringを含むいくつかのAPIの動作結果が異なることがあります。その影響で動作しなくなるアプリケーションのために、Windows XP以前の挙動をするEmulateSorting修正プログラムが提供されています。そこで、アプリケーションの互換性にてWindows XP以前を指定した場合にEmulateSorting 修正プログラムが有効となります。

    本スレッドや他所でも話題に上がっているということは、EmulateSorting修正プログラムを有効化した場合に、実際にはWindows XP以前とも異なる誤った結果が返されているのかなぁ~?

    2015年12月11日 8:21
  • Tak1waさん

    返信ありがとうございます。

    Login.exe、menu.exe、ProcA.exe、StrConv実装.exeのそれぞれの互換モードも確認していましたが、「互換モード」についての理解が不安であったので、念のためWordの互換モードも確認してみました。

    ちなみに上記EXEの互換モードは、ネットワークにあるサーバのEXEファイルを確認しましたが、これは互換モードではありませんでした。([互換モード]未チェック状態)

    また、念のため調査した事象が発生している端末のWordも互換モードではありませんでした。

    実験として、ダミー.exeをネットワークサーバ上で互換モードにして実行してみましたが、私の端末では正常に動作しました。

    ただし、以下だけを実行するテストEXEをローカルに置き、互換モードに実行したところ、同様の事象が再現しました。("01"を全角変換すると"0"になる)

    string msgdbg2 = string.Format("test:01→\"{0}\"", Strings.StrConv("01", VbStrConv.Wide, 0x0411));
    MessageBox.Show(msgdbg2);
    

    ネットワークサーバのEXEをダブルクリックしてローカルPC上で実行する場合の互換モードは、実体(ネットワークサーバのEXE)に設定されている互換モードではなく、ローカルPC上の「互換モードに影響する設定」が何かあるのでしょうか。


    • 編集済み Norihiro Omori 2015年12月14日 8:47 サンプルコードが抜けていたため。
    2015年12月11日 10:13
  • 佐祐理さん

    コメントありがとうございます。

    他の端末では発生していないので、LCMapStringAの動作に影響する(EXEを互換モードで動作するようにしてしまう)ローカルPC側の設定が、どこかにあるのでしょうか。

    EmulateSorting修正プログラムがインストールされていれば互換モードになっていたとしても正常に動作するというのであれば、どこか知らないところで互換モードで動作するようになっていて、かつ、端末にEmulateSorting修正プログラムがインストールされていないという認識です。

    2015年12月11日 10:33
  • 互換モードの設定は、その端末のレジストリ上に保存されます。
    ですのでEXEファイルを格納しているサーバー上の設定、あるいは無関係な端末からネットワーク経由で先サーバのEXEファイルを参照したときの設定ではなく、
    今回の問題が発生している端末からネットワークサーバ上のEXEに対して、どのように互換モード設定を行っているのかを確認する必要があります。

    また、念のため調査した事象が発生している端末のWordも互換モードではありませんでした。

    このタイミングで発生端末からの実行ファイル互換モード設定は確認されなかったのでしょうか。
    レジストリからも確認はできますが誤って編集しないようご注意ください。

    HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers

    2015年12月11日 14:59
    モデレータ
  • Tak1waさん

    返信ありがとうございます。

    ローカルPCでサーバのEXEファイルを右クリック-[プロパティ]-[互換性]タブを確認したところ、

    「このプログラムはネットワーク ドライブにあるため、互換モードを永続的に設定することはできません。」

    と表示されて[互換モードでこのプログラムを実行する]チェックボックスがグレイアウトしていたので、サーバ側でしか設定できないものと思い込んでしまいました。

    自分のローカルPCからサーバのEXEファイル(Login.exe)を右クリック-[互換性のトラブルシューティング]を選択して互換モードを設定してみたところ、ご教示頂いたレジストリに設定が追加されました。
    (ちなみに自分の環境では以下レジストリにも追加されていました。
    HKEY_USERS\S-1-5-21-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers)

    この状態で
    Login.exe -> menu.exe -> ProcA.exe -> StrConv実装.exe
    を実行したところ事象が再現しました。

    また、以下の手順で互換モードが解除できるようです。(事象が解消されました。)

    1)EXEファイル右クリック
    2)[互換性のトラブルシューティング]を選択
    3)[問題のトラブルシューティング]を選択
    4)全てのチェックを外して[次へ]ボタンクリック
    5)「いいえ、この問題を調査しません。変更を元に戻し、すべての設定をクリアします」を選択して[次へ]ボタンクリック
    6)[閉じる]ボタンクリック

    上記手順でレジストリを確認したところ、レジストリキー自体が消えました。(互換モードを設定して一度シャットダウンした後に同じ手順を実行すると、レジストリキー自体は残りデータが「#」になるようです。)

    後ほどお客様の端末でレジストリを確認してみます。
    2015年12月14日 6:16
  • 先程投稿した互換モード解除の手順について補足します。

    レジストリキー自体が消えるのは[ファイル右クリック]-[プロパティ]選択から、[互換性モードでこのプログラムを実行する]のチェックボックスを外した時でした。

    [互換性のトラブルシューティング]から解除した場合はシャットダウンにかかわらず、レジストリキー自体は残りデータが「#」になります。

    以上、念のため。

    2015年12月14日 8:55
  • Tak1waさん

    事象の発生したお客様の端末でレジストリを確認したところ、Login.exeの設定が「WINXPSP2」となっており、[互換性トラブルシューティング]からの互換モード解除の手順を実施したところ、正常に帳票が出力できました。

    次に、

    「なぜ互換モードになってしまったのか」
    「互換モードによる影響がStrConv以外にも無いか」

    という問題を検証しなくてはなりませんが、当面の問題は解決しましたので、本件についてはクローズ致します。

    コメントを頂いたTak1waさん、佐祐理さん、それから他のこの問題を考えて頂いた皆さんに感謝致します。
    ありがとうごさいました。

    2015年12月14日 9:13
  • 他の端末では発生していないので、LCMapStringAの動作に影響する(EXEを互換モードで動作するようにしてしまう)ローカルPC側の設定が、どこかにあるのでしょうか。

    EmulateSorting修正プログラムがインストールされていれば互換モードになっていたとしても正常に動作するというのであれば、どこか知らないところで互換モードで動作するようになっていて、かつ、端末にEmulateSorting修正プログラムがインストールされていないという認識です。

    設定場所についてはTak1waさんが説明されているレジストリになります。「EmulateSorting修正プログラム」という名前ではありますが個別に修正プログラムをインストールするものではなく、Windowsに標準で含まれているものです。したがって互換性の設定をすることで自動的に適用されます。端末によって動作が異なるということは互換性の設定がされているものといないものと混在しているのではと思います。
    2015年12月14日 11:56
  • 佐祐理さん

    コメントありがとうございます。

    「EmulateSorting修正プログラム」がWindows標準でかつ、互換性の設定をすることで適用されるのであれば、互換モードに設定されていた場合にStrConvが返す値は正常なものになるという認識ですが、実際は互換モードに設定すると、"01"を全角変換したら"01"と返ってきて欲しいのに"0"が返ってきてしまいます。

    以下を読むとWindows7には標準で用意されている仕組みのようですが、

    https://technet.microsoft.com/ja-jp/library/cc722305(v=ws.10).aspx

    何かが邪魔して「EmulateSorting修正プログラム」が正常に動いていないのでしょうか。

    それとも「互換モードに設定しない」ことで「EmulateSorting修正プログラム」が自動的に動作するということなのでしょうか。

    念のため、上記URLに

    「Compatibility Administrator を (昇格された特権のある) 管理者として起動した場合、すべての修正されたアプリケーションが正常に実行されるが~」

    との記載があったので、ローカルPCに"01"を全角変換するEXEを置き、互換モードに設定した後、[管理者として実行]をしてみたところ、"0"が返ってきてしまいました。

    2015年12月14日 14:46
  • 佐祐里さん

    気付きました。

    「EmulateSorting修正プログラム」の説明に、

    「CompareStringW/LCMapString 並べ替えテーブルを使用するアプリケーションで、Windows Vista でそのテーブルが採用される前の古いバージョンが強制的に使用されるようにします。」

    とあるので、「EmulateSorting修正プログラム」が正常に動作することでWin7でビルドしたEXEが正常に動作しなくなると理解しました。

    今回こちらの環境では、互換モードにしたことによってStrConvが正常動作しなくなったことが分かりましたので、念のため全端末で互換モードが設定されていないかチェックしてみたいと思います。

    しかし、StrConvには他にもWin7にしたことにより動作が変わってしまう(濁点問題 等)ことがあるようで、このケースはXPで開発していたアプリケーションをWin7でビルドして動作させた時の現象です。
    StrConv以外のメソッドがWin7で想定外の動作をしていないかを検証するには、本来ならば、Win7でビルドし直したEXEに対し、全ルートを通して値を検証するということをしなくてはならないと感じました。(期間と予算の関係でエラーになるかならないかの観点でしかテストできていません。)

    2015年12月15日 5:18
  • StrConv を使わないアプローチを考えるというのは今回の話の範囲外なのでしょうか?

    例えば、数字だけでいいなら置き換え表を作るのも簡単なので、それを使って Regex.Replace メソッドで置き換えるとか、

    public static string Convert(string source)
    {
        Regex reg = new Regex("[0-9]+");
        return reg.Replace(source, Replacer);
    }
    
    public static Dictionary<char, char> dictionary = new Dictionary<char, char>() {
          {'1','1'},{'2','2'},{'3','3'},{'4','4'},{'5','5'},
          {'6','6'},{'7','7'},{'8','8'},{'9','9'},{'0','0'}};
    
    public static string Replacer(Match m)
    {
        return new string(m.Value.Select(n => dictionary[n]).ToArray());
    }

    もっと簡単に以下のようにするとか?

    string replaced = 
        new string(source.Select(n => (dictionary.ContainsKey(n) ? dictionary[n] : n)).ToArray());

    そういう話は考えないということなら失礼しました。
    2015年12月15日 7:34
  • static string WideNumber(int number) {
        const string wideNumbers = "0123456789";
        return string.Concat(wideNumbers[number / 10], wideNumbers[number % 10]);
    }
    
    元が整数iで2桁決めうちなので、これだけかもしれないデス
    2015年12月15日 7:56
  • SurferOnWwwさん
    佐祐理さん

    サンプルコードのご提示ありがとうございます。

    対処するならWord側bookmarkの連番を半角にしようと考えていましたが、お二人にご教示頂いたコードはいずれ別なところで役に立つと思うので、覚えておきたいと思います。

    SurferOnWwwさんのコードは汎用的で分かり易いので好みです。
    佐祐理さんのコードは随分シンプルにまとまるものですね。

    勉強になります。

    2015年12月15日 9:18