C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

メッセージボックスのワードを取得する方法

[トピック内 7 記事 (1 - 7 表示)]  << 0 >>

■103172 / inTopicNo.1)  メッセージボックスのワードを取得する方法
  
□投稿者/ tokio (1回)-(2024/06/07(Fri) 14:38:53)

分類:[.NET 全般] 

C#に関する質問です、

MessageBoxで表示で
YesNoCancelを選択すると
日本語環境だと、はい、いいえ、キャンセルとダイアログに表示されます。

恐らく英語環境だと、YesNoCancelと表示され、
その他、中国語やスペイン語など別の言語だとそれぞれの言語で表示されるのだと思います。

YesNoCancelを選択した際の
この環境毎に異なるワードを取得したいのですが
このようなことは可能でしょうか?




引用返信 編集キー/
■103173 / inTopicNo.2)  Re[1]: メッセージボックスのワードを取得する方法
□投稿者/ 魔界の仮面弁士 (3778回)-(2024/06/07(Fri) 15:11:15)
2024/06/07(Fri) 15:14:28 編集(投稿者)

No103172 (tokio さん) に返信
> 日本語環境だと、はい、いいえ、キャンセルとダイアログに表示されます。
OS の言語設定に依存する話ではありますが、世界的に見れば、
ローカライズされていない(Yes / No / Cancel のまま)な
地域の方が多いんじゃなかったかな…。

日本語、ドイツ語、中国語、韓国語、ロシア語あたりは
ローカライズされていたかと思います。


> この環境毎に異なるワードを取得したいのですが
以前は、多言語リソースの検索に使用できる
Microsoft ランゲージポータル
という公式サイトがあったのですけれどね。
https://nishinos.com/news/2023/ms-terminology-search/

現在は、[Microsoft Terminology Search] からある程度調査できます。プログラム的な解決策では無いですが。
https://msit.powerbi.com/view?r=eyJrIjoiODJmYjU4Y2YtM2M0ZC00YzYxLWE1YTktNzFjYmYxNTAxNjQ0IiwidCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0NyIsImMiOjV9
引用返信 編集キー/
■103174 / inTopicNo.3)  Re[1]: メッセージボックスのワードを取得する方法
□投稿者/ KOZ (460回)-(2024/06/07(Fri) 18:00:23)
No103172 (tokio さん) に返信
> YesNoCancelを選択した際の
> この環境毎に異なるワードを取得したいのですが
> このようなことは可能でしょうか?

日本語版 Windows 11 の場合、C:\Windows\System32\ja-jp\user32.dll.mui 
にリソースとして格納されているようです。

どこかのフォルダにコピーし、拡張子を dll に変えて Visual Studio に読み込ませると
StringTable の中に、OK キャンセル 中止(&A) などの文字列がありました。

MessageBox のボタンは 800 〜 810 のようです。

using System;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    static void Main(string[] args) {
        string dllPath = @"C:\Windows\System32\ja-jp\user32.dll.mui";
        IntPtr hLib = LoadLibrary(dllPath);
        if (hLib != IntPtr.Zero) {
            StringBuilder sb = new StringBuilder(1024);
            for (int id = 800; id <= 810; id++) {
                LoadString(hLib, id, sb, sb.Capacity);
                Console.WriteLine("{0}:{1}", id, sb.ToString());
            }
            FreeLibrary(hLib);
        }
        Console.ReadKey();
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    static extern int LoadString(IntPtr hInstance, int uID, StringBuilder lpBuffer, int nBufferMax);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool FreeLibrary(IntPtr hModule);
}

引用返信 編集キー/
■103175 / inTopicNo.4)  Re[2]: メッセージボックスのワードを取得する方法
□投稿者/ 魔界の仮面弁士 (3779回)-(2024/06/07(Fri) 20:24:57)
2024/06/07(Fri) 21:07:04 編集(投稿者)

No103174 (KOZ さん) に返信
> 日本語版 Windows 11 の場合、C:\Windows\System32\ja-jp\user32.dll.mui
> にリソースとして格納されているようです。

ということは、LoadLibraryEx で LOAD_LIBRARY_AS_IMAGE_RESOURCE を指定すれば切り替わるかも?
(手元の環境には en-us と ja-jp しかインストールしておらず、この 2 種しか確認できませんでした)


// en-US
OK
Cancel
&Abort
&Retry
&Ignore
&Yes
&No
&Close
Help
&Try Again
&Continue

// ja-JP
OK
キャンセル
中止(&A)
再試行(&R)
無視(&I)
はい(&Y)
いいえ(&N)
閉じる(&C)
ヘルプ
再実行(&T)
続行(&C)


public partial class Form1 : Form
{
  public Form1()
  {
    InitializeComponent();
  }

  private void Form1_Load(object sender, EventArgs e)
  {
    comboBox1.DisplayMember = "DisplayName";
    comboBox1.ValueMember = "LCID";
    // comboBox1.DataSource = CultureInfo.GetCultures(CultureTypes.InstalledWin32Cultures);
    comboBox1.DataSource = CultureInfo.GetCultures(CultureTypes.SpecificCultures);
  }

  private void Form1_Shown(object sender, EventArgs e)
  {
    comboBox1.SelectedValue = CultureInfo.CurrentCulture.LCID;
  }

