Fog Creek Software
Discussion Board




Problem converting handle to file descriptor

I'm trying to open a file for read-only access using CreateFile(), then convert the Windows file handle to a file descriptor (suitable for use by functions such as read()). [1]

The following test program demonstrates what I'm trying to do:

int main(void)
{
int fd;
char c;
HANDLE hFile;
HANDLE hFile2;
 
    hFile = CreateFile("C:\\home\\try\\junk.txt",
                        GENERIC_READ,
                        0, // no sharing
                        NULL, // no security
                        OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL, // normal file
                        NULL); // no attr. template
 
    if (hFile == INVALID_HANDLE_VALUE)
        {
        printf("CreateFile failed\n");
        fd = -1;
        }
    else
        {
        // Get the file descriptor from the handle
        fd = _open_osfhandle((long)hFile, O_RDONLY);
       
        printf("File descriptor = %d\n",fd);
       
        // Get the handle from the file descriptor
        hFile2 = (void*) _get_osfhandle(fd);

        printf("hFile = %d, hFile2 = %d\n",hFile,hFile2);

        if (hFile == hFile2)
            printf("Handles match\n");
        else
            printf("Handles don't match\n");

        // Do a simple dump of the file contents
        if (fd>0)
            {
            lseek(fd,0,0);
           
            printf("File contents:\n");
           
            while (read(fd, &c, 1) == 1)
                printf("%c",c);
           
            printf("\n");

            if (errno>0)
                perror("Error: ");
            }
        }
    return 0;
}

When I build this in Visual C++ 6.0 (with all the MFC stuff), it runs exactly as expected, dumping the file contents to the screen.

When I build it in Cygwin, the _open_osfhandle() call produces a valid-looking fd (3), but it doesn't appear to work; the _get_osfhandle() doesn't successfully convert it back to a handle, and the read() call fails ("Bad file descriptor").

I don't know why it's a problem in Cygwin.

One area of suspicion I have is the way I've linked with the msvcrt library (C++ run-time library). Linking with the libmsvcrt.a library in /lib (I think it's actually from MinGW) doesn't satisfy the symbol _open_osfhandle, even though running nm on the library shows that it is in there.

So what I did (and I can hear the shrieks already  ) is this:

In /lib, I did:
ln -s /c/WINNT/system32/msvcrt.dll libnewmsvcrt.a
Then in the gcc line, I added -lnewmsvcrt. This works, in that the code builds successfully. The _open_osfhandle() call produces a fd that looks right, so this method of linking isn't obviously absurd.

However, it does feel really 'filthy' to me, but I couldn't find a way to use the actual libmsvcrt.a. If you know of a solution to this problem, I'd be most grateful.

And if you know what's going on with my file descriptor problem, please let me know.

Many thanks
Pat Galea

[1] I realise this might appear to be an odd thing to want to do. I'm updating some code to support long paths. Currently, files are opened by calling open() (which returns the file descriptor), but unfortunately there isn't an open() equivalent which supports long paths, so I'm stuck with using CreateFileW(), which only returns the handle. The problems I describe in this post occur with both CreateFile() and CreateFileW(), so I figured I'd better solve it for normal paths before I tackle the other problem. Eliminate variables,
and all that.

Pat Galea
Wednesday, November 26, 2003

42


Wednesday, November 26, 2003

If my post really is the ultimate question behind the universe, then boy are we in big trouble!

:-)

Pat Galea
Thursday, November 27, 2003

I've solved part of my problem.

If I build with -mno-cygwin and -lmsvcrt, then I can link with msvcrt, and the handle conversion stuff works correctly. There seems to be an incompatibility between cygwin.dll and libmsvcrt.a.

Unfortunately, that now sticks me with another problem, which is that the real code I'm trying to build actually does need cygwin.dll!

Pat Galea
Thursday, November 27, 2003

Why not update the code to call a new reading function that takes a handle that of the correct type for the underlying system?

Were I in your situation, that is what I would do. Replace all "read" calls with "file_read" (or whatever), and forward it as appropriate. This would be a simple-ish search and replace, and would prevent any problems of this nature.

Insert half smiley here.
Thursday, November 27, 2003

*  Recent Topics

*  Fog Creek Home