c++ - 使用 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 显示它们,而不转换它们)。
解决方案
您在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;
}
推荐阅读
- google-cloud-platform - 将 GCR 图像从一个项目复制到另一个项目
- mysql - 如何使用 MySQL 或 R 获取 JSON 是否包含特定文本
- r - 将文件读入 R 比 while{rbind(read.table)} 更快
- python - 将对象类型的字段扩展为熊猫中的单独字段
- javascript - 在 React 中关闭对话框时保存计数变量
- javascript - page.evaluate 中的 Puppeteer XMLHttpRequest 是阻塞代码
- mocking - 如何在同一个测试用例中多次使用 sequelize-mock 变量
- jquery - JSZip 3.0 的问题
- css - 使用 CS 获取点划线和点划线边框
- c++ - 连接和压缩 std::vector 的最佳方法