Windows XP 環境への既存アプリケーションの移行

トピック

Windows 9x 環境から Windows XP 環境へのアプリケーション移行
Windows NT 環境から Windows XP 環境へのアプリケーション移行
XP ロゴ要件に見る、XP 用アプリケーション開発のポイント
開発環境の移行
おわりに

Windows 9x 環境から Windows XP 環境へのアプリケーション移行

大きく分類すれば、これまでのデスクトップ用 Windows は、Windows 9x 系と Windows NT 系に分かれます。これらの系統は、そもそもの成り立ちが異なるのはご存じの通りです。簡単に歴史を振り返ってみることにしましょう。Windows 9x は、MS-DOS、16 ビット版の Windows から進化してきました。もともと個人利用が主なターゲットであったため、ネットワーク機能やセキュリティに関する機能は持っていません。また、GDI など、各所に 16 ビットによる制限を抱えています。

これに対して、Windows NT はネットワークやセキュリティなどに対応した、企業向けのワークステーションとして作られています。また、当初から32ビット版であり、Windows 9x が抱えていたような、たとえばシステムリソースに関する問題などは存在しません。Windows 9x 系とWindows NT 系の統合は、しばらく前から計画されていましたが、ようやく Windows XP によって達成されることになりました。この統合は、Windows 9x が Windows XP に吸収されるという形で実現されています。

Windows 9x の後継ということで、コンシューマ向け Windows XP である Home Edition も用意されていますが、ドメインに参加できない、IIS(INT ernet Information Server)が用意されていないなどの、ワークステーションとして必要な機能が含まれていないことを除き、Professional Edition と同等です。もちろん、Windows NT からの流れを受け継ぐオペレーティングシステムであり、セキュリティやネットワークに関する機能も備えています。そのため、特に Professional Edition に比べて、移行が簡単だというわけではありません。

ついつい忘れてしまいがちですが、このように Windows 9x とWindows NT は、成り立ちからして大きく異なるオペレーティングシステムです。逆に言えば、「問題が発生して当然」というほどのスタンスで、移行作業に立ち向かった方がいいのかもしれません。

固定ピッチフォントに関する問題

概要

まず紹介するのは、「固定ピッチフォント」に関する問題です。固定ピッチフォントは、幅が文字によって異なるプロポーショナルフォントとは異なり、1バイト文字と2バイト文字(この言い方は、Unicodeで統一された今となってはあまり適切なものではありません。こちらも同じく適切な言い方ではありませんが、いわゆる「半角・全角」のことを意味しています)の幅が「1:2」になっているフォントのことです。

「1:2」になっている、と言いましたが、ご存知のように従来のWindowsでは、この値はフォントサイズに依存していました。「1.5の倍数フォント」のときのみ、この「1:2」の関係が成り立ち、それ以外のフォントサイズでは、ずれてしまっていたのです。これは、次のような計算式によって、フォントの幅が決定されるためです。

文字幅 ( ピクセル ) =フォントサイズ ( ポイント ) × 96(dpi) / 72

「1.5 の倍数フォント」以外のときは、計算結果が割り切れないため「1:2」の関係とはなりません。これにより、画面やプリンタに文字を表示したときに、2 バイト文字の幅が、1 バイト文字の 2 倍にならないという現象が発生します。

exappmigratoxp1
1: 固定ピッチフォント問題

この問題は、Windows 2000 のサービスパック1で、どのフォントサイズでも正確に 2 倍になるように修正され、Windows XP でも同様の実装となっています。正確に計算されるようになったわけですから、問題が発生することなどないようにも思えます。しかし、独自に計算してフォントサイズを変更したり、各文字の表示位置を修正したりといった「1.5 の倍数以外のフォント」に対応するアプリケーションでは、問題が発生することになります。特に、日本のビジネスアプリケーションで重要な位置を占める「帳票」において、この問題が表面化することが考えられます。

テスト方法

この問題が発生するかどうかを確認するには、そのような微調整を行っているプログラムコードを洗い出すことと、実際に画面やプリンタに表示させるという二つの方法を利用します。また、この問題は日本語の固定ピッチフォントでしか発生しませんし、画面に表示するのであれば、文字幅のずれがそれほど大きな問題にならない場合もあります。状況に応じて、修正を行うかどうか判断するようにしましょう。

回避方法

この問題に対しては「汎用的なアプリケーションの実装」として、次のような回避方法が用意されています。

  1. 可能な限り固定ピッチフォントではなく、MS P ゴシックなどのプロポーショナルフォントを使用する

  2. 可能な限り、文字の表示領域に余裕を持たせる

  3. 文字の表示位置が重要である場合には、個々の文字ごとに表示位置を明確に指定する

  4. VisualBasic のフォーム編集や、Visual C++ のダイアログリソースの編集など開発ツールを使用してウィンドウやダイアログを設計する際には、配置するテキストフィールドやボタンで使用されるフォントが固定ピッチのフォントではないか、また従来の Windows と Windows XP で大きさの異なるフォントサイズを指定していないか確認する

  5. 必要であれば、フォントのサイズを指定する際に、最終的に使用される文字のポイントサイズを判断し、それに応じた処理を行うようにする

また、もうひとつの回避策として、緊急避難的な方法を取ることも可能です。Windows XP では、以下のレジストリ値を設定すれば、従来の Windows と同様の仕様で固定ピッチフォントのサイズが選択されるようになります。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT \CurreNT Version\GRE_Initialize 
"Jpn98FiXP itch"=dword:00000001

しかし、この設定はシステム全体に適用されるという副作用を持っています。これにより、Windows XP の仕様に合わせて実装されたアプリケーションでは、文字の表示や印刷で不具合が発生する可能性があります。また、Windows XP でのみ有効な回避策であり、今後のバージョンでも保証されるものではありません。あくまで、既存のアプリケーションを移行する間の一時的な措置として利用するようにしましょう。

参考

ユーザー権限に関する問題

概要

Windows 9x からWindows XP へ移行する際に、一番の変更点といえば、この「権限」に関するものとなるでしょう。Windows 9x は、主に個人利用を目的として設計されています。確かに、Windows 98 以降では、複数ユーザーを管理できる機能が盛り込まれましたが、セキュリティ面での強化というよりも、ユーザーの使い勝手の面が向上されたにすぎません。

権限が関係してくるのは、主にファイルやフォルダ、レジストリ、そしてプリンタなどのリソースです。権限の仕組みは簡単ではありませんが、ひとことで言えば「適切な権限を持っていないと、リソースを利用することができない」ということになります。例を挙げれば、あるフォルダへの書き込みが行えない、プリンタの設定が変更できないなどの現象が発生します。もう少し、詳しく見ていくことにしましょう。

Windows9x のファイルシステムは、FAT16/32 です。このファイルシステムは、Windows NT 系、そして、Windows XP でデフォルトで用いられている NT FS(NT FileSystem)とは異なり、セキュリティに関する機能を持っていません(もちろん、Windows XP で FAT32 を利用することもできますが、セキュリティの観点からはお勧めできません)。言い換えれば、どのフォルダもどのファイルも、ユーザーの望み通りに読み書きできるということになります。

これに対して、Windows XP では、読み書きできるフォルダ、ファイルは、NT FSを使うことによって制限されます。例を挙げれば、C:\やWindowsフォルダ、Systemフォルダなどは、Administrator権限を持っていないと書き込むことができません(図2)。ルートフォルダやWindowsフォルダは、どのWindowsでも存在するフォルダです(Windowsフォルダが「C:\Windows」であるとは限りません)。しかし、常に書き込めるということを期待してはいけません。

exappmigratoxp2
2: C:\ に対する権限

