Windows の謎を調べた

少し前に調べていた個人メモを書き出す。色んなサイトで知った情報なので引用多め。

Windowsのファイルシステムの区切り文字が「¥」なのは

実は、Windowsの区切り文字が「¥」というのは、日本語版のWindowsだけのことです。システムロケールが「日本語(ja-jp)」またはコードページが「日本語シフトJIS」で、表示フォントが「日本語フォント(MSゴシックなど)」の場合と言ってもいいかもしれません。

Windows標準の区切り文字は「¥」ではなく、半角の「\」(逆斜線、バックスラッシュ、backslash、reverse solidus)です

どちらもASCII文字コード「5C(0x5C)」ですが、日本語シフトJISの文字コード表ではその場所に「¥」が割り当てられたため、日本語版Windowsでは区切り文字が「¥」と表示されることになってしまいました。

日本語版Windowsを使っている上では違和感はありませんが、韓国語版だと区切り文字はウォン記号(₩)で表示されます。日本語版や英語版に慣れている人にとっては、ウォン記号の区切り文字の前後に「W」(Windowsなど)があると、とても違和感があります。きっと韓国の方は見慣れていることでしょう

Windowsが登場する前のDOS(Disk Operating System、PC-DOSやMS-DOS)が開発された当時、企業への導入が始まったころのコンピュータ(ミニコン、オフコン)の主流OSはベンダーの独自OSがほとんどで、その後、UNIXへとオープン化が進みました。UNIXのファイルシステムの区切り文字は「/」(斜線、スラッシュ、slash、solidus)であり、その後、インターネット上にあるリソースの場所を示すURI(Uniform Resource Identifier)やLinuxにも引き継がれました

UNIXに対する皮肉を込めたオマージュなのでしょうか、あるいは反骨精神なのでしょうか。それをあえて逆にした、これがWindowsの区切り文字がバックスラッシュになったゆえんです……というのは全くの冗談です。

DOSをIBMが開発した当初(後にMicrosoftも参加)、コマンドのスイッチに「/」を使用するという方針があったため、区切り文字に「/」を使う余地がなかったという説が有力のようです。

ちなみに、UNIXにおけるコマンドのスイッチは「-」(ハイフン)で、バックスラッシュはUNIXシェルや正規表現環境で、エスケープしなければならない特殊な文字の一つでした。最近では、Windowsのコマンドでも「/」と「-」の両方を使えるものが多く、Windows PowerShellのコマンドレットでは「-」を採用しています。

しかし、コマンドプロンプト(cmd.exe)に組み込まれているコマンド(MKDIR、RMDIR、COPY、DIRなど)は、今でもスイッチとして「/」のみを受け付けます。コマンドプロンプトで「DIR /?」と「DIR -?」を実行してその違いを確認してみてください。

Windowsのテキスト改行コードが「CR+LF」なのは

Windowsで作成するテキストファイルの標準の改行コードは「キャリッジリターン(CR)+ラインフィード(LF)」(\r\n、0x0D0A)です。一方、Windows以外のOSでは「LF」(\n、0x0A)が主流です。

DOS(PC-DOS/MS-DOS)は、8bit PC用OSの代表格であったCP/M(Control Program for Microcomputer)をモデルに開発されたと言われています。「8+3文字」というファイル名の命名規則、「.COM」という実行ファイルの拡張子、ドライブレター、デバイス名、コマンドプロンプト(>)と組み込みコマンド(DIRなど)は、全てCP/Mに由来するものです。そして、CP/Mが採用していた改行コードがCR+LFであり、それがDOS、そしてWindowsへと引き継がれてきたということです。この説は説得力がありますし、きっと正解なのでしょう。 こんな新説はいかがでしょう。さまざまなOSの混在環境において、後発のDOSやWindowsが異種OSに忖度(そんたく)した説です。メインフレームからのダウンサイジングが進み、企業にコンピュータ(ミニコンやオフコン、PCサーバなど)やパーソナルコンピュータが導入され始めたころ、DOS/Windowsは決して主流のサーバOSやクライアントOSではありませんでした。周りを見渡せばUNIX(SVR4やBSD)、Apple Mac OS(Mac OS 9以前のクラシックMacintosh、日本では「漢字Talk」と呼ばれていた)、IBM OS/2などさまざまなOSがありました。

