1

我很好奇在访问返回可变长度结构的 Win32 API 时使用联合是否是个好主意,以避免手动管理分配的内存。

考虑以下:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    static union //Don't care about being thread safe
    {
        QUERY_SERVICE_CONFIG svcConfig;
        unsigned char bufferSizer[8000];
    };
    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
            OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
            serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                            &svcConfig, 8000, &garbage);
        //Use svcConfig for something here.
    }
}

这有什么大问题吗?

4

4 回答 4

4

您的联合方法的一个问题是,使用联合的整个想法是相当强制且完全没有必要的。您似乎想要做的就是用一些“大”大小的本地静态缓冲区替换动态内存分配。然后明确地做

unsigned char buffer[8000];    
QUERY_SERVICE_CONFIG *svcConfig = (QUERY_SERVICE_CONFIG *) buffer;
...
QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()), 
  svcConfig, sizeof buffer, &garbage);
// Use `svcConfig` here, keeping in mind it is a pointer now

我不清楚你试图通过专门使用 union 来实现什么。你认为工会的方法更优雅吗?我不这么认为。

更新:在其中一条评论中指出,据称这种方法可能不适用于执行严格别名优化的编译器。由于不止一个原因,该评论绝对不正确。首先,上述方法根本不依赖于任何依赖于别名的行为,因为所有访问都应该仅通过svcConfig指针和svcConfig指针执行。其次,如果别名数据是字符数组,则永远不会执行严格的别名优化。语言规范明确允许通过字符数组进行别名访问,这就是为什么所有优化编译器在涉及字符数组时都禁用其严格的别名优化。任何不这样做的编译器都会损坏,因此不值得考虑。

于 2009-12-16T02:49:32.723 回答
3

据我所知,没有任何重大问题。但不是使用联合,我认为只是声明缓冲区然后使用指针来获取你想要的结构会更清楚。

我想我从你的例子中并没有真正得到的是这样做的意义所在。

编辑:我的意思是,这样做:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    unsigned char bufferSizer[8000];
    QUERY_SERVICE_CONFIG *pSvcConfig = reinterpret_cast<QUERY_SERVICE_CONFIG*>(bufferSizer);

    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
            OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
            serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                            pSvcConfig, sizeof(bufferSizer), &garbage);
        //Use pSvcConfig for something here.
    }
}

至少对我来说,这更清楚。

于 2009-12-16T01:27:45.517 回答
1

除了使用比必要更多的堆栈空间外,没有。

更新:

是的,完全错过了static关键字,对不起。

只要您不需要从多个线程中使用它,它就可以正常工作,并且该函数不可能重入。

于 2009-12-16T01:21:52.043 回答
0

另一种方式,只需使用基本类型的静态数组:

void displayServices(std::wostream& log, std::tr1::shared_ptr<void> manager, LPENUM_SERVICE_STATUS currentServiceToDisplay, DWORD number, bool whitelist)
{
    static QUERY_SERVICE_CONFIG svcConfig[8000/sizeof(QUERY_SERVICE_CONFIG)]; // Or just pick an arbitrary number

    for(DWORD idx = 0; idx < number; currentServiceToDisplay++, idx++)
    {
        DWORD garbage = 0;
        std::tr1::shared_ptr<void> currentServiceHandle(
                OpenService(reinterpret_cast<SC_HANDLE>(manager.get()), currentServiceToDisplay->lpServiceName, SERVICE_QUERY_CONFIG),
                serviceCloser);
        QueryServiceConfig(reinterpret_cast<SC_HANDLE>(currentServiceHandle.get()),
                                                svcConfig, sizeof(svcConfig), &garbage);
        //Use svcConfig for something here.
    }
}
于 2009-12-16T01:30:49.620 回答