Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

win32 C prog - Another problem with FindFirstFile (Ansi version)

235 views
Skip to first unread message

Thomas Steinbach

unread,
Aug 11, 2009, 1:12:13 PM8/11/09
to
Hello,

got another problem with FindFirstFile and hope one
of the professional C programmers knows the problem
or this behavior and knows a solution.

I have the following (exact the same) funtion in my project
and all is working fine if I compile as UNICODE version.
But if I compile as asni version I get
"ERROR_PATH_NOT_FOUND" with GetLastError
after FindFirstFile()

Why? I can't explain that by myself. The whole
program comiles without any errors or warnings
(Warn Level 4)

BOOL ProfileFileCheck(void)
{
WIN32_FIND_DATA wfd = {0};
HANDLE hFindFile = NULL;
TCHAR szSearch[MAX_PATH] = _T("");
LPTSTR lpszSearch = NULL;
DWORD dwErr = 0;

lpszSearch = (LPTSTR)szSearch;

_tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
_T("D:\\install.ini"));
szSearch[_tcslen(szSearch)] = _T('\0');

ZeroMemory(&wfd, sizeof(wfd));
hFindFile = FindFirstFile(lpszSearch, &wfd);
dwErr = GetLastError();
/* dwErr = 3 => ERROR_PATH_NOT_FOUND */

if( hFindFile == INVALID_HANDLE_VALUE ) {
MessageBox(NULL, _T("File not found"), _T("Message"), MB_OK);
}else {
MessageBox(NULL, _T("File found"), _T("Message"), MB_OK);
}

return TRUE;
}

The file D:\install.ini was copied from C: and exist
(comes form the vc2008 redist install) and will be found if
I compile as unicode.
btw: The drive D: is a 32 GB FAT 32 Filesystem.
OS is Vista SP2 with VS 2008 sp1

If I copy this function to a new project, the Ansi version of
FindFirstFile works too, but not in my project (about ~5000
lines of code, which is a bit complexer, but copiles without
any warnings (Warn Level 4) too. Full std C99
btw: mingw gcc 3.x compiles without any warnings too.
using: CFLAGS = -c -O2 -Wall -pedantic -std=c99

I don't understand that because there shouldn't be any
diffrences betwwen a simple project and my project.
The function ProfileFileCheck() should be full autarchic
from any other code.

Does anybody know this behavior and why this
happens?

A working _simple_ version of this source can be downloaded
at: http://www.failure.bravehost.com/prog/win32/example/fff/
But that's a working version.

Thomas

r_z_...@pen_fact.com

unread,
Aug 11, 2009, 3:33:10 PM8/11/09
to
On Tue, 11 Aug 2009 19:12:13 +0200, "Thomas Steinbach"
<stei...@gmx-topmail.de> wrote:

>Hello,
>
>got another problem with FindFirstFile and hope one
>of the professional C programmers knows the problem
>or this behavior and knows a solution.
>
>I have the following (exact the same) funtion in my project
>and all is working fine if I compile as UNICODE version.
>But if I compile as asni version I get
>"ERROR_PATH_NOT_FOUND" with GetLastError
>after FindFirstFile()
>
>Why? I can't explain that by myself. The whole
>program comiles without any errors or warnings
>(Warn Level 4)
>
>BOOL ProfileFileCheck(void)
>{
> WIN32_FIND_DATA wfd = {0};
> HANDLE hFindFile = NULL;
> TCHAR szSearch[MAX_PATH] = _T("");
> LPTSTR lpszSearch = NULL;
> DWORD dwErr = 0;
>
> lpszSearch = (LPTSTR)szSearch;

Casts like this are especially dangerous for character strings. If you
need to convert from ANSI to UNICODE or back, you really need to use
MultiByteToWideChar or something similar. In this case, I think the
cast is unnecessary, and thus safe. In fact, I see no use for
lpszSearch.


>
> _tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
>_T("D:\\install.ini"));

I'm pretty sure the second argument should be sizeof( szSearch), but
_maybe_ sizeof( szSearch) * sizeof(TCHAR). Definitely not what you
have.

> szSearch[_tcslen(szSearch)] = _T('\0');

Have you checked the contents of szSearch and lpszSearch at this
point? That's likely to give you a big clue.

-----------------------------------------
To reply to me, remove the underscores (_) from my email address (and please indicate which newsgroup and message).

Robert E. Zaret, eMVP
PenFact, Inc.
20 Park Plaza, Suite 400
Boston, MA 02116
www.penfact.com
Useful reading (be sure to read its disclaimer first):
http://catb.org/~esr/faqs/smart-questions.html

Thomas Steinbach

unread,
Aug 11, 2009, 5:33:30 PM8/11/09
to
Hello R Z Aret,

>> [...]


>> lpszSearch = (LPTSTR)szSearch;
>
> Casts like this are especially dangerous for character strings. If you
> need to convert from ANSI to UNICODE or back, you really need to use
> MultiByteToWideChar or something similar. In this case, I think the
> cast is unnecessary, and thus safe. In fact, I see no use for
> lpszSearch.

I always think that a pointer would be faster than a string.
And if there are many search operations with different files,
this would be much more faster, wouldn't it?
And the FindFirstFile funtion expets a pointer.
Perhaps I'm wrong...

Anyway. If I remove the lpszSearch, I still have the same behavior
and the FindFirstFile doesn't find the file :-(
btw: I have the FindFirstFile function in other parts of the
code and ist working fine... Either with Unicode and with Ansi.
So I don't understand this strange behavior.
Thought this is a well known problem (perhaps a bug).
And if I just make copy & paste of the working parts with
findfirstfile, the bahavior is the same => ERROR_PATH_NOT_FOUND

>> _tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
>> _T("D:\\install.ini"));
>
> I'm pretty sure the second argument should be sizeof( szSearch), but
> _maybe_ sizeof( szSearch) * sizeof(TCHAR). Definitely not what you
> have.

