c++转换问题

C++ conversion question

本文关键字:问题 转换 c++      更新时间:2023-10-16

我知道我可以像这样正确地使用 WinApi函数DsGetDcName:

DOMAIN_CONTROLLER_INFO* dcInfo = nullptr;
unsigned long res = ::DsGetDcName(nullptr,
                        nullptr,
                        nullptr,
                        nullptr,
                            0,  &dcInfo);

我知道这是不自然的,但我想理解为什么不能这样写:

    void* dcInfo = nullptr;
unsigned long res = ::DsGetDcName(nullptr,
                        nullptr,
                        nullptr,
                        nullptr,
                        0,  (DOMAIN_CONTROLLER_INFO**) dcInfo);
if (res)
{
       wchar_t* name;
       name = static_cast<DOMAIN_CONTROLLER_INFO*> (dcInfo)->DomainControllerName;
}

第二个版本使用void*作为指针类型,这就是为什么我在运行它时得到访问冲突(在调用::DsGetDcName时)。但我不明白这是为什么?当为dcInfo指定void*而不是类型DOMAIN_CONTROLLER_INFO* dcInfo时,它是否与内存对齐的方式有关?

<

解决方案/strong>

我找到了问题,我可以使用复杂的不安全的void*版本,我只是没有传递正确的指针地址给那个函数。

void* dcInfo = nullptr;
unsigned long res = ::DsGetDcName(nullptr,
                        nullptr,
                        nullptr,
                        nullptr,
                        0,  (DOMAIN_CONTROLLER_INFO**) &dcInfo);

注意我传递的是(DOMAIN_CONTROLLER_INFO**) &dcInfo而不是(DOMAIN_CONTROLLER_INFO**) dcInfo。我之前只是把自己关在脚上,因为我告诉编译器我知道我在做什么,但传递给函数一个指针值,而不是所需指针的地址(是的,指针值是nullptr):-))

这是使用正确版本(版本1)的另一个原因。在第二种情况下,缺点也是必须像这样再次强制转换结果:

wchar_t* name;
name = static_cast<DOMAIN_CONTROLLER_INFO*>(dcInfo)->DomainControllerName; // Get DC

因为函数接受双间接指针。它类似于:

void AllocateMemory(int** pTarget)
{
    *pTarget = new int[10];
    (*pTarget)[0] =  110;
}

你可以这样称呼它:

int main()
{
    int* pAllocHere;
    AllocateMemory(&pAllocHere);
    int nValue;
    nValue= pAllocHere[0]; // 110
    return 0;
}

将分配内存给您传递的int指针,并且必须传递指针地址,而不仅仅是int**类型转换的int*

这不是因为DsGetDcName函数,而是由C/c++语言。函数不知道所需的大小,如果它为您分配它。有许多Windows函数需要两个函数调用—一个用于确定大小(主要是DWORD dwNeeded),另一个用于实际执行该工作。此函数在一次调用中为您分配内存,并要求您稍后调用NetApiBufferFree。

在c++中,你可以使用int*&来修改签名:

void AllocateMemory(int*& pTarget);

和调用:

int* pAllocHere;
AllocateMemory(pAllocHere);

但是Windows API必须使用C语言

您需要查看程序集。两个可用的选项(#2 )两者都使用LEA指令。这将把数据结构的地址加载到EAX中,在本例中为空。失败的示例加载该地址的值,该值为空。如你所知,你不能取消对null的引用。

// #1 - Fails
::DsGetDcName(nullptr, nullptr, nullptr, nullptr, 0, (DOMAIN_CONTROLLER_INFO**)void_ptr);
01101A63  mov         esi,esp  
01101A65  mov         eax,dword ptr [void_ptr]  
01101A68  push        eax  
01101A69  push        0  
01101A6B  push        0  
01101A6D  push        0  
01101A6F  push        0  
01101A71  push        0  
01101A73  call        dword ptr [__imp__DsGetDcNameW@24 (1108350h)]  
01101A79  cmp         esi,esp  
01101A7B  call        @ILT+310(__RTC_CheckEsp) (110113Bh)  
// #2 - Works
::DsGetDcName(nullptr, nullptr, nullptr, nullptr, 0, (DOMAIN_CONTROLLER_INFO**)&void_ptr);
00EE1A63  mov         esi,esp  
00EE1A65  lea         eax,[void_ptr]  
00EE1A68  push        eax  
00EE1A69  push        0  
00EE1A6B  push        0  
00EE1A6D  push        0  
00EE1A6F  push        0  
00EE1A71  push        0  
00EE1A73  call        dword ptr [__imp__DsGetDcNameW@24 (0EE8350h)]  
00EE1A79  cmp         esi,esp  
00EE1A7B  call        @ILT+310(__RTC_CheckEsp) (0EE113Bh)  
// #3 - Works
::DsGetDcName(nullptr, nullptr, nullptr, nullptr, 0, &dc_ptr);
013D1A5C  mov         esi,esp  
013D1A5E  lea         eax,[dc_ptr]  
013D1A61  push        eax  
013D1A62  push        0  
013D1A64  push        0  
013D1A66  push        0  
013D1A68  push        0  
013D1A6A  push        0  
013D1A6C  call        dword ptr [__imp__DsGetDcNameW@24 (13D8350h)]  
013D1A72  cmp         esi,esp  
013D1A74  call        @ILT+310(__RTC_CheckEsp) (13D113Bh)