Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9541

C# PEファイルの文字列リソースをすべて取得する

$
0
0

言語と動作確認環境

  • C# 8、.NET Core 3.1 (Preview)
  • Visual Studio Community 2019 Preview(Version 16.7.0 Preview 1.0)、Windows 10。

目的

C# 8と.NET Core 3.1でPEファイルの文字列リソースをすべて取得するサンプルコードです。同様のコードはQiitaや海外サイトでも公開されていますが、自身の学習のために作成しています。

サンプルコード

usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Runtime.InteropServices;namespaceConsoleApp1{classProgram{staticvoidMain(){usingvarhandle=NativeMethods.LoadLibraryExW("user32.dll",IntPtr.Zero,NativeMethods.DONT_RESOLVE_DLL_REFERENCES|NativeMethods.LOAD_LIBRARY_AS_DATAFILE|NativeMethods.LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);if(handle.IsInvalid){thrownewWin32Exception();}varstrings=GetStringResources(handle.DangerousGetHandle());}/// <summary>/// ある型のリソースのIDをすべて取得します。/// </summary>privatestaticushort[]GetResourceIDs(IntPtrmoduleHandle,IntPtrresourceType){varresnames=newList<ushort>();NativeMethods.EnumResourceNamesW(moduleHandle,resourceType,(IntPtrhModule,IntPtrlpszType,IntPtrlpszName,nintlParam)=>{varid=lpszName.ToInt64();if(id>>16!=0)thrownewException();resnames.Add((ushort)id);returntrue;},0);returnresnames.ToArray();}/// <summary>/// モジュールの文字列リソースをすべて取得します。/// </summary>privatestaticstring[]GetStringResources(IntPtrmoduleHandle){varstringResIds=GetResourceIDs(moduleHandle,NativeMethods.RT_STRING);Array.Sort(stringResIds);varstrings=newList<string>();foreach(varstrResIdinstringResIds){varresHandle=NativeMethods.FindResourceW(moduleHandle,newIntPtr(strResId),NativeMethods.RT_STRING);varmemoryHandle=NativeMethods.LoadResource(moduleHandle,resHandle);varsize=NativeMethods.SizeofResource(moduleHandle,resHandle);// pointerの中身は2バイト(文字数N)+N*2バイト(UTF-16文字列)の配列varpointer=NativeMethods.LockResource(memoryHandle);for(intoffset=0;offset<size;){uintlen=(ushort)Marshal.ReadInt16(pointer+offset);strings.Add(Marshal.PtrToStringUni(pointer+offset+sizeof(ushort),(int)len));offset+=sizeof(ushort)+(int)len*2;}}returnstrings.ToArray();}privatestaticclassNativeMethods{[DllImport("kernel32.dll",SetLastError=true,ExactSpelling=true,CharSet=CharSet.Unicode)]publicstaticexternSafeModuleHandleLoadLibraryExW([In]stringlpLibFileName,IntPtrhFile,uintdwFlags);[DllImport("kernel32.dll",SetLastError=true,ExactSpelling=true,CharSet=CharSet.Unicode)][return:MarshalAs(UnmanagedType.Bool)]publicstaticexternboolEnumResourceNamesW(IntPtrhModule,IntPtrlpType,EnumResNameProcWlpEnumFunc,nintlParam);publicconstuintDONT_RESOLVE_DLL_REFERENCES=0x00000001;publicconstuintLOAD_LIBRARY_AS_DATAFILE=0x00000002;publicconstuintLOAD_LIBRARY_SEARCH_DEFAULT_DIRS=0x00001000;[UnmanagedFunctionPointer(CallingConvention.StdCall,CharSet=CharSet.Unicode)][return:MarshalAs(UnmanagedType.Bool)]publicdelegateboolEnumResNameProcW(IntPtrhModule,IntPtrlpszType,IntPtrlpszName,nintlParam);publicstaticreadonlyIntPtrRT_STRING=newIntPtr(6);[DllImport("kernel32.dll",SetLastError=true,ExactSpelling=true,CharSet=CharSet.Unicode)]publicstaticexternIntPtrFindResourceW(IntPtrhModule,IntPtrlpName,IntPtrlpType);[DllImport("kernel32.dll",SetLastError=true)]publicstaticexternIntPtrLoadResource(IntPtrhModule,IntPtrhResInfo);[DllImport("kernel32.dll",SetLastError=true)]publicstaticexternIntPtrLockResource(IntPtrhResData);[DllImport("kernel32.dll",SetLastError=true)]publicstaticexternuintSizeofResource(IntPtrhModule,IntPtrhResInfo);}}publicsealedclassSafeModuleHandle:SafeHandle{privatestaticclassNativeMethods{[DllImport("kernel32.dll")][return:MarshalAs(UnmanagedType.Bool)]publicstaticexternboolFreeLibrary(IntPtrhLibModule);}publicSafeModuleHandle():base(IntPtr.Zero,true){}publicSafeModuleHandle(IntPtrhandle,boolownsHandle):base(handle,ownsHandle){}publicoverrideboolIsInvalid=>handle==IntPtr.Zero;protectedoverrideboolReleaseHandle(){returnNativeMethods.FreeLibrary(handle);}}}

HMODULE型のSafeHandleによるラップ

LoadLibraryEx関数の戻り値はHMODULE型のハンドルであり、使用後はFreeLibrary関数で解放する必要があります。C#ではusing構文とSafeHandleの派生クラスにより確実な解放を保証できるため、ここではSafeHandleを継承したクラスを作成しています。

このハンドルを解放しなかった場合、プロセスの終了までライブラリがメモリ存在したままとなります。なお、同じHMODULE型を返す関数でもGetModuleHandle関数の戻り値は基本的に解放してはいけません。

LoadLibraryEx関数とフラグ

LoadLobraryEx関数の呼び出し時にはいくつかのフラグを指定することができます。今回は既定の場所からPEファイルを読み込み、そのリソースのみが必要なので以下のフラグを指定しています。

フラグ目的
DONT_RESOLVE_DLL_REFERENCESDLL参照の解決を無効化する。
LOAD_LIBRARY_AS_DATAFILEデータファイルとして読み込む。
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS既定の場所から検索する。

LoadResource関数とLockResource関数

LoadResource関数とLockResource関数は後方互換性のために別々に存在します。Windows 10ではどちらも同じ値を返します。戻り値はプロセスの終了時に自動的に解放されます(Microsoft Docs)。

ポインタから整数や文字列の読み込み

System.Runtime.InteropServices名前空間のMarshalクラスを使用してポインタから整数や文字列を読み込むことができます。16ビット整数はMarshal.ReadInt16、UTF-16文字列の読み込みはMarshal.PtrToStringUniです。

RT_STRING型リソースの中身

RT_STRING型リソースは各IDに長さ(UInt16、2バイト)とその長さのUTF-16文字列(Char型)のペアが1~32個含まれます。各IDに含まれる文字列の個数は記録されていませんが、SizeofResource関数で取得したバイト数まで上記ペアを読み込むことですべての文字列を取得できます。

RT_STRING型リソースの模式図
#ID 1 <- バイト数はSizeofResource関数で取得
Length(2バイト), String(Length文字 = Length*2バイト)
...
Length(2バイト), String(Length文字 = Length*2バイト) <- 最大32個

#ID 2
Length(2バイト), String(Length文字 = Length*2バイト)
...
Length(2バイト), String(Length文字 = Length*2バイト)

...

#ID N <- EnumResourceNamesで見つかった個数だけ存在
...

Viewing all articles
Browse latest Browse all 9541

Trending Articles