1. Use Powershell
long fileTimeUtc = 0;
InputStream inpStrm = Runtime.getRuntime().exec(new String[] {
"powershell", "[System.IO.File]::GetLastWriteTimeUtc('" + fullFilename.replace("'", "''") + "').ToFileTimeUtc();"
}).getInputStream();
for (;;) {
int incomingByte = inpStrm.read();
if (incomingByte < 48 || incomingByte > 57) break;
fileTimeUtc = fileTimeUtc * 10 + incomingByte - 48;
}
There is always a newline at the end of this ps command's output. So only need to check the byte value to end the loop. Single quote (') should be safer and easier than double quote (") for powershell in this case.
If the file doesn't exist, the output is 0.
DateTime structure of .net also has 100-nanosecond resolution, so the conversion ToFileTimeUtc() should be lossless for most practical applications (negative long
value is not handled in this code, you can add the logic, although negative result should be uncommon). (For some rare cases, there could be a problem, because the time range of DateTime is 0001-9999, and NTFS has 1601-60056.)
2. Use executable
Similarly, you can always write native code to get the value of last write time, and then Java can run that executable to get its output.
#include <stdio.h>
#include <windows.h>
int main(int argc, char *argv[])
{
HANDLE hFile;
if( argc != 2 )
{
printf("0");
return 1;
}
hFile = CreateFile(argv[1], GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
printf("0");
return 1;
}
FILETIME ftCreate, ftAccess, ftWrite;
if (GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite)){
printf("%lld\n",*(long long *)(&ftWrite));
CloseHandle(hFile);
return 0;
} else {
printf("0");
CloseHandle(hFile);
return 1;
}
}
Then call it with short path name like this:
Runtime.getRuntime().exec(new String[]{
"cmd",
"/c @echo off & for %I in (\""+fullFilename+"\") do a.exe %~fsI"
})
This way is not so different from the first one, but without conversion, it's more straightforward.