Edited at

Windows 8.1 や Windows 10 が Delphi 2007 の .target ファイルを消すのでどうにかする

More than 1 year has passed since last update.


はじめに

Windows 8.1 や Windows 10 は大型アップデートがあると Delphi 2007 / R2 の .target ファイルを削除してくれやがるので Delphi 2007 / R2 でコンパイルができなくなります。

image.png

*** 一応断っておくと Delphi 2007 は 2007 年の製品です (Vista の頃) ***

解決方法は以下の 4 つのファイルを確保しておき、削除されたら書き戻せばよいのです。


  • Borland.Common.Targets

  • Borland.Cpp.Targets

  • Borland.Delphi.Targets

  • Borland.Group.Targets

これらのファイルは C:\Windows\Microsoft.NET\Framework\v2.0.50727 に存在します。大型アップデートで消された直後なら、C:\Windows.old\WINDOWS\Microsoft.NET\Framework\v2.0.50727 に存在するハズです。

これらのファイルを ZIP で固めてクラウドストレージにでも放り込んでおけば問題解決なのですが、それでは面白くないので、ファイルを配布する単一バイナリを作ってみます。


コード

コンパイラとして Delphi 10.2 Tokyo を使います。問題が発生するのは Delphi 2007 / R2 ですが、最新版で作った方が何かと便利ですので。無償の Starter Edition でも構いません。


プロジェクト

Delphi を起動したら [ファイル | 新規作成 | VCL フォームアプリケーション - Delphi ] でプロジェクトを新規作成します。

次に [ファイル | すべて保存] でプロジェクトを保存します。任意のフォルダを作成しその中に保存します。今回は TargetFileCopy というフォルダを作成してそこに保存する事にします。

image.png

最初はユニットです。名前はこのまま (Unit1.pas) でいいです。

image.png

次にプロジェクトファイルです。名前は TargetFileCopy.dproj にしておきましょうか。

image.png

プロジェクトマネージャで確認するとこのようになっていると思います。

image.png

TargetFileCopy.exe になっている所を右クリックして [エクスプローラで表示] を選択します。

image.png

プロジェクトフォルダが Explorer で開かれるのでここに res というフォルダを作ってその中に入ります。

image.png

ここに以下の二つのファイルを作成します。


GetFiles.current.cmd

@echo off

cls
copy C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Borland.*.Targets .\


GetFiles.old.cmd

@echo off

cls
copy C:\Windows.old\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Borland.*.Targets .\

前者は現在の Windows フォルダから Borland.*.targets ファイルをコピーしてくるスクリプトで、後者はバックアップされた Windows.old フォルダからファイルをコピーしてくるスクリプトです。

スクリプトファイルができたらどちらかをダブルクリックして実行してください。カレントフォルダに 4 つのファイルがコピーされたと思います。

Delphi IDE に戻ったら [ファイル | 新規作成 | その他...] で新規作成ダイアログを開きます。[その他のファイル] から "テキストファイル" を選択します。

image.png

[新規ファイル] でリソースファイル (.rc) を選択します。

image.png

そのまま [ファイル | 名前を付けて保存] で resources.rc という名前で保存します。

image.png

resources.rc には以下のように記述します。


resources.rc

TARGETS1        XML     "res\Borland.Common.Targets"

TARGETS2 XML "res\Borland.Cpp.Targets"
TARGETS3 XML "res\Borland.Delphi.Targets"
TARGETS4 XML "res\Borland.Group.Targets"

次に IDE で Unit1 のフォームエディタを開きます。

image.png

オブジェクトインスペクタで以下のプロパティを設定します。

image.png

プロパティ

BorderIcons
[biSystemMenu]

Caption
.target File Copy

ClientHeight
200

ClientWidth
400

Position
poScreenCenter

フォームにボタン (TButton) を貼ります。Caption は "Copy Files" とでもしておいてください。

image.png

そして [プロジェクト | オプション] でプロジェクトプションを開きます。アプリケーションマニフェストでこの EXE を管理者権限で実行するように変更します。

image.png

