Aaron Manning Ответов: 3

C++ WMI EnableStatic с несколькими IP-адресами не работает


Добрый День Всем,

Я пытался заставить функцию EnableStatic работать с несколькими IP-адресами/сетевыми масками, и у меня возникли проблемы. Ни в коем случае я не получаю никаких ошибок ни в одном из вызовов функций, но ни одна из моих переданных пар IP/Netmask на самом деле не программируется. Надеюсь, что кто-то здесь может иметь представление о том, что я, возможно, делаю неправильно или упускаю.

Код для выполнения C++ EnableStatic был основан на этой статье в разделе сообщений из "Brian Clow For God's Sake 10:38 30 May '06":

Создание запросов WMI на языке C++[^]

Я изменил эту функцию, чтобы вместо этого взять список IP/сетевых масок и построить из них параметры EnableStatic.

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

typedef struct IP_INFO
{
	char ip[40];
	char netmask[40];
} IP_INFO, *PIP_INFO;

bool AddIPAddressToAdapterPersistant(PIP_INFO &ipInfo, INT count, INT fIndex)
{
	HRESULT hr;
    IWbemLocator *pLocator = NULL;
    IWbemServices *pNamespace = NULL;
	BSTR path = SysAllocString(L"ROOT\\CIMV2");

    // Initialize COM and connect to WMI.
    hr = CoInitialize(0);
	if(FAILED(hr))
	{
		return (false);
	} // if

    hr  =  CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); 
	if(FAILED(hr))
	{
		CoUninitialize();
		return(false);
	} // if

    hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLocator);
	if (FAILED(hr))
	{
		CoUninitialize();
		return (false);
	} // if

    hr = pLocator->ConnectServer(path, NULL, NULL, NULL, 0, NULL, NULL, &pNamespace);
	if(FAILED(hr))
	{
		pLocator->Release();
		CoUninitialize();
		return(false);
	} // if

	// Grab class required to work on Win32_NetworkAdapterConfiguration
	IWbemClassObject *pClass = NULL;
	BSTR ClassPath = SysAllocString(L"Win32_NetworkAdapterConfiguration");
	hr = pNamespace->GetObject(ClassPath, 0, NULL, &pClass, NULL);
	SysFreeString(ClassPath);
	if( WBEM_S_NO_ERROR == hr )
	{
		// Grab pointer to the input parameter class of the method we are going to call
		BSTR MethodName_ES = SysAllocString(L"EnableStatic");
		IWbemClassObject *pInClass_ES = NULL;
		if(WBEM_S_NO_ERROR == pClass->GetMethod(MethodName_ES, 0, &pInClass_ES, NULL))
		{
			// Spawn instance of the input parameter class, so that we can stuff our parameters in
			IWbemClassObject *pInInst_ES = NULL;
 
			if(WBEM_S_NO_ERROR == pInClass_ES->SpawnInstance(0, &pInInst_ES))
			{
				//
				// (Step 3) - Pack desired parameters into the input class instances
				//
				// Convert from multibyte strings to wide character arrays
				wchar_t tmp_ip[_countof(ipInfo->ip)];
				SAFEARRAY *ip_list = SafeArrayCreateVector(VT_BSTR, 0, count);
				// Insert into safe arrays, allocating memory as we do so (destroying the safe array will destroy the allocated memory)
				long idx[] = {0};
				for(int i = 0; i < count; i++)
				{
					mbstowcs(tmp_ip, ipInfo[i].ip, _countof(ipInfo[i].ip));
					BSTR ip = SysAllocString(tmp_ip);
					idx[0] = i;
					if(FAILED(SafeArrayPutElement(ip_list, idx, ip)))
					{
						return (false);
					} // if
					// Destroy the BSTR pointer
					SysFreeString(ip);
				} // for

				// Convert from multibyte strings to wide character arrays
				wchar_t tmp_netmask[_countof(ipInfo->netmask)];
				SAFEARRAY *netmask_list = SafeArrayCreateVector(VT_BSTR, 0, count);
				// Insert into safe arrays, allocating memory as we do so (destroying the safe array will destroy the allocated memory)
				for(int i = 0; i < count; i++)
				{
					mbstowcs(tmp_netmask, ipInfo[i].netmask, _countof(ipInfo[i].netmask));
					BSTR netmask = SysAllocString(tmp_netmask);
					idx[0] = i;
					if(FAILED(SafeArrayPutElement(netmask_list, idx, netmask)))
					{
						return (false);
					} // if
					// Destroy the BSTR pointer
					SysFreeString(netmask);
				} // for

				// Now wrap each safe array in a VARIANT so that it can be passed to COM function
				VARIANT arg1_ES;
				VariantInit(&arg1_ES);
				arg1_ES.vt = VT_ARRAY|VT_BSTR;
				arg1_ES.parray = ip_list;
 
				VARIANT arg2_ES;
				VariantInit(&arg2_ES);
				arg2_ES.vt = VT_ARRAY|VT_BSTR; 
				arg2_ES.parray = netmask_list;
  
				if((WBEM_S_NO_ERROR == pInInst_ES->Put(L"IPAddress", 0, &arg1_ES, 0)) &&
			       (WBEM_S_NO_ERROR == pInInst_ES->Put(L"SubNetMask", 0, &arg2_ES, 0)))
				{
					//
					// (Step 4) - Call the methods
					//

					// First build the object path that specifies which network adapter we are executing a method on
					char indexString[10];
					itoa(fIndex, indexString, 10);
 
					char instanceString[100];
					wchar_t w_instanceString[100];
					strcpy(instanceString, "Win32_NetworkAdapterConfiguration.Index='");
					strcat(instanceString, indexString);
					strcat(instanceString, "'");
					mbstowcs(w_instanceString, instanceString, 100);
					BSTR InstancePath = SysAllocString(w_instanceString);
 
					// Now call the method
					IWbemClassObject * pOutInst = NULL;
					hr = pNamespace->ExecMethod(InstancePath, MethodName_ES, 0, NULL, pInInst_ES, &pOutInst, NULL);
					if(FAILED(hr))
					{
						// false
						return(false);
					} // if
					SysFreeString(InstancePath);
				} // if
 
				// Clear the variants - does this actually get ride of safearrays?
				VariantClear(&arg1_ES);
				VariantClear(&arg2_ES);

				// Destroy safe arrays, which destroys the objects stored inside them
				SafeArrayDestroy(ip_list); ip_list = NULL;
				SafeArrayDestroy(netmask_list); netmask_list = NULL;
 			}
			else
			{
				 // false
			} // if
 
			// Free up the instances that we spawned
			if(pInInst_ES)
			{
				pInInst_ES->Release();
				pInInst_ES = NULL;
			} // if
		}
		else
		{
			// false
		} // if
 
		// Free up methods input parameters class pointers
		if(pInClass_ES)
		{
			pInClass_ES->Release();
			pInClass_ES = NULL;
		} // if
		SysFreeString(MethodName_ES);
	}
	else
	{
		// false
	} // if
 
	// Variable cleanup
	if (pNamespace)
	{
		pNamespace->Release();
		pNamespace = NULL;
	}
	if (pLocator)
	{
		pLocator->Release();
		pLocator = NULL;
	} // if
	if(pClass)
	{
		pClass->Release();
		pClass = NULL;
	} //if
	CoUninitialize();

	return(true);
}


