potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# レジストリの間接文字列から対応するテキストリソースを抽出するクラス

概要

shlwapi.dllのSHLoadIndirectString関数を使用してレジストリで使用されるindirect string("@location,id"等)を元の文字列に変換するクラスとサンプルコードです。この表記は主にレジストリで使用され、DLLに含まれる文字列リソースを参照する場合に使用されます。

クラスのソースコード

Potisan.Windows.ShellIndirectString.cs

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace Potisan.Windows.Text
{
    public static class ShellIndirectString
    {
        [SuppressUnmanagedCodeSecurity]
        private static class NativeMethods
        {
            [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
            public static extern int SHLoadIndirectString(
                string pszSource,
                StringBuilder pszOutBuf,
                uint cchOutBuf,
                IntPtr ppvReserved);

            public const int ERROR_INSUFFICIENT_BUFFER = 122;
        }

        public static string Load(string source, int expanding = 5)
        {
            var buffer = new StringBuilder(expanding);
            for (;;)
            {
                var hr = NativeMethods.SHLoadIndirectString(
                    source, buffer, (uint)buffer.Capacity, IntPtr.Zero);
                if (hr == 0)
                {
                    return buffer.ToString();
                }
                else if (Marshal.GetLastWin32Error() != NativeMethods.ERROR_INSUFFICIENT_BUFFER)
                {
                    throw new Exception(
                        "SHLoadIndirectString関数(shlwapi.dll)が失敗しました。",
                        new Win32Exception());
                }
                buffer.Capacity += expanding;
            }
        }

        public static string LoadDefault(string source, string defaultValue, int expanding = 5)
        {
            var buffer = new StringBuilder(expanding);
            for (; ; )
            {
                var hr = NativeMethods.SHLoadIndirectString(
                    source, buffer, (uint)buffer.Capacity, IntPtr.Zero);
                if (hr == 0)
                {
                    return buffer.ToString();
                }
                else if (Marshal.GetLastWin32Error() != NativeMethods.ERROR_INSUFFICIENT_BUFFER)
                {
                    return defaultValue;
                }
                buffer.Capacity += expanding;
            }
        }
    }
}

サンプルコード1

sample1.cs

//
// 間接文字列(@記号から始まる文字列)から対応するテキストリソースを抽出する。
//

using Microsoft.Win32;
using Potisan.Windows.Text;

namespace ConsoleApp2
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var key = Registry.ClassesRoot.OpenSubKey("lnkfile"))
            {
                var explorerApplicationDescriptionIndirect = (string)key.GetValue("FriendlyTypeName");
                var explorerApplicationDescription = ShellIndirectString.Load(explorerApplicationDescriptionIndirect);
            }
        }
    }
}

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。