Отображение возвращаемых значений из iphlpapi.h (GetAdaptersInfo) с winforms (преобразование из char * / wchar * в System :: String) - PullRequest
0 голосов
/ 12 февраля 2020

У меня проблема с использованием значений из возвращенной структуры 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;
}

Использование main () в Form1.h:

    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, не преобразовывая их).

1 Ответ

0 голосов
/ 13 февраля 2020

Вы теряете память как сумасшедший внутри вашей функции main(). Вы вообще не освобождаете структуру IP_ADAPTER_INFO и просто храните указатели на поля IP_ADAPTER_INFO, а не копируете данные. Вы действительно должны избавиться от излишнего количества указателей.

Вы также не совсем правильно используете GetAdaptersInfo() в целом. Вы называете это слишком много раз. Одно дело вызывать его, чтобы предварительно рассчитать размер буфера, но как только вы получите реальные данные, перестаньте их называть! Прямо сейчас вы вызываете его, чтобы предварительно выделить буфер, затем вызываете его снова, чтобы получить фактические данные, и затем вы снова вызываете, чтобы получить данные (что не гарантируется, чтобы быть согласованным во второй раз!).

Попробуйте что-то вроде этого, используя больше семантики C ++ - i sh, чем семантики C -i sh:

#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;
}
...