テスト方法

アプリケーションが、フォルダやファイルなどのリソースにアクセスしているプログラムコードを、すべて洗い出します。そして、その対象リソースが適切な権限で実行されたときに、問題なくアクセスできるかどうかをテストしなくてはなりません。たとえば、次のようなプログラムコードは、失敗する場合があります(図3)。

サンプルコード

Open "C:\test.txt" For Output As #1 
 Print #1, "ファイルへ書き込み" 
Close #1

exappmigratoxp3
3: 書き込みエラー

回避方法

このようなことを避けるためには、強く制限されるフォルダ、ファイルへのアクセスを、原則的に行わないようにします。そして、ユーザーごとに作成されるフォルダをあらかじめ取得し、エラーチェックを行った上で書き込みします。また、プリンタなどのリソースであれば、Win32 API を利用して、適切な権限を付与した後で設定を行います。

サンプルコード - Visual Basic

On Error GoTo errhandler ' ファイルを書き込み Open "C:\test.txt" For Output As #1 Print #1, "ファイルへ書き込み" Close #1 Exit Sub errhandler: ' エラー処理 MsgBox Err.Description Close #1

サンプルコード - Visual C++

CFile f; CFileException e; char* pFileName = "c:\\test.dat"; // ファイルを書き込み if(!f.Open(pFileName, CFile::modeCreate | CFile::modeWrite, &e)) { // エラー処理 }

参考

備考

注: この問題は、直接権限が関係するものではありませんが、リソースに関するものですので、ここにあげておきます

特殊フォルダ

概要

Windows オペレーティングシステムのフォルダは「C:\Windows」、テンポラリフォルダは「C:\Temp」、ユーザーのドキュメントフォルダは「C:\My DocumeNT s」というのが、Windows 9x では一般的な位置ではありますが、これらが常に同じであると期待してはいけません。

さらに、アプリケーションをインストールする「C:\Program Files」や、デスクトップを示す「C:\Windows\デスクトップ」なども同様です。これらのフォルダは、インストール時の設定や、その後の設定変更などで、他の場所に変わることがあります。そのため、これらのフォルダが固定であることを期待してしまっていると、問題が生じてしまう危険性があります。

テスト方法

どのタイミングで読み書きを行っているかは、アプリケーションによって異なります。そのため、ファイルやフォルダの読み書きを行うプログラムコードを、すべて探し出して確認しなくてはなりません。

また、問題が発生した後の状況も、アプリケーションによって異なります。単にファイルやフォルダが見つからないだけなら、それほど大きな問題ではありません。しかし、必要な情報が取得できなかったり、変な場所にファイルを作成してしまったりとなると、少々困る状況が発生するでしょう。これらを含めて、慎重に検証する必要があります。

回避方法

これらの特殊フォルダに関しては、固定での処理を行わないようにし、代わりに SHGetSpecialFolderPath() や SHGetFolderPath() などの Win32 API、もしくは、WshShell.SpecialFolders オブジェクトなどを利用するようにします。また、アプリケーションが自動的にファイルやフォルダに対して読み書きするのではなく、コモンダイアログを利用して、ユーザーに指定してもらうようにするというのも、ひとつの解決策です。

以下のサンプルコードでは、現在のユーザーのデスクトップパスを取得しています。

サンプルコード - Visual Basic

Dim wsh As WshShell ' WSHのWshShellオブジェクトを生成 Set wsh = New WshShell ' デスクトップフォルダを表示 MsgBox wsh.SpecialFolders("Desktop")

サンプルコード - Visual C++

TCHAR szPath[MAX_PATH]; // デスクトップフォルダを取得 SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, szPath); AfxMessageBox(szPath);

参考

備考

同様の問題として、「末尾の\」問題があります。とあるフォルダに、ファイルを作成しようとすると、次のようなプログラムコードを利用します。

strFilePath = CurDir() & "\" & "temp.txt"

ほとんどの場合、このプログラムコードはうまく動作しますが、たとえば、現在のカレントフォルダが「C:\」であった場合、エラーになります。当然、「C:\\」というパスが不正であるためですが、これなども、カレントフォルダがルートフォルダ以外だという「勝手な決めつけ」が原因となっています。

レジストリに関する問題

概要

レジストリに関しても、フォルダやファイルと同じような問題が発生します。あるユーザーは、そのユーザー用に用意されたレジストリへの読み書きは可能ですが、たとえば、システムに関する設定を保存する HKEY_LOCAL_MACHINE への書き込みは、PowerUsers 以上のユーザー権限が必要になります。これらの変更は、Windows 2000 から強化されたものであり、それ以前のユーザー権限とは異なるものになっています。これにより、Windows NT 4.0 では問題なく動作していたアプリケーションが、正常に動作しなくなったり、インストールできなくなったりします。

なお、Visual Basic に用意されている、GetSetting()、SaveSetting() ステートメントでは、レジストリの HKEY_CURRENT _USER\Software\VB and VBA ProgramSettings\ 以下の値を読み書きするため、このような問題が発生することはありません。

テスト方法

この問題は、コンピュータ上で共通の設定を行うために、HKEY_LOCAL_MACHINE へ RegSetValueEx() などの Win32 API を利用して書き込みをすることにより発生します。そのため、まずは、Win32 API によるレジストリ操作を行っていないかどうか確認します。

これらの操作を行っていなければ、特に問題が発生することはありません。とはいえ、サードパーティ製のコンポーネントなどが行っていることも考えられますので、十分な検証をするようにした方がよいでしょう。

回避方法

回避方法としては、HKEY_LOCAL_MACHINE などの共有領域への書き込みを行わないようにします。コンピュータで共通の設定などに関しては、適切な権限を持ったユーザーしか行えないように、別の設定メニューを用意するようにします。

なお、どうしても共有領域に書き込みたいときは、ユーザーを偽装し、偽装したユーザーの権限を持って書き込みを行います。もちろん、この方法はあまりお勧めできるものではありません。

参考

備考

exappmigratoxp4
4: 別のユーザーとして実行

これら、権限に関する問題を検証する方法として、「別のユーザーとして実行」を利用する方法があります。この方法を利用すると、いったんログオフしてユーザーを切り替える必要がないため、検証が簡単に行えます。方法は簡単で、実行したいアプリケーションを右クリックして「別のユーザーとして実行」を選択するだけです。ユーザーを指定するダイアログが表示されるので、検証したい権限を持つユーザーを選択し、アプリケーションを実行します(図4)。

: なおこの問題に関しては、Windows NT 4.0 からの移行時にも修正が必要になります

ロングファイル名、 UNC 名に関する問題

概要

exappmigratoxp5
5: 8.3 形式」とロングファイル名

フォルダやファイルの扱いについて、もう一点注意しなくてはならないことがあります。それは、ネットワークフォルダやロングファイル名への対応です。Windows以前の環境では、いわゆる「8.3形式」のファイル名が使われていましたが、Windows 95が登場してから、使える文字列の長さが255文字までになり、また、ファイル名の中に空白文字も使えるようになりました(図5)。

ファイル名の長さやファイル名に空白文字が含まれていることを検査しているアプリケーションでは、これらのチェックに引っかかってしまい、その後の処理を続行できなくなることもあるでしょう。正しいファイル名以外は指定できないようにしたい。これは、よい考え方です。しかし、ユーザーの利便性を考慮した結果、このような問題を引き起こしてしまうこともあるのです。一般的に、ファイル名が有効かどうか、といった検査は、プログラムが責任を持つのではなく、オペレーティングシステムに任せるべきでしょう(たとえば、ファイルを作成してみることによって)。