UNIXの改行コードはLF、当時のMac OSの改行コードはCR、Windowsと同期と言えなくもないOS/2の改行コードはCR+LFです。DOSやWindowsで作成したテキストファイルの改行コードはCR+LFなので、LFが改行コードのUNIX環境でも、CRが改行コードのMac OS環境でも、取りあえず改行されます

DOS/Windowsが仲介役に徹することで、異種混在環境の課題の一つを解消できたわけです(アプリケーションによっては余計な文字(^M)が表示されることがありましたが)。この新説は結果からのこじつけなので、ちょっと無理がありますね。

hostsファイルがあんな深くにあるのは、UNIXソケットをまねたから?

UNIX/Linuxの「/etc」ディレクトリにある「hosts」「networks」「protocols」「services」は、いずれもTCP/IPネットワークの主要な定義ファイルであり、TCP/IPの教科書的な書籍には必ず登場します。「networks」はネットワーク名の定義(最近は使用しない)、「protocols」はIP層のプロトコルとプロトコル番号の対応(ICMP、TCP、UDPなど)、「services」はアプリケーションごとのTCP/UDPポート番号の対応(FTP、HTTPなど)を定義するものです。

Windowsは当初、TCP/IPネットワーク用のソフトウェアを標準搭載していませんでした。TCP/IPスタックを標準搭載するようになったのはWindows 95からです(日本では発売されなかったWindows for Workgroups 3.11が最初という説もあります)。

TCP/IPはもともと、ソケット(BSD Socket)APIとしてUNIXに実装された、UNIX標準のネットワーク機能でした。DOSやWindows 3.1でTCP/IPを使ってUNIX(FTPサーバやTelnetサーバなど)と通信するには、通常、OSとは別にサードベンダーからTCP/IPスタックとアプリケーションを購入する必要がありました。

筆者がよく扱っていたのは「Novell LAN WorkPlace」や「NETMANAGE Chameleon TCP/IP」だったと思いますが、各ベンダーがTCP/IPの機能を独自にWindows向けに実装したため、定義ファイルの場所がそれぞれ違っていました。

Microsoftも当時、NetBEUI、NetBIOS、TCP/IPに対応した「LAN Manager」を販売していましたが、LAN Managerのhostsファイルは確か「ドライブ:\LANMAN.DOS\ETC\HOSTS」のような場所にあったと記憶しています。

MicrosoftはUNIXのソケットAPIを「WinSock(Windows Sockets)API」としてWindows向けに実装し、Windows 95に標準搭載しました。このとき、hostsファイルなどのTCP/IP定義ファイルはそのまま採用されたのですが(ただし、「protocols」についてはファイル名「protocol」に)、ファイルシステムが異なるため、当然のことながら配置場所をどうするのか問題になったのでしょう。

ネットワーク機能はドライバが大きく関係するので、「drivers」ディレクトリからの相対パスとして「\drivers\etc\hosts」に決まったのではと簡単に想像してしまいそうです。

しかし、Windows 95からのWindows 9x系のWindowsは、「%Windir%\hosts」に配置していました。つまり、WinSock APIが初めて実装されたときからの場所ではなかったのです。現在の「%SystemRoot%\System32\drivers\etc\hosts」の場所になったのは、TCP/IPを標準搭載したWindows NTからのNT系Windowsからだと思います(Windows NT/2000の「%Windir%」および「%SystemRoot%」は「ドライブ名:\WINNT」、Windows XP/Server 2003以降は「ドライブ名:\Windows」)。

Windows 9x系のWindowsでは、%Windir%ディレクトリにシステムファイルだけでなく、定義ファイル(INIファイル)なども配置していました。8bit/16bitドライバは「%Windir%\System\Drivers」、32bitドライバは「%Windir%\System32\drivers」が標準的な場所だったと記憶しています。

