Как правильно реализовать (c ++) локальное хранилище потоков в динамически загружаемой DLL? - PullRequest
3 голосов
/ 03 марта 2012

В этом случае моя динамически загружаемая dll загружается проводником Windows для добавления новой таблицы свойств (новой вкладки) на страницу свойств файла / папки.

Простым примером этого является StrmExt.dll ( источник загрузки ).В этом примере (источник предоставлен Microsoft) dll НЕ использует локальное хранилище потоков (TLS) и поэтому вызывает серьезные проблемы при загрузке нескольких страниц свойств одновременно.

При просмотре источника dll требуется один поток-base переменная (путь к файлу файла) ...

static TCHAR g_szFile[MAX_PATH];

Изменение этой строки кода на:

_declspec (thread) TCHAR g_szFile[MAX_PATH];

... позволило DLL поддерживать несколько потокови, следовательно, несколько экземпляров таблицы свойств.Тем не менее, я знал, что это изменение будет поддерживаться только Windows Vista и более новыми (тесты на Windows 7 были очень положительными).XP, например, не будет поддерживать это для динамически загружаемой библиотеки ... и известно, что вызывает сбой приложения .(см. последний абзац).

Для запуска на XP я не мог использовать это объявление.Я подозревал, что мне нужно было улучшить их точку входа DLL с:

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
{
    if (dwReason == DLL_PROCESS_ATTACH)
    {
        _Module.Init(ObjectMap, hInstance, &LIBID_STRMEXTLib);
        DisableThreadLibraryCalls(hInstance);
    }
    else if (dwReason == DLL_PROCESS_DETACH)
        _Module.Term();
    return TRUE;    // ok
}

... до чего-то подобного ... как мы видели ранее здесь

struct ThreadData {
    static TCHAR g_szFile[MAX_PATH];
};
...
DWORD g_dwThreadIndex;

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, 
                      DWORD dwReason, LPVOID /*pReserved*/)
{
    ThreadData* pData;   
    switch (dwReason) {
        case DLL_PROCESS_ATTACH:

            g_dwThreadIndex = ::TlsAlloc();
            if (g_dwThreadIndex == TLS_OUT_OF_INDEXES)
                return FALSE;

           // execute the DLL_THREAD_ATTACH code

        case DLL_THREAD_ATTACH:

            // allocate memory for this thread
            pData = (ThreadData*) ::LocalAlloc(LPTR, sizeof(ThreadData));
            if (pData == 0)
                return FALSE;

            ::TlsSetValue(g_dwThreadIndex, (LPVOID) pData);
            break;

        case DLL_THREAD_DETACH:

            // release memory for this thread
            pData = (ThreadData*) ::TlsGetValue(g_dwThreadIndex);
            if (pData != 0)
                ::LocalFree((HLOCAL) pData);
            break;

        case DLL_PROCESS_DETACH:

            // release memory for this thread
            pData = (ThreadData*) ::TlsGetValue(g_dwThreadIndex);
            if (pData != 0)
                ::LocalFree((HLOCAL) pData);
            // release the TLS index
            ::TlsFree(g_dwThreadIndex);
            break;
    } 
    return TRUE;
}

Это прекрасно работает во время первой загрузки DLL, независимо от того, создаю ли я 1 или 2 потока.После освобождения библиотеки DLL при следующей загрузке библиотеки происходит сбой Explorer.

Что я неправильно понимаю?Я заметил, что оригинальный разработчик намеренно отключил уведомление о потоке при уведомлении о прикреплении процесса к dll.Зачем?

DisableThreadLibraryCalls(hInstance);

Спасибо заранее.

1 Ответ

1 голос
/ 09 марта 2012

В этом случае проблему лучше всего избегать вообще.Да, у вас, вероятно, будет больше потоков, чем процессов, и да, каждый лист свойств будет связан только с одним потоком, но обратное не гарантируется.Два листа свойств могут совместно использовать один поток, это зависит от ОС.(И такие недокументированные решения меняются между версиями.)

Вместо этого используйте lParam член PROPSHEETPAGE.Он достаточно большой, чтобы содержать указатель, также в 64-битных системах.Укажите это на свой собственный класс.Управление временем жизни намного проще, чем попытка подключить / отключить DLL;Windows звонит вам PropSheetPageProc в нужный момент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...