Windows Embedded Compact 7服务和线程本地存储

时间:2014-08-12 09:15:51

标签: windows-services windows-ce thread-local-storage

我开发了Windows Embedded Compact 7(又名Windows Embedded CE 7)服务。 Boost.Log library用于记录,以防使用Boost Thread library。尝试从 xxx_IOControl 服务方法添加日志条目时崩溃。

经过一些调查后我发现,崩溃的原因是在thread.cpp Boost Thread源文件的add_thread_exit_function中取消引用NULL指针。 NULL指针本身的最初原因是 TlsGetValue/TlsSetValue Windows API函数可能不正确的行为。具体来说,在以下代码中:

BOOL setOk = TlsSetValue(tlsKey, originalData);
LPVOID returnedData = TlsGetValue(tlsKey);

在xxx_IOControl调用中被调用, returnedData为NULL,而originalData不为NULL ; setOk为TRUE,在TlsXXX函数返回ERROR_SUCCESS后调用GetLastError()。 在 xxx_Init 调用中调用的相同代码中,返回的data正确返回等于originalData值。在两个调用(xxx_Init和xxx_IOControl)中,使用相同的tlsKey,它们之间没有TlsFree个调用。

此外,还有一个事实可能是相关的。调用xxx_IOControl的线程没有调用DllMain(DLL_THREAD_ATTACH) - 在我的服务DLL和boost_thread.dll中。

Windows CE服务是否有一些特殊的线程机制?有没有人有任何可能有帮助的相关信息?

2 个答案:

答案 0 :(得分:1)

您是对的,Windows CE服务在CE 6及更高版本中作为驱动程序,用户模式驱动程序进行管理。 调用线程在不同进程的上下文中“迁移”(nk用于内核模式驱动程序,服务服务),并且可以在调用时访问其地址空间(当您在常规内部运行时通常不可能执行此操作)处理)。迁移与线程创建不同,这就是当线程迁移到您自己的进程地址空间时不调用DllMain的原因。 GetDirectCallerProcessId()将返回调用者的进程ID(在任何XXX_ * fnction中,而不仅仅是在XXX_IoControl内),但对您遇到的TLS问题没有多大帮助。另一种方法是使用字典来存储您当前使用TLS与线程关联的信息。当一个线程在另一个进程中关闭时你没有得到任何通知,所以你不知道什么时候可以释放特定线程的数据,但是你可以通过使用processid + threadid作为键并从你的内部字典中删除所有键来克服这个问题。与调用XXX_Close入口点时的特定进程相关(如果进程关闭句柄,它将无法再调用ioctl,并且当进程终止时,它的句柄将被关闭,因此您的XXX_close函数被调用一次) 。如果你有一个进程从不同的线程调用你的服务而没有关闭它们之间的句柄可能是一个问题,并在系统上产生一些内存泄漏。您还可以使用toolhelp库函数定期检查线程是否仍在运行,并删除非有效ID,并在收到调用后立即为该线程创建新上下文。

答案 1 :(得分:0)

进行了更多调查。看起来像Windows CE 7服务的xxx_IOControl(和其他一些)函数以某种特殊方式调用,特别是在不同进程的线程,而不是servicesd.exe进程,最初加载并调用xxx_Init函数。

这可以通过比较GetCurrentProcessId()和GetProcessIdOfThreadGetCurrentThread())的返回值来证明。前者总是返回servicesd.exe进程的ID,而后者在xxx_Init函数中返回相同的ID,但在xxx_IOControl函数中返回不同的ID。

也可以通过ToolHelp API枚举正在运行的线程来检查相同的事实。在xxx_Init调用中,THREADENTRY32结构的 th32OwnerProcessID th32CurrentProcessID 成员具有相同的当前线程值,而xxx_IOControl中的则为th32OwnerProcessID当前线程不同

最后,xxx_IOControl中的TlsSetValue / TlsGetValue函数使用TLS密钥调用,为不同的进程创建,因此无提示失败。 Since

  

TLS索引跨进程边界无效。一个DLL不能   假设在一个进程中分配的索引在另一个进程中有效   过程

<强>更新 基于Valter Minute answer我自己最终实现了Windows TlsXXX API。基本上,我在Boost线程库中创建了相应的函数,它根据字典实现线程本地存储功能,使用TLS索引,所有者进程ID和线程ID作为键来保存线程本地数据。然后我将所有对TlsXXX Windows API的调用更改为这些新功能。 Thread Local Storage API用于Thread,Log和Asio库。目前在我的实现中,仅在明确调用TLS自由函数的情况下才释放线程本地数据。如果拆下螺纹,则不进行处理。这适用于我的情况,其中API来自几个永久运行的线程。