NT系Windowsは完全に32bit OSになり、「%SystemRoot%\System32」にシステムファイルを配置するようになりました。現在は64bit OSが主流になりつつありますが、64bit OSでは64bitドライバが「%SystemRoot%\System32\drivers」ディレクトリ内に配置されています(64bit OSでは「%SystemRoot%\sysWOW64\drivers」に32bitドライバが配置されます)。

Windows NTの開発中、「%SystemRoot%\System32\drivers」ディレクトリを起点として場所を検討し、「\etc\hosts」に決めたのは、当時としては自然なことだったのかもしれません。今となっては、なぜこんな深い場所にあるんだと思ってしまいますし、特に若い人は不思議に思うかもしれません。

Windows のファイルのコピーは、驚くほど奥が深い。

Windows で、ファイルやディレクトリのコピーなど、ファイル操作のコードを書くときは、決して油断してはならない。UNIX の開発者が Windows の世界にいざ足を踏み入れるときなど、Windows の素人は、以下のすべての点について、当然、万全の注意を払わなければ、大変なひどい目に遭うのである。

(1)♪ 当然、ファイルやディレクトリのパス文字列は 260 文字を超える可能性があるのだから、当然、先頭に謎の呪文である "\\?\" という文字列を付加する必要がある。これにより最大約 32767 文字までのパス名を取り扱えるようにすることを忘れるな。

(2)♪ 当然、コピー先パスにすでにファイルが存在しており、かつ、ファイルが「読み取り専用」属性になっている可能性があるのだから、十分警戒しなければならない。もし「読み取り専用」属性になっていた場合、上書きすることはできない。ファイルを書き込み可能モードで開こうとする前に、属性を解除することを忘れるな。

(3)♪ 当然、コピー元ファイルには代替ストリーム (Alternate Stream) が含まれている可能性がある。代替ストリームとは、あの Windows でよく見られる、Web ブラウザでダウンロードしたファイルをダブルクリックすると「本当に開きますか?」と確認画面を出す機能の裏側で利用されている、1 つのファイルの本体データのほかに任意の個数の隠しデータを保存する機能である。昔の Mac でいうリソースフォークである。代替ストリームは 1 つのファイルに何百個も付いている可能性があるし、サイズも制限がないが、これを忘れずにコピーしなければならない。なお、あるファイルに付いている代替ストリームの一覧を列挙する API は「FindFirstStreamW」であるが、素人の中には、これを使ってただただ安心している者が多い。しかし、これには重大なワナがある。(11) で述べる。

(4)♪ 当然、コピー元のファイルやディレクトリの「作成日時」、「更新日時」、「アクセス日時」をコピー先にコピーすることを怠ってはならない。ただし、ファイルシステムの種類によって、これらの日時の精度が異なるので、これらの情報に基づくファイル同期コードを書くときには、ある程度の時差を許容するコードを書くことを忘れるな。そうしなければ、毎回、日時が変化しているように見えてしまうのである。

(5)♪ 当然、ファイルコピー時にコピー元ファイルの属性ビットをコピー先にコピーしたからといって、それだけで油断してはならない。ファイルコピーにおける各種の書き込み操作で、コピー先ファイルに「アーカイブ属性」が自動的に付いてしまうことがある。各種操作をした後、最後に「アーカイブ属性」をもう一度確認してコピーすることを忘れるな。

(6)♪ 当然、ファイルの属性をコピーするとき、単なる「属性フラグ」では操作することができない、「特殊な API でしか読み書きできない特殊な属性フラグ」が元ファイルや元ディレクトリに設定されているかどうか、チェックしなければならない。具体的には、圧縮ファイル属性、「シンボリックリンク属性」、「ジャンクション属性」である。

(7)♪ 当然、元ファイルまたは元ディレクトリは、シンボリックリンクである可能性があるから、コピー時は必要に応じてこれを再現しなければならない。なんと、Windows にはシンボリックリンク的な実装として、「シンボリックリンク」と「ジャンクション」の 2 つがある。そして、これを見分ける方法は通常のファイル API には存在せず、通常のファイル API を用いるとリンク先のファイルやディレクトリが透過的に見えてしまう。そこで、プロは、DeviceIoControl API を用いて「シンボリックリンク」または「ジャンクション」の生のメタデータにアクセスするのである。