Are you sure?

In case of Unicode sizeof(szSearch) would be 520 divided by
sizeof(TCHAR) which would be 2 and the reslut ist 260. Right?
The funtction _tcscpy expect the number of ellements of
the given destination buffer, which is MAX_PATH => 260
With a star the destination buffer must be 1040 not 260 as
given by declaration.

In case of Ansi the sizeof(szSearch) would be 260 divided by
sizeof(TCHAR) which would be 1 ant the result is the
number of Elements of the destination string, which is 260. Right?

perhaps I missunderstand something. But have a look at:
http://msdn.microsoft.com/en-us/library/td1esda9(VS.80).aspx

Anyway. Even if I replace / by the *
I have the same behavior.

>> szSearch[_tcslen(szSearch)] = _T('\0');
>
> Have you checked the contents of szSearch and lpszSearch at this
> point? That's likely to give you a big clue.

yes. The string and the pointer have the same address.
And th content of szSearch is terminated by "\0"
See the screenshot #1 and #2 at:

http://www.failure.bravehost.com/prog/win32/example/fff/
(press F5 several times if you don't see the screenshots)

and in the case of using star instead of slash see
screenshot #3 and #4 the string and the pointer has
the same address and szSearch is terminated by "\0"

But tha shouldn't be the problem. The problem ist that _after_
the FindFirstFile function I get the ERROR_PATH_NOT_FOUND
in dwErr - don't know why :-(

Hope that someone has an idea.

Thomas

Scott Seligman

unread,
Aug 11, 2009, 6:25:07 PM8/11/09
to
"Thomas Steinbach" <stei...@gmx-topmail.de> wrote:
> lpszSearch = (LPTSTR)szSearch;

Why create a pointer to a local variable?

> _tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
>_T("D:\\install.ini"));

It's generally cleaner to use _countof().

> szSearch[_tcslen(szSearch)] = _T('\0');

Why are you null-terminating a null-terminated string?

> ZeroMemory(&wfd, sizeof(wfd));

Why are you zeroing out a zero'd out structure?

> hFindFile = FindFirstFile(lpszSearch, &wfd);
> dwErr = GetLastError();

This is probably your issues. Only call GetLastError() if
FindFirstFile returns INVALID_HANDLE_VALUE. Calling it at any other
time is invalid.

Additionally, you never call FindClose()

You could accomplish the same thing with a one line call
to GetFileAttributes()

--
--------- Scott Seligman <scott at <firstname> and michelle dot net> ---------
Tragedy is when I cut my finger. Comedy is when you fall into an open
sewer and die.
-- Mel Brooks

Jerry Coffin

unread,
Aug 11, 2009, 8:00:57 PM8/11/09
to
In article <h5s998$9a4$02$1...@news.t-online.com>, steinbach@gmx-
topmail.de says...

>
> Hello,
>
> got another problem with FindFirstFile and hope one
> of the professional C programmers knows the problem
> or this behavior and knows a solution.

The solution, in this case, is to not use it at all. You should just
attempt to open the file, and give the user an error message (or
whatever) if you can't open it. Doing otherwise is asking for trouble
from things like the file existing, but the user doesn't have access
to it.

