首页 > 解决方案 > 使用 winforms 显示 iphlpapi.h (GetAdaptersInfo) 的返回值(从 char* / wchar* 转换为 System::String)

问题描述

我在使用返回的 GetAdaptersInfo 结构中的值时遇到问题。它返回一个 IP_ADAPTER_INFO 结构(https://docs.microsoft.com/de-de/windows/win32/api/iptypes/ns-iptypes-ip_adapter_info),我从中获取一些值到自定义结构中。

最小的可重现示例:

#include <winsock2.h>
#include <iphlpapi.h>
#include <vector>
#pragma comment(lib, "IPHLPAPI.lib")

struct Adapter_Info {
    DWORD ComboIndex;
    char* AdapterName;
    WCHAR* AdapterFriendlyName;
    char* Description;
    DWORD Index;
    char AdapterAddress;
    char* Type;
    char* CurrentIpAddress;
    IP_ADDR_STRING IpAddressList;
    char* GatewayList;
    bool DhcpServerStatus;
    char* DhcpServerStatusChar;
    char* DhcpServer;
};


#define MALLOC(x) HeapAlloc(GetProcessHeap(), 0, (x))
#define FREE(x) HeapFree(GetProcessHeap(), 0, (x))

int main(std::vector<Adapter_Info>* p_retrieved_adapters, int* p_number_of_adapters)
{
    //creating returning vector and number_of_adapters
    std::vector<Adapter_Info> retrieved_adapters;
    int number_of_adapters = 0;

    /* Declare and initialize variables */
    PIP_ADAPTER_INFO pAdapterInfo;
    PIP_ADAPTER_INFO pAdapter = NULL;
    DWORD dwRetVal = 0;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    pAdapterInfo = (IP_ADAPTER_INFO*)MALLOC(sizeof(IP_ADAPTER_INFO));
    if (pAdapterInfo == NULL) {
        printf("Error allocating memory needed to call GetAdaptersinfo\n");
        return 1;
    }
    // Make an initial call to GetAdaptersInfo to get
    // the necessary size into the ulOutBufLen variable
    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
        FREE(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO*)MALLOC(ulOutBufLen);
        if (pAdapterInfo == NULL) {
            printf("Error allocating memory needed to call GetAdaptersinfo\n");
            return 1;
        }
    }

    //call GetAdaptersInfo for counting number of adapters
    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        pAdapter = pAdapterInfo;

        //count number of adapters
        while (pAdapter) {
            number_of_adapters++;
            pAdapter = pAdapter->Next;
        }
    }
    else {
        printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);

    }

    //call GetAdaptersInfo again and retrieving the information for every adapter
    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR) {
        pAdapter = pAdapterInfo;

        //retrieve info for every adapter
        while (pAdapter) {

            Adapter_Info temp_adapter_info;

            temp_adapter_info.ComboIndex = pAdapter->ComboIndex;
            temp_adapter_info.AdapterName = pAdapter->AdapterName;
            //for testing purposes
            printf("\tAdapter Name: \t%s\n", pAdapter->AdapterName);
            printf("\tAdapter Name: \t%s\n", temp_adapter_info.AdapterName);
            temp_adapter_info.Description = pAdapter->Description;
            //for testing purposes
            printf("\tAdapter Desc: \t%s\n", pAdapter->Description);
            printf("\tAdapter Desc: \t%s\n", temp_adapter_info.Description);
            temp_adapter_info.Index = pAdapter->Index;

            retrieved_adapters.push_back(temp_adapter_info);
            pAdapter = pAdapter->Next;
        }
    }

    else {
        printf("GetAdaptersInfo failed with error: %d\n", dwRetVal);

    }

*p_retrieved_adapters = retrieved_adapters;
*p_number_of_adapters = number_of_adapters;

//free memory
if (pAdapterInfo)
    FREE(pAdapterInfo);
if (pAddresses) {
    FREE(pAddresses);
}

return 0;
}