(8)♪ 当然、元ファイルまたはディレクトリには「NTFS 圧縮属性」が指定されている可能性があるから、コピー先に対してもこの属性を再現する必要がある。そうしなければ、保存先のディレクトリの属性が適用されてしまう。ところが、この属性は標準のファイル API では設定することができず、なんと DeviceIoControl で特殊な方法を用いて属性を設定する必要があるのである。そして、圧縮属性は、すでにデータがあるファイルに対して適用しようとすると、大変長い時間がかかる上に、Windows のバグ (仕様 ?) として、CancelIo というキャンセル用 API が効かない (つまり、一度圧縮を開始してしまうと、Windows を強制シャットダウンするしか、圧縮処理を中断できない) という問題があるので、十分に警戒をしなければならない。

(9)♪ 当然、元ファイルは NTFS の暗号化ファイルシステム (EFS) によって暗号化されている可能性がある。そして、暗号化がされている場合、① ファイルをコピーしようとするユーザーがその秘密キーを有しているケース、② 秘密キーは有していないが管理者権限を有しているケース、③ 秘密キーは有していないし管理者権限も有していないケース、の 3 通りが考えられる。そして、ユーザーの希望により、[A] ファイルを一度メモリ上で復号化し、再暗号化して保存して欲しい場合、[B] ファイルを復号化せず、暗号化された生ストリームのままビット列としてコピーしてほしい場合、[C] ファイルは復号化し、コピー先では平文ファイルとしてほしい場合、の 3 通りが考えられる。この 3 × 3 = 9 通りのすべてのパターンで、可能な限り正しくファイルをコピーすることが、プロには求められる。② のケースでは、ReadEncryptedFileRaw を用いて、鍵を持っていなくても、暗号化された生の物理的なビットデータのコピーが可能であるから、プロはこれを活用するべきである。

(10)♪ 当然、元ファイルまたは元ディレクトリ、または先ファイル・先ディレクトリへのアクセスの際に、NTFS で ACL が設定されていることから、アクセス権が無い可能性がある。これはローカルの場合も、ネットワークファイル共有上の UNC パスにアクセスする際にも、同様である。しかしながら、ユーザーは一般的にファイルをバックアップ / 復元するためにコピーをするのであるから、当然、NTFS によるローカルまたはリモートのアクセス制限は無視してコピーしなければならない。このようなことは一般的には禁止されているが、AdjustTokenPrivileges などの API を用いてバックアップ特殊権限を有効化した後、CreateFile API において FILE_FLAG_BACKUP_SEMANTICS を指定してファイルやディレクトリを開くことにより、NTFS の ACL を完全に無視してファイルの読み書きが可能になるのである。Windows のプロは、当然、このようなことをするコードを、無意識に書くことができるはずである。

(11)♪ 当然、(3) で説明した代替ストリーム付きファイルのコピー処理を行なおうとする際に、コピー元ファイルが NTFS によってアクセス制限されている場合は、(3) で説明したアクセス権限無視の特殊モードを有効化する必要があるが、なんと、(3) で説明した FindFirstStreamW API を用いた代替ストリーム一覧の列挙処理においては、NTFS のアクセス権限を無視して列挙ができない。これは明らかに Windows のバグ (仕様 ?) であり、全くけしからんことである。Win32 SDK の API ドキュメントを見渡したが、NTFS のアクセス権限を無視した代替ストリームの列挙を可能とする API は 1 つも存在しなかった。それでは Windows 標準の ntbackup (Windows XP まで付いていた) はどのようにして NTFS のアクセス権限を無視した代替ストリームのコピーをしているのか? また、「BackupExec」などの市販ソフトではどのように対応しているのか? ここで Windows のプロ必携の逆アセンブラの出番である。調査したところ、なんと、非公開 DLL「ntdll.dll」の「NtQueryInformationFile」を用いて、NTFS 権限を無視した代替ストリームの列挙が可能な機能が実装されていたのである。これを用いることで、ようやくファイルのバックアップモードにおける代替ストリームの正しいバックアップが可能になるのである。

