I searched the forums and found that noone else had posted a solution for getting the file creation date on Win32. So I rolled up my sleeves, read a JNI book and what follows is a solution that worked for me. I need to enhance it in the future to handle other bits of information that Win32 provides at the file system level but, being a minimalist, this code shows that it is doable and it is fairly easy to get at the info with JNI.
To compile the C source with Microsoft C Compiler, use a line similar to the following (change the include paths to match the location of your jdk includes):
cl -Ic:\jdk1.4\include -Ic:\jdk1.4\include\win32 -MD -LD Win32FileAttributes.c -FeWin32FileAttributes.dll
Put the output DLL on the Windows PATH. My experimenting with the Sun JVM on NT is that the java.library.path environment variable includes both the ALL_USERS PATH and the USER PATH variables munged together.
NOTE: There are 2 spots in the C file with comments indicating that I should throw an exception. I didn't get around to coding them and they should be sufficiently rare. The side effect even without throwing an exception is that the result is zero (0) which matches the contract of java.io.File.lastModified() failure mode. So I'm not too concerned.
Win32FileAttributes.java
import java.util.Date;
import java.text.SimpleDateFormat;
import java.io.File;
public final class Win32FileAttributes {
public static native long getCreationDate(String path);
public static native long getAccessDate(String path);
private Win32FileAttributes() {
}
public static void main(String[] args) {
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yy HH:mm:ss.SSS");
File file = new File("c:\\xml.zip");
long creationDateTime = Win32FileAttributes.getCreationDate(file.getPath());
long accessDateTime = Win32FileAttributes.getAccessDate(file.getPath());
System.out.println(file.getPath() + " created: " + dateFormat.format(new Date(creationDateTime)));
System.out.println(file.getPath() + " last accessed: " + dateFormat.format(new Date(accessDateTime)));
}
static {
System.loadLibrary("Win32FileAttributes");
}
}
Win32FileAttributes.h
#include <jni.h>
/* Header for class Win32FileAttributes */
#ifndef _Included_Win32FileAttributes
#define _Included_Win32FileAttributes
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: Win32FileAttributes
* Method: getCreationDate
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_Win32FileAttributes_getCreationDate
(JNIEnv *, jclass, jstring);
/*
* Class: Win32FileAttributes
* Method: getAccessDate
* Signature: (Ljava/lang/String;)J
*/
JNIEXPORT jlong JNICALL Java_Win32FileAttributes_getAccessDate
(JNIEnv *, jclass, jstring);
#ifdef __cplusplus
}
#endif
#endif
Win32FileAttributes.c
#include <jni.h>
#include <stdio.h>
#include <windows.h>
#include "Win32FileAttributes.h"
static ULARGE_INTEGER G_ULI_1970;
BOOL ConvertDateTimeToMillisecondsSince1970(const FILETIME *lpDateTime, jlong *lqwMillis)
{
ULARGE_INTEGER uliTime;
BOOL bResult;
/* Determine the equivalent FILETIME for Jan 1, 1970 */
if (G_ULI_1970.QuadPart == 0) {
SYSTEMTIME stSystemTime1970;
FILETIME stFileTime1970;
stSystemTime1970.wYear = 1970;
stSystemTime1970.wMonth = 1;
stSystemTime1970.wDayOfWeek = 0;
stSystemTime1970.wDay = 1;
stSystemTime1970.wHour = 0;
stSystemTime1970.wMinute = 0;
stSystemTime1970.wSecond = 0;
stSystemTime1970.wMilliseconds = 0;
bResult = SystemTimeToFileTime(&stSystemTime1970, &stFileTime1970);
if (bResult == 0) {
/* Throw exception here */
return FALSE;
}
G_ULI_1970.LowPart = stFileTime1970.dwLowDateTime;
G_ULI_1970.HighPart = stFileTime1970.dwHighDateTime;
}
uliTime.LowPart = lpDateTime->dwLowDateTime;
uliTime.HighPart = lpDateTime->dwHighDateTime;
/* Subtract off 1970 from our FILETIME */
uliTime.QuadPart -= G_ULI_1970.QuadPart;
/* uliTime is in 100 nanosecond ticks since 1970. We need it in millisecond ticks since 1970 */
uliTime.QuadPart /= 10000;
*lqwMillis = uliTime.QuadPart;
return TRUE;
}
JNIEXPORT jlong JNICALL
Java_Win32FileAttributes_getCreationDate(JNIEnv *env, jclass cls, jstring jPath)
{
const jbyte *szPath;
jlong qwMillisSince1970;
HANDLE hFile;
WIN32_FIND_DATA stFileFindData;
BOOL bResult;
qwMillisSince1970 = 0;
szPath = (*env)->GetStringUTFChars(env, jPath, NULL);
if (szPath == NULL) {
return qwMillisSince1970; /* OutOfMemoryError already thrown */
}
hFile = FindFirstFile(szPath, &stFileFindData);
(*env)->ReleaseStringUTFChars(env, jPath, szPath);
if (hFile == INVALID_HANDLE_VALUE) {
return qwMillisSince1970;
}
if (stFileFindData.ftCreationTime.dwLowDateTime == 0 && stFileFindData.ftCreationTime.dwHighDateTime == 0) {
FindClose(hFile);
return qwMillisSince1970;
}
bResult = ConvertDateTimeToMillisecondsSince1970(&stFileFindData.ftCreationTime, &qwMillisSince1970);
if (bResult == 0) {
/* Throw exception here */
}
FindClose(hFile);
return qwMillisSince1970;
}
JNIEXPORT jlong JNICALL
Java_Win32FileAttributes_getAccessDate(JNIEnv *env, jclass cls, jstring jPath)
{
const jbyte *szPath;
jlong qwMillisSince1970;
HANDLE hFile;
WIN32_FIND_DATA stFileFindData;
BOOL bResult;
qwMillisSince1970 = 0;
szPath = (*env)->GetStringUTFChars(env, jPath, NULL);
if (szPath == NULL) {
return qwMillisSince1970; /* OutOfMemoryError already thrown */
}
hFile = FindFirstFile(szPath, &stFileFindData);
(*env)->ReleaseStringUTFChars(env, jPath, szPath);
if (hFile == INVALID_HANDLE_VALUE) {
return qwMillisSince1970;
}
if (stFileFindData.ftLastAccessTime.dwLowDateTime == 0 && stFileFindData.ftLastAccessTime.dwHighDateTime == 0) {
FindClose(hFile);
return qwMillisSince1970;
}
bResult = ConvertDateTimeToMillisecondsSince1970(&stFileFindData.ftLastAccessTime, &qwMillisSince1970);
if (bResult == 0) {
/* Throw exception here */
}
FindClose(hFile);
return qwMillisSince1970;
}