在 Form1.h 中使用 main():

    std::vector<Adapter_Info> retrieved_adapters;
    std::vector<Adapter_Info>* p_retrieved_adapters = &retrieved_adapters;;

    int number_of_adapters;
    int* p_number_of_adapters = &number_of_adapters;

    private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
           main(p_retrieved_adapters, p_number_of_adapters);
                for (int xr = 0; xr < number_of_adapters; xr++) {
                     printf("\tAdapter Name: \t%s\n", retrieved_adapters[xr].AdapterName);
                     printf("\tAdapter Desc: \t%s\n", retrieved_adapters[xr].Description);
                 }

            String^ str0_0 = WcharToSysString(retrieved_adapters[0].AdapterFriendlyName);
            String^ str0_2 = CharToSysString(retrieved_adapters[0].Description);
    }

要在带有 winforms 的标签中显示这些值,我需要将它们转换为 System::String^。为此,我创建了一些转换函数。

//wchar_t* to char
char* WcharToChar(wchar_t* wcharStr)
{
    // Create a string of wide characters, display it, and then
   // use this string to create other types of strings.
    wchar_t* orig = wcharStr;
    std::wcout << orig << _T(" (wchar_t *)") << std::endl;

    // Convert the wchar_t string to a char* string. Record
    // the length of the original string and add 1 to it to
    // account for the terminating null character.
    size_t origsize = wcslen(orig) + 1;
    size_t convertedChars = 0;

    // Use a multibyte string to append the type of string
    // to the new string before displaying the result.
    char strConcat[] = " (char *)";
    size_t strConcatsize = (strlen(strConcat) + 1) * 2;

    // Allocate two bytes in the multibyte output string for every wide
    // character in the input string (including a wide character
    // null). Because a multibyte character can be one or two bytes,
    // you should allot two bytes for each character. Having extra
    // space for the new string is not an error, but having
    // insufficient space is a potential security problem.
    const size_t newsize = origsize * 2;
    // The new string will contain a converted copy of the original
    // string plus the type of string appended to it.
    char* nstring = new char[newsize + strConcatsize];

    // Put a copy of the converted string into nstring
    wcstombs_s(&convertedChars, nstring, newsize, orig, _TRUNCATE);
    // append the type of string to the new string.
    //_mbscat_s((unsigned char*)nstring, newsize + strConcatsize, (unsigned char*)strConcat);
    // Display the result.
    std::cout << nstring << std::endl;
    return nstring;
}


//wchar_t* to std::string
std::string WcharToStdStr(const wchar_t* s, char dfault, const std::locale& loc)
{
    std::ostringstream stm;

    while (*s != L'\0') {  //EV. HIER FEHLER
        stm << std::use_facet< std::ctype<wchar_t> >(loc).narrow(*s++, dfault);
    }
    return stm.str();
}

//char* to std::string
std::string CharToStdStr(char* charStr)
{
    std::string stdStr(charStr);
    return stdStr;
}

//std::string to System::String^
System::String^ StdStrToSysString(std::string stdStr)
{
    System::String^ sysStr = gcnew System::String(stdStr.c_str());
    return sysStr;
}

//wchar_t* to System::String^
System::String^ WcharToSysString(const wchar_t* wcharStr)
{
    System::String^ sysStr = gcnew System::String(WcharToStdStr(wcharStr).c_str());
    return sysStr;
}

//char* to System::String^
System::String^ CharToSysString(char* charStr)
{
    System::String^ sysStr = gcnew System::String(CharToStdStr(charStr).c_str());
    return sysStr;
}

有时这些功能根本不起作用。在某些情况下,它们在第一次尝试时无法工作,然后在再次调用 GetAdaptersInfo 函数后开始工作。

AdapterFriendlyName我的Description自定义结构只有问题。

我会很感激任何提示(特别是如果有更好的方法来管理这些值,用 Winforms 显示它们,而不转换它们)。

标签: c++winformstype-conversion

解决方案


您在main()函数内部疯狂地泄漏内存。您根本没有释放IP_ADAPTER_INFO结构,您只是存储指向IP_ADAPTER_INFO字段的指针而不是复制数据。你真的应该摆脱你过多的指针。