(12)♪ 当然、コピー元のパーティションと、コピー先のパーティションとが、異なる NTFS クラスタサイズでフォーマットされている可能性に注意する必要がある。実装上の都合により、NTFS クラスタサイズによって、最大ファイルサイズが異なる。たとえば、64KB クラスタの場合、実験したところ、1 個あたり約 17TB のファイルが最大サイズであり、これを超えると書き込みがうまくできないことがあることが分かっている。プロはこのような点にも気を配らなければならない。

(13)♪ 当然、元ファイルは NTFS スパースファイル (Sparse File) である可能性があり、この場合は、コピー先ファイルもスパースファイルとすることが、ユーザー万人が期待することである。そこで、Windows のプロは、DeviceIoControl を用いて、スパース領域ブロックを列挙し、正しくこれをコピー先ファイルに再現することを忘れることがない。なお、Linux などの生易しい OS の生易しい ext4 などとは異なり、単に seek をしてファイルポインタを飛ばしたとしても自動的にスパースファイルにしてくれることはなく、まず DeviceIoControl で特殊な操作をしなければ決してスパースファイルにはなってくれないのであるから、十分注意すること。

(14)♪ 当然、元ファイルまたは元ディレクトリの NTFS セキュリティ属性は、新たなファイルまたはディレクトリにコピーされることをユーザーが期待している場合は、そのコピーを忠実に実行しなければならない。ここで、NTFS のファイルまたはディレクトリには、① 複雑なアクセス制御リスト (ACL)、② 所有者データ、③ (UI 上では表示されないが NTFS 上には存在する) グループ所有者データ、④ 監査設定、の 4 つが存在しているので、これらを漏れなくコピーする必要がある。① 複雑なアクセス制御リスト (ACL) は、アクセス制御エントリ (ACE) のリストであるが、それぞれの ACE には、継承関係があり、「このオブジェクトのみ」、「このオブジェクトおよびサブオブジェクト」、「このオブジェクトおよびサブオブジェクトおよびその下部のすべてのサブオブジェクト」の 3 種類が存在する。また、上位の親オブジェクトからの継承を受け入れるかどうかのフラグが存在する。これらに十分注意して NTFS ACL をコピー先で再現すること。なお、ネットワーク上の共有フォルダにおいては、ローカルマシンで存在しない SID のユーザーやグループに対する ACL が設定されていることもあるが、それも正しくコピー先で再現しなければ、ユーザーの期待を裏切ることになるであろう。

(15)♪ 当然、上記すべてのコピー処理は、非同期に (つまり、いつでも呼び出し元ユーザーによって取消し可能な状態で) 行なわなければならない。大きなファイルのコピーには時間がかかるものであり、ユーザーはいつでもキャンセルしたいと思うのである。そのためには、通常の方法で ReadFile, WriteFile, DeviceIoControl を呼んではならない。Windows のプロだけが利用できるあの素晴らしいオーバーラップド構造体を設定した I/O 完了ポートを用いてファイルコピーを実装する必要があるのである。このようにすれば、複数のファイル操作を並列して実行できるし、いつでも CancelIo を用いて途中でキャンセルすることができる。なお、CancelIo を無視する Windows API もあるから (例: NTFS 圧縮処理)、警戒を忘れてはならない。

(16)♪ 当然、ユーザーがファイルコピーを途中でキャンセルした場合は、中途半端な状態のファイルが見えないように、いったんコピー中の宛先ファイルを削除し、元のファイルをコピー前に戻さなければならない。元のファイルをコピー前の状態に戻すための良い方法は、コピーを開始する前に、まず、宛先ファイルを、同一のパーティション上の別の場所に移動しておき (この際のコストはほぼ 0 である)、キャンセルが発生したら再度移動をして元の場所に戻す、コピーが完了したらその移動先ファイルを削除する、という方法である。ただし、これらの一連の動作はアトミックでないので、プロセスがクラッシュしたり、ユーザーによって強制終了されたり、またコンピュータが停止したりした場合に、不整合が発生してしまう可能性がある。これを避けるためには、Transactional NTFS というものを使うと良いらしい。しかしながら十分長けた Windows のプロであってもそこまで踏み込んでいる例は少ない。

