【问题标题】:GetTokenInformation() first call. What for?GetTokenInformation() 第一次调用。做什么的?
【发布时间】:2010-09-08 18:52:20
【问题描述】:

查看 GetTokenInformation()Getting the Logon SID 示例的 MSDN 文档,GetTokenInformation() 需要调用两次。第一个调用是获取缓冲区大小。

那么,缓冲区大小是多少?就说我用 TokenUser 作为它的第二个参数,我看到第一次调用返回的 dwReturnLength 不是 TOKEN_USER 结构的大小。

提前致谢

【问题讨论】:

    标签: c++ c windows delphi winapi


    【解决方案1】:

    TOKEN_USER 结构包含指针(特别是指向本身具有可变长度的SID 的指针)。这些指针必须指向某个地方。 API 函数需要一个足够大的缓冲区,不仅可以容纳TOKEN_USER 结构,还可以容纳该结构指向的所有内容。该函数告诉您所有内容需要多少内存。它将全部驻留在相邻的内存中。

    【讨论】:

    • 我手动统计了第二次调用返回的SID字符串,等于43个字符,但是第一次调用返回的dwReturnLength是36,那么,36是从哪里来的呢?只是好奇。 :-)
    • @Phantom:SID 以二进制形式存储,而不是字符串形式。字符串形式只是为了使其易于阅读。
    • 调用两次有什么问题?这是惯用的做法。如果您调用两次,没有人会混淆您的代码,但是人们怀疑您的硬编码缓冲区大小。你怎么知道你能得到的每一个可能的 SID 的长度?
    • 是的,你很固执。至少现在您有一些选择固定大小缓冲区的证据,只要您检查TokenUser 信息类。做得好。但是,您仍然没有正确的大小,因为您必须考虑完整的 TOKEN_USER 结构,而不仅仅是它所指向的东西之一。确保考虑不同平台上不同大小的指针。现在,其他 27 个类呢?
    • 如果微软在更高版本的 Windows 中更改这些限制会怎样?
    【解决方案2】:

    您的第二个 URL 中的完整示例应该清楚地说明如何使用从第一个调用返回的长度。您可以使用它来分配该大小的原始内存 - 这里是变量 ptg - 并将其转换为 PTOKEN_GROUPS 以在第二次调用中使用。

    // Get required buffer size and allocate the TOKEN_GROUPS buffer.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             0,              // size of buffer
             &dwLength       // receives required buffer size
          )) 
       {
          if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
             goto Cleanup;
    
          ptg = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(),
             HEAP_ZERO_MEMORY, dwLength);
    
          if (ptg == NULL)
             goto Cleanup;
       }
    
    // Get the token group information from the access token.
    
       if (!GetTokenInformation(
             hToken,         // handle to the access token
             TokenGroups,    // get information about the token's groups 
             (LPVOID) ptg,   // pointer to TOKEN_GROUPS buffer
             dwLength,       // size of buffer
             &dwLength       // receives required buffer size
             )) 
       {
          goto Cleanup;
       }
    

    【讨论】:

    • 看我对第一个答案的回复。
    • @Phantom:你永远不能说出“第一个”答案,因为答案是按随机顺序显示的。它也受到支持数的影响。
    • @Phantom - 你无法避免两次调用,除非你希望第一次调用的缓冲区大小很大,以确保它总是足够大。我想不出你将如何确定什么是安全的。在这里分配大缓冲区可能会比两次调用更慢。值得吗?
    • @Phantom:使用回复者的句柄。所以你会说“我已将我的问题作为评论放在史蒂夫汤森的回答中。”
    • 我想说的是,您无法预测 Windows 在您在这里拨打的每个电话上的表现。这可能会工作几个月,然后在一些意外的输入上反复失败。我真的不明白为什么遵循 API 规则如此重要。是的,它们很糟糕,但还有很多更大的交易需要担心。
    【解决方案3】:

    看最后三个参数:

    TokenInformation [out, optional]

    指向函数填充所请求信息的缓冲区的指针。 放入此缓冲区的结构 取决于信息的类型 由 TokenInformationClass 指定 参数。

    TokenInformationLength [in]

    指定缓冲区所指向的缓冲区的大小(以字节为单位) 令牌信息参数。如果 TokenInformation 为 NULL,这 参数必须为零。

    返回长度 [out]

    指向接收所需字节数的变量的指针 对于指向的缓冲区 令牌信息参数。如果这 值大于值 中规定的 TokenInformationLength 参数, 函数失败并且不存储任何数据 缓冲区。

    如果 TokenInformationClass 参数的值为 TokenDefaultDacl 并且令牌没有 默认 DACL,该函数设置 ReturnLength 指向的变量 sizeof(TOKEN_DEFAULT_DACL) 并设置 DefaultDacl 的成员 TOKEN_DEFAULT_DACL 结构为 NULL。

    由于您不知道需要为参数 #2 传递多大的缓冲区,因此您需要查询 API 以了解确切的大小。然后你传入一个足够大的缓冲区并取回你想要的信息。

    您总是可以猜测缓冲区大小,它可能会起作用。

    请注意,这是典型的 Win32 API。这有助于一劳永逸地正确使用这个成语。

    【讨论】:

    • 我不想调用它两次。顺便说一句,请参阅我对第一个答案的回复。
    • @Phantom:你别无选择。对于明确定义的行为,您必须调用它两次。
    • @Phantom:为什么不呢。这是记录和推荐的方法来做到这一点。这不会是一个繁重的函数调用
    • @Gerry:我最近在 MSDN 中发现 sid 的二进制最大长度为 68 字节。我已经在对 Rob Kennedy 的回答中引用了该链接。
    【解决方案4】:

    结构中嵌入了一个可变长度的 SID,因此缓冲区大小将取决于将包含在结果中的 SID 的大小。

    【讨论】:

    • 看我对第一个答案的回复。
    • @Phantom:在我的屏幕上,这是第一个答案(因为答案顺序是随机的)
    【解决方案5】:

    如果您的缓冲区足够大,您可以在第一次调用中完成这项工作。大多数使用这些方法的代码会首先尝试使用固定大小的缓冲区,然后在调用指示它需要更多内存时分配更大的缓冲区。

    【讨论】:

    • '先尝试一个固定大小的虫子' - 总是有一些关于 Win32 的新知识,嗯?
    • @Steve 希望这个错字能让你微笑。 :)
    • 也就是说,我怎样才能安全地猜测所需的最大缓冲区?顺便说一句,请参阅我对第一个答案的回复。
    • @Phantom:你不能。您必须尝试更大的缓冲区,如果不够大,请使用更大的缓冲区。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多