一般来说,您也没有真正GetAdaptersInfo()正确使用。你叫它太多次了。调用它来预先计算缓冲区大小是一回事,但是一旦你有了实际数据,就停止调用它!现在,您正在调用它来预分配缓冲区,然后再次调用它以获取实际数据,然后再次调用以再次获取数据(不能保证第二次保持一致!)。

尝试更多类似的东西,使用更多的 C++-ish 语义而不是 C-ish 语义:

#include <winsock2.h>
#include <iphlpapi.h>
#include <vector>
#include <string>
#include <new>

#pragma comment(lib, "IPHLPAPI.lib")

struct Adapter_Info {
    DWORD ComboIndex;
    std::string AdapterName;
    std::wstring AdapterFriendlyName;
    std::string Description;
    DWORD Index;
    std::string AdapterAddress;
    std::string Type;
    std::string CurrentIpAddress;
    std::vector<std::string> IpAddressList;
    std::vector<std::string> GatewayList;
    bool DhcpServerStatus;
    std::string DhcpServerStatusChar;
    std::vector<std::string> DhcpServer;
};

int getAdapters(std::vector<Adapter_Info>& retrieved_adapters)
{
    std::vector<BYTE> buffer;
    PIP_ADAPTER_INFO pAdapterInfo;
    DWORD dwRetVal = 0;
    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO) * 5;

    //call GetAdaptersInfo to retrieve the information for every adapter
    do {
        try {
            buffer.resize(ulOutBufLen);
        }
        catch (const std::bad_alloc &) {
            std::cerr << "Error allocating memory needed to call GetAdaptersInfo\n";
            return -1;
        }

        pAdapterInfo = reinterpret_cast<IP_ADAPTER_INFO*>(&buffer[0]);

        dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen);
        if (dwRetVal == ERROR_NO_DATA)
            return 0;
    }
    while (dwRetVal == ERROR_BUFFER_OVERFLOW);

    if (dwRetVal != NO_ERROR) {
        std::cerr << "GetAdaptersInfo failed with error: " << dwRetVal << "\n";
        return -1;
    }

    //retrieve info for every adapter
    PIP_ADAPTER_INFO pAdapter = pAdapterInfo;
    int number_of_adapters = 0;

    do {
        Adapter_Info temp_adapter_info;

        temp_adapter_info.ComboIndex = pAdapter->ComboIndex;
        temp_adapter_info.AdapterName = pAdapter->AdapterName;
        //for testing purposes
        std::cout << "\tAdapter Name: \t" << pAdapter->AdapterName << "\n";
        std::cout << "\tAdapter Name: \t" << temp_adapter_info.AdapterName << "\n";
        temp_adapter_info.Description = pAdapter->Description;
        //for testing purposes
        std::cout << "\tAdapter Desc: \t" << pAdapter->Description << "\n";
        std::cout << "\tAdapter Desc: \t" << temp_adapter_info.Description << "\n";
        temp_adapter_info.Index = pAdapter->Index;

        retrieved_adapters.push_back(temp_adapter_info);
        pAdapter = pAdapter->Next;
    }
    while (pAdapter);

    return number_of_adapters;
}
std::vector<Adapter_Info> retrieved_adapters;
int number_of_adapters;

private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {
    getAdapters(retrieved_adapters);
    if (!retrieved_adapters.empty()) {
        for (size_t xr = 0; xr < retrieved_adapters.size(); xr++) {
            std::cout << "\tAdapter Name: \t" << retrieved_adapters[xr].AdapterName << "\n";
            std::cout << "\tAdapter Desc: \t" << retrieved_adapters[xr].Description << "\n";
        }

        String^ str0_0 = StdWStrToSysString(retrieved_adapters[0].AdapterFriendlyName);
        String^ str0_2 = StdStrToSysString(retrieved_adapters[0].Description);
    }
}

您可以将其添加到您的助手中:

//std::wstring to System::String^
System::String^ StdWStrToSysString(const std::wstring &stdStr)
{
    System::String^ sysStr = gcnew System::String(stdStr.c_str());
    return sysStr;
}

推荐阅读