(17)♪ 当然、コピー元ファイルが、排他モードで別プロセスによって開かれている場合がある。この場合は、ファイルを開こうとすると失敗する。また、コピー元ファイルがデータベースや VM エンジンなどによって常時読み書きされている場合がある。この場合は、ファイルを開くことができても、読み取り中に一貫性が失われる。このような問題に対処するために、プロが書くソフトウェアは、当然、Volume Shadow Copy (VSS) の COM API を用いて NTFS のスナップショットを作成し、そのスナップショット上のファイルを読み取るべきである。しかしながら十分長けた Windows のプロであってもそこまで踏み込んでいる例は少ない。

(18)♪ 当然、ユーザーは Windows 95 ~ Windows 10 のすべての Windows 上で、プログラムを実行する可能性があるのである。ところで、上記の各種 API は、それぞれ、特定のバージョンの Windows 以降で実装されたものが多いので、プログラムを書く際には、呼び出そうとしている機能がこの Windows でサポートされているかどうか注意深く検査する必要がある。また、実行しようとしているユーザー権限にも注意しなければならない。さらに、一部のバージョンでは Unicode 版 API が利用不可能なので、文字コード変換を実施しなければならない。Windows 9x を切り捨てるということも十分検討に値するのであるが、未だ Windows 9x は色々なシステム (特に産業用システムなど) で現役で利用されているのであり、「ファイルバックアッププログラム」というものは、まさにそのようなシステムを含めたすべてのシステムで動作しないと意味がないのであるから、これらの古いバージョンの Windows を安易に切り捨てることなど、できないのである。

(19)♪♫♪♫♪♫ 最大限の恐怖!当然、Windows API のファイル API の仕様では、ファイルを書き込みモードで開いた後に、UNIX のいわゆる lseek や ftruncate に相当する API である、SetFilePointer および SetEndOfFile で、ファイルのサイズを拡張または縮小することができるのだが、ファイルを拡張する場合には、最大限の用心が必要である。UNIX においては、lseek や ftruncate でファイルを拡張した場合は、拡張領域はゼロクリアされていることが保証されている。ところが、なんと、Windows の場合、API ドキュメントを用心深く目を皿のようにして読んだ方ならば誰でも刮目することが書かれているのである。すなわち、Windows では、ファイルサイズを拡張した場合は、「元の EOF から新しい EOF までの領域は(実際に書き込みが行われた領域を除き)初期化されません。」(SetFilePointer API のドキュメント)、「元の EOF の位置と新しい位置の間にあるファイルの内容は未定義です。」(SetEndOfFile ドキュメント) のとおり、拡張された部分には「未定義のデータ」(!) が含まれる可能性がある仕様となっているのである。つまり、Windows におけるファイル拡張において、拡張領域がゼロクリアされている保証は全く無い。実装上、偶然ゼロクリアされているかも知れないし、「未定義のデータ」(!)、つまり、ゼロでないゴミデータが入っているかも知れないのである。これは誠に困ったことである。ファイルをコピーする際に、コピー元ファイルが巨大なゼロが連続するファイルの場合は、元ファイルのゼロを自動的に検出して、その部分を先ファイルにおいて書き込みスキップすることでパフォーマンスを上げるという作業を行なう必要があるが、その際にファイルサイズの拡張が必要となる。ファイルコピープログラムに限らず、データベースプログラムなど、巨大なデータを扱うプログラムは、データの書き込みを伴わないファイルサイズの拡張処理をひんぱんに利用している。この際、ゼロクリアが行なわれず、「未定義のデータ」(!) が入る可能性があるということになると、ファイルのデータは意図したとおりになっておらず、一貫性が欠如し、内容が破損しているということになる。したがって、Windows では、SetFilePointer や SetEndOfFile だけでは、安全なファイル拡張は不可能である。UNIX から Windows の世界に足を踏み入れた多くの UNIX のプロのプログラマーは、Windows の世界では素人であるので、この Windows の恐怖の API ドキュメントの最大限の恐怖の記述を注意深く警戒してよく読まずに、ゼロクリアしているつもりで「未定義のデータ」(!) が混入するというバグがある Windows のコードを書いているに違いない。Windows のプロは、API 仕様書をますますよく読むと、DeviceIoControl で FSCTL_SET_ZERO_DATA という素晴らしいコントロールコードの存在をついに発見するであろう。このコントロールコードこそが、Windows において、領域拡張後のファイルを、実際の書き込みを伴わずにゼロクリアされることが保証されている唯一の拡張操作と組み合わせて利用される、プロしか知らないコントロールコードなのである。Windows では lseek や ftruncate 的な API を用いてファイル拡張した後には、忘れずに、DeviceIoControl で、FSCTL_SET_ZERO_DATA を使ってゼロクリアをしなければならないのである。これを読んで驚いた方は、最新の Windows 10 の SDK の SetFilePointer および SetEndOfFile の英語版ドキュメントを読んでみるとよい。SetFilePointer の Remarks には "The size of the file does not increase until you call the SetEndOfFile, WriteFile, or WriteFileEx function. A write operation increases the size of the file to the file pointer position plus the size of the buffer written, which results in the intervening bytes uninitialized." 、SetEndOfFile の Remarks には "The SetEndOfFile function can be used to truncate or extend a file. If the file is extended, the contents of the file between the old end of the file and the new end of the file are not defined." という恐怖の記述があるのである。すべての Windows のプロは、一度、これまでに、自分の書いたコードが、ファイルサイズを拡張した後にその拡張した領域に対して WriteFile で実際にゼロを書き込むか、または、DeviceIoControl で FSCTL_SET_ZERO_DATA を用いてゼロクリアを宣言するという動作をし忘れていないか、十分慎重に思い返して、コードを確認してみるとよい。おそらくほとんどのプログラマーは、(当然 UNIX と同様に) ゼロクリアが暗黙で行なわれていると信じて、Windows においてこの恐怖の仕様に対して油断をし、FSCTL_SET_ZERO_DATA などというようなプロしか知らない救世主コントロールコードのことなど調べたこともないはずであろう。♪♫♪♫♪♫