ネットワークフォルダに関する問題としては、次のようなものが考えられます。たとえば、次のプログラムコードは、ファイルやフォルダがドライブ上に存在することを前提としているため、ネットワークフォルダから利用したときに破綻します(ネットワークフォルダを論理ドライブにマッピングしていれば、問題は発生しません)。同様のことを行っているのかどうかはわかりませんが、ネットワークフォルダからは実行できないインストーラ(Visual Basic 4.0に付属しているセットアップウィザードで作成したものもそうです)なども、かなりの頻度で見かけます。

サンプルコード

' 「C:\Program Files」から、ドライブを示す「C:」を取得する 
 strCurrentDrive = Left(strCurrentPath)

テスト方法としては、アプリケーションを検査したいフォルダ、すなわち、ロングファイル名を持つフォルダ、空白文字を含むフォルダ、さらに、ネットワークフォルダに配置して実行する、というものがもっとも簡単、かつ、有効です。カレントフォルダの取得など、何らかの処理において、従来の命名規約を前提としている場合、この検証でチェックすることができます。また、ファイルやフォルダを指定するときに、ロングファイル名、空白文字を含む名称、ネットワークフォルダ上のファイルを指定します。

ファイルやフォルダを読み書きする場面で、これらの問題が発生しないか確認するわけですが、これには慎重な検証が必要です。また、概要のところでも説明しましたが、ファイルやフォルダ名の正当性をチェックするルーチンがないかどうかについても、確認するようにしましょう。

回避方法

回避方法は、それほど簡単ではありません。何しろ、ファイルやフォルダを操作するところでは、すべてこの問題が発生する可能性があります。ひとつの指針としては、ロングファイル名、ショートファイル名を適切に変換する、ということがあげられます。これらは、それぞれGetLongPathName()、GetShortPathName()APIで取得できます。もちろん、どちらかに統一(将来のことを考えて、ロングファイル名を選択すべきですが)できるのであれば、それに越したことはありません。

なお、「C:\Program Files」は、「C:\Progra~1」となることがほとんどですが、必ずそうなるわけではありません。この点についても、固定的な値は決して利用せず、適切なWin32 APIを呼び出して取得するようにしなければなりません。

ネットワークフォルダに関しては、サンプルコードで示したカレントドライブを取得するような処理を行わなければ、ほとんど問題は起こりません。もちろん、ユーザーの権限はローカルコンピュータ以上に厳しく制限されることがほとんどですので、適切な処理を行う必要があります。

備考

また、セキュリティの面から言えば、次のようなプログラムは大きな問題となる可能性があります。これは、Shell() 関数が内部的に呼びだしている CreateProcess()API 関数がはらんでいる問題ですが、たとえば、C:\に「Program」というアプリケーションがあったとき、呼びだされるのは「C:\Program Files\Test\Test.exe」ではありません。「C:\Program」というアプリケーションに「Files\Test\Test.exe」という引数が渡される形で、実行されることになります。

サンプルコード

Shell "C:\Program Files\Test\Test.exe"

これを防ぐためには、Shell()関数でアプリケーションを起動するときに、明示的にショートファイル名に変換してから呼びだすか、CreateProcess()関数の呼び出し方を少々変更する必要があります。

ディスクスペースに関する問題

概要

ハードウェアの進化は、オペレーティングシステムを含むソフトウェアの進化より速いものです。それは、ハードディスクの容量についても例外ではありません。旧い記憶を呼び起こしてみると、わたしが最初に Windows 95 に触れたとき、ハードディスクは540メガバイトのものを利用していました。当初はこの程度の容量のものしかなかったため、特に問題が起こることはなかったのですが、ハードディスクの容量はどんどん増えていき、2ギガバイトを超えたところで、問題が発生するようになったのです。

それが、このディスクスペースに関する問題です。ディスク容量を取得するための Win32API、GetDiskFreeSpace() は引数に 32 ビット値をとります。このため、約 2 ギガバイトを超えると、オーバーフローしてしまうことになります。結果的に、ハードディスクの空き容量が十分あるにもかかわらず、空き容量0バイトと判定されてしまい、インストールやその他の作業が行えないという現象が発生します。

テスト方法

Windows 環境では、DiskFreeSpace()API でディスク容量、空き容量を取得します。そのため、まずはプログラムコードでこの処理を行っていないか確認します。実際の動作で確認するときは、十分な容量、空き容量のある環境で試しましょう。取得するだけでは特に問題が発生しませんが、その後に空き容量によって処理を変更するようなときは注意が必要です。

回避方法

回避策としては、DiskFreeSpace()API の代わりに、DiskFreeSpaceEx()API を使います。Visual Basic の場合、以下のようなプログラムコードになるでしょう。Win32 API の使い方を説明するのは、この記事の目的ではありません。そのため、詳細については割愛しますが、Visual Basic の場合、64 ビット数値型の代わりに Currency 型を使っている点に注意するようにしてください。Currency 型では、小数点表現に下位ビットが使われているため、計算の結果によってはオーバーフローする可能性があります。

Visual C++ では、LARGE_INT EGER 型の変数がサポートされているので、それを利用することになります。

サンプルコード - Visual Basic

' ディスクに関する情報を取得する 
 lngResult = _ 
 GetDiskFreeSpaceEx( _ 
 "C:\", _ 
 curFreeBytesAvailable, _ 
 curlpTotal, _ 
 curlpTotalFreeBytes) 
 ' 情報を表示する 
 Debug.Print "使用可能: " & _ 
 Format(curFreeBytesAvailable * 10000, _ 
 "#,##0 MB") 
 Debug.Print "総容量: " & _ 
 Format(curlpTotal * 10000, _ 
 "#,##0 MB") 
 Debug.Print "空き容量: " & _ 
 Format(curlpTotalFreeBytes * 10000, _ 
 "#,##0 MB")

サンプルコード - Visual C++

// 関数ポインタの宣言 typedef BOOL (WINAPI *P_GDFSE)(LPCTSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); P_GDFSE pGetDiskFreeSpaceEx = NULL; LARGE_INTEGER i64FreeBytesToCaller; LARGE_INTEGER i64TotalBytes; LARGE_INTEGER i64FreeBytes; BOOL fResult; char *pszDrive = "C:\\"; // 関数ポインタを取得する pGetDiskFreeSpaceEx = (P_GDFSE)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetDiskFreeSpaceExA"); // 取得できたときは、GetDiskFreeSpaceEx()を呼び出す if(pGetDiskFreeSpaceEx){ fResult = pGetDiskFreeSpaceEx( pszDrive, (PULARGE_INTEGER)&i64FreeBytesToCaller, (PULARGE_INTEGER)&i64TotalBytes, (PULARGE_INTEGER)&i64FreeBytes); }else{ // 取得できなかったときは、GetDiskFreeSpace()を呼び出す DWORD dwSectPerClust; DWORD dwBytesPerSect; DWORD dwFreeClusters; DWORD dwTotalClusters; fResult = GetDiskFreeSpace( pszDrive, &dwSectPerClust, &dwBytesPerSect, &dwFreeClusters, &dwTotalClusters)

また、ファイルシステムオブジェクトのDrive.AvailableSpace、FreeSpace、TotalSizeの各プロパティでも取得することができます。これらのプロパティは、2Gバイトを超える容量も、正常に返します。ファイルシステムオブジェクト自体が、対象のWindowsによって含まれていたり含まれていなかったりするために、少々使いづらいコンポーネントではありますが、配布できる状況であれば、有効に利用できるでしょう。

参考

備考

