potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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:この記事は別のブログで投稿した記事を移動したものです。