ファイルサーバーに置いたファイルに対し、TFile.GetLastWriteTime()を実行すると返す値がまちまちなことに頭を抱えています。
D2010では完璧に日付が取れていましたが、D10.2以降でトライすると度々0を返します(D11も所持しているので試しましたが同じくおかしいです)。
具体的には下記のプログラムで、D2010はfailCountが0、D10.2以降はfailCountが80等になります。
var
i, failCount: Integer;
mTime: TDateTime;
fPath: String;
begin
fPath := '\\file-server\temp\forTest.pdf';
failCount := 0;
for i := 1 to 100 do begin
mTime := TFile.GetLastWriteTime(fPath);
if (mTime=0) then
Inc(failCount);
Sleep(1000);
end;
end;
同じファイルを見ているのに何故結果が変わるのか、不思議です。
D2010のように、D10.2以降でも一様の正しい値が取れるようにするための対策等何かあるのでしょうか。
各Delphiを入れているPCは同じでしょうか?
FileExistsを使ってみてファイルサーバーへのアクセスに失敗しているのか
書き込み日時の取得に失敗しているのか切り分けしてみるとどうなるでしょうか?
take様
Delphi2010も10.2も同じPCに入っていますが、2010では毎回確実に成功し、10.2以降では度々失敗します。
ファイル自体が見つからないということもなく、10.2でもGetLastWriteTime()を使わずにWindowsのAPIを直接使用すれば普通に情報を取得することができます。
ライブラリのソースを見てみると、Delphi2010でも10.2でもTFile.GetLastWriteTime()はGetFileAttributesEx()というWindowsのAPIを使用して更新日時を取得していました。
このAPI自体は普通に成功で通過して更新日時も取得できていますが、10.2ではその後に「ファイル属性にfaSymLink(FILE_ATTRIBUTE_REPARSE_POINT)が含まれるとき」という条件での追加処理が記述されており、この処理に突入した結果失敗しているようです。
加えて同じファイルを見ているはずなのに何故かフラグが立っていたり立っていなかったりするため、このことがGetLastWriteTime()の成功・失敗が安定しない結果に繋がっているように見えます。
直接GetFileAttributesEx()を呼び出せばこのようにフラグが不安定になることはないので、Delphi特有の問題なのでしょうか。
2010のGetLastWriteTime()はGetFileAttributesEx()で取得できた更新日時を素直にそのまま返しているだけのようで、こちらに関しては失敗したことはありません。
10.2もAfter処理で失敗しても2010と同じようにGetFileAttributesEx()の結果をそのまま返してくれれば安定して使えそうなのですが…。
多分ですがD10.2は
対象ファイルのFILE_ATTRIBUTE_REPARSE_POINT
フラグを確認して、このフラグがある場合は、通常はシンボリックリンク(またはジャンクション)なので、シンボリックリンクとみなして、シンボリックリンクリンクが表す実体ファイルを探しにいこうとするが、シンボリックリンクではなく、普通のファイルなので、失敗しているのではないでしょうか?
外していたらすいません。
mam様
現象としてはその通りです。
何故シンボリックリンクと誤判定されてしまうことがあるのかはわかりませんが、ファイルサーバーや環境になんらかの問題があるのでしょうか。
特にサーバーが不調というわけでもないので、謎が多いですね…。
TFile.GetLastWriteTime()自体の問題は解決していませんが、とりあえずはWindowsのAPIを直接呼んで更新日時を取得することで対応したいと思います。
成功時と失敗時で他に違いがないか調べてみたところ、失敗するときはFILE_ATTRIBUTE_REPARSE_POINTだけでなくFILE_ATTRIBUTE_SPARSE_FILEというファイル属性もONになっていました。
これはスパースファイルが関係しているのでしょうか…。
最初の何回は成功してその後連続して失敗し続けるというケースが多いことから、SMBのキャッシュ機能等が影響しているのかもしれません。
(SMBのバージョンはSMB3.1.1)
この仮説が正しければ、10.2以降のTFile.GetLastWriteTime()やその類似の関数は(少なくとも単体では)使用しないほうがいいという結論になりそうです。