Windows
Delphi
VCL
Pascal
objectpascal

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

はじめに

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 の大型アップデート。本当に困ったものです。