さらに、Windows XP では、Windows 2000 から搭載されたディスククォータ機能についても注意する必要があります(図6)。この機能は、ユーザーごとに利用できるディスク容量を制限するというものです。これにより、GetDiskFreeSpaceEx() で取得した結果が変化することがあります。実際の空き容量と、この Win32 API を呼び出したユーザーが利用できる空き容量が異なる可能性がありますので、総空き容量を示す lpTotalNumberOfFreeBytes 引数ではなく、呼び出し側が利用できる空き容量を示す lpFreeBytesAvailable 引数の値を利用しなくてはなりません。

**:**この問題は、Windows NT からの移行であっても、GetDiskFreeSpace()API を使っていれば、同様に発生します。

exappmigratoxp6
6: ディスククォータ

最前面ウィンドウの扱い

概要

今まで作業していたウィンドウの代わりに、バックグラウンドに存在する、あるいはアイコン化しているウィンドウを最前面に表示する。これは、ユーザーに注意を促したり、アプリケーションの画面遷移を制御したりするため行われてきた方法です。しかし、この「ウィンドウを最前面に表示する」という操作は、必ずしもユーザーに便利な方法ではありません。

たとえば、メニューの深い階層をたどっているとき、勝手に他のウィンドウがフォアグランドになってしまうと、メニューは自動的に閉じられてしまいます。これにより、それまでのユーザーの操作が無効になってしまうため、Windows 98、Windows 2000から、動作が変更されることになりました。この変更の結果、以下のような条件が適用されます。

  • プロセスがフォアグラウンドプロセスの場合、ウィンドウをフォアグラウンドに表示できます。

  • プロセスがフォアグラウンドプロセスによって開始されたばかりの場合、ウィンドウをフォアグラウンドにできます。

  • プロセス直前の入力を受け付けていた場合は、そのウィンドウをフォアグラウンドにできます。

  • その時点でフォアグラウンドウィンドウがない場合、ウィンドウをフォアグラウンドにできます。

  • フォアグラウンドプロセスのデバッグ中は、任意のウィンドウをフォアグラウンドにできます。

  • フォアグラウンドプロセスでタイムアウトロックが発生した場合 ( フォアグラウンドプロセスがしばらくの間何も行わず、応答していないような場合 ) 、ほかのウィンドウをフォアグラウンドにできます。

  • 何らかのシステムメニューがアクティブの場合、アプリケーションはフォアグラウンドにはできません。

これらの条件を満たさないため、オペレーティングシステムが最前面ウィンドウを変更しないときは、代わりにタスクバーのボタンを点滅させます。ユーザーは、これを見て、アプリケーションに「呼ばれている」ことに気づきます。ここで、そのウィンドウを選ぶかどうかは、ユーザーの気持ち次第です。基本的に、最前面ウィンドウを変更できるのはユーザーのみ、ということになります。

テスト方法

Visual Basic であれば、AppActivate() ステートメントによって、この最前面表示を行います。しかし、この方法は、ほとんどの場合ウィンドウタイトルに頼らざるを得ないため、Win32 API の SetForegroundWindow() が使われるケースが多いことでしょう。これらの処理をコードの中で行っていないか確認するとともに、実際にアプリケーションを動作させて検証することになります。

検証する際は、他のアプリケーションで作業しているときや、システムメニューを操作しているときに、最前面ウィンドウの挙動がどうなるかを確認します。Windows XP の仕様どおりの動きとしては、タスクバーのボタンが点滅し、ユーザーへ通知が行われます。

回避方法

これまで見てきたように、この最前面ウィンドウの問題は Windows の仕様変更に起因しています。Windows の流儀にあわせて、最前面ウィンドウの変更は行わない、というのもひとつの考え方でしょう。もし、そういう選択を行うのなら、プログラムを修正する必要はありません。

それでもアプリケーションの要求として最前面ウィンドウを変更したいのであれば、Windows の流儀に反していることをきちんと理解した上で、修正したプログラムコードを実装しなくてはなりません。その方法ですが、いくつか考えられます。最前面ウィンドウ側のアプリケーションを修正できるのであれば、AllowSetForegroundWindow()API を利用して、他のアプリケーションに最前面ウィンドウ変更の許可を与えることができます。

最前面ウィンドウ側のアプリケーションが修正できない、他社製のアプリケーションであるといったときは、AttachThreadInput()API を利用して、対象のアプリケーションの入力処理機構にアタッチし、その後に SetForegroundWindow() を呼び出すことになります。

サンプルコード - Visual Basic

' 自アプリケーションのスレッドIDを取得する lngTargetThreadID = _ GetWindowThreadProcessId( _ Me.hWnd, _ lngProcessID) ' 最前面アプリケーションのスレッドIDを取得する lngForegroundThreadID = _ GetWindowThreadProcessId( _ GetForegroundWindow(), _ lngProcessID) ' 最前面アプリケーションの入力処理機構に接続する lngResult = _ AttachThreadInput( _ lngTargetThreadID, _ lngForegroundTHreadID, _ 1) ' 最前面ウィンドウを変更する lngResult = _ SetForegroundWindow(Me.hWnd)

サンプルコード - Visual C++

int foregroundID; // 最前面プロセスのスレッドIDを取得する foregroundID = ::GetWindowThreadProcessId( ::GetForegroundWindow(), NULL); // 最前面アプリケーションの入力処理機構に接続する AttachThreadInput( ::GetCurrentThreadId(), foregroundID, TRUE); // 最前面ウィンドウを変更する ::SetForegroundWindow(this->m_hWnd);

参考

備考

: この問題に関しては、Windows NT 4.0 からの移行時にも修正が必要になります。

Win32 API の違い

概要

オペレーティングシステムが提供する機能を使うとき、Win32 APIを呼び出します。Visual Basicでは、これが裏側で行われるため、開発者が直接意識する必要はありません。それでも、Visual Basicが提供していない機能を利用したいときは、Win32 APIを直接呼び出すことになります。多くのWin32 APIは、Windows 9x とWindows NT 、さらに、Windows XP で同じものとなっていますが、一部に違いがあります。

たとえば、フォルダをネットワークで共有するための設定を行う、NetShareAdd() という Win32 API を見てみましょう。この API は、Windows 9x と Windows NT で、存在する DLL、引数の順序が異なります。当然のことながら、これらのAPIを、Windows 9x の方法で呼び出している場合、そのアプリケーションはWindows XP 環境で期待通りに動作しません。

Windows 9x - svrapi.dll