とりあえずお膳立てはできました。


ロジック

動作についての説明です。

コピーすべきファイルはリソースとして EXE に埋め込むようになっており、実行時に指定フォルダに展開するようにします。アプリケーションマニフェストで requireAdministrator を含めたので、Windows フォルダへのファイル展開が可能になります。

後はそのように動作するコードを書くだけですね。まずはボタンをダブルクリックするかオブジェクトインスペクタの [イベント] タブにある OnClick をダブルクリックしてイベントハンドラを作成します。

image.png

この中身はこうなります。


Unit1.pas

procedure TForm1.Button1Click(Sender: TObject);

begin
CopyFiles;
end;

CopyFiles() という自作のメソッドを呼び出すだけです。では次に CopyFiles メソッドを実装します。まずは uses 句に System.IOUtils と System.UITypes を追加します。


Unit1.pas

unit frmuMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Vcl.StdCtrls, System.IOUtils, System.UITypes, System.IOUtils, System.UITypes; // 追加


定義部を記述します。スコープは private でいいでしょう。


Unit1.pas

type

TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
procedure CopyFiles; // 追加
public
{ Public 宣言 }
end;


実装部はこうなります。


Unit1.pas

procedure TForm1.CopyFiles;

const
TARGET_DIR = 'C:\Windows\Microsoft.NET\Framework\v2.0.50727';
FILES: array [1..4] of string =
('Borland.Common.Targets',
'Borland.Cpp.Targets',
'Borland.Delphi.Targets',
'Borland.Group.Targets');
var
i: Integer;
begin
if MessageDlg('Copy .targets files. Are you sure?', mtConfirmation, [mbOK, mbCancel], 0) <> mrOK then
Exit;
for i:=1 to 4 do
with TResourceStream.Create(HInstance, 'TARGETS' + i.ToString , 'XML') do
begin
SaveToFile(TPath.Combine(TARGET_DIR, FILES[i]));
Free;
end;
MessageDlg('Done.', mtConfirmation, [mbOK], 0);
end;

コードを全部載せてもそんなに長くはありません。


Unit1.pas

unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.IOUtils, System.UITypes,
Vcl.StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
procedure CopyFiles;
public
{ Public 宣言 }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
CopyFiles;
end;

procedure TForm1.CopyFiles;
const
TARGET_DIR = 'C:\Windows\Microsoft.NET\Framework\v2.0.50727';
FILES: array [1..4] of string =
('Borland.Common.Targets',
'Borland.Cpp.Targets',
'Borland.Delphi.Targets',
'Borland.Group.Targets');
var
i: Integer;
begin
if MessageDlg('Copy .targets files. Are you sure?', mtConfirmation, [mbOK, mbCancel], 0) <> mrOK then
Exit;
for i:=1 to 4 do
with TResourceStream.Create(HInstance, 'TARGETS' + i.ToString , 'XML') do
begin
SaveToFile(TPath.Combine(TARGET_DIR, FILES[i]));
Free;
end;
MessageDlg('Done.', mtConfirmation, [mbOK], 0);
end;

end.



実行

コンパイルして実行しようとすると以下のように怒られたのではないでしょうか?

image.png

これは管理者権限で実行されていない EXE (Delphi IDE) から管理者権限を必要とする EXE を実行しようとしたからです。とりあえず [ファイル | すべて保存] してから Delphi IDE を終了してください。

そして Delphi IDE を管理者権限で実行します。

image.png

image.png

コンパイルして実行すれば今度は正しく実行されるハズです。EXE 単独で実行する場合には昇格ダイアログが出るので、わざわざ [管理者で実行] から実行する必要はありません。

image.png

今後は大型アップデートがあったら、この EXE を実行するだけで済みますね...「これってバッチファイル一個で済むんじゃないか...?」そんな事言っちゃダメです (w


おわりに

最近の Delphi では大型アップデートの度にコンパイルできなくなるなんて事はありません。

Firebird SQL とかはインストールされているものを勝手にアンインストールされたりするんですよねぇ > Windows 10 の大型アップデート。本当に困ったものです。