Windowsには、アプリが自分の動作条件や必要とする機能を OS に伝えるためのアプリケーション マニフェスト(Application Manifest)という仕組みがあります。以降マニフェストと呼びます。
マニフェストは実行ファイルに付随する小さなXML情報で、アプリがどのような環境で動くべきかをWindowsに宣言する役割を持ちます。
バージョン情報設定やアプリアイコン設定は、アプリ(EXE)やDLLのファイルのプロパティ情報の設定であり、アプリの挙動自体に作用するものではありませんが、マニフェストの設定はアプリの挙動について影響を与えるものもあります。
マニフェストの設定項目(抜粋)
アプリ(EXE)やDLLの識別情報
外部ライブラリ(DLL/コンポーネント)の依存関係
UAC(ユーザーアカウント制御)の昇格レベル
アプリケーション互換性(対応OS)
高DPI対応
長いパス名対応(MAX_PATH(260)の制限解除)
プロセスの既定のコードページの設定
詳細にしては https://learn.microsoft.com/ja-jp/windows/win32/sbscs/application-manifests を参照してください。
LazarusのIDEを使用するとマニフェストファイル(XML)を意識しないで設定することができます。
IDEによりマニフェストファイルが作成されてアプリに設定されます。
この方法の場合、マニフェストの設定項目はIDEの設定項目に限定されます。
Lazarusのメニュー[プロジェクト(P)]-[プロジェクトオプション...]で以下のウインドウを開き左の[アプリケーション]を選択します。
[マニフェストリソースを使う(テーマを有効にする)]をチェックします。チェックすると以下の設定が有効(活性化)になります。
・[DPIを意識]
アプリがDPIを意識する内容を選択します。Windows 10/11では、「Vista-8: on, 8.1/10+: per monitor/V2(True/PM_2)」を選択します。
・[実行レベル]
アプリの実行権限を選択します。
asInvoker
親プロセスと同じ権限で実行する一般的なアプリの設定です。
highestAvailable
現在のユーザが管理者ユーザの場合は管理者権限に昇格(UACダイアログが表示される)して実行され、標準ユーザの場合は標準権限で実行される設定です。
requireAdministrator
必ず管理者権限に昇格(UACダイアログが表示される)して実行される設定です。
・[UIアクセス(uiAccess)]
通常は設定しません。自動化ツールやアクセシビリティ支援ツールが、管理者権限の画面を操作する必要がある場合のみ使用されます。コードのデジタル署名が必要になります。
・[Long path awareness(Windows 10 1607+)]
チェックするとMAX_PATH(260文字)の制限が解除されます。
・[ANSI codepage is UTF-8(Windows 10 1903+)]
Window ANSI系APIが、UTF-8で動作するように設定します。
日本語版Windows(ja-JP)ではCP932(Shift_JISの拡張版)で動作します。
・[名前]
アプリの識別名を指定します。実行ファイル名と同じある必要はありません。できるだけユニークになるように設定します。
「会社や組織.部門や製品.アプリ名」など"."表記で設定するとニユーク性が高まります。
ユニークではなくても動作しますが、ユニークな設定をしましょう。
・[説明]
簡単な説明(コメント)です。任意で、空でも問題ありません。
Lazaris IDEではIDEのGUIで設定できますが、Free Pascalなどのコンパイラを使用する場合はマニフェスト(XML)をファイルとして作成します。
アプリに対してマニフェストを有効化するためには以下の方法があります。
マニフェストファイルをアプリ(EXE)と同じ場所に配置する
アプリが、Appli01.exe であればマニフェストファイル名はそれに .manifest を付けてものとして同一ディレクトリに配置する
・Appli01.exe
・Appli01.exe.manifest
マニフェストファイルをアプリのリソースとして組み込む
設定方法は後述
XMLの構文に従って書き方は決まっているので、ひな型を用意して、それを加工して利用するのがよいと思います。
Windowx 11アプリ用マニフェストの例を以下に示します。ひな型として利用してください。
Lazarus IDEで設定できる項目に加えて「ウインドウのフィルター処理を無効化」と「印刷時のドライバープロセス分離」を加えてみました。
コードページ UTF-8 で作成してください(MBCSは使用していないので実際にはUTF-8ではなくてもよいですが)。
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="CompanyName.ProductName.AppName"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
<!-- requestedExecutionLevel level="highestAvailable" uiAccess="false"/ -->
<!-- requestedExecutionLevel level="requireAdministrator" uiAccess="false"/ -->
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
上記のマニフェストファイルに簡単なコメントを付けたものを以下に示します。
マイクロソフトの説明サイトと合わせて参照すると詳細はわかると思いますのでここでは個々の設定の詳細については述べません。
コードページ UTF-8 で作成してください。
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<!-- WindowsのアプリやDLLの識別情報 -->
<!-- name属性はアプリを区別するユニークな名前が望ましい -->
<assemblyIdentity
type="win32"
name="CompanyName.ProductName.AppName"
version="1.0.0.0"
processorArchitecture="*"
/>
<!-- アプリの説明文(指定は任意でWindowsのUIには通常表示されない) -->
<description>Windows Application.</description>
<!-- 外部ライブラリ(DLL/コンポーネント)の依存関係 -->
<!-- GUIアプリはCommon Controls v6を指定する(指定しないと古い外観になる) -->
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<!-- UAC(ユーザーアカウント制御)の昇格レベル -->
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!-- ▼Windowsの管理者権限の昇格レベルを定義する -->
<!-- ・level="asInvoker"は呼び出し元と同じ権限を要求される(UACダイアログは表示されない) -->
<!-- ・level="requireAdministrator"は管理者権限を要求される(管理者権限に昇格する場合はUACダイアログが表示される) -->
<!-- ・level="highestAvailable"ユーザが持っている最も高い権限を要求される(管理者権限に昇格する場合はUACダイアログが表示される) -->
<!-- ※親プロセスがすでに管理者権限で動作している場合はいずれの場合もUACダイアログは表示されない -->
<!-- 以下のコメントの中から1つをコメントの外に指定する。何も指定しない場合は"asInvoker"指定と同じ -->
<!--
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
<requestedExecutionLevel level="highestAvailable" uiAccess="false"/>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
-->
</requestedPrivileges>
</security>
</trustInfo>
<!-- アプリケーション互換性 -->
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- アプリがサポートするWindowsバージョン(指定順に意味はない) -->
<!-- アプリがターゲットとしないバージョンについては指定不要 -->
<!-- ▼Windows 10、Windows 11、Windows Server 2016、Windows Server 2019、Windows Server 2022 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- ▼Windows 8.1、Windows Server 2012 R2 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- ▼Windows 8、Windows Server 2012 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- ▼Windows 7、Windows Server 2008 R2 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- ▼Windows Vista および Windows Server 2008 -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<!-- Windows固有の動作設定 -->
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<!-- ▼高DPI対応(Windows Vista以降) -->
<!-- ・Trueは高DPI対応、PM_V2はWindows 10を含む複数モニター対応 -->
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<!-- ▼ウインドウのフィルター処理を無効化 -->
<!-- Trueにするとウインドウのフィルター処理を無効にする -->
<disableWindowFiltering>True</disableWindowFiltering>
<!-- ▼印刷時のドライバープロセス分離 -->
<!-- Trueにすると印刷ドライバーをプロセスから分離される -->
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<!-- ▼高DPI対応(Windows 10 バージョン 1607以降) -->
<!-- ・PerMonitorV2はWindows 10以降で推奨されるDPIモード(高DPI/複数モニター対応) -->
<!-- ・PerMonitorはWindows 8.1以降で推奨されるDPIモード(高DPI/複数モニター対応) -->
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<!-- ▼MAX_PATH(260文字)の制限解除(Windows 10 バージョン 1607以降) -->
<!-- ・TrueにするとMAX_PATH(260文字)の制限が解除される -->
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<!-- ▼プロセスコードページをUTF-8に設定する(Windows バージョン 1903以降) -->
<!-- ・UTF-8はWindows API(ANSI系)がUTF-8でのやり取りにかわる -->
<!-- ※ UTF-8を指定するとGetACP関数の戻り値もUTF-8(65001)になる -->
<!-- ※ OS全体のUTF-8モードを変更せず、アプリ単体でUTF-8が使用される -->
<!-- ※ Windows 11ではja-JPなどのローケール名を指定するともできるja-JPは932となる -->
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
app.manifest をひな型にする場合、各アプリに展開する場合は最低限以下のname属性にユニークな識別子を設定してください。アプリファイル名に合わせる必要はありません。ユニークではなくてもたいていは動作しますが、ルールなので。
<assemblyIdentity
type="win32"
name="ユニークな識別子"
version="1.0.0.0"
processorArchitecture="*"
/>
ひとつのアプリで複数のマニフェストファイルを設定することはできません。重複しない項目に分けて複数のマニフェストファイルにすることもできません。
Lazarus IDEのGUIでマニフェストを設定した場合は、以降のリソースでマニフェストを設定する方法は使用できません。エラーとなります。リソースで設定したい場合はIDEの[□マニフェイトリソースを使う(テーマを有効にする)]のチェックをオフにしてください。
不要な設定や設定したくない項目については削除可能です。
コンソールアプリの場合「Common Controls v6を指定」や「高DPI対応」は、そもそも不要なので削除可能ですが、存在しても無視されるだけなのでそのままでも問題ありません。
マニフェストファイルに不備があると実行開始時に以下のようなエラーとなり終了します。
コンパイルではエラーにならないので、必ず実行確認は必要です。
このアプリケーションのサイド バイ サイド構成が正しくないため、アプリケーションを開始できませんでした。詳細については、アプリケーションのイベント ログを参照するか、コマンド ライン ツール sxstrace.exe を使用してください。
マニフェストファイル(XML)自体の説明は以上です。
以降、主な設定の確認デモです。
以降では、各機能ごとの設定方法やアプリの機能確認デモを紹介します。
追加した「ウインドウのフィルター処理を無効化」と「印刷時のドライバープロセス分離」については割愛します。
各アプリは機能確認のためだけの最低限のコードを示しています。アプリケーションとしてのひな型にはなりませんので注意してください。
自分でマニフェストの設定を有効にしたり無効にしたりして違いを見てください。
コースコードに以下のディレクティブがある場合はマニフェストが有効です。
{$R Manifest.rc}
これをコメントアウトするとマニフェストが無効になります。
// {$R Manifest.rc}
リソースの設定についてはこちらも参照してください。バージョン情報を設定する、アプリのアイコンを設定する。
プログラムやマニフェストなど重複する内容が多いですが、いちいち他の場所を参照しないでよいように、すべて表示しています。
ファイル名が重複するので、デモごとにディレクトリ(フォルダ)を分けてください。
Object Pascal 備忘録ページから[DM0007A.zip]をダウンロードすると全ファイルがデモ別のディレクトリ(フォルダ)で構成されています。
Windows Updateの問題に対する対応について(解消済み)。
リソースコンパイルも含むので以下の情報も確認してください。
Windows Updateでcrtdll.dllが新しい形式に更新されるとリソースコンパイラwindres.exeが動作しなくなります。
Windows Updateの問題に対する対応について
※この問題は2026年4月15日のWindows Updateで一旦ロールバックされたようで、解消しています。
上記問題が生じた場合に対応するコマンドスクリプトも作成したので参照してください。
Free Pascal用のコマンドスクリプト(その2) TDM-GCCも必要ですTDM-GCC インストール参照。
fpc64.cmd でエラーになる場合は fpcrc64.cmd を使用してください。
デモ インデックス
アプリがWindowsのシステムに影響を与える処理をするには、管理者権限が必要になります。
例えば、Windowsの「Program Files」にファイルを作成したい場合は、アプリを管理種権限に昇格させる必要があります。
マニフェストでは「UAC(ユーザーアカウント制御)の昇格レベル」で「<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>」を指定するとアプリの起動時に管理者権限に昇格しUACダイアログが表示されます。管理種権限のプロセスから起動された場合は管理者権限が継承されるのでUACダイアログは表示されません。
このデモは、Windowsの「Program Files」 に「TestFile.txt」を作成して削除します。
マニフェストファイルはアプリ(EXE)とは別ファイルとして配置します。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください。
ManifestDemo1.pas
ManifestDemo1.exe.manifest
(*****************************************************************************)
(* プログラム - ManifestDemo1 *)
(* UAC管理者権限昇格のデモ(外部マニフェストファイル使用) *)
(*****************************************************************************)
program ManifestDemo1;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
Windows, SysUtils, WinDirs;
(*****************************************************************************)
(* プログラムブロック - 宣言部 *)
(*****************************************************************************)
{ 定数 }
const
SECURITY_NT_AUTHORITY : TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
FILENAME = 'TestFile.txt';
{ 変数 }
var
ProgramFilesDir : String;
FName : String;
File1 : TextFile;
{--------------------------------------------------------}
{ 関数 - WinAPI参照 (Windows Unitで宣言されていないため) }
{--------------------------------------------------------}
function CheckTokenMembership(TokenHandle: HANDLE; SidToCheck: PSID; out IsMember: WINBOOL): WINBOOL;
stdcall; external 'advapi32.dll' name 'CheckTokenMembership';
{-------------------------}
{ 関数 - アプリの権限確認 }
{-------------------------}
function IsInGroup(Group: DWORD): Boolean;
var
PIdentifierAuthority : TSIDIdentifierAuthority;
PtSid : PSID;
IsMember : WINBOOL;
begin
PIdentifierAuthority := SECURITY_NT_AUTHORITY;
Result := False;
PtSid := Nil;
try
if AllocateAndInitializeSid(PIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, Group, 0, 0, 0, 0, 0, 0, PtSid) then
begin
if CheckTokenMembership(0, PtSid, IsMember) then
begin
Result := IsMember;
end;
end;
finally
if PtSid <> Nil then
begin
FreeSid(PtSid);
end;
end;
end;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
TextRec(File1).Mode := fmClosed;
try
try
WriteLn;
{ アプリの実行レベルを表示 }
WriteLn('[アプリの実行レベル]');
if IsInGroup(DOMAIN_ALIAS_RID_ADMINS) then
begin
WriteLn('管理者権限で実行中');
WriteLn;
end;
Writeln('管理者グループ : ', IsInGroup(DOMAIN_ALIAS_RID_ADMINS).ToString(TUseBoolStrs.True));
Writeln('一般ユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_USERS).ToString(TUseBoolStrs.True));
Writeln('ゲストグループ : ', IsInGroup(DOMAIN_ALIAS_RID_GUESTS).ToString(TUseBoolStrs.True));
Writeln('パワーユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_POWER_USERS).ToString(TUseBoolStrs.True));
WriteLn;
{ C:\Program Files に'TestFile.dat'を作成する(要管理者権限) }
ProgramFilesDir := GetWindowsSpecialDir(CSIDL_PROGRAM_FILES, False);
FName := ConcatPaths([ProgramFilesDir, FILENAME]);
AssignFile(File1, FName);
Rewrite(File1); { 標準ユーザ権限の場合例外(EInOutError)が発生する }
WriteLn('"', FName, '" の作成に成功しました')
except
on Ex: EInOutError do
begin
WriteLn(StdErr, '*** I/Oエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
if TextRec(File1).Mode <> fmClosed then
begin
CloseFile(File1); { 'TestFile.dat'をクローズ }
end;
if FileExists(FName) then
begin
DeleteFile(FName); { 作成した'TestFile.dat'を削除 }
WriteLn;
WriteLn('"', FName, '" を削除しました(後始末)')
end
else
begin
WriteLn;
WriteLn('"', FName, '" の作成に失敗しました')
end;
WriteLn(StdErr);
Write(StdErr, 'Enterキーを押してください...');
ReadLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo1"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
ManifestDemo1.pas をコンパイルして ManifestDemo1.exe を作成します。
C:\PG\FreePascal\ManifestDemo_001>fpcrc64.cmd ManifestDemo1.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo1.pas
Linking ManifestDemo1.exe
121 lines compiled, 0.1 sec, 87056 bytes code, 6660 bytes data
ManifestDemo1.exe を実行するとUACダイアログが表示され、管理権限のターミナルが開いて実行結果が表示されます。
[アプリの実行レベル]
管理者権限で実行中
管理者グループ : True
一般ユーザグループ : True
ゲストグループ : False
パワーユーザグループ : False
"C:\Program Files\TestFile.txt" の作成に成功しました
"C:\Program Files\TestFile.txt" を削除しました(後始末)
Enterキーを押してください...
このデモは、Windowsの「Program Files」 に「TestFile.txt」を作成して削除します。
マニフェストファイルをリソースファイルとしてアプリ(EXE)に組み込みます。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください。
ManifestDemo2.pas
UAC.manifest
UAC.rc
(*****************************************************************************)
(* プログラム - ManifestDemo2 *)
(* UAC管理者権限昇格のデモ(マニフェストファイルをリソースとして組み込み) *)
(*****************************************************************************)
program ManifestDemo2;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
Windows, SysUtils, WinDirs;
{$R UAC.rc}
(*****************************************************************************)
(* プログラムブロック - 宣言部 *)
(*****************************************************************************)
{ 定数 }
const
SECURITY_NT_AUTHORITY : TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
FILENAME = 'TestFile.txt';
{ 変数 }
var
ProgramFilesDir : String;
FName : String;
File1 : TextFile;
{--------------------------------------------------------}
{ 関数 - WinAPI参照 (Windows Unitで宣言されていないため) }
{--------------------------------------------------------}
function CheckTokenMembership(TokenHandle: HANDLE; SidToCheck: PSID; out IsMember: WINBOOL): WINBOOL;
stdcall; external 'advapi32.dll' name 'CheckTokenMembership';
{-------------------------}
{ 関数 - アプリの権限確認 }
{-------------------------}
function IsInGroup(Group: DWORD): Boolean;
var
PIdentifierAuthority : TSIDIdentifierAuthority;
PtSid : PSID;
IsMember : WINBOOL;
begin
PIdentifierAuthority := SECURITY_NT_AUTHORITY;
Result := False;
PtSid := Nil;
try
if AllocateAndInitializeSid(PIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, Group, 0, 0, 0, 0, 0, 0, PtSid) then
begin
if CheckTokenMembership(0, PtSid, IsMember) then
begin
Result := IsMember;
end;
end;
finally
if PtSid <> Nil then
begin
FreeSid(PtSid);
end;
end;
end;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
TextRec(File1).Mode := fmClosed;
try
try
WriteLn;
{ アプリの実行レベルを表示 }
WriteLn('[アプリの実行レベル]');
if IsInGroup(DOMAIN_ALIAS_RID_ADMINS) then
begin
WriteLn('管理者権限で実行中');
WriteLn;
end;
Writeln('管理者グループ : ', IsInGroup(DOMAIN_ALIAS_RID_ADMINS).ToString(TUseBoolStrs.True));
Writeln('一般ユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_USERS).ToString(TUseBoolStrs.True));
Writeln('ゲストグループ : ', IsInGroup(DOMAIN_ALIAS_RID_GUESTS).ToString(TUseBoolStrs.True));
Writeln('パワーユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_POWER_USERS).ToString(TUseBoolStrs.True));
WriteLn;
{ C:\Program Files に'TestFile.dat'を作成する(要管理者権限) }
ProgramFilesDir := GetWindowsSpecialDir(CSIDL_PROGRAM_FILES, False);
FName := ConcatPaths([ProgramFilesDir, FILENAME]);
AssignFile(File1, FName);
Rewrite(File1); { 標準ユーザ権限の場合例外(EInOutError)が発生する }
WriteLn('"', FName, '" の作成に成功しました')
except
on Ex: EInOutError do
begin
WriteLn(StdErr, '*** I/Oエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
if TextRec(File1).Mode <> fmClosed then
begin
CloseFile(File1); { 'TestFile.dat'をクローズ }
end;
if FileExists(FName) then
begin
DeleteFile(FName); { 作成した'TestFile.dat'を削除 }
WriteLn;
WriteLn('"', FName, '" を削除しました(後始末)')
end
else
begin
WriteLn;
WriteLn('"', FName, '" の作成に失敗しました')
end;
WriteLn(StdErr);
Write(StdErr, 'Enterキーを押してください...');
ReadLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo2"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アプリケーションのマニフェストを設定する。
//
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "UAC.manifest"
ManifestDemo2.pas コンパイルして ManifestDemo2.exe を作成します。
C:\PG\FreePascal\ManifestDemo_002>fpcrc64.cmd ManifestDemo2.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo2.pas
Compiling resource C:\PG\FreePascal\ManifestDemo_002\UAC.rc
Compiling resource ManifestDemo2.obj
Linking ManifestDemo2.exe
123 lines compiled, 0.9 sec, 87056 bytes code, 6660 bytes data
ManifestDemo2.exe を実行するとUACダイアログが表示され、管理権限のターミナルが開いて実行結果が表示されます。
[アプリの実行レベル]
管理者権限で実行中
管理者グループ : True
一般ユーザグループ : True
ゲストグループ : False
パワーユーザグループ : False
"C:\Program Files\TestFile.txt" の作成に成功しました
"C:\Program Files\TestFile.txt" を削除しました(後始末)
Enterキーを押してください...
このデモは、アプリの実行権限(Windows APIで取得)やその他の設定内容(リソースのマニフェストXMLを参照)を表示します。
バージョン情報やアイコンのリソース定義との共存も問題ないことを示します。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください(AppIcon.icoは除く)。
ManifestDemo3.pas
app.manifest
Manifest.rc
VerInfo.rc
Icon.rc
AppIcon.ico
(*****************************************************************************)
(* プログラム - ManifestDemo3 *)
(* マニフェストファイル(組み込み)の設定情報を表示 *)
(*****************************************************************************)
program ManifestDemo3;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
{$WARN 4104 OFF} (* UnicodeString <- AansiString *)
{$WARN 4105 OFF} (* AnsiString <- UnicodeString *)
uses
Windows, Classes, SysUtils, DOM, XMLRead;
{$R Manifest.rc}
{$R VerInfo.rc}
{$R Icon.rc}
// {$R MyResource.rc} { Manifest & Icon & VersionInfo 合体版 }
(*****************************************************************************)
(* プログラムブロック - 宣言部 *)
(*****************************************************************************)
{ 定数 }
const
SECURITY_NT_AUTHORITY : TSIDIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5));
{ 型 }
type
{ マニフェスト情報 }
TManifestInfo = record
DpiAware : String;
DpiAwareness : String;
LongPathAware : String;
ActiveCodePage : String;
DisableWindowFiltering : String;
PrinterDriverIsolation : String;
end;
{ 変数 }
var
ManifestXML : String;
Info : TManifestInfo;
Dict : TStringList;
OSList : TStringList;
I : Integer;
{--------------------------------------------------------}
{ 関数 - WinAPI参照 (Windows Unitで宣言されていないため) }
{--------------------------------------------------------}
function CheckTokenMembership(TokenHandle: HANDLE; SidToCheck: PSID; out IsMember: WINBOOL): WINBOOL;
stdcall; external 'advapi32.dll' name 'CheckTokenMembership';
{--------------------------}
{ 関数 - Manigest XML 取得 }
{--------------------------}
function GetManifestXml(const ExePath: String): String;
var
HLib : HMODULE;
HRes : HANDLE;
HGlob : HGLOBAL;
PData : Pointer;
ResSize : DWORD;
begin
Result := '';
{ EXEファイル(自分)をリソースとしてロード }
HLib := LoadLibraryEx(PChar(ExePath), 0, LOAD_LIBRARY_AS_DATAFILE);
if HLib = 0 then
begin
Exit;
end;
try
{ RT_MANIFEST(ID: 1)を探す }
HRes := FindResource(HLib, MAKEINTRESOURCE(1), RT_MANIFEST);
if HRes <> 0 then
begin
ResSize := SizeofResource(HLib, HRes);
HGlob := LoadResource(HLib, HRes);
if HGlob <> 0 then
begin
PData := LockResource(hGlob);
if PData <> Pointer(Nil) then
begin
{ XMLテキストとして取得 }
SetString(Result, PChar(PData), ResSize);
end;
end;
end;
finally
FreeLibrary(HLib);
end;
end;
{-------------------------}
{ 関数 - アプリの権限確認 }
{-------------------------}
function IsInGroup(Group: DWORD): Boolean;
var
PIdentifierAuthority : TSIDIdentifierAuthority;
PtSid : PSID;
IsMember : WINBOOL;
begin
PIdentifierAuthority := SECURITY_NT_AUTHORITY;
Result := False;
PtSid := Nil;
try
if AllocateAndInitializeSid(PIdentifierAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, Group, 0, 0, 0, 0, 0, 0, PtSid) then
begin
if CheckTokenMembership(0, PtSid, IsMember) then
begin
Result := IsMember;
end;
end;
finally
if PtSid <> Nil then
begin
FreeSid(PtSid);
end;
end;
end;
{---------------------------------}
{ 関数 - Manifestのノード値を取得 }
{---------------------------------}
function GetManifestValue(const XMLStr: String; const TargetName: String): String;
var
Doc : TXMLDocument;
SStream : TStringStream;
{ 内部関数 - 再帰検索を行う }
function FindRecursive(Node: TDOMNode): String;
var
Child: TDOMNode;
begin
Result := '';
if Node = Nil then
begin
Exit;
end;
{ 要素ノードかつ名前が一致するか(接頭辞なしのLocalNameで比較) }
if (Node.NodeType = ELEMENT_NODE)
and (SameText(Node.LocalName, TargetName) or SameText(Node.NodeName, TargetName)) then
begin
if Node.FirstChild <> Nil then
begin
Result := Node.FirstChild.NodeValue;
Exit;
end;
end;
{ 子ノードを再起検索 }
Child := Node.FirstChild;
while Child <> Nil do
begin
Result := FindRecursive(Child);
if Result <> '' then
begin
Exit;
end;
Child := Child.NextSibling;
end;
end;
begin
Result := '';
Doc := Nil;
SStream := Nil;
try
try
{ XMLを読み込む }
SStream := TStringStream.Create(XMLStr, TEncoding.UTF8);
ReadXMLFile(Doc, SStream);
Result := FindRecursive(Doc);
except
on Ex: Exception do
begin
Result := '<データなし>'
end;
end;
finally
if Result = '' then
begin
Result := '<データなし>';
end;
SStream.Free;
Doc.Free;
end;
end;
{-----------------------------------}
{ 関数 - Manifestの属性値一覧を取得 }
{-----------------------------------}
function GetManifestAttributeList(const XMLStr: String; const TagName, AttrName: String): TStringList;
var
Doc : TXMLDocument;
SStream : TStringStream;
Nodes : TDOMNodeList;
I : Integer;
Attr : TDOMNode;
begin
Result := TStringList.Create;
Doc := Nil;
SStream := TStringStream.Create(XMLStr, TEncoding.UTF8);
try
try
ReadXMLFile(Doc, SStream);
{ 指定したタグ名をすべて取得 }
Nodes := Doc.GetElementsByTagName(TagName);
if Assigned(Nodes) then
begin
for I := 0 to Nodes.Count - 1 do
begin
{ 属性を探す }
Attr := Nodes.Item[I].Attributes.GetNamedItem(AttrName);
if Assigned(Attr) then
Result.Add(Attr.NodeValue);
end;
end;
except
on Ex: Exception do
begin
Result.Add('<データなし>');
end;
end;
finally
SStream.Free;
Doc.Free;
end;
end;
{-----------------------------}
{ 関数 - マニフェスト情報取得 }
{-----------------------------}
function GetManifestInfo(const XMLStr: String): TManifestInfo;
begin
Result.DpiAware := GetManifestValue(XMLStr, 'dpiAware');
Result.DpiAwareness := GetManifestValue(XMLStr, 'dpiAwareness');
Result.LongPathAware := GetManifestValue(XMLStr, 'longPathAware');
Result.ActiveCodePage := GetManifestValue(XMLStr, 'activeCodePage');
Result.DisableWindowFiltering := GetManifestValue(XMLStr, 'disableWindowFiltering');
Result.PrinterDriverIsolation := GetManifestValue(XMLStr, 'printerDriverIsolation');
end;
{-----------------------}
{ 関数 - OS名の辞書作成 }
{-----------------------}
function CreateOSDictionary: TStringList;
begin
Result := TStringList.Create;
Result.CaseSensitive := False;
Result.Values['{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}'] := 'Windows 10/11';
Result.Values['{1f676c76-80e1-4239-95bb-83d0f6d0da78}'] := 'Windows 8.1';
Result.Values['{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}'] := 'Windows 8';
Result.Values['{35138b9a-5d96-4fbd-8e2d-a2440225f93a}'] := 'Windows 7';
Result.Values['{e2011457-1546-43c5-a5fe-008deee3d3f0}'] := 'Windows Vista';
end;
{---------------------------------}
{ 関数 - GUIDに対応するOS名を返す }
{---------------------------------}
function GUIDToOSName(const GUID: String; Dict: TStringList): String;
begin
Result := Dict.Values[GUID];
if Result = '' then
begin
Result := 'Unknown OS (' + GUID + ')';
end;
end;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
{ Manifestで「UTF-8 」の設定があれば次の2行は不要(CP_UTF8が設定される)。あっても良い }
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
OSList := Nil;
Dict := Nil;
try
try
WriteLn;
WriteLn('[アプリの実行レベル]');
Writeln('管理者グループ : ', IsInGroup(DOMAIN_ALIAS_RID_ADMINS).ToString(TUseBoolStrs.True));
Writeln('一般ユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_USERS).ToString(TUseBoolStrs.True));
Writeln('ゲストグループ : ', IsInGroup(DOMAIN_ALIAS_RID_GUESTS).ToString(TUseBoolStrs.True));
Writeln('パワーユーザグループ : ', IsInGroup(DOMAIN_ALIAS_RID_POWER_USERS).ToString(TUseBoolStrs.True));
WriteLn;
ManifestXML := GetManifestXml(ParamStr(0));
WriteLn('[互換性(サポートOS)]');
Dict := CreateOSDictionary;
OSList := GetManifestAttributeList(ManifestXML, 'supportedOS', 'Id');
for I := 0 to OSList.Count - 1 do
begin
Writeln(GUIDToOSName(OSList[I], Dict));
end;
WriteLn;
WriteLn('[マニフェスト情報]');
Info := GetManifestInfo(ManifestXML);
Writeln('dpiAware : ', Info.DpiAware);
Writeln('dpiAwareness : ', Info.DpiAwareness);
Writeln('longPathAware : ', Info.LongPathAware);
Writeln('activeCodePage : ', Info.ActiveCodePage);
WriteLn('disableWindowFiltering : ', Info.DisableWindowFiltering);
WriteLn('printerDriverIsolation : ', Info.PrinterDriverIsolation);
WriteLn;
WriteLn('[WindowsのANSIコードページ]');
WriteLn('Windows.GetACP : ', GetACP);
WriteLn;
WriteLn('[FreePascalのDefaultコードページ]');
WriteLn('DefaultSystemCodePage : ', DefaultSystemCodePage);
WriteLn('DefaultFileSystemCodePage : ', DefaultFileSystemCodePage);
WriteLn('DefaultRTLFileSystemCodePage : ', DefaultRTLFileSystemCodePage);
except
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
Dict.Free;
OSList.Free;
WriteLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo3"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アプリケーションのマニフェストを設定する。
//
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
// GUIモード (CUIモードでも利用可)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest"
// CUIモード (GUIモードのみに必要な情報を削除)
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "console.manifest"
/////////////////////////////////////////////////////////////////////////////
//
// ヘッダファイル windres.h のインクルード
//
#include "windres.h"
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// リソースの言語(日本語リソース)
//
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
/////////////////////////////////////////////////////////////////////////////
//
// バージョン情報データ (この値を編集する) ※変数は任意の名前で作成したもの。
//
// この情報データはフィクションであり、実在の人物・団体とは一切関係ありません。
//
#define VER_FILEVERSION 1,0,0,1 // ファイルバージョン
#define VER_FILEVERSION_STR "1.0.0.1" // ファイルバージョン(文字列)
#define VER_PRODUCTVERSION 1,0,0,0 // 製品バージョン
#define VER_PRODUCTVERSION_STR "1.0.0" // 製品バージョン(文字列)
#define VS_FFI_FILEFLAGSMASK 0x3FL // ファイルフラグマスク
#define VER_FFLAGS 0L // ファイルフラグ
#define VER_FILETYPE VFT_APP // アプリ(EXE)。DLLの場合VFT_DLL
#define VER_COMPANYNAME_STR "超力ソフト" // 会社名
#define VER_FILEDESCRIPTION_STR "ObjectPascalプログラム" // ファイルの説明
#define VER_INTERNALNAME_STR "ManifestDemo3" // 内部名
#define VER_LEGALCOPYRIGHT_STR "©2026 CHOURIKI-SOFT Inc." // 著作権表示
#define VER_LEGALTRADEMARKS_STR "" // 商標/登録商標
#define VER_ORIGINALFILENAME_STR "ManifestDemo3.exe" // オリジナル名
#define VER_PRODUCTNAME_STR "超力アプリ" // 製品名
#define VER_COMMENTS_STR "マニフェストデモ" // 追加情報
/////////////////////////////////////////////////////////////////////////////
//
// バージョン情報定義
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILEVERSION
PRODUCTVERSION VER_PRODUCTVERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
FILEFLAGS VER_FFLAGS
FILEOS VOS_NT_WINDOWS32
FILETYPE VER_FILETYPE
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "041104B0" // 0x0411(日本語), 0x04B0(=1200 ユニコード)
BEGIN
VALUE "CompanyName", VER_COMPANYNAME_STR
VALUE "FileDescription", VER_FILEDESCRIPTION_STR
VALUE "FileVersion", VER_FILEVERSION_STR
VALUE "InternalName", VER_INTERNALNAME_STR
VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR
VALUE "LegalTrademarks", VER_LEGALTRADEMARKS_STR
VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
VALUE "ProductName", VER_PRODUCTNAME_STR
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
VALUE "Comments", VER_COMMENTS_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x411, 1200 // 日本語, ユニコード
END
END
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アイコン
//
APPICON ICON "AppIcon.ico"
任意のアイコンを用意します。ManifestDemo3.pas と同じディレクトリに置きます。
![]()
ファイル名は「AppIcon.ico」とします。
ManifestDemo3.pas コンパイルして ManifestDemo3.exe を作成します。
C:\PG\FreePascal\ManifestDemo_003>fpcrc64.cmd ManifestDemo3.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo3.pas
Compiling resource C:\PG\FreePascal\ManifestDemo_003\Icon.rc
Compiling resource C:\PG\FreePascal\ManifestDemo_003\VerInfo.rc
Compiling resource C:\PG\FreePascal\ManifestDemo_003\Manifest.rc
Compiling resource ManifestDemo3.obj
Linking ManifestDemo3.exe
322 lines compiled, 0.6 sec, 255824 bytes code, 11092 bytes data
ManifestDemo3.exe を実行するとマニフェストの設定情報が表示されます。
[アプリの実行レベル]
管理者グループ : False
一般ユーザグループ : True
ゲストグループ : False
パワーユーザグループ : False
[互換性(サポートOS)]
Windows 10/11
Windows 8.1
Windows 8
Windows 7
Windows Vista
[マニフェスト情報]
dpiAware : True/PM_V2
dpiAwareness : PerMonitorV2, PerMonitor
longPathAware : True
activeCodePage : UTF-8
disableWindowFiltering : True
printerDriverIsolation : True
[WindowsのANSIコードページ]
Windows.GetACP : 65001
[FreePascalのDefaultコードページ]
DefaultSystemCodePage : 65001
DefaultFileSystemCodePage : 65001
DefaultRTLFileSystemCodePage : 65001
マニフェストで「<longPathAware>True</longPathAware>」を設定すると、Windows APIで扱うパス名の長さ制限(MAX_PATH(260))が解除をされ長いパス名が使用できるようになります。
これはWindows APIを直接使用する場合の機能であり、Free Pascalの標準関数やライブラリはその限りではありません。Free Pascal 3.2.2 の AssignFile 手続きではでは MAX_PATH(260) ではなく、TextRecNameLength(256) の制限があり、Manifest の設定の影響は受けません。
現在、次リリースに向けて開発中の Free Pascal 3.3.1 では、長いパス名に対応する仕組みが実装されているようです。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください。
ManifestDemo4.pas
app.manifest
Manifest.rc
(*****************************************************************************)
(* プログラム - ManifestDemo4 *)
(* マニフェストによるロングパス有効化の検証 *)
(* - Windows APIでMAX_PATH(260)より長いパス名が有効 *)
(* - Free Pascal標準関数(AssignFileなど)では効果がない *)
(*****************************************************************************)
program ManifestDemo4;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
uses
Windows, SysUtils;
{$R Manifest.rc} (* この設定を外すとパス名の長さがMAX_PATH(260)に制限される *)
{ 定数 }
const
TESTDIR = 'C:\PG\Test'; { テスト用の既存のディレクトリ(フォルダ)を指定する }
{ 変数 }
var
FilePath : String;
File1 : TextFile;
FH : HANDLE;
FN : UnicodeString;
{--------------------------------------------------------------}
{ 関数 - 長いバス(>MAX_PATH)のファイルを作成しフルパス名を返す }
{--------------------------------------------------------------}
function CreateLongPathFile: String;
const
FILENAME = 'TestFile.txt';
NEWDIRLEN = 80;
NEWDIRSET = ['A', 'B', 'C', 'D'];
var
InitialDir : String;
NewDir : String;
Ch : Char;
begin
InitialDir := EmptyStr;
TextRec(File1).Mode := fmClosed;
try
InitialDir := GetCurrentDir;
if not SetCurrentDir(TESTDIR) then
begin
WriteLn('SetCurrentDir(TESTDIR)');
RaiseLastOSError;
end;
WriteLn('(1) 起点のディレクトリ下にサブディレクトリ(A〜D)を作成しそのサブディレクトリをカレントディレクトリとする');
for Ch in NEWDIRSET do
begin
NewDir := StringOfChar(Ch, NEWDIRLEN);
if not DirectoryExists(NewDir) and not CreateDir(NewDir) then
begin
WriteLn('CreateDir(NewDir)');
RaiseLastOSError;
end
else
begin
WriteLn(Ch, ' : ', {Ch, '..', Ch}NewDir);
end;
if not SetCurrentDir(NewDir) then
begin
WriteLn('SetCurrentDir(NewDir)');
RaiseLastOSError;
end;
end;
WriteLn;
{ 相対パス(<TextRecNameLength(256))でファイルを作成(FreePascal標準手続き使用) }
WriteLn('(2) 標準手続き(AssignFile)でテスト用ファイルの相対パス名(<256)を設定');
AssignFile(File1, FILENAME);
WriteLn('<<< File1内のファイルパス名(name) >>>');
WriteLn(String(TextRec(File1).name));
WriteLn;
WriteLn('(3) 標準手続き(Rewrite)でテスト用ファイル', FILENAME,'をサブディレクトリ下に作成');
Rewrite(File1);
WriteLn(File1, 'テストデータ');
WriteLn('ファイルの作成が成功しました');
finally
if TextRec(File1).Mode <> fmClosed then
begin
CloseFile(File1);
end;
Result := ConcatPaths([GetCurrentDir, FILENAME]); { 戻り値にファイルのフルパス名を設定 }
if InitialDir <> EmptyStr then
begin
if not SetCurrentDir(InitialDir) then
begin
WriteLn('SetCurrentDir(InitialDir)');
RaiseLastOSError;
end;
end;
end;
end;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
{ Manifestで「<activeCodePage>UTF-8</activeCodePage>」の設定があれば次の2行は不要(CP_UTF8が設定される)。あっても良い }
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
FH := INVALID_HANDLE_VALUE;
TextRec(File1).Mode := fmClosed;
try
try
WriteLn;
WriteLn('MAX_PATH : ', MAX_PATH);
WriteLn('TextRecNameLength : ', TextRecNameLength);
WriteLn;
WriteLn('起点のディレクトリ : ', TESTDIR);
WriteLn;
{ 長いバス(>MAX_PATH)のファイルを作成 }
FilePath := CreateLongPathFile;
WriteLn;
WriteLn('(4) ファイルのフルパス名(起点のディレクトリ名+サブディレクトリ名(A〜D)+ファイル名)を表示');
WriteLn(FilePath);
WriteLn;
{ フルパス(>MAX_PATH)でファイルオープン(Windows API使用) }
WriteLn('(5) Windows API(CreateFileW)でファイルのフルパス名を指定してオープン');
FN := UTF8Decode(FilePath); { FN := FilePath; でもよい }
FH := CreateFileW(PWideChar(FN), GENERIC_READ, FILE_SHARE_READ, Nil, OPEN_EXISTING, 0, 0);
if FH = INVALID_HANDLE_VALUE then
begin
WriteLn('CreateFileW');
RaiseLastOSError;
end;
WriteLn('フルパスでファイルのオープンが成功しました(ロングパス設定が有効)');
WriteLn;
{ フルパス(>MAX_PATH)でファイルオープン(FreePascal標準手続き) }
WriteLn('(6) 標準手続き(AssignFile)でファイルのフルパス(>=256)を設定 - 全部は入らない');
AssignFile(File1, FilePath);
WriteLn('<<< File1内のファイルパス名(name) >>>');
WriteLn(String(TextRec(File1).name));
WriteLn;
{$IF DEFINED(VER3_2)}
WriteLn('(7) 標準手続き(Reset)でファイルをオープン - Free Pascal 3.2.2 では上記の理由(パス不完全)でエラーになる(想定済)');
Reset(File1); { <== Free Pascal 3.2.2 ではここで例外が発生するはず }
{$ENDIF}
{$IF DEFINED(VER3_3) OR DEFINED(VER3_4)}
WriteLn('(7) 標準手続き(Reset)でファイルをオープン - Free Pascal 3.3.1 では長いパス名に対応している');
WriteLn('<<< File1内のファイルパス名(FullName) >>>');
Reset(File1); { <== Free Pascal 3.1.1 では正常に実行できる }
WriteLn(String(PWideChar(TextRec(File1).FullName)));
WriteLn('フルパスでファイルのオープンが成功しました(ロングパス設定が有効)');
{$ENDIF}
except
on Ex: EOSError do
begin
WriteLn(StdErr, '*** OSエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: EInOutError do
begin
WriteLn(StdErr, '*** I/Oエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
{ オープンしたファイルをクローズ }
if FH <> INVALID_HANDLE_VALUE then
begin
CloseHandle(FH);
end;
if TextRec(File1).Mode <> fmClosed then
begin
CloseFile(File1);
end;
WriteLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo4"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アプリケーションのマニフェストを設定する。
//
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
// GUIモード (CUIモードでも利用可)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "app.manifest"
// CUIモード (GUIモードのみに必要な情報を削除)
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "console.manifest"
ManifestDemo4.pas コンパイルして ManifestDemo4.exe を作成します。
C:\PG\FreePascal\ManifestDemo_003>fpcrc64.cmd ManifestDemo4.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo4.pas
Compiling resource C:\PG\FreePascal\ManifestDemo_004\Manifest.rc
Compiling resource ManifestDemo4.obj
Linking ManifestDemo4.exe
175 lines compiled, 0.2 sec, 89872 bytes code, 5348 bytes data
ManifestDemo4.exe を実行すると Windows API のファイル操作で長いパス名が有効になっていることがわかります。
「(7)」で、Free Pascal 3.2.2 の標準関数が例外を発生します(想定した例外)。長いパス名が使えないことがわかります。
「(1)」「(2)」では、長くない相対パスで Free Pascal 3.2.2 の標準関数を使用しているので例外は発生しません。
MAX_PATH : 260
TextRecNameLength : 256
起点のディレクトリ : C:\PG\Test
(1) 起点のディレクトリ下にサブディレクトリ(A〜D)を作成しそのサブディレクトリをカレントディレクトリとする
A : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
B : BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
C : CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
D : DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
(2) 標準手続き(AssignFile)でテスト用ファイルの相対パス名(<256)を設定
<<< file1内のファイルパス名(name) >>>
TestFile.txt
(3) 標準手続き(Rewrite)でテスト用ファイルTestFile.txtをサブディレクトリ下に作成
ファイルの作成が成功しました
(4) ファイルのフルパス名(起点のディレクトリ名+サブディレクトリ名(A〜D)+ファイル名)を表示
C:\PG\Test\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD\TestFile.txt
(5) Windows API(CreateFileW)でファイルのフルパス名を指定してオープン
フルパスでファイルのオープンが成功しました(ロングパス設定が有効)
(6) 標準手続き(AssignFile)でファイルのフルパス(>=256)を設定 - 全部は入らない
<<< file1内のファイルパス名(name) >>>
C:\PG\Test\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\D
(7) 標準手続き(Reset)でファイルをオープン - Free Pascal 3.2.2 では上記の理由(パス不完全)でエラーになる(想定済)
*** I/Oエラー(2) : File not found
【参考』マニフェストを指定しない場合の実行結果
「(1)」のサブフォルダを作成する過程でパスの長さが MAX_PATH を超えたため例外が発生しています。
MAX_PATH : 260
TextRecNameLength : 256
起点のディレクトリ : C:\PG\Test
(1) 起点のディレクトリ下にサブディレクトリ(A〜D)を作成しそのサブディレクトリをカレントディレクトリとする
A : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
B : BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
C : CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
CreateDir(NewDir)
*** OSエラー(3) : System error, (OS Code 3):
指定されたパスが見つかりません。
マニフェストで「<activeCodePage>UTF-8</activeCodePage>」を設定すると、アプリのプロセスに対してWindowsの既定のANSIコードページが UTF-8(65001) になります。ANSI系Windows APIの引数や戻り値も UTF-8 になります。マニフェストを指定しない場合、日語Windows(ja_JP)では既定のANSIコードページは Shift_JIS(932) です。これは当該プロセスに対して設定されるもので、Windows全体の設定が変わるものではありません。また、他のプロセスにも影響しません。
Windows API以外のサードパーティ製品のAPIなどについては、それぞれの仕様に準じます。
Windows APIの「GetACP」関数は、ANSIコードページ番号を返します。マニフェストを設定しないと932、設定すると65001を返します。プログラムでGetACPの値を調べることによりANSIコードページ番号を知ることができます。
「𠮷野家」の"𠮷"(俗にいう つちよし)は Shift_JIS では表すことができません。ユニコード(UTF-8,UTF-16,UTF-32)で表すことができます。
Wide系Windows APIはUTF-16(1200)でやりとりするので既定のANSIコードページやマニフェストの影響は受けません。
俗にいう つちよし は、𠮷 普通の よし は、吉
ファイル名にこの"𠮷"(俗にいう つちよし)を設定してアクセス検証します。
マニフェストで「<activeCodePage>UTF-8</activeCodePage>」を設定したときの ANSI系Windows APIの動作検証をします。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください。(𠮷野家.txtはファイルの内容は問いません)
ManifestDemo5A.pas
appA.manifest
ManifestA.rc
𠮷野家.txt
(*****************************************************************************)
(* プログラム - ManifestDemo5A *)
(* マニフェストによるアクティブコードページのUTF-8設定の検証(ANSI系API) *)
(* - 日本語Windows(ja-JP)では既定のコードページはSJIS(Shift_JIS) *)
(* - ANSI系APIは設定されたアクティブコードページの影響を受ける *)
(*****************************************************************************)
program ManifestDemo5A;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
{$WARN 4104 OFF} (* UnicodeString <- AansiString *)
{$WARN 4105 OFF} (* AnsiString <- UnicodeString *)
uses
Windows, SysUtils;
{$R ManifestA.rc} (* この設定を外すとMessageBoxAやCreateFileAは正しく動作しない *)
(*****************************************************************************)
(* プログラムブロック - 宣言部 *)
(*****************************************************************************)
{ 定数 }
const
FILENAME = '𠮷野家.txt';
{ 変数 }
var
FH : HANDLE;
FN : String;
Msg : String;
Cap : String;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
{ Manifestで「UTF-8 」の設定があれば次の2行は不要(CP_UTF8が設定される)。あっても良い }
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
FH := INVALID_HANDLE_VALUE;
FN := ConcatPaths([ExtractFileDir(ParamStr(0)), FILENAME]);
Msg := 'デモプログラム';
Cap := 'Windowsコードページ : ' + GetACP.ToString;
try
try
WriteLn;
WriteLn('ANSI系Windows API(MessageBoxA,CreateFileA)使用');
WriteLn;
WriteLn('コードページ情報');
WriteLn('GetACP : ', GetACP, ' - WindowsのANSIコードページ');
WriteLn('メッセージ : ', StringCodepage(Msg));
WriteLn('キャプション : ', StringCodepage(Cap));
WriteLn('ファイル名 : ', StringCodepage(FN));
{ ANSI系APIはGetACPのコードページを使用するので文字列のコードページもGetACPと同じである必要がある }
WriteLn;
WriteLn('メッセージボックスの表示内容を確認して[OK]ボタンを押してください');
Write('MessageBoxA実行');
MessageBoxA(0, PChar(Msg), PChar(Cap), MB_OK); { WinAPIのAnsi系 }
WriteLn(' -> 終了しました');
WriteLn;
Write('CreateFileA実行');
FH := CreateFileA(PChar(FN), GENERIC_READ, FILE_SHARE_READ, Nil, OPEN_EXISTING, 0, 0);
if FH = INVALID_HANDLE_VALUE then
begin
WriteLn(' -> 例外発生');
RaiseLastOSError; { EOSErrorをスロー }
end;
WriteLn(' -> ファイルのオープンが成功しました');
except
on Ex: EOSError do
begin
WriteLn(StdErr, '*** OSエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
if FH <> INVALID_HANDLE_VALUE then
begin
CloseHandle(FH);
end;
WriteLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo5A"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アプリケーションのマニフェストを設定する。
//
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
// GUIモード (CUIモードでも利用可)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "appA.manifest"
// CUIモード (GUIモードのみに必要な情報を削除)
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "consoleW.manifest"
ManifestDemo5A.pas コンパイルして ManifestDemo5A.exe を作成します。
C:\PG\FreePascal\ManifestDemo_003>fpcrc64.cmd ManifestDemo5A.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo5A.pas
Compiling resource C:\PG\FreePascal\ManifestDemo_005\ManifestA.rc
Compiling resource ManifestDemo5A.obj
Linking ManifestDemo5A.exe
88 lines compiled, 0.2 sec, 75680 bytes code, 5300 bytes data
ManifestDemo5A.exe を実行すると Windows API の MessageBoxA でメッセージボックスを表示します。
[OK]ボタンで閉じると Windows API の CreateFileA で「𠮷野家.txt」ファイルをオーブンします。
ANSI系Windows API(MessageBoxA,CreateFileA)使用
コードページ情報
GetACP : 65001 - WindowsのANSIコードページ
メッセージ : 65001
キャプション : 65001
ファイル名 : 65001
メッセージボックスの表示内容を確認して[OK]ボタンを押してください
MessageBoxA実行 -> 終了しました
CreateFileA実行 -> ファイルのオープンが成功しました
GetACPの値もプログラムの文字列のコードページも65001で一致しているので、メッセージボックスも正しく表示され、「𠮷野家.txt」のオープンも成功するわけです。
【参考』マニフェストを指定しない場合の実行結果
ANSI系Windows API(MessageBoxA,CreateFileA)使用
コードページ情報
GetACP : 932 - WindowsのANSIコードページ
メッセージ : 65001
キャプション : 65001
ファイル名 : 65001
メッセージボックスの表示内容を確認して[OK]ボタンを押してください
MessageBoxA実行 -> 終了しました
CreateFileA実行 -> 例外発生
*** OSエラー(2) : System error, (OS Code 2):
指定されたファイルが見つかりません。
GetACPの値は932でプログラムの文字列のコードページが65001となり一致していないので、メッセージボックスは文字化けし、「𠮷野家.txt」の認識もできずオープンが失敗するわけです。
マニフェストで「<activeCodePage>UTF-8</activeCodePage>」を設定したときの Wide系Windows APIの動作検証をします。
プログラムファイルは以下のセットです。同一ディレクトリに置いてください。
ファイルはコードページ UTF-8(65001)で作成してください。(𠮷野家.txtは内容は問いません)
・ManifestDemo5W.pas
・appW.manifest
・ManifestW.rc
・𠮷野家.txt
(*****************************************************************************)
(* プログラム - ManifestDemo5W *)
(* マニフェストによるアクティブコードページのUTF-8設定の検証(Wide系API) *)
(* - 日本語Windows(ja-JP)では既定のコードページはSJIS(Shift_JIS) *)
(* - Wide系APIは設定されたアクティブコードページの影響を受けない *)
(* - 既定のANSIコードページに関係なくWide文字UTF-16が使用される *)
(*****************************************************************************)
program ManifestDemo5W;
{$MODE OBJFPC}{$H+}{$J-}
{$MINFPCONSTPREC 64}
{$CODEPAGE UTF8}
{$WARN 4104 OFF} (* UnicodeString <- AansiString *)
{$WARN 4105 OFF} (* AnsiString <- UnicodeString *)
uses
Windows, SysUtils;
{$R ManifestW.rc} (* ManifestがなくてもMessageBoxWやCreateFileWは正しく動作 *)
(*****************************************************************************)
(* プログラムブロック - 宣言部 *)
(*****************************************************************************)
{ 定数 }
const
FILENAME = '𠮷野家.txt';
{ 変数 }
var
FH : HANDLE;
FN : UnicodeString;
Msg : UnicodeString;
Cap : UnicodeString;
(*****************************************************************************)
(* プログラムブロック - 実行文部 *)
(*****************************************************************************)
begin
{ Manifestで「UTF-8 」の設定があれば次の2行は不要(CP_UTF8が設定される)。あっても良い }
SetMultiByteConversionCodePage(CP_UTF8);
SetMultiByteRTLFileSystemCodePage(CP_UTF8);
FH := INVALID_HANDLE_VALUE;
FN := ConcatPaths([ExtractFileDir(ParamStr(0)), FILENAME]);
Msg := 'デモプログラム';
Cap := 'Windowsコードページ : ' + GetACP.ToString;
try
try
WriteLn;
WriteLn('Wide系Windows API(MessageBoxW,CreateFileW)使用');
WriteLn;
WriteLn('コードページ情報');
WriteLn('GetACP : ', GetACP, ' - WindowsのANSIコードページ');
WriteLn('メッセージ : ', StringCodepage(Msg));
WriteLn('キャプション : ', StringCodepage(Cap));
WriteLn('ファイル名 : ', StringCodepage(FN));
{ Wide系APIはUTF-16を使用するのでGetACPには左右されない }
WriteLn;
WriteLn('メッセージボックスの表示内容を確認して[OK]ボタンを押してください');
Write('MessageBoxW実行');
MessageBoxW(0, PWideChar(Msg), PWideChar(Cap), MB_OK);
WriteLn(' -> 終了しました');
WriteLn;
Write('CreateFileW実行');
FH := CreateFileW(PWideChar(FN), GENERIC_READ, FILE_SHARE_READ, Nil, OPEN_EXISTING, 0, 0);
if FH = INVALID_HANDLE_VALUE then
begin
WriteLn(' -> 例外発生');
RaiseLastOSError; { EOSErrorをスロー }
end;
WriteLn(' -> ファイルのオープンが成功しました。');
except
on Ex: EOSError do
begin
WriteLn(StdErr, '*** OSエラー(', Ex.ErrorCode, ') : ', Ex.Message);
end;
on Ex: Exception do
begin
WriteLn(StdErr, '*** エラー : ', Ex.Message);
end;
end;
finally
if FH <> INVALID_HANDLE_VALUE then
begin
CloseHandle(FH);
end;
WriteLn;
end;
end.
<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
type="win32"
name="e-hiyori.org.ManifestDemo5W"
version="1.0.0.0"
processorArchitecture="*"
/>
<description>Windows Application.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<asmv3:application xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>True/PM_V2</dpiAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2011/WindowsSettings">
<disableWindowFiltering>True</disableWindowFiltering>
<printerDriverIsolation>True</printerDriverIsolation>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<dpiAwareness>PerMonitorV2, PerMonitor</dpiAwareness>
<longPathAware>True</longPathAware>
</asmv3:windowsSettings>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
<activeCodePage>UTF-8</activeCodePage>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>
/////////////////////////////////////////////////////////////////////////////
//
// 本ファイルのコードページ
//
#pragma code_page(65001) // Free Pascalの{$CODEPAGE UTF8}と同じようなもの。
/////////////////////////////////////////////////////////////////////////////
//
// アプリケーションのマニフェストを設定する。
//
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
// GUIモード (CUIモードでも利用可)
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "appW.manifest"
// CUIモード (GUIモードのみに必要な情報を削除)
// CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "consoleW.manifest"
ManifestDemo5W.pas コンパイルして ManifestDemo5W.exe を作成します。
C:\PG\FreePascal\ManifestDemo_003>fpcrc64.cmd ManifestDemo5W.pas
Free Pascal Compiler version 3.2.2 [2021/05/15] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Win64 for x64
Compiling ManifestDemo5W.pas
Compiling resource C:\PG\FreePascal\ManifestDemo_005\ManifestW.rc
Compiling resource ManifestDemo5W.obj
Linking ManifestDemo5W.exe
89 lines compiled, 0.2 sec, 75680 bytes code, 5300 bytes data
ManifestDemo5W.exe を実行すると Windows API の MessageBoxW でメッセージボックスを表示します。
[OK]ボタンで閉じると Windows API の CreateFileW で「𠮷野家.txt」ファイルをオーブンします。
Wide系Windows API(MessageBoxW,CreateFileW)使用
コードページ情報
GetACP : 65001 - WindowsのANSIコードページ
メッセージ : 1200
キャプション : 1200
ファイル名 : 1200
メッセージボックスの表示内容を確認して[OK]ボタンを押してください
MessageBoxW実行 -> 終了しました
CreateFileW実行 -> ファイルのオープンが成功しました。
Wide系Windows APIもプログラムの文字列もGetACPの値(ANSIコードページ)と関係なくUTF-16(1200)で一致するので、メッセージボックスも正しく表示され、「𠮷野家.txt」のオープンも成功するわけです。
【参考』マニフェストを指定しない場合の実行結果
メッセージボックスのキャプションに表示されるコードページ番号とGetACPの番号が違うだけで、あとはマニフェストを指定した場合と同じです。
Wide系Windows API(MessageBoxW,CreateFileW)使用
コードページ情報
GetACP : 932 - WindowsのANSIコードページ
メッセージ : 1200
キャプション : 1200
ファイル名 : 1200
メッセージボックスの表示内容を確認して[OK]ボタンを押してください
MessageBoxW実行 -> 終了しました
CreateFileW実行 -> ファイルのオープンが成功しました。
本記事のプログラムコードなどは以下のページでダウンロードできます。