extern API_FUNCTION NetShareAdd( const char FAR * pszServer, // 実行対象のリモートサーバー short sLevel, // 情報レベル const char FAR * pbBuffer, // 情報を保持しているバッファ unsigned short cbBuffer // バッファのサイズ );

Windows NT - Netapi32.dll

NET_API_STATUS NetShareAdd( LPWSTR servername, // 実行対象のリモートサーバー DWORD level, // 情報レベル LPBYTE buf, // 情報を保持しているバッファ LPDWORD parm_err // エラーの発生場所を示すインデックス );

この他、使用できる引数の種類に違いがあったり、Windows 9x でサポートされていないAPIがあったり、Windows 9x では16ビットの制限があったりと、オペレーティングシステム間でかなりの違いがあります。

テスト方法

Win32 API を呼び出しているプログラムコードをすべて洗い出し、すべての API 呼び出しが期待通りに行われていることを検証します。この検証は、かなりの作業になりますので、あらかじめ使用している Win32 API に変更がないか、問題なく使用できるかどうかを確認しておいた方がよいでしょう。

回避方法

回避方法はケースバイケースになりますが、ほとんどの場合は、プログラムコードの修正を行うことになるでしょう。オペレーティングシステムのバージョンチェックを行い、それぞれのオペレーティングシステムにあわせた処理を行うようにします。

なお、APIによっては、システムDLLに存在しない、DLLには存在するが呼び出しても常に失敗する、適切な引数を設定しないと失敗するなど、いくつかのパターンがあります。仕様については、MSDNライブラリ、Platform SDK DocumeNT などで確認し、実装について(DLLに存在するかどうかなど)は、Dependency Walkerなどで確認します。

参考

備考

: Win32 API の一覧は、Visual Studio 6.0 をインストールしたフォルダの、\VC98\Lib\WIN32API.CSV(図7)にあります

exappmigratoxp7
7: Win32API.csv

配布と WFP に関する問題

概要

アプリケーションの移行が完了したからといって、安心することはできません。実際にユーザーの環境にインストールする、すなわち、配布に関する問題が残されています。Visual Basic には、伝統的にセットアップウィザードという簡易インストーラ作成ツールが用意されていました(Visual Basic 6.0 では、ディストリビューションウィザード)。このツールを使って、配布パッケージを作成するわけですが、簡易ツールというだけあって、細かいところで問題が発生することがあります。

問題のひとつは、Windows XP 環境においてユーザー名に 2 バイト文字を利用していると、正常にインストールが行えない、というものです。これは、ディストリビューションウィザードの内部処理で、2 バイト処理を正しく行っていないことに起因するもので、いったんユーザーを作成してしまうと、その名前を利用したフォルダが生成されてしまうため、その後のユーザー名変更では対応できません。

また、Windows 98SE、Windows 2000 以降で共通して発生する問題ですが、配布パッケージに含まれている Windows のシステムファイルを置き換えられないため、繰り返し再起動が行われる、という問題があります。Windows 98SE、Windows 2000 以降では、重要なシステムファイルが置き換えられないようになっています。いわゆる、Windows File Protection(WFP)という機能で、これにより、システムファイルの不整合による問題がおきないようになっているためです。

テスト方法

配布先の環境と(できる限り)同一な環境を用意して、その環境にインストールが正常にできることを確認します。このとき、Administrator権限を持たないユーザーで検証したり、名前に 2 バイト文字を使っているユーザーで検証したりする必要があります。

ひと通りの検証が終わったら、アプリケーションを実行して、問題が発生しないかどうかについても、確認しておきましょう。

回避方法

まずは、2 バイトユーザー名の問題について見ていきましょう。回避方法のひとつとして、Visual Studio Installerを使う(図8)というものがあげられます。Visual Studio Installerは、Windows Installerに対応した配布パッケージを作成するためのツールで、Visual Studio 6.0のユーザーであれば、ダウンロードして利用することができます。

exappmigratoxp8
8: Visual Studio Installer

もしくは、ディストリビューションウィザードのソースコードを修正して、2バイト文字の処理が正しく行われるようにします。さらに、一時的な回避策としては、ユーザー名を1バイト文字のみに変更して(新しいユーザーを作成して)、インストールを行うようにします。

もうひとつの問題、WFPに関する問題については、Visual Studio Installer を使ったからといって、回避できるわけではありません。システムファイルを最新にしなくてはならないわけですが、配布パッケージでは更新を行うことができません。そのため、オペレーティングシステムのサービスパックやホットフィックス、WindowsUpdate などを利用して、あらかじめシステムファイルを最新の状態にしておく必要があります。

なお、すべてがこのWFPに起因するというわけではなく、配布パッケージの設定ファイルを修正することで回避できる問題もあります。サポート技術情報には、多くの問題点が報告されています。ディストリビューションウィザード、Visual Studio Installerの個々の問題を確認して、適切な回避策を取るようにしましょう。

参考

: この問題は、Visual C++ に固有な問題ですが、対応するためには修正プログラムをインストールする必要があります。

備考

この問題に関しては、Windows NT からの移行時にも修正が必要になります。

Windows NT 環境から Windows XP 環境へのアプリケーション移行

Windows NT 4.0、Windows 2000 からの移行は、Windows 9x からの以降に比べると問題が発生する可能性は低くなります。事実、Windows 2000で動作しているアプリケーションのほとんどが、修正をすることなく、Windows XP で動作します。これは、Windows XP がWindows NT から進化してきたオペレーティングシステムであるからです。

とはいっても、問題がまったく発生しないわけではありません。Windows XP で行われた変更、追加された新機能に伴い、少ないながらもいくつかの問題が発生することがわかっています。ここからは、Windows NT からの移行時に発生する問題、また、Windows 9x からの移行では説明できなかった問題について紹介していきます。

数値の「丸め」処理に関する問題

概要

Visual Basicで作成したアプリケーションは、ランタイムやさまざまなシステムDLLに依存しています。このことが、問題を引き起こす場合もあります。最近になって表面化した「丸め」処理に関する問題は、システムDLLの仕様変更が原因となっています。

数値や日付を文字列に変換したり、指定の書式に変換したりするための関数、Format()は、桁数を指定して四捨五入を行うときにも利用されてきました。たとえば、次のようなプログラムコードです。

MsgBox "0.25 → 小数点一桁 : " & Format(0.25, "0.0") & _ 
         vbNewLine & _ 
         "0.35 → 小数点一桁 : " & Format(0.35, "0.0")

このプログラムコードを実行すると、前者の変換は「0.2」となり、後者の変換は「0.4」となります(図9)。四捨五入であれば、それぞれ「0.3」「0.4」となるはずですが、そうはなりません。これは、指定した桁の値が「5」のとき、一つ前の桁が偶数のときは「切捨て」、奇数のときは「切り上げ」を行うという、いわゆる「銀行式丸め」という処理が行われるからです。

exappmigratoxp9
9: Windows XP での丸め処理

Visual Basic のヘルプを参照してみると、Format() 関数は「指定した桁を四捨五入する」とあります。確かに、Windows XP 以外のWindows では、この仕様どおりの動作となります。しかし、Windows XP において、システム DLL で行われる変換がこのような仕様に変更されたため、その DLL が提供している関数を内部的に呼び出している Format() 関数で、このような違いが発生してしまうのです。

テスト方法

この現象は、Format()で四捨五入を行っている処理で発生します。また、Round()関数でも同様の結果となるようです。このため、これらの処理を行っているプログラムコードを見つけ、すべて修正する必要があります。

指定の桁の値が「5」で、かつ、その前の桁が偶数のときのみ、この現象が発生します。そのため、実際のテストで問題を発見するには、通常のテストに加えて「0.15」や「0.25」などの、問題を内在する数値で検証する必要があります。

回避方法

回避方法としては、Format() 関数やRound() 関数が使えないので、独自のプログラムコードを利用して、四捨五入を行う必要があります。たとえば、次のようなプログラムコードになります。

Fix(1.5 + 0.5)

ただし、このプログラムコードでは、整数へ四捨五入する場合、また、マイナス値を考慮していないときのみしか対応できません。指定した桁数で四捨五入するためには、Excelのワークシート関数「Round()」を利用したり、次のようなプログラムコードを実装したりする必要があるでしょう。

サンプルコード

Public Function JapaneseRound( _ 
 value As Double, _ 
 decimals As Integer _ 
 ) As Double 
 Dim t1 As Integer 
 Dim t2 As Double 
 t1 = 10 ^ decimals 
 t2 = (value * t1 + 0.5) 
 JapaneseRound = Fix(t2) / t1 
 End Functio

呼び出し例

MsgBox "0.25 → 小数点一桁 : " & JapaneseRound(0.25, 1) & _ 
 vbNewLine & _ 
 "0.35 → 小数点一桁 : " & JapaneseRound(0.35, 1)

exappmigratoxp10
10: 修正を加えた四捨五入処理

参考

備考

: この問題に関しては、Windows 9x からの移行時にも修正が必要になります。また、NET Framework の Math.Round() メソッドでも、同様の問題が発生しますが、この問題は Windows XP のサービスパックで修正されるようです。

データアクセスコンポーネント

概要

このデータアクセスコンポーネントに関する問題は、特に Windows NT からの移行のときに問題となるわけではありません。状況によっては、Windows 9x からの移行でも発生しますし、Windows XP 環境で開発されたアプリケーションであっても、同様の現象が発生する場合があります。

まず、データアクセスコンポーネントについてまとめてみましょう。Visual Basic でのデータアクセスには、おもに DAO、RDO、ADOが使われています。この他、たとえば、Oracle に接続する場合、oo4o などのコンポーネントが必要になりますが、今回の記事の範囲外ですので、紹介はしません。Oracle の提供するドキュメントなどを参照してください。

テスト方法

まず、アプリケーションがどのような手段でデータベースにアクセスしているのかを確認します。続いて、アプリケーションが必要とするデータベースアクセスコンポーネントが配布パッケージに含まれていること、さらに、インストールが正常に終了していることを確認します。このうち、ADO のコンポーネントに関しては、コンポーネントチェッカーを利用することで、正しいバージョンがインストールされているかどうかを確認できます。

icodownl コンポーネント チェッカー

exappmigratoxp11
11: コンポーネントチェッカー

これらに問題がないようであれば、アプリケーションをインストールした後で、データベースにアクセスします。一般的な読み込みと書き込み、さらには、データベースのテーブルスキーマなどの作成や修正、ひと通りの作業を行って、問題が発生しないかを確認します。

回避方法

回避策としては、アプリケーションが必要とするデータアクセスコンポーネントを正しくインストールする、これ以外にはありません。各データアクセスコンポーネントは、バージョンが複数ある場合もあるので、注意する必要があります。基本的には、次のようなデータアクセスコンポーネントをインストールします。

  • DAO - DAO(2.51/3.51/3.6などのバージョンがあるので注意)

    DAO を利用して ODBC 接続する場合、ODBC コンポーネントも必要になる

  • RDO - インストールの前に、あらかじめ、ODBC コンポーネントをインストールしておく必要がある(Windows XPの場合は、すでにインストール済み)。また、データベースにあわせた ODBC ドライバが必要になる

  • ADO - Mdac_typ.exe によって配布する。Mdac_typ.exe で配布される基本コンポーネントに加え、データベースによってはOLEDBプロバイダが必要になることもある。たとえば、Jet 3.51 用の OLEDB プロバイダは、MDAC 2.0 にしか含まれていないので、最新のMdac_typ.exe を配布する際は、Jetコンポーネントを配布する必要がある。なお、Windows 9x 環境に Mdac_typ.exe をインストールする際は、Dcom9x をあらかじめインストールしておかなくてはならない

参考

備考

: この問題に関しては、Windows 9x からの移行時にも修正が必要になります。

ユーザーの簡易切り替えにおける、アリケーション重複起動の抑止

概要

Windows XP の新機能の一つに、ユーザーの簡易切り替え機能があります(図12)。この機能により、コンピュータを現在利用しているユーザーがログオフしなくても、別のユーザーがログオンできるようになりました。各ユーザーのネットワーク接続、個人データ、および実行中のアプリケーションは、ユーザーが再度ログオンするまで保持されます。これによって、ユーザーの変更がやりやすくなるため、一台のコンピュータを複数で利用している環境などでよく使われることでしょう。

exappmigratoxp12
12: ユーザーの簡易切り替え

これは便利な機能ではありますが、問題を引き起こすこともあります。たとえば、アプリケーションの重複起動を抑制しているときなどです。Visual Basicには、同一のアプリケーションが動作していないかどうかを示す、App.PrevInstance プロパティがあります。ひとりのユーザーのセッション上では、このプロパティは期待通りに動作します。しかし、「ユーザーの簡易切り替え」が利用されているとき、すなわち、別のユーザーのセッションが対象になると、アプリケーションがすでに動作していることを検出できません。

これは、FindWindow() や Mutex を用いる、Win32 API を利用した方法でも同様の結果となります。このため、Win32 API を利用しているから大丈夫、と早合点してはいけません。また、この機能はターミナルサービステクノロジーがベースとなっているため、同じテクノロジーを利用しているリモートデスクトップ機能などでも、同様の問題が発生します。なお、ドメイン環境では、ユーザーの簡易切り替え機能が利用できないため、問題が発生することはありません。

テスト方法

このテストはそれほど難しくありません。何の対応もとられていなければ、ほぼ間違いなく重複起動を抑止することはできません。ユーザーAでログオンし、アプリケーションを起動します。続いて、アプリケーションを終了しないまま、ユーザーBに切り替えて、同じアプリケーションが起動できるか確認するだけです。

回避方法

回避策を考える前に、本当に重複起動できてしまってはいけないのかどうかを検討しなくてはなりません。データベースやファイル、インターネットへのコネクションなど、コンピュータ上で、ひとつしか利用できないリソースというのは、それほど多くありません。もし、リソースを複数利用しても問題がないのならば、重複起動の抑止という考え方を改めた方がよいのかもしれません。

この点を十分に検証した上で、それでも重複起動の抑止が必要なのであれば、次のように、CreateMutex() でグローバルなミューテックスを作成するなプログラムコードを利用します。これにより、異なるユーザー間でも共用されるミューテックスが作成され、結果的に重複起動が抑止できるようになります。しかし、グローバルなミューテックスは、Windows 9x 環境ではエラーとなるため、注意が必要です。また、このサンプルコードでは、正常に終了したときのミューテックス解放処理は割愛しています。

サンプルコード - Visual Basic

Dim IsRunning As Long 
 Dim sa As SECURITY_ATTRIBUTES 
 ' ミューテックスの名称を指定 
 Dim strMutexName As String 
 strMutexName = "Global\ApplicationName" 
 ' ミューテックスを作成 
 IsRunning = _ 
 CreateMutex(sa, _ 
 0, _ 
 strMutexName) 
 MsgBox IsRunning 
 MsgBox Err.LastDllError 
 ' 作成できなかったときは、LastDllErrorで理由を確認 
 If IsRunning = 0 Then 
 If Err.LastDllError = ERROR_ALREADY_EXISTS Then 
 CloseHandle IsRunning 
 MsgBox "実行中" 
 End If 
 End If

サンプルコード - Visual C++

HANDLE IsRunning = NULL; 
 // ミューテックスの名称を指定 
 TCHAR szMutexName[] = TEXT("Global\\ApplicationName"); 
 // ミューテックスを作成 
 IsRunning = CreateMutex(NULL, 
 FALSE, 
 szMutexName); 
 // 作成できなかったときは、GetLastError()で理由を確認 
 if(IsRunning != NULL && GetLastError() == ERROR_ALREADY_EXISTS){ 
 // 実行中 
 CloseHandle(IsRunning); 
 AfxMessageBox("実行中"); 
 }

参考

備考

: この問題に関しては、Windows 9x からの移行時にも修正が必要になります。

XP ロゴ要件に見る、XP 用アプリケーション開発のポイント

この章では、問題点というよりも、サポートしておくと将来有効である項目などを紹介します。すなわち、これらをサポートしなくても、Windows XP 上での動作に直接影響を及ぼすということではありません。しかし、「Windows XP アプリケーションロゴ」を取得するためには必要な機能であり、サポートすることによってユーザーの利便性をさらに向上することができるでしょう。

バージョンチェック

概要

ここで言う「バージョン」は、Windows のバージョンのことです。これは、アプリケーションがオペレーティングシステムに対する互換性を保持しているのであれば、特に問題となるわけではありません。しかし、ときには問題となることもあります。たとえば、Windows 2000 以降の環境でインストール、または、実行したいアプリケーションが、バージョンを「=(イコール)」で検査しているためにインストール、または、実行ができないという問題と、その逆のケースです。

バージョンチェックには、Visual Basic の SysInfo コントロールを利用するほか、Win32 APIのGetVersion()などが用いられます。これらのプロパティ、関数は「バージョンがいくつ」という結果を返すもので、「バージョンいくつ以降」という判定を行うわけではありません。そのため、プログラムコード内で、適切に比較する必要があります。

実装方法

インストール、または、実行できるオペレーティングシステムを限定するならば、バージョンが比較する値と等しいかをチェックするようにします。たとえば、動作確認済みのオペレーティングシステムでのみ実行することができるようにしたいときは、実行前にGetVersionEx()などで取得した値を検査をします。

逆に、「バージョンいくつ以降」で実行できるようにしたいときは、取得した値が同じ、または、大きいかを検査します。このような目的には、Win32 API の VerifyVersionInfo() が適しています。このAPIを利用すると、バージョンやサービスパックなどが条件を満たしているかどうか、という検査を行うことができます。たとえば、以下のようなプログラムコードになります。この例は、Windows XP 以降かどうかを確認します。

サンプルコード - Visual Basic

Dim udtOsInfoEx As OSVERSIONINFOEX 
 Dim curMask As Currency 
 Dim lngResult As Long 
 ' オペレーティングシステムを指定する 
 With udtOsInfoEx 
 .dwOSVersionInfoSize = Len(udtOsInfoEx) 
 .dwMajorVersion = 5 
 .dwMinorVersion = 1 
 End With 
 ' 条件のマスクを生成する 
 curMask = _ 
 VerSetConditionMask( _ 
 curMask, _ 
 VER_MAJORVERSION, _ 
 VER_GREATER_EQUAL) 
 curMask = _ 
 VerSetConditionMask( _ 
 curMask, _ 
 VER_MINORVERSION, _ 
 VER_GREATER_EQUAL) 
 ' オペレーティングシステムを判定する 
 lngResult = _ 
 VerifyVersionInfo( _ 
 udtOsInfoEx, _ 
 VER_MAJORVERSION Or _ 
 VER_MINORVERSION, _ 
 curMask) 
 If lngResult <> 0 Then 
 Debug.Print "条件を満たしています" 
 End If

サンプルコード - Visual C++

OSVERSIONINFOEX osvi; 
 ULONGLONG dwlConditionMask; 
 BOOL fResult; 
 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); 
 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); 
 osvi.dwMajorVersion = 5; 
 osvi.dwMinorVersion = 0; 
 // 条件のマスクを生成する 
 VER_SET_CONDITION(dwlConditionMask, 
 VER_MAJORVERSION, 
 VER_GREATER_EQUAL); 
 VER_SET_CONDITION(dwlConditionMask, 
 VER_MINORVERSION, 
 VER_GREATER_EQUAL); 
 // オペレーティングシステムを判定する 
 fResult = VerifyVersionInfo( 
 &osvi, 
 VER_MAJORVERSION | VER_MINORVERSION, 
 dwlConditionMask); 
 // 結果を表示する 
 if(fResult){ 
 AfxMessageBox("条件を満たしています。"); 
 }else{ 
 AfxMessageBox("条件を満たしていません。"); 
 }