> BOOL ProfileFileCheck(void)
> {
> WIN32_FIND_DATA wfd = {0};
> HANDLE hFindFile = NULL;
> TCHAR szSearch[MAX_PATH] = _T("");
> LPTSTR lpszSearch = NULL;
> DWORD dwErr = 0;
>
> lpszSearch = (LPTSTR)szSearch;
>
> _tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
> _T("D:\\install.ini"));
> szSearch[_tcslen(szSearch)] = _T('\0');
>
> ZeroMemory(&wfd, sizeof(wfd));
> hFindFile = FindFirstFile(lpszSearch, &wfd);
> dwErr = GetLastError();
> /* dwErr = 3 => ERROR_PATH_NOT_FOUND */
>
> if( hFindFile == INVALID_HANDLE_VALUE ) {
> MessageBox(NULL, _T("File not found"), _T("Message"), MB_OK);
> }else {
> MessageBox(NULL, _T("File found"), _T("Message"), MB_OK);
> }
>
> return TRUE;
> }

bool file_exists(LPCTSTR file_name) {
WIN32_FIND_DATA data;
HANDLE h;

h = FindFirstFile(file_name, &data);
FindClose(h);
return h != INVALID_HANDLE_VALUE;
}

if (file_exists(_T("d:\\install.ini"))
// use file
else
MessageBox(NULL, _T("Ini File not found"), _T("Message"), MB_OK);

I'll say again, though: what you really want is more like this:

FILE *file;
if (NULL == (file=fopen(_T("d:\\install.ini"), _T("r"))))
MessageBox(NULL, _T("INI file not found"), _T("Message"), MB_OK);
// code the uses file here.

If you're sticking to using the Win32 API, that would be CreateFile
intead, but the idea comes out the same -- attempt to open the file
when you're ready to use it, and report an error on failure. Anything
else is prone to problems, both from permissions that allow you to
find the file but not use it, and from race conditions where the
file's existence changes between the time you look for it and the
time you try to open it for use.

--
Later,
Jerry.

r_z_...@pen_fact.com

unread,
Aug 12, 2009, 1:41:43 PM8/12/09
to
On Tue, 11 Aug 2009 23:33:30 +0200, "Thomas Steinbach"
<stei...@gmx-topmail.de> wrote:

>Hello R Z Aret,
>
>>> [...]
>>> lpszSearch = (LPTSTR)szSearch;
>>
>> Casts like this are especially dangerous for character strings. If you
>> need to convert from ANSI to UNICODE or back, you really need to use
>> MultiByteToWideChar or something similar. In this case, I think the
>> cast is unnecessary, and thus safe. In fact, I see no use for
>> lpszSearch.
>
>I always think that a pointer would be faster than a string.
>And if there are many search operations with different files,
>this would be much more faster, wouldn't it?
>And the FindFirstFile funtion expets a pointer.
>Perhaps I'm wrong...

szSearch is a pointer

>
>Anyway. If I remove the lpszSearch, I still have the same behavior
>and the FindFirstFile doesn't find the file :-(
>btw: I have the FindFirstFile function in other parts of the
>code and ist working fine... Either with Unicode and with Ansi.
>So I don't understand this strange behavior.
>Thought this is a well known problem (perhaps a bug).
>And if I just make copy & paste of the working parts with
>findfirstfile, the bahavior is the same => ERROR_PATH_NOT_FOUND

Something is different in your use of the function. Maybe one of the
lines you changed. Maybe the file.


>
>>> _tcscpy_s(szSearch, sizeof(szSearch)/sizeof(TCHAR),
>>> _T("D:\\install.ini"));
>>
>> I'm pretty sure the second argument should be sizeof( szSearch), but
>> _maybe_ sizeof( szSearch) * sizeof(TCHAR). Definitely not what you
>> have.
>
>Are you sure?
>
>In case of Unicode sizeof(szSearch) would be 520 divided by
>sizeof(TCHAR) which would be 2 and the reslut ist 260. Right?
>The funtction _tcscpy expect the number of ellements of
>the given destination buffer, which is MAX_PATH => 260
>With a star the destination buffer must be 1040 not 260 as
>given by declaration.

Oops. I thought sizeof gives number of elements, but I just checked.

I usually declare a const, declare the array to be one element larger
than that (leaving room for trailing NULL, just in case), and then use
the const as appropriate:

const int Max = MAX_PATH;
TCHAR sString[Max + 1];
_tcscpy_s( sString, Max, _T( "something" ) );

Seems clearer to me.

0 new messages