Спасибо!

Member 10557037

Tipp: для лучшего обзора может быть хорошей идеей создать функцию seaparate, которая строит SAFEARRAY из массива строк.

Member 13339507

Можете ли вы поделиться исходным кодом?
Я пытаюсь сделать что-то подобное, но не могу.

kj_easy_DN

Работает ли это все еще в Windows 10?

Я скопировал исходный код без каких-либо изменений и просто добавил такую основную функцию:

IP_INFO ipData[1];
strcpy_s (ipData[0].ip, sizeof(ipData[0].ip), "10.0.2.15");
strcpy_s (ipData[0].netmask, sizeof(ipData[0].netmask), "255.255.255.0");

PIP_INFO pipData = &(ipData[0]);
bool ret = AddIPAddressToAdapterPersistant(pipData, 1, 1);

Я один прошел через всю процедуру, каждый вызов возвращался нормально, но в конце концов конфигурация одного и единственного сетевого адаптера не была изменена.
Я проверил это с помощью ipconfig/all и, открыв страницу панель управления/сетевая карта/свойства, TCP/IPv4.
Все осталось по-прежнему, ничего не изменилось.

kj_easy_DN

Ну, мне пришлось запустить студию разработчиков в качестве администратора. Тогда все шло хорошо.
Пожалуйста, смотрите мой пост ниже, как немного улучшить образец, чтобы получить некоторые подсказки о причине вызова, который возвращает hr=S_OK, но, похоже, не имеет "никакого эффекта".

3 Ответов

