Get Extended File Properties (Java, JNI & C++)

Hi everyone,
I have written the following C++ code in a dll to get file extended attributes:
public void long getAttrib(char * filename)
{
const char * _filename = filename;
WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
BOOL result = GetFileAttributesEx(_filename, GetFileExInfoStandard, &fileAttrs);
if (!result) {
return -1;
}
return fileAttrs.dwFileAttributes;
}
When I call this function from the dll from within another program, it works fine for every file path that has normal ASCII characters in its pathname like E:\test\folder_1\Backup-0005.txt. But when a filepath with a special character (e.g. Mu µ) in it (e.g. E:\test\folder_µ_1\Backup-0007.txt) is sent to this function, it does not get properties of this file.
I search the net and tried different things but could not solve the problem. Can somebody guide on this issue? It would be very helpful if someone provide a C++ code to get extended file properties of a file that has special characters in its file path.

Thanks in advance
Last edited on
Some of the characters are reserved, and should never be used in a Windows Operating system. What specific character are you having trouble with?
But when a filepath with a special character (e.g. Mu µ) in it (e.g. E:\test\folder_µ_1\Backup-0007.txt)
µ and other characters (eg. ąčęėįšųū...) are Unicode character (not ASCII). You have to use wchar_t* instead of char*

1
2
3
4
5
6
7
8
9
10
 long getAttrib(const wchar_t * filename)
{
const wchar_t * _filename = filename;
WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
BOOL result = GetFileAttributesExW(_filename, GetFileExInfoStandard, &fileAttrs);
if (!result) {
return -1;
}
return fileAttrs.dwFileAttributes;
}


Also, read this: http://www.cplusplus.com/forum/articles/16820/

Ok Incubbus I'll take it at face value. Try setting an output in your code right before passing the file name to see what exactly you are trying to pass to this function, if it shows up garbeled then we know that you need a differant approach.

EDIT: Null stepped in and solved this.
Last edited on
Thanks everyone.

Provided solution worked perfectly. I modified it a little bit like this:

1
2
3
4
const char * _filename = "E:\\test\\folder_µ_1\\Backup-0008.txt";
const TCHAR * file = _T(_filename);
WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
BOOL result = GetFileAttributesEx(file, GetFileExInfoStandard, &fileAttrs);


I tested it in a simple C++ application and it worked perfectly. It worked for both ascii filepath and Unicode filepath. I also tried to get file size from fileAttrs structure and it was correct.

Now let me explain the situation in more details. I created a function using the code above in a C++ dll like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
JNIEXPORT jlong JNICALL Java_FileAttrs_getAttrib(JNIEnv *env, jobject obj, jstring filename) 
{
   const char * _filename = env->GetStringUTFChars(filename, 0);
   const TCHAR * file = _T(_filename);
   printf("\n>>>_filename: "); printf(_filename);
   printf("\n>>>     file: "); printf(file);
   WIN32_FILE_ATTRIBUTE_DATA fileAttrs;
   BOOL result = GetFileAttributesEx(file, GetFileExInfoStandard, &fileAttrs);
   if (!result) {
      return -1;
   }
   return fileAttrs.dwFileAttributes;
}


This dll function is called from a Java application using jni (for Java, I’m using Eclipse). This java app sends filepath as a parameter to this dll function. Java has no problem with ascii and Unicode file paths. Now if the filepath has only ascii characters in it, C++ function works correctly and returns file attributes but if the filepath contains Unicode characters C++ function fails and return -1. I hard-coded the value of _filename variable for testing purpose and then called the function from Java using Eclipse and it worked perfectly.

1
2
//const char * _filename = env->GetStringUTFChars(filename, 0);
const char * _filename = "E:\\test\\folder_µ_1\\Backup-0008.txt";


So I suppose that env->GetStringUTFChars(filename, 0) function is creating problem.

The printf() statements in the code produced following output (captured from Eclipse output window):

When using env->GetStringUTFChars(filename, 0):
const char * _filename = env->GetStringUTFChars(filename, 0);
>>>_filename: E:\test\folder_µ_1\Backup-0008.txt
>>> file: E:\test\folder_µ_1\Backup-0008.txt

When using hard-coded value:
const char * _filename = "E:\\test\\folder_µ_1\\Backup-0008.txt";
>>>_filename: E:\test\folder_µ_1\Backup-0008.txt
>>> file: E:\test\folder_µ_1\Backup-0008.txt

You can see that printf() displayed correct filepath when hard-coded.

Maybe I need to convert utf chars to Unicode/ascii chars somehow.

Sorry if it is not related to the forum anymore.
Last edited on
why _filename is type of const char*?

Try something like this to check if GetStringUTFChars causes the problem:

1
2
3
4
5
6
7
jstring filename="E:\\test\\folder_µ_1\
\Backup-0008.txt";  
wchar_t *_filename=env-
>GetStringUTFChars(filename, 0);
const TCHAR * file = _T(_filename);
printf("\n>>>_filename: "); printf(_filename);
printf("\n>>>     file: "); printf(file);
Function env->GetStringUTFChars() is the one creating problem here. This function inserts an extra character in the returned string for every special character. E.g. it changed “E:\test\folder_µ_1\Backup-0008.txt” to “E:\test\folder_µ_1\Backup-0008.txt”. Notice an extra char (Â). Thus the whole path becomes invalid. That’s the reason GetFileAttributesEx() is not getting file attributes.

I found two solutions to overcome the problem:

Using MultiByteToWideChar():

Use the combination of env->GetStringUTFChars(), MultiByteToWideChar(), and GetFileAttributesExW() functions.

Here MultiByteToWideChar() will convert the string to wide char data (Unicode data) and store the result in a variable of wchar_t data type.

1
2
3
4
5
6
7
8
9
10
11
12
LPCCH tempfile = env->GetStringUTFChars(filename, 0);
const int lenA = lstrlenA(tempfile);
WCHAR * _filename;
int lenW;
lenW = ::MultiByteToWideChar(CP_UTF8, 0, tempfile, lenA, 0, 0);
if (lenW > 0)
{
// Check whether conversion was successful
_filename = SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_UTF8, 0, tempfile, lenA, _filename, lenW);
}
BOOL result = GetFileAttributesExW(_filename, GetFileExInfoStandard, &fileAttrs);


Using GetStringChars():

Use env->GetStringChars(), a null terminated string to hold filepath, and GetFileAttributesEx() functions to properly get Unicode file path. GetStringChars() functions returns Unicode data but it returns unsigned short data that needs to be converted/copied to char data type. Here is the solution:

1
2
3
4
5
6
7
8
unsigned short * tempfile = (unsigned short *) env->GetStringChars(filename,0);
char _filename [520] = { '\0' };
int len = (long)env->GetStringLength(filename);
for (int i=0; i<len; i++)
{
_filename[i] = (char)tempfile[i];
}
BOOL result = GetFileAttributesEx(_filename, GetFileExInfoStandard, &fileAttrs);


Both solutions work perfectly for both ASCII file path and Unicode file path.

Thanks everyone for helping me.
Topic archived. No new replies allowed.