views:

139

answers:

2

I have a Dllmain that allocates Thread local storage when a thread attaches to this DLL. Code as below:

BOOL APIENTRY DllMain(HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)
{
    LPVOID lpvData; 
    BOOL fIgnore; 

    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        onProcessAttachDLL();
        // Allocate a TLS index.
        if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
            return FALSE; 
        // how can it jump to next case???
    case DLL_THREAD_ATTACH:
        // Initialize the TLS index for this thread.
        lpvData = (LPVOID) LocalAlloc(LPTR, MAX_BUFFER_SIZE); 
        if (lpvData != NULL) 
            fIgnore = TlsSetValue(dwTlsIndex, lpvData);  
        break; 
    ...

}

I know that for the main thread, the DLL_THREAD_ATTACH is not entered, as per Microsoft Documentation. However, the above code worked. I am using VC2005. When I entered the debugger, I saw that after it entered DLL_THREAD_ATTACH case when ul_reason_for_call = 1! How can that happen? If I add `break' at the end of DLL_PROCESS_ATTACH block, the DLL failed to work.

How can this happen?

+7  A: 

If I understand you correctly, you are wondering why, after entering the DLL_PROCESS_ATTACH case, the execution continues on the DLL_THREAD_ATTACH case, instead of after the end of the switch.

This behaviour is called "fall through", and it is standard C. Without an explicit break statement, the execution continues on the next case.

Of course, it is quite counterintuitive for programmers who see it the first time, so it is a constant source of misunderstanding and even bugs (you may not always know whether the break was left out intentionally, or by mistake). It is considered good practice, therefore, when using this construct, to mark it explicitly with a comment, like:

switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
    onProcessAttachDLL();
    // Allocate a TLS index.
    if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES) 
        return FALSE; 
    // fall through
case DLL_THREAD_ATTACH:
    // Initialize the TLS index for this thread.
    lpvData = (LPVOID) LocalAlloc(LPTR, MAX_BUFFER_SIZE); 
    if (lpvData != NULL) 
        fIgnore = TlsSetValue(dwTlsIndex, lpvData);  
    break; 
...
Péter Török
Thanks. Although I have multi-year experience with C/C++, I never wrote such a construt. I always put break in each branch or multiple cases (sharing the same code block). It looks weird to me at the first.
Sherwood Hu
@Sherwood Hu - Yes, it is weird at first sight :-) I realize that my wording might have felt degrading to you, so I reworded it. Now that you got a satisfying answer to your question, may I ask you to accept it, in line with SO conventions? Thanks.
Péter Török
+1  A: 

Do you understand how switch statements work? if you do NOT put a break at the end of the case, then the code just continues into the next case:

switch (3)
{
case 3:
   cout << "3";
case 4:
   cout << "4";
}

prints both 3 and 4.

tony