Рейтинг:
28

Aaron Manning

Ну вот, нашел проблему. *вздыхать*

Мой список ip/netmask, который я передавал функции, содержал первый IP 10.0.1.0 ... надзор с моей стороны, так как это недопустимый IP. Но вызов EnableStatic() принял параметры и вернул HRESULT OK. Не похоже, что функция EnableStatic() вернет ошибку, если вы передадите ей недопустимую пару IP/NETMASK.

Код и учись, я думаю.


Рейтинг:
1

Member 2163164

VariantClear(&arg1_ES);
VariantClear(&arg2_ES);


Эти две строки не нужны, потому что SafeArrayDestroy будет иметь функцию VariantClear, вызываемую для каждого элемента, а безопасные массивы BSTR будут иметь функцию SysFreeString, вызываемую для каждого элемента.


Рейтинг:
0

kj_easy_DN

Хорошо знать:
а) запустите этот код как администратор, в противном случае hRes возвращает OK, но "retValue"(pOutInst в приведенном выше примере), возвращаемый в качестве выходного параметра, будет указывать на ошибку.

б) получить retValue, использование этих дополнительных строк кода может помочь:

// existing code from above
IWbemClassObject * pOutInst = NULL;
hr = pNamespace->ExecMethod(InstancePath, MethodName_ES, 0, NULL, pInInst_ES, &pOutInst, NULL);
if(FAILED(hr))
{
	// false
	return(false);
} // if FAILED

// added ---------------------------
// From the point of view of WMI, the function succeeded, but the 
// operating system might have some objections.
// The operating system return code is stored in the property 
// "ReturnValue" of the result parameter. 

else  
{  
	VARIANT vtRet;  
	VariantInit(&vtRet);
  
	hr = pOutInst->Get(L"ReturnValue", 0, &vtRet, NULL, 0);
	if (FAILED(hr)) 
		return (false); 
	else  
	{
		if (vtRet.intVal == 0 || vtRet.intVal == 1)
		{
			// no error: 0=OK, 1=OK, but reboot required
		}
		else
		{
			// if (vtRet.intVal == 0x80070005 || vtRet.intVal == 81)
			// 0x80070005=E_ACCESS_DENIED: Access denied by DCOM security.
			// 81=no access rights
			return (false);
	}

	VariantClear(&vtRet);  
	pOutInst->Release();  
}  
// ----------------------------------
SysFreeString(InstancePath);


Наконец, если вы хотите получить какое-то читаемое сообщение об ошибке обратно, вы можете использовать Функция formatmessage и скажите этой функции, чтобы она заглянула в библиотеку DLL WMI "wbem\wmiutils.dll", и использовать vtRet.intVal в качестве входных данных, например, вот так:

<pre>#include "Shlobj.h"
PWCHAR WMI_GetErrorString(DWORD err)                                        
{
	static WCHAR szErrorString[1024]; // returned data buffer
	szErrorString[0] = '\0';

	WCHAR path[MAX_PATH];
	PWSTR pDllPath=NULL;
	HMODULE hDllModule=NULL;

	// get path to Windows\system32
	HRESULT hr = ::SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &pDllPath);

	if(SUCCEEDED(hr))                                                
	{ /* succeeded */
		// append path to wmiutils.dll, and attempt to load the DLL
		wcscpy_s (path, pDllPath);
		wcscat_s (path, L"\\wbem\\wmiutils.dll");

		hDllModule = ::LoadLibrary(path);                                   
		if(hDllModule != NULL) 
		{/* DLL load succeeded */

			// use FormatMessage to get the error text for the current error code
			// from wmiutils.dll

			if(::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
				(LPCVOID)hDllModule,                                 
				err,                                          
				0,						// language ID
				szErrorString,
				sizeof(szErrorString),
				NULL)
				== 0)
			{ /* not found */
				;
			} /* not found */

			PWCHAR eol = wcschr(szErrorString, L'\r');                            
			if(eol != NULL)
				*eol = L'\0';// remove trailing \r\n, if necessary                                              


		} /* succeeded */
	} /* succeeded */


	// housekeeping
	if (pDllPath != NULL)
		 CoTaskMemFree(pDllPath);
	if (hDllModule != NULL)
		::FreeLibrary(hDllModule); 

	return szErrorString;
} // ErrorString


Richard Deeming

Спрашивали, отвечали и решали ШЕСТЬ ЛЕТ НАЗАД.

Придерживайтесь ответов на недавние вопросы.