参考

備考

: VerifyVersionInfo()API に対応しているのは、現時点で Windows 2000、XP のみです。そのため、他のオペレーティングシステムでも利用するときは、GetVersionEx() などで検査しなくてはなりません。また、この問題に関しては、Windows 9x からの移行時にも修正が必要になります。

Side-By-Side

概要

Side-By-Side は、Windows 98SE から用意された機能であり、いわゆる「DLL Hell」を克服することを目的としています。特に、Visual Basic は、その仕組みからランタイムライブラリとシステム DLL を必要とし、それらのバージョン不整合によって、簡単に動かなくなってしまうということがありました。これを回避するために、今までいろいろな方法が考えられてきましたが、結局のところ、Side-By-Side を使う以外の回避策は無いようです。Side-By-Side の具体的な内容と機能の詳細は、各種技術文書を参照していただくとして、ここでは、その実装方法を紹介していきます。

実装方法

実装方法といっても、アプリケーション開発者は特にプログラムコードに修正を加える必要がありません。このことは、Side-By-Side の大きなメリットのひとつです。これにより、すでに開発済みのアプリケーションやコンポーネントに手を加えることなく、Side-By-Side のメリットを享受することができます。

アプリケーション開発者がすることは、次の 2 点です。

  • アプリケーション名 .exe.local ファイルを、アプリケーションと同じフォルダにインストールする

  • アプリケーションが必要とするコンポーネントをアプリケーションフォルダにインストールし、適切にレジストリの登録を行う