上記のように、Windows でファイルをコピーする場合は、色々な点に気を配らなければならない。上のような複雑な問題 (他にもあるかも知れない) を十分に把握している人たちは、当の Microsoft 社においてももはや少数となっている程度である。そして市販ソフトウェアであっても、フリーソフトウェアであっても、Windows に対応したファイルバックアップソフトウェアで、上記すべてのファイルのメタデータを含めて忠実にバックアップができるろくなものがほとんど存在しない理由は、上記のようなことすべてに対応しなければ、実装ができないためなのである。あのライセンス料金が高い BackupExec が高い理由は、だいたいは上記のようなヘンテコの機能すべてに対応するための膨大な開発やテストの費用が含まれているからなのであろう。

あとめちゃくちゃついでに。メールの文字コードの話。

  • (427 レス) 漢字が全部中国語の文字コードじゃん

通常、メールの仕様(RFCという)上、「ヘッダー」という見えない所に本文の「文字コード」を指定する必要がある。
日本ならISO-2022-JP,通称JISコードという。他、SJIS,EUCもRFC上使えないことはない(確か、5,6年くらい前に「使える」文字コードとしてIANAに登録された。確か非推奨みたいな地位。)

で、なぜ中国語という話が出るかというと、中国のメールの文字コードは確かGB2312という。GB2312には「漢字」と「ひらがな」が入っている。
だから中国人が自前の環境で日本語のメールを作るとGB2312のメールで送られることが多い(最近はUTF-8かもしれない)。

で、ここから。ヘッダーでGB2312が指定されたメールを印刷すれば、GB2312用のフォントが使われることになる。当然、ISO-2022-JP(JIS)用のフォントとは違う。
結果、「いつもの見慣れたフォント」とは全く違うフォントで印刷される。
中華の怪しい家電製品の怪しい日本語マニュアルにおいて、フォントの字体が違ったりするのと同じこと。

これが、427の言いたいこと。もちろん、環境によって違うから、「中国語が中心となったPCでメールが作られた」可能性が存在するという指摘である。