windows - Hvordan implementerer du korrekt (c ++) tråd lokal opbevaring i en dynamisk indlæst DLL?

Indlæg af Hanne Mølgaard Plasc

Problem



I dette tilfælde er min dynamisk indlæste DLL indlæst af Windows Stifinder for at tilføje et nyt egenskabsark (ny fane) til siden for fil/mappeegenskaber.


Et simpelt eksempel på dette er StrmExt.dll (download source). I dette eksempel (kilde leveret af Microsoft) bruger DLL IKKE tråd lokal opbevaring (TLS) og forårsager derfor store problemer, når du lægger flere ejendoms sider på samme tid. [9]


Efter at have gennemgået kilden krævede dll en trådbaseret variabel (filens filsti) ...


static TCHAR g\_szFile[MAX\_PATH];


Ændring af denne ene linje med kode til:


\_declspec (thread) TCHAR g\_szFile[MAX\_PATH];


... aktiveret dll'en til at understøtte flere tråde og derfor flere forekomster af egenskabsarket. Jeg vidste imidlertid, at denne ændring kun kunne understøttes af Windows Vista og nyere (tests på Windows 7 har været meget positive). XP, for eksempel, ville ikke understøtte dette for et dynamisk indlæst bibliotek ... og det er kendt at nedbryde applikationen. (se sidste afsnit). [10]


For at kunne køre på XP kunne jeg ikke bruge denne erklæring. Jeg formodede, at jeg havde brug for at forbedre deres DLL-adgangspunkt fra:


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
}


... til noget som dette ... som tidligere set her [11]


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;
}


Dette virker fint under den første belastning af dllet, om jeg laver 1 eller 2 tråde. Efter at dll'en er frigjort, kolliderer Explorer ved den næste belastning på biblioteket.


Hvad forstår jeg misforståelse? Jeg bemærkede den oprindelige udvikler med viljeaktiveret trådbesked på dll-processen vedhæfte underretning. Hvorfor?


DisableThreadLibraryCalls(hInstance);


På forhånd tak.

Bedste reference


I dette tilfælde er det bedst at undgå problemet. Ja, du har nok flere tråde end processer, og ja, hvert ejendomsark vil kun være forbundet med en tråd, men det omvendte er ikke garanteret. To ejendomsark kan dele en enkelt tråd, der er op til OS.
(Og sådanne ubestemte beslutninger ændres mellem versioner).


Brug i stedet lParam medlem af PROPSHEETPAGE. Det er stort nok til at holde en peger, også på 64 bits systemer. Peg det til din egen klasse. Livstidsstyring er meget enklere end DLL'en vedhæft/loser, du forsøgte; Windows kalder din PropSheetPageProc til højre øjeblik. [12]