  private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
  {
    //textBox1.Lines = GetMessageBoxButtonsText();

    var culture = comboBox1.SelectedItem as CultureInfo;
    Text = culture?.Name;
    textBox1.Lines = GetMessageBoxButtonsText(culture);
  }

  internal string[] GetMessageBoxButtonsText() => GetMessageBoxButtonsText(null);
  internal string[] GetMessageBoxButtonsText(CultureInfo culture)
  {
    var results = new string[11];
    var sb = new StringBuilder(1024);
    IntPtr h = default;
    try
    {
      h = LoadLibraryEx("user32.dll", default, LoadLibraryFlags.LOAD_LIBRARY_AS_IMAGE_RESOURCE);
      ushort langid = (culture == null) ? default : GetThreadUILanguage();
      if (langid != default)
      {
        var newLangId = LANGIDFROMLCID(culture.LCID);
        var setLangId = SetThreadUILanguage(newLangId);
        if (setLangId != newLangId)
        {
          var ex = new Win32Exception();
          SetThreadUILanguage(langid);
          // throw ex;
        }
      }
      for (int id = 800; id <= 810; id++)
      {
        LoadString(h, id, sb, sb.Capacity);
        results[id - 800] = sb.ToString();
      }
      if (langid != default) { SetThreadUILanguage(langid); }
    }
    finally
    {
      if (h != default) { FreeLibrary(h); }
    }
    return results;
  }

  private ushort LANGIDFROMLCID(int lcid) => unchecked((ushort)lcid);

  [Flags]
  public enum LoadLibraryFlags : uint
  {
    DONT_RESOLVE_DLL_REFERENCES = 0x00000001,
    LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010,
    LOAD_LIBRARY_AS_DATAFILE = 0x00000002,
    LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040,
    LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020,
    LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008,
    LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100,
    LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800,
    LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000,
  }

  [DllImport("kernel32", CharSet = CharSet.Auto)]
  private static extern IntPtr LoadLibraryEx([In] string lpLibFileName, IntPtr hFile, LoadLibraryFlags dwFlags);

  [DllImport("kernel32", CharSet = CharSet.Auto)]
  private static extern ushort GetThreadUILanguage();

  [DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern ushort SetThreadUILanguage(ushort langId);

  [DllImport("kernel32", SetLastError = true)]
  public static extern bool FreeLibrary(IntPtr hModule);

  [DllImport("user32", CharSet = CharSet.Auto)]
  private static extern int LoadString(IntPtr hInstance, int uID, StringBuilder lpBuffer, int nBufferMax);
}
引用返信 編集キー/
■103176 / inTopicNo.5)  Re[3]: メッセージボックスのワードを取得する方法
□投稿者/ KOZ (461回)-(2024/06/07(Fri) 22:11:29)
No103175 (魔界の仮面弁士 さん) に返信
> ということは、LoadLibraryEx で LOAD_LIBRARY_AS_IMAGE_RESOURCE を指定すれば切り替わるかも?

おお、英語(米国) で切り替わりました。
これでカスタムダイアログの国際化もできますね。

私は予定ないですが(^_^;)

引用返信 編集キー/
■103177 / inTopicNo.6)  Re[4]: メッセージボックスのワードを取得する方法
□投稿者/ 魔界の仮面弁士 (3780回)-(2024/06/08(Sat) 17:06:31)
2024/06/08(Sat) 19:47:53 編集(投稿者)

No103176 (KOZ さん) に返信
> おお、英語(米国) で切り替わりました。
> これでカスタムダイアログの国際化もできますね。
>
> 私は予定ないですが(^_^;)

自作したダイアログの Button や Label 表記の切り替えの場合、
WinForms ならば Localizable プロパティ / Language プロパティを
使うのが楽でしょうね。(.resx)
https://atmarkit.itmedia.co.jp/ait/articles/0506/17/news112.html

WPF ならこのあたり。
https://qiita.com/Ueta-Kone/items/27343ed2787df455e2ff


OS 標準のメッセージボックス(MessageBox.Show など)のボタンについては、
OS の言語設定を切り替えないと追従されないはず…。
https://knowledge.support.sony.jp/electronics/support/articles/S1507280073122

一方、プログラム的に取得する方法については、KOZ さんが書かれた MUI リソースが必要なので、
実行環境に、その地域向けの「言語パック」がインストールされていないと取得できないでしょうね。

実行時に動的取得することが目的ではないのなら、検証環境において
全ての言語パックを入れた環境を用意して取得し、
変換表を自前で作ってしまう…という選択肢もあるかもしれません。


最終的には、最初に tokio さんが書かれた
>>> YesNoCancelを選択した際の
>>> この環境毎に異なるワードを取得したいのですが
という行為が、「何のため」に必要なのかによって、
採るべき手段が変わってきそうですね。

たとえば、マニュアル作成が目的の調査である、とか。
たとえば、カスタムダイアログを自作するために必要である、とか。

カスタムダイアログの自作の場合は、「右から左に向けて記述する地域」向けに
RightToLeft プロパティの切り替えなども必要になってくるかも?
引用返信 編集キー/
■103178 / inTopicNo.7)  Re[5]: メッセージボックスのワードを取得する方法
□投稿者/ tokio (2回)-(2024/06/08(Sat) 20:14:35)
皆さんありがとうございます。

しかし、とんでもなく複雑な方法が必要なのですね。
そして、環境によっては動かない場合もあり得るのですね・・・

やはり、日本語か英語だけで切り替えた方が良さそうですね。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