| I've encountered problems instantiating the libhttrack.dll
dynamically, that is using the Windows LoadLibrary API and
then trying to invoke HTTrack. Crazy enough, I can call
hts_init, and for example hts_is_available, but as soon as
I tried to call hts_main I get a GPF. However, when I
statically link the DLL, there's no problem and everything
works OK. Yet I needed the LoadLibrary functionality so
this didn't really solve my problem.
Eventually I managed to recompile the DLL with debugging
information, and it turned out that a simple call to
hts_main resulted in a GPF in a function named concat
(htslib.c). It seems to do some fancy 'strcat'-like
mathingy I don't fully understand, but the GPF was inside
the NOSTATIC_RESERVE macro. Anyway, after reverse-
understanding this macro I found the following in MSDN:
"If a DLL declares any nonlocal data or object as
__declspec( thread ), it can cause a protection fault if
dynamically loaded. After the DLL is loaded with
LoadLibrary, it causes system failure whenever the code
references the nonlocal __declspec( thread ) data. Because
the global variable space for a thread is allocated at run
time, the size of this space is based on a calculation of
the requirements of the application plus the requirements
of all of the DLLs that are statically linked. When you
use LoadLibrary, there is no way to extend this space to
allow for the thread local variables declared with
__declspec( thread ). Use the TLS APIs, such as TlsAlloc,
in your DLL to allocate TLS if the DLL might be loaded
with LoadLibrary."
And that is exactly the problem: the macro uses compiler
supported thread local storage (using __declspec(thread))
and this indeed causes "problems" as Microsoft
acknowledges above.
I think I now have solved the problem (following
Microsoft's advice) by changing the NOSTATIC_XRESERVE
macro (htsnostatic.h) as follows:
#define NOSTATIC_XRESERVE(name,type,nelt) \
{ \
static DWORD tlsIndex = 0; \
static int initValue = 0; \
if (initValue == 0) \
{ \
name = (type*)malloc(sizeof(type)*nelt); \
assert(name != 0); \
memset(name, 0, sizeof(type)*nelt); \
tlsIndex = TlsAlloc(); \
assert(tlsIndex != 0xFFFFFFFF); \
TlsSetValue(tlsIndex, name); \
initValue = 1; \
} \
else \
{ \
name = TlsGetValue(tlsIndex); \
assert(name != 0); \
} \
}
Using this version of the macro I can use the DLL both in
a static and in the dynamic way using LoadLibrary. I've
only tested this quickly and in a rather limited scenario
but it might be useful to incorporate this change into the
DLL version.
/d | |