これだけで、必要な DLL は、すべてアプリケーションフォルダに存在しているものが利用されるようになります。すなわち、今まで発生していた DLL のバージョン不整合問題は解消されます。もちろん、インストールした DLL との問題に関しては、開発者が責任を持たなくてはなりません。また、今まで DLL がシステム上ひとつしか存在しなかったのに対し、各アプリケーションフォルダに DLL をインストールすることで、ハードディスク領域を余分に消費することになります。

レジストリに登録する際は、従来のように絶対パスを登録するのではなく、相対パスで登録しなくてはなりません。これは、従来のインストーラ作成ツールでは対応していないものもありますので、その部分に関して手動で修正するか、Windows Installer 1.1 などのSide-By-Side コンポーネントインストールに対応しているツールを使う必要があります。

参考

備考

Windows XP では、Manifest ファイルを使って Side-By-Side コンポーネントの指定を行うことも可能です。

新しい表示スタイルのサポート

概要

新しい表示スタイル「Visual Style」は、Windows XP の特徴的な機能のひとつです。「Luna」と呼ばれる新しいユーザーインターフェイスは、従来のWindowsのものとは大きく異なっているため、賛否両論ではありますが、慣れるとなかなかよいものです。せっかく新しい機能を持っているのに、見た目が古めかしいのは損ではないでしょうか。アプリケーションは機能が重要なのは当然ですが、見た目も大事です。

実装方法

新しい表示スタイルを利用するには、いくつか方法があり、今回はそのうちのひとつである、マニフェストファイルを使った方法を紹介します。まず、次のような内容のファイルを作成し、「アプリケーション名.exe.manifest」という名前にします。このファイルをアプリケーションと同じフォルダにインストールするだけで、アプリケーションの見た目が Windows XP の表示スタイルを持ったものに変わります。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
  <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> 
  <assemblyIdentity 
      version="1.0.0.0" 
      processorArchitecture="X86" 
      name="CompanyName.ProductName.YourApplication" 
      type="win32" 
  /> 
  <description>Your application description here. 
  <dependency> 
      <dependentAssembly> 
          <assemblyIdentity 
              type="win32" 
              name="Microsoft.Windows.Common-Controls" 
              version="6.0.0.0" 
              processorArchitecture="X86" 
              publicKeyToken="6595b64144ccf1df" 
              language="*" 
          /> 
     </dependentAssembly> 
  </dependency> 
  </assembly>

なお、当然のことながら、これらの変化が適用されるのは、Windowsが描画しているコントロールのみです。描画をアプリケーションが行っている、いわゆる「オーナードロー」コントロールでは、従来どおりのスタイルとなります。

exappmigratoxp13
13: マニフェスト使用前

exappmigratoxp14
14: マニフェスト使用後

参考

備考

Visual Basic 6.0で新しい表示スタイルを適用すると、フレームなど、一部のコントロール上に配置したコントロールが正常に描画されないという問題が発生します。これは、フレームの代わりにピクチャーボックスなどを使うことで回避できます。

開発環境の移行

この章では、開発環境を移行するときの注意点を紹介します。開発環境が、果たしてWindows XP で問題なく動作するかどうか、というのは簡単な問題ではありません。そもそも、マイクロソフト自体が明示的に動作を保障しているわけではありません。

また、今回紹介する開発ツールのうち、いくつかのものはすでにマイクロソフトによる公式サポートが終了しています。このため、何か問題があったとしても、その回避策を見つけ出すのは、開発者自身の手にゆだねられていることになります。

今回行った動作確認については、まず「問題なくインストールできること」、そして「簡単なアプリケーションが作成できること」の2点にとどめました。これには、以下のような理由があります。第1に、複雑なアプリケーションを準備、また、テストするのは容易ではありません。第2に、アプリケーションで行っている処理は、それこそ千差万別です。そのため、すべてを網羅するのは、現実的に不可能です。さらに、サードパーティ製コンポーネントを利用していたり、開発者自身で作成したコンポーネントを作成していることもあります。このような状況を考えると、あまり複雑なアプリケーションで検証したとしても、それほど得るものは多くないと思います。

検証のターゲットとしては、Windows XP Home Edition 、Windows XP Professional Edition とVisual Basic 4.0~6.0(それぞれ、ENT erprise Edition )の組み合わせで行いました。Windows XP Home Edition は、そもそもWindows 9x の後継であり、あまり開発には使われないものかもしれません。しかし、ニュースグループなどのコミュニティで、「Windows XP Home Edition 上にVisual Studioがインストールできない」といった投稿があったため、その真偽を確かめる意味でも検証することにしました。

また、開発ツールの混在によって別の問題が引き起こされるのを避けるため、各開発ツールをインストールする前に、オペレーティングシステムを初期化状態に戻しています。

なお、この検証結果については正確であることを心がけましたが、著者やグレープシティ株式会社、株式会社翔泳社は検証結果について、また、その結果から発生するすべてのことがらにおいて、何の責任も負わないものとします。必要に応じて、環境に合わせた形で再度検証を行って頂くことをお勧めいたします。

Windows XP + Visual Basic 4.0(16 ビット )

インストール

Visual Basic 4.0(16ビット)は、Windows 3.1での利用も考えられている、かなり旧い開発ツールです。そのため、正常にインストールできるかどうか不安でしたが、特にエラーなどもなく、インストールが完了しました。ただし、どのドライブにWindowsがインストールしてあっても、デフォルトで「C:\VB」にインストールしようとしてしまうようです。また、インストール後に再起動が行われるのですが、正常に再起動することができず、ログオフしただけでした。

アプリケーションの作成

簡単なアプリケーションを作成したり、付属のサンプルプロジェクトをいくつか動作させてみた限りでは、特に問題は確認できませんでした。なお、Windows XP の新しいVisual Styleには対応していません。これは、32 ビット版 Windows のコンポーネントを利用しているのではなく、16 ビット版のコンポーネントが使われているためだと思われます。

Windows XP + Visual Basic 4.0(32 ビット )

インストール

Visual Basic 4.0(16ビット)のときと異なり、正常に Program Files フォルダにインストールされます。そのほか、インストールに関する問題は特にありませんでした。

アプリケーションの作成

アプリケーションの作成も特に問題ありませんでしたが、ツリービューのフォント表示が少々おかしくなりました(図15)。ツリービューのサイズを小さくすると、デフォルトのフォントにしているのにも関わらず、大きなフォントで表示されます。

exappmigratoxp15
15: ツリービューのフォント

Windows XP + Visual Basic 5.0

インストール

インストール、再起動ともに、問題なく終了しました。

アプリケーションの作成

アプリケーションの作成も特に問題ありません。なお、Visual Basic 4.0とは異なり、Windows XP の新しいVisual Styleに対応しています。

Windows XP + Visual C++ 5.0

インストール

インストール中に、一部のファイルが読み込めない、という現象が発生しましたが、これは、おそらくCD-ROM、もしくは、CD-ROMドライブ側の問題だと思われます。5年も前のメディアで、かつ、保存状態がよくないと、こういうことも発生するのでしょう。その点を除けば、インストールはスムーズに終わりました。

アプリケーションの作成

アプリケーションの作成も特に問題はありませんでした。

Windows XP + Visual Basic 6.0

インストール

Home Edition において「正常にインストールできない」という報告があったため、慎重に検証を進めたのですが、特に問題なくインストールできました。なお、Windows XP には、デフォルトでJava VMが含まれていないため、Visual Studio をインストールする際にあわせてインストールする必要があります。

アプリケーションの作成

アプリケーションの作成についても、特に問題はありませんでした。環境に依存する部分もありますが、サポート技術情報には、以下のような問題が報告されています。

Windows XP + Visual C++ 6.0

インストール

Visual Basic と同様、インストールは問題なく進みました。

アプリケーションの作成

Visual C++ のインストール後、アプリケーションを作成して検証してみました。アプリケーションのビルド時、実行時にフリーズするという現象がありましたが、再現性は無かったため、Windows XP と Visual C++ 6.0 の組み合わせによるものとは判断できません。なお、Visual C++ 6.0 をインストールしただけでは、Windows XP の機能をすべて利用することはできません。ヘッダファイルやライブラリが旧いためです。最新の機能(Win32 API)を使うためには、Platform SDK をインストールする必要があります。

サポート技術情報には見当たりませんでしたが、英語版である Knowledge Base には、Windows XP 上で発生するいくつかの障害が報告されています。

おわりに

この記事では、開発したアプリケーション、そして、アプリケーションを開発するための開発環境を、Windows XP に移行する際に発生する問題点について見てきました。もちろん、問題はこの記事で紹介したものだけではありません。サードパーティ製のツール、コンポーネントを利用していれば、それらについても検証する必要があります。

また、Office 製品や Internet Explorer など、他のアプリケーションとの関係、ネットワークやハードウェアなど、直接アプリケーションに関係のない部分での問題など、検証しなくてはならない点は、数多くあります。これらは、アプリケーションを動作させる環境によって異なり、それこそ星の数ほどの組み合わせが考えられます。この点を踏まえると、簡単な方法で移行を行うことはできない、という結論に至ります。

必要なのは、配布先と同一の環境で十分に検証をすること、それに加えて、事前の情報収集と調査です。また、あらかじめオペレーティングシステムや開発ツールのサービスパックを適用し、予想できる問題を排除しておくことも必要でしょう。検証については、「配布先と同一の環境」で行うことが必須です。複数の環境を用意するのは、なかなか大変ですが、他のオペレーティングシステムを動作させるアプリケーションを使ったり、ハードディスクを入れ替えたりすることで、検証の手間を減らすことができます。

事前の情報収集と調査には、マイクロソフトの提供するサポート技術情報やホワイトペーパーが役に立ちます。また、メーリングリストやニュースグループでは、これらの問題とその解決策などについて、活発な議論が行われています。これらの情報を定期的に収集することで、発生する問題に備えることができるでしょう。できる限りの準備を整えてから、アプリケーションの移行に立ち向かってください。この記事が、その際に何らかの助けとなれば幸いです。

ダウンロード

Cc835597.icon_Word(ja-jp,TechNet.10).gifWindows XP 環境への既存アプリケーションの移行
745 KB
Microsoft Word file