Windows services Windows嵌入式Compact 7服务和线程本地存储

Windows services Windows嵌入式Compact 7服务和线程本地存储,windows-services,windows-ce,thread-local-storage,Windows Services,Windows Ce,Thread Local Storage,我开发了一个WindowsEmbeddedCompact7(又名WindowsEmbeddedCE7)服务。用于日志记录,在本例中使用。试图从服务方法添加日志项时崩溃 经过一些调查,我发现崩溃的原因是在thread.cpp Boost thread源文件中的add_thread_exit_函数中取消对空指针的引用。空指针本身的最初原因可能是Windows API函数的行为不正确。具体而言,在以下代码中: BOOL setOk = TlsSetValue(tlsKey, originalData)

我开发了一个WindowsEmbeddedCompact7(又名WindowsEmbeddedCE7)服务。用于日志记录,在本例中使用。试图从服务方法添加日志项时崩溃

经过一些调查,我发现崩溃的原因是在thread.cpp Boost thread源文件中的add_thread_exit_函数中取消对空指针的引用。空指针本身的最初原因可能是Windows API函数的行为不正确。具体而言,在以下代码中:

BOOL setOk = TlsSetValue(tlsKey, originalData);
LPVOID returnedData = TlsGetValue(tlsKey);
在xxx_IOControl调用中调用时,returnedData为NULL,而originalData不为NULL;setOk为TRUE,在两个TlsXXX函数返回ERROR\u SUCCESS后调用GetLastError()。 在调用中调用的同一代码中,返回的数据正确地等于originalData值。在两个调用(xxx_Init和xxx_IOControl)中使用相同的tlsKey,它们之间没有调用

此外,还有一个事实可能与此有关。对于调用xxx_IOControl的线程不调用,无论是在我的服务DLL还是boost_thread.DLL中


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

更多的调查已经完成。似乎Windows CE 7服务的xxx_IOControl(和其他一些)函数是以某种特殊方式调用的,特别是在不同进程的线程中调用的,而不是servicesd.exe进程,该进程最初加载并调用xxx_Init函数

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

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

最后,xxx_IOControl中的TlsSetValue/TlsGetValue函数将使用为不同进程创建的TLS密钥进行调用,从而以静默方式失败

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

更新:
基于这一点,我最终自己实现了WindowsTLSxxAPI。基本上,我在Boost线程库中创建了相应的函数,它以字典的形式实现线程本地存储功能,该字典使用TLS索引、所有者进程id和线程id作为键保存线程本地数据。然后我将所有对TlsXXX Windows API的调用都更改为这些新函数。线程本地存储API用于线程、日志和Asio库。目前在我的实现中,只有在显式调用TLS free函数的情况下,才会释放线程本地数据。如果螺纹脱落,则不进行处理。在我的情况下,API是从几个永久运行的线程中使用的。

没错,Windows CE服务在CE 6和更高版本中作为驱动程序、用户模式驱动程序进行管理。 调用线程在不同进程的上下文中“迁移”(nk用于内核模式驱动程序,服务用于服务),并且可以在调用期间访问其地址空间(当您在常规进程中运行时,这通常是不可能的)。迁移与线程创建不同,这就是为什么当线程迁移到您自己的进程地址空间时不调用DllMain。 GetDirectCallerProcessId()将返回调用方的进程id(在任何XXX_*fn操作中,而不仅仅是在XXX_IoControl中),但对您遇到的TLS问题没有多大帮助。另一种方法是使用字典存储当前与使用TLS的线程关联的信息。当一个线程在另一个进程中关闭时,您不会收到任何通知,因此您不知道何时可以发布特定线程的数据,但您可以通过使用processid+threadid作为键,并在调用XXX\U Close入口点时从内部字典中删除与特定进程相关的所有键来克服这一问题(如果进程关闭句柄,它将无法再调用ioctls,并且当进程终止时,它的句柄将被关闭,因此XXX\u close函数将被调用一次)。如果您有一个进程从不同线程调用您的服务,而没有关闭其间的句柄,则可能会出现问题,并在系统上产生一些内存泄漏。您还可以使用toolhelp库函数定期检查线程是否仍在运行,并删除无效ID,并在启动后立即为线程创建新上下文他给我打了个电话