A Handy Guide To Handling Handles |
|
I'm working on this essay. However, the information that is already here is sufficiently useful that I decided to post in, in spite of the fact that I've only covered a small part of handles. Over the next few months, I'll be enhancing it. So be patient; more is coming.
I have a FILE *. Or I have a CFile or CStdioFile. What is its handle? And what handle is it?
There are several representations of handles for files at the application level. There are the file handles delivered by the C library, there are FILE * objects, and there are operating system file handles. They are all interchangeable, but only if you know what you are doing.
They are not necessarily interchangeable on an instant's notice, because of issues such as buffering. Thus if you reach into a FILE * and grab the underlying operating system handle and do a WriteFile, you will probably end up with a seriously corrupted file because there is buffered data being managed by the FILE * object. Therefore, unless you understand what needs to be done to flush buffers and maintain consistency in the various file handle images of the file contents and position, you are going to end up in serious trouble.
More typically, you have a fresh, newly-opened handle of one type and wish to now associate it with a representation more suitable for your task. For example, the C library function fopen does a pitiful job of handling file sharing, a concept which was nonexistent on the Unix operating system in which it was first specified. You want to use the full Win32 file sharing, but don't want to have to do raw WriteFile operations. And maybe you can't because you are retrofitting something into an existing, possibly portable-across-operating-systems source set, and you're writing the OS-specific module. So you can get a genuine Win32 HANDLE from CreateFile, and you want to associate it with a FILE * so you can now use it conveniently. Or even associate it with a C++ fstream. Keep reading!
C library "handles" are small integers that index into a table in the C runtime library. The C library has traditionally limited the number of simultaneously open files to some very restrictive number of handles, such as 16. This is no longer true in Win32. The C library in Win32 now supports up to 2048 low-level handles. The default is to allow a maximum of 512 FILE * objects, although you can easily change this limit up to the maximum of 2048 by calling _setmaxstdio.
If you don't see the transformation you need in the table below, you will need to use a combination of the transformations. For example, to transform a HANDLE to an fstream, you need to do something like
HANDLE h = ::CreateFile(...); fstream f; f.attach(_open_osfhandle(h));
From | Source | To | use |
HANDLE | Win32
CreateFile |
C Library handle | _open_osfhandle |
CFile | CFile::CFile | ||
C Library handle | <io.h>
_open |
HANDLE | _get_osfhandle |
FILE * | _fdopen | ||
fstream | fstream::fstream or fstream::attach |
||
FILE * | <stdio.h>
fopen |
C Library handle | _fileno |
CStdioFile | CStdioFile::CStdioFile | ||
CFile | MFC | HANDLE | m_hFile data member |
CStdioFile | MFC | FILE * | m_pStream data member |
stdxxx | Win32 | HANDLE | GetStdHandle |
fstream | C++ Library | C Library handle | .fd method of fstream |
<io.h> int _open_osfhandle(long oshandle, int flags)
This takes a HANDLE value and returns a small integer that can be used to interface with the C library. The flags value includes O_APPEND, O_RDONLY, and O_TEXT. Note that this prototype assumes that a long and a HANDLE are the same size, and you will have to do a cast to get it past the compiler, for example
int h = _open_osfhandle((long) myhandle, 0);
It is not clear at this point to me what Microsoft will do in Win64 for this library call, because handles in Win64 (I think) will be 64 bits wide.
Given a C library file handle, you can convert it to a FILE* by calling the function _fdopen, passing in the C library file handle and getting in return a FILE *.
<stdio.h> int _fdopen(int filehandle, const char * mode)
where mode is any of the mode values you can provide to fopen, such as "r", "rw", "w", etc.
Give a FILE * object, you can obtain the underlying C library handle by using _fileno
<stdio.h> int _fileno(FILE * f)
<io.h> long _get_osfhandle(int filehandle)
This takes a C library file handle and returns the underlying Win32 HANDLE.
Underlying a CFile object is a system file handle. Sometimes. Maybe. In a raw CFile, the m_hfile member is a system file handle. However, Microsoft cautions that this may change in a derived class.
To associate a CFile object with an operating system file handle, you use the CFile constructor of the form
CFile::CFile(HANDLE h)
Whether you do this in a stack variable or use heap allocation depends on the nature of your application.
CFile file(myhandle);
or
CFile * file = new CFile(myhandle);
The m_pStream member of CStdioFile is the reference to the FILE * object that underlies it.
To associate a CStdioFile object with an operating system file handle, you use the CStdioFile constructor of the form
CStdioFile::CStdioFile(FILE * f)
Whether you do this in a stack variable or use heap allocation depends on the nature of your application.
CStdioFile file(myfile);
or
CStdioFile * file = new CStdioFile(myfile);
If you need a handle to stdin, stdout, or stderr, without using the stdio.h library, you can use the API call GetStdHandle, specifying one of the constants, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, or STD_ERROR_HANDLE. These return a HANDLE value which can be used for ReadFile, WriteFile, or with any of the above.
If you have a C Library handle (the small integer from _open or _sopen) and need a C++ fstream-class object, take a look at the fstream constructor. One form takes a C file descriptor:
fstream(filedesc fd)
You can also use the fstream::attach method to attach a file descriptor to an existing fstream:
fstream f; f.attach(fd);
For this to work, there must not be a file descriptor already attached to the fstream.
To obtain the underlying C library handle from an fstream, use the .fd method
fstream f; f.attach(fd) ASSERT(fd == f.fd())
When you create a process, you can set a flag in the CreateProcess call telling the new process that it inherits the inheritable handles of its parent. Typically, these will be the stdin, stdout and stderr handles (for example, see my Process Class essay). But you can inherit any other handles. There is no selection here; the new process either inherits all the inheritable handles, or no inheritable handles.
HANDLE process = ::CreateProcess(name, cmdline, NULL, NULL, // security attributes TRUE, // inherit handles! 0, NULL, NULL, // flags, env, directory &startup, &procinfo);
You can pass the stdin, stdout and stderr handles in via the STARTUPINFO, but you are on your own to pass in any other handles you want. You could send messages to the new process, or write them to a file the new process uses, or put them as text values on the command line, or pass them via stdin or a named pipe, shared memory via a DLL or memory-mapped file, or whatever you want to do. But there is no "automatic" mechanism to get these handle values into the new process. The handles will exist in the new process, but the process will not know the values to use for them unless you tell it what those numeric values are.
The one thing you are guaranteed about inheritance is that the handles will have the same numeric value in both your process and the inheriting process.
The following handles exist. Note that only some are inheritable, and to be so, they must be created with an inheritable attribute. If a handle is not created as inheritable, you can create an inheritable duplicate using DuplicateHandle.
Handle type | Source | Inheritable? |
File | CreateFile | Yes |
Device | CreateFile | Yes |
Anonymous pipe | CreatePipe | Yes |
Mailslot | CreateFile | Yes |
Console input buffer | implicitly created by system when a console app is spawned | Yes |
Console screen buffer | CreateConsoleScreenBuffer | Restricted |
Process | CreateProcess | Yes |
Thread | CreateThread (_beginthread, _beginthreadex, AfxBeginThread) | Yes |
Mutex | CreateMutex | Yes |
Event | CreateEvent | Yes |
Semaphore | Semaphore | Yes |
Named pipe | CreateNamedPipe | Yes |
File mapping | CreateFileMapping | Yes |
Local memory handle | LocalAlloc | Never |
Global memory handle | GlobalAlloc | Never |
Heaps | GetDefaultHeap, CreateHeap | Never |
Pseudo-handles | GetCurrentProcess, GetCurrentThread | Never1 |
Module | LoadLibrary | Never |
GDI handles | CreatePen, CreateSolidBrush, CreateHatchedBrush, CreatePatternBrush, CreateRgn, CreatePalette, CreateFont, CreateFontIndirect | Never |
USER handles | CreateMenu, CreatePopupMenu | Never |
Window handles | CreateWindow | Always2 |
FindFile | FindFirstFile | Never |
1Pseudo-handles
can be converted to real, inheritable handles using DuplicateHandle
2Window handles are global and implicitly known system-wide |
Note that an inheritable handle is passed to a process at the time the process is created. If you create an inheritable handle later, it is not automatically inherited by existing processes that were created from the process that spawned them. However, you can make these handles available to another process that already exists by using DuplicateHandle, and then notifying the process in some what what the value of the handle now is.
If you have a non-inheritable handle that could be inherited, you can create an inheritable handle from it using the DuplicateHandle API. Here's the complete definition
BOOL DuplicateHandle( HANDLE hSourceProcessHandle, // handle to source process HANDLE hSourceHandle, // handle to duplicate HANDLE hTargetProcessHandle, // handle to target process LPHANDLE lpTargetHandle, // duplicate handle DWORD dwDesiredAccess, // requested access BOOL bInheritHandle, // handle inheritance option DWORD dwOptions // optional actions ); |
In the simplest case, you use ::GetCurrentProcess() for both the source handle and destination handle. This returns a pseudo-handle that is recognized as meaning "this process". To make a simple duplicate, do
HANDLE result; ::DuplicateHandle(handle, ::GetCurrentProcess(), // source handle and process &result, ::GetCurrentProcess(), // target handle location and process 0, // new access, ignored TRUE, // make inheritable DUPLICATE_SAME_ACCESS); // exact duplicate
Because I only need an exact duplicate, I used DUPLICATE_SAME_ACCESS and didn't need to specify the desired access, which would be ignored. The newly-created handle stored in result will now be inherited by any process created by CreateProcess with the inherit parameter set to TRUE.
If you wanted to retroactively create a handle in another process, you can do DuplicateHandle with the handle of the target process as the third parameter. You will then have to notify that process in some fashion what the value of the handle is. You will not be able to use the result handle in the current process, but its numeric value needs to be communicated to the target process.
Note that not all handles can be arbitrarily made visible to another process. For example, a console buffer handle can only be inherited by a child process, not passed to an arbitrary process.
MFC represents handles by "wrapping" them in a C++ structure. For example, the handle to a window, an HWND, is wrapped in a CWnd object (or in most cases, an object derived from CWnd, such as a CMainFrame, CMDIChildWnd, or most commonly a class derived from CView or CDialog, among the most common examples). A HANDLE to a thread is in a CWinThread-derived class. An HPEN is in a CPen class, an HBRUSH is in a CBrush class, and so on.
This leads to some confusion among those learning MFC. The existence of the C++ object does not imply the existence of the underlying HANDLE and kernel object. These are separable concerns.
Therefore, it is critical to realize that when you instantiate a C++ object, you have only done one of the two steps required to create a functional entity. You have a memory representation which could hold a handle. It probably does not. You must then instantiate the kernel object and bind the C++ object and kernel object together by storing the handle in the C++ object.
In the case of windows, this is done by some seriously heavy-duty magic involving per-process hook functions. The bottom line is that the act of creating the window binds the handle to the object.
Some GDI constructors will create a handle. For example
CBrush br;
does not create a brush; it declares a variable and creates a CBrush object and nothing else. You could not use this to draw. If you want a brush, you could do
br.CreateSolidBrush(RGB(255,0,0));
to cause the kernel to create an HBRUSH and bind it to the CBrush. But you could also use the constructor form
CBrush br(RGB(255,0,0));
which invokes a constructor that creates the brush itself and does the binding. But don't be fooled: the declaration only declared the C++ object; the overloaded constructor that took a COLORREF parameter is what invoked CreateSolidBrush to implicitly complete the second step of the creation.
We will ignore GDI handle objects for the moment; they don't suffer from many of the more serious problems of other handles.
Whenever a message is intercepted, for example, a WM_COMMAND message from a control in a dialog, MFC must locate the instance of the parent window in order to call its methods using the correct this parameter. When a message is sent to a child control you have subclassed, the same problem occurs. How is this done?
MFC maintains a table called the "handle map", into which it puts pointers to each CWnd-derived class object. The exact details of the implementation don't matter (you can read the code), but the key idea is that by using this map, when presented with an HWND, the map returns the corresponding CWnd * reference.
What if there is no CWnd * reference? For example, suppose I wrote
CWnd * wnd = GetDlgItem(IDC_WHATEVER);
in my dialog (I would probably never do this; see my essay on Avoiding GetDlgItem, but I want to illustrate a point here and this is the simplest example I can come up with). There is no existing CWnd-derived object in my application that represents this. What happens?
MFC creates a temporary object to represent this window. It is allocated, its m_hWnd member is set to be the handle that the low-level ::GetDlgItem returned, and a pointer to this temporary object is returned. At some point in the future, this temporary object will be "garbage-collected", so you must not store this pointer for future use. You can assume that if you have a local variable into which you store it the validity of the pointer is the validity of the local variable that holds it (this is an approximation that is a good enough rule that you can usually rely on it).
If there is already a CWnd object (for example, you had the declaration in your dialog header file
CEdit c_InputName;
then you will get a reference to that object, and a temporary object will not be created.
However, there are some serious problems here. They do not arise until you start playing at the boundaries of the model MFC supports.
There are three ways to get a CWnd-derived object reference from a handle:
These have quite different effects and implications.
When you force MFC to create a temporary object, that is the only representation of that object. There is no other reference to the object. In particular, it is a "bare" CWnd reference. You cannot attach any other interpretation to it, for example, by casting (see the discussion of threads, below). The next time the application returns to the message loop, the CWinApp::OnIdle handler will go screaming down the list of temporary handles, deleting each object. So if you've retained the pointer, it is now pointing to garbage. The space may be later reallocated to some other purpose. Your program will fail, most likely in an ASSERT on an ::IsWindow test. So you can feel free to create local variables that refer to these handles and use them, but assume that as soon as you go out of scope, the handle can no longer be trusted to exist.
If you have a handle, you can use the FromHandle method of the appropriate class to get an object that wraps that class. For example, if you call CWnd::FromHandle(hwnd), and it will give you a bare CWnd * reference. The reference will remain valid (unlike the temporary pointer) until you delete it. However, when you delete a C++ object that contains a handle, the kernel object is also destroyed. If you call CMyWnd::FromHandle, you will get a CMyWnd object with all its class member variables. and will be free to use them. However, they will be variables in a completely new instance of the object (see the dangers I discuss in the threading section, below).
If you do CWnd::Attach, you attach the handle to an existing object. That is,
CMyWnd * wnd = new CMyWnd; wnd->Attach(hwnd);
is essentially the same as
CMyWnd * wnd = CMyWnd::FromHandle(hwnd);
But note that you don't need to do new; in fact, there is an over-reliance on new among programmers who seem to have forgotten that the & prefix operator exists. I could declare a member variable in my class
CEdit * c_Name;
and somewhere I could do
c_Name = CEdit::FromHandle(::GetDlgItem(m_hWnd, IDC_NAME));
or
c_Name = new CEdit; c_Name->Attach(::GetDlgItem(m_hWnd, IDC_NAME));
if I wanted a permanent reference, but it would make a whole lot more sense to write
CEdit c_Name;
as the declaration and simply write
c_Name.Attach(::GetDlgItem(m_hWnd, IDC_NAME));
When you use the ClassWizard to create a control member variable, which places a DDX_Control call in your DoDataExchange handler, this is in effect the line that gets executed, so it rarely makes sense to actually use Attach, or even class::FromHandle in most contexts.
The most common use of FromHandle is to take an HDC, such as you might get in a DrawItem handler, and obtain a CDC * that wraps it, which makes drawing in your handler much easier.
The most common set of problems arise because the handle-to-object map is maintained on a per-thread basis. This means that if you have a CWnd-derived class known in one thread, it is not known to other threads.
This is the source of the commonly-held myth that you must not pass a CWnd reference across thread boundaries. It is a myth, but it is often cited because irresponsible use of the CWnd object in the thread will also be fatal. With only a moderate amount of care, this restriction does not apply.
One of the most serious implications of believing in this myth goes like this: since I can't pass the CWnd object across the thread handle, I will pass its HWND (this is what the myth recommends). Now I want to modify the value of one of the variables in the CWnd object from within the thread.
The following code cannot possibly work!!!
/* static */ UINT CMyClass::mythreadfunc(LPVOID p) { HWND hwnd = (HWND)p; // avoid passing CWnd across! (See Microsoft warnings!) CMyClass * me = CMyClass::FromHandle(hwnd); while(me->running) { /* take action */ } /* take action */ return 0; }
Why doesn't this work? Because the FromHandle call creates an entirely new instance of a CMyClass object! Therefore, if you have a Stop button that sets running to FALSE, it sets the value in the instance of CMyClass that exists in the main GUI thread to FALSE, and the instance that has been created in the thread is not affected. Exactly what that test is testing is pure guesswork (in the debug version, it will always be seen as a "true" value because it will probably be initialzied to 0xCDCDCDCD; in the release version, who knows? It might be FALSE and the loop will do nothing!)
Of course, it is even worse if you do something that creates a temporary CWnd reference. In this case, the variable does not even exist; your cast causes it to be misinterpreted as a type it is not, and you will be accessing random memory. For example, if you were to write in your thread
CMyControl * ctl = (CMyControl *)::GetDlgItem(hDlg, IDC_MY_LISTBOX); ctl->MySpecialVariable = FALSE; // disable my special feature
who knows what memory you have set to FALSE? You will damage some piece of memory, and if you are lucky, at some future time you will get an ASSERT from within the storage allocator claiming that you have corrupted storage blocks. If you are not lucky, this will not be noticed until your product has been in the field for six months. What happened? Well, MFC created a temporary CWnd * object. It has none of your fields in it. But you've lied to the compiler. You told it that ctl was really to be viewed as a CMyControl object. It isn't. The compiler trusts you, and knows that at offset 88 in the object it will find MySpecialVariable. It happily stores something there! But suppose a CWnd isn't even 88 bytes long? Then you store off the end of the object.
If I want to pass an instance of my CWnd class across a thread boundary, I just pass it. I share variables in it between the main GUI thread and the running thread. As long as you mark them as volatile, properly synchronize access with a CRITICAL_SECTION or mutex, and do nothing that involves the HWND other than PostMessage, you are safe.
A common problem arises when using CAsyncSocket (if you are using CSocket, you are in such deep trouble you will never recover. Do not use CSocket. I will ignore its existence) in a thread.
The first problem is usually the result of creating a worker thread using AfxBeginThread(func, param). This cannot work. A CAsyncSocket requires a message pump. Therefore, you must create a "UI" thread (a lousy name; it should be called "a worker thread with a message pump"). Failure to use a UI thread means your socket won't work.
Suppose you have used a UI thread. Your main handler looks like
void CMySocket::OnAccept(DWORD err) { ... deal with err CMySocket * talker = new CMySocket; Accept(*talker); AfxBeginThread(RUNTIME_CLASS(MyTalkerThread), 0, talker); }
The first time a message comes in, your application suffers an ASSERT, somewhere deep in the innards of CAsyncSocket code. What happened?
When the socket is able to perform a requested operation (such as OnSend or OnReceive), it sent or posted a message to the thread which is associated with the operation; this message is intercepted by the message dispatch logic. This sends the message off to the CAsyncSocket logic, which attempts to locate the CAsyncSocket object that is associated with the SOCKET handle that was sent with the message.
It looks in the thread's local socket map. It finds nothing. That's because you created the socket in the main GUI thread, and established the mapping from the SOCKET to the CMySocket object in that thread, and that thread alone.
The way of dealing with this is a bit clumsy, because you can't have constructor parameters on a UI thread object. It looks like this
void CMySocket::OnAccept(DWORD err) { ... deal with err CAsyncSocket s; Accept(s); CMyTalkerThread * talker = (CMyTalkerThread *)AfxBeginThread( RUNTIME_CLASS(CMyTalkerThread), THREAD_PRIORITY_NORMAL, 0, // default stack size CREATE_SUSPENDED) talker->sock = s.Detach(); talker->ResumeThread(); }
Now, in the CMyTalkerThread, you need to use the InitInstance handler. First, have declared in your thread class, the member variable
public: SOCKET sock; protected: CMyOwnAsyncSocket talkingsocket;
then in InitInstance do
BOOL CMyTalkerThread::InitInstance() { ... other stuff you might want talkingsocket.Attach(sock); ...more stuff return TRUE; }
Now when you perform operations on the socket, the responses will be routed to the correct instance of your socket object.
Typically you will also include a CWnd * reference to a window in your main GUI thread that is supposed to receive the notifications you want to post from your socket.
Under construction...
9-Jan-03 Added discussion of handle inheritance and DuplicateHandle
14-Jun-03 Added discussion of per-thread handle maps in MFC.
The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.