Access violation in official Microsoft WMI example - c++

I'm trying to learn how the WMI works, but the default examples given have so far been atrocious.
Here's the example for calling the Create method of the Win32_Process class:
https://learn.microsoft.com/en-us/windows/win32/wmisdk/example--calling-a-provider-method
I have added proper error handling to this, we store the HRESULT of each call in a variable hres and check if the calls failed. As such:
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
if (FAILED(hres))
{
wprintf("Failed to get class. Error code = 0x%lx\n", hres);
return hres;
}
The code executes correctly right up until here:
// Create the values for the in parameters
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
// Store the value for the in parameters
hres = pClassInstance->Put(L"CommandLine", 0,
&varCommand, 0);
wprintf(L"The command is: %s\n", V_BSTR(&varCommand));
Where the pClassInstance->Put throws 'ol c5.
At this point, hres is S_OK for the call to SpawnInstance but these are the pointers we have for the class instances:
+pClass 0x000001c04e73fca0 IWbemClassObject *
- pClassInstance 0x000001c04e749d60 IWbemClassObject *
- IUnknown {...} IUnknown
- __vfptr 0x00007ff9f8d0ee98 {fastprox.dll!const CWbemInstance::`vftable'{for `_IWmiObject'}} {0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)}, ...} void * *
[0x00000000] 0x00007ff9f8c6f450 {fastprox.dll!CWbemObject::QueryInterface(void)} void *
[0x00000001] 0x00007ff9f8c907d0 {fastprox.dll!CWbemObject::AddRef(void)} void *
[0x00000002] 0x00007ff9f8c8ffd0 {fastprox.dll!CWbemObject::Release(void)} void *
+pInParamsDefinition 0x000001c04e743ca0 IWbemClassObject *
And varCommand:
+varCommand BSTR = 0x000001c04e74ffe8 tagVARIANT
The call stack:
oleaut32.dll!SysAllocString()
vfbasics.dll!AVrfpSysAllocString()
wbemcomn.dll!CVar::SetVariant()
fastprox.dll!CWbemInstance::Put()
> Ele.exe!WMIConnection::InvokeMethod()
So it appears that bstrVal isn't being properly set, I think? I tried initializing it first with VariantInit, and I also tried dynamically allocating it on the heap instead. Neither resolved the issue:
VARIANT varCommand;
VariantInit(&varCommand);
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
I also tried manually zeroing out the Variant buffer, to no effect. This is what we have for bstrVal in the memory dump when the access violation occurs:
bstrVal 0x000001c04e74ffe8 <Error reading characters of string.> wchar_t *
<Unable to read memory> wchar_t

On this line:
varCommand.bstrVal = _bstr_t(L"notepad.exe");
The code creates a temporary _bstr_t object that goes out of scope, destroying the allocated BSTR memory, immediately after varCommand.bstrVal has been assigned to. Thus, varCommand.bstrVal is left dangling, pointing at invalid memory, when varCommand is passed to pClassInstance->Put(). That is undefined behavior.
Use this instead to keep the BSTR alive until you are actually done using it:
_bstr_t str(L"notepad.exe");
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = str;
// use varCommand as needed...
// DO NOT call VarClear() or SysFreeString()!
// You don't own the BSTR memory...
//VarClear(&varCommand);
Alternatively:
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = SysAllocString(L"notepad.exe");
// use varCommand as needed...
// You DO own the BSTR memory, so free it!
VarClear(&varCommand);
Otherwise, consider using _variant_t instead, let it manage the memory for you:
_variant_t varCommand(L"notepad.exe");
hres = pClassInstance->Put(L"CommandLine", 0, &varCommand, 0);
wprintf(L"The command is: %s\n", V_BSTR(&varCommand));

I figured it out. There are several forum posts on the internet asking about this example, with no solutions given, so I am very happy to provide this now.
The Microsoft example is using the incorrect classes.
In the Microsoft example, they attempt to call the Put method on a class instance of Win32_Process to set the parameters.
This is incorrect. We need to set the parameters via first getting the class method definition for Win32_Process::Create and then setting its parameters inside a new instance of Win32_Process::Create.
We additionally need to construct an instance of the Win32_ProcessStartup class object as it's a required input parameter for Win32_Process::Create.
In the example below I will populate one field of the Win32_ProcessStartup class instance, you can figure out the rest.
So this code from the Microsoft example:
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(MethodName, 0,
&pInParamsDefinition, NULL);
IWbemClassObject* pClassInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
// Create the values for the in parameters
VARIANT varCommand;
varCommand.vt = VT_BSTR;
varCommand.bstrVal = _bstr_t(L"notepad.exe");
// Store the value for the in parameters
hres = pClassInstance->Put(L"CommandLine", 0,
&varCommand, 0);
wprintf(L"The command is: %s\n", V_BSTR(&varCommand));
Becomes (sans error handling for readability):
// Get the class object
hres = pClass->GetMethod(lpwMethodName, 0,
&pInParamsDefinition, NULL);
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
// Get the Win32_ProcessStartup class object
IWbemClassObject* pStartupObject = NULL;
hres = pSvc->GetObject(_bstr_t(L"Win32_ProcessStartup"), 0, NULL, &pStartupObject, NULL);
// Create an instance of the Win32_ProcessStartup class object
IWbemClassObject* pStartupInstance = NULL;
hres = pStartupObject->SpawnInstance(0, &pStartupInstance);
// Create the value for the ShowWindow variable of the Win32_ProcessStartup class
VARIANT varParams;
VariantInit(&varParams);
varParams.vt = VT_I2;
varParams.intVal = SW_SHOW;
// And populate it
hres = pStartupInstance->Put(_bstr_t(L"ShowWindow"), 0, &varParams, 0);
// Get the method definition for Win32_Process::Create and store it in pInParamsDefinition
hres = pClass->GetMethod(_bstr_t(lpwMethodName), 0, &pInParamsDefinition, NULL);
// Spawn an instance of the Create method and store it in pParamsInstance
IWbemClassObject* pParamsInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pParamsInstance);
// Now we can set the parameters without error
hres = pParamsInstance->Put(_bstr_t(L"CurrentDirectory"), 0, pvCurrentDirectory, 0);
Please note that all of these hres returns should be checked for failure.
The definition for Win32_ProcessStartup is provided here:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/win32-processstartup
And the definition for the Create method of Win32_Process:
https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-process

Related

WMI C++ IWbemServices . ExecMethod for WBEM_E_INVALID_METHOD_PARAMETERS

I used the mechanism of WMI. Through the modification of dsdt.dsl and production of MOF file, I accomplish the custom WMI function by C#. But there is a problem, when I want to use the C++ - MFC to communicate with the MOF file. While the code runs to the IWbemServices . ExecMethod function, it shows the error message: WBEM_E_INVALID_METHOD_PARAMETERS(0x8004102F). I think the reason occurs with the input parameter: boolean… Hope everyone can provide some suggestions!
Many thanks!
acpimof.mof:
class WMIEvent : __ExtrinsicEvent
{
};
[WMI,
Dynamic,
Provider("WmiProv"),
Locale("MS\\0x409"),
Description("Acpi_Commands"),
guid("{ABBC0F6D-8EA1-11d1-00A0-C90629100000}")
]
class Acpi_Commands
{
[key, read]
string InstanceName;
[read] boolean Active;
[WmiMethodId(1),
Implemented,
read, write,
Description("setReadLight")]
void setReadLight([in, Description("Status")] boolean Status);
};
acpi.cpp:
Copy the MSDN – Example: Calling a Provider Method (https://msdn.microsoft.com/en-us/library/aa390421(v=vs.85).aspx ). The Step 1, 2, 3 & 5 are same totally with the example, so I don’t show the code. I modify the Step 4 & 6.
// Step 4: ---------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
// Connect to the local namespace
// and obtain pointer pSvc to make IWbemServices calls.
hres = pLoc->ConnectServer(
_bstr_t(L"ROOT\\WMI"),
NULL,
NULL,
0,
NULL,
0,
0,
&pSvc
);
if (FAILED(hres))
{
cout << "Could not connect. Error code = 0x"
<< hex << hres << endl;
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// set up to call the Win32_Process::Create method
BSTR ClassName = SysAllocString(L"Acpi_Commands");
BSTR MethodName = SysAllocString(L"setReadLight");
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(MethodName, 0, &pInParamsDefinition, NULL);
IWbemClassObject* pClassInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
// Create the values for the in parameters
VARIANT varCommand;
varCommand.vt = VT_BOOL;
varCommand.boolVal = VARIANT_TRUE;
// Store the value for the in parameters
hres = pClassInstance->Put(L"Status", 0, &varCommand, CIM_BOOLEAN);
// Execute Method
IWbemClassObject* pOutParams = NULL;
hres = pSvc->ExecMethod(ClassName, MethodName, 0, NULL, pClassInstance, &pOutParams, NULL);
//Get error message: WBEM_E_INVALID_METHOD_PARAMETERS(0x8004102f)
if (FAILED(hres))
{
cout << "Could not execute method. Error code = 0x" << hex << hres << endl;
// Clean up(don't show here)
return 1; // Program has failed.
}
// Clean up(don't show here)
system("pause");
return 0;
I found the solution by myself, and I share this for someone who needs:)
The issue occurs in the first parameter for the IWbemServices::ExecMethod which needs the specified property value rather than the simple class name. So it needs some setting to get the specified property value.(But the "Example: Calling a Provider Method" only sets the class name for the parameter and it works... I guess the reason happen in the namesapce(original: "ROOT\CIMV2" => that is a standard model for Windows), and I use the "ROOT\WMI" to connect with the WMI, so it needs specified property value. If I'm wrong, please correct me!)
Please revise the Step 6 of the acpi.cpp above.
// Step 6: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
BSTR ClassName = SysAllocString(L"Acpi_Commands");
BSTR MethodName = SysAllocString(L"setReadLight");
BSTR bstrQuery = SysAllocString(L"Select * from Acpi_Commands");
//The IEnumWbemClassObject interface is used to enumerate Common Information Model (CIM) objects
//and is similar to a standard COM enumerator.
IEnumWbemClassObject *pEnum = NULL;
hres = pSvc->ExecQuery(_bstr_t(L"WQL"), //Query Language
bstrQuery, //Query to Execute
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, //Make a semi-synchronous call
NULL, //Context
&pEnum /*Enumeration Interface*/);
hres = WBEM_S_NO_ERROR;
ULONG ulReturned;
IWbemClassObject *pObj;
DWORD retVal = 0;
//Get the Next Object from the collection
hres = pEnum->Next(WBEM_INFINITE, //Timeout
1, //One of object requested
&pObj, //Returned Object
&ulReturned /*No of object returned*/);
IWbemClassObject* pClass = NULL;
hres = pSvc->GetObject(ClassName, 0, NULL, &pClass, NULL);
IWbemClassObject* pInParamsDefinition = NULL;
hres = pClass->GetMethod(MethodName, 0, &pInParamsDefinition, NULL);
IWbemClassObject* pClassInstance = NULL;
hres = pInParamsDefinition->SpawnInstance(0, &pClassInstance);
VARIANT var1;
VariantInit(&var1);
BSTR ArgName0 = SysAllocString(L"Status");
V_VT(&var1) = VT_BOOL;
V_BOOL(&var1) = VARIANT_FALSE;
hres = pClassInstance->Put(ArgName0, 0, &var1, CIM_BOOLEAN);
printf("\nPut ArgName0 returned 0x%x:", hres);
VariantClear(&var1);
// Call the method
VARIANT pathVariable;
VariantInit(&pathVariable);
//The IWbemClassObject::Get method retrieves the specified property value, if it exists.
hres = pObj->Get(_bstr_t(L"__PATH"),
0,
&pathVariable,
NULL,
NULL);
printf("\npObj Get returned 0x%x:", hres);
hres = pSvc->ExecMethod(pathVariable.bstrVal,
MethodName,
0,
NULL,
pClassInstance,
NULL,
NULL);
VariantClear(&pathVariable);
printf("\nExecMethod returned 0x%x:", hres);
printf("Terminating normally\n");
// Clean up
SysFreeString(ClassName);
SysFreeString(MethodName);
pClass->Release();
pClassInstance->Release();
pInParamsDefinition->Release();
pLoc->Release();
pSvc->Release();
CoUninitialize();
system("pause");
return 0;

Deleting WMI instance with C++

I have found some samples with C# and VBS for WMI instance deletion, however I need this implemented with C++.
My sample code:
CoInitialize(NULL);
HRESULT hRes;
//Obtain the initial locator to WMI
CComPtr<IWbemLocator> pLoc = NULL;
hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*) &pLoc);
if(FAILED(hRes))
return 1;
//Connect to WMI through the IWbemLocator::ConnectServer method
CComPtr<IWbemServices> pSvc = NULL;
//Connect to the root namespace with the current user and obtain pointer pSvc to make IWbemServices calls.
hRes = pLoc->ConnectServer(L"ROOT\\SUBSCRIPTION", NULL, NULL, 0, NULL, 0, 0, &pSvc);
if(FAILED(hRes))
return 1;
hRes = pSvc->DeleteInstance(
L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'",
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
return 0;
According to that I've found here and here, my code should work. I surely have CommandLineEventConsumer named {709782F3-E860-488E-BD8A-89FBC8C1495C}
And my code fails on IWbemServices::DeleteInstance, error code 0x80041008 (One of the parameters to the call is not correct).
I would appreciate if someone spot mistake in my code. Or maybe some privileges are required to do that?
The first parameter to IWbemServices::DeleteInstance is a BSTR. A BSTR is different from a UTF-16 encoded C-style string in that it stores an explicit length argument. Even though BSTR is of type wchar_t*, you cannot pass a plain string literal in place of a BSTR.
To create a BSTR from a string literal you need to call SysAllocString:
BSTR objPath = ::SysAllocString(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(
objPath,
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
::SysFreeString(objPath);
Alternatively, since you are already using ATL for CComPtr you could use CComBSTR to make your life easier:
CComBSTR objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(
objPath,
WBEM_FLAG_RETURN_IMMEDIATELY, NULL, NULL);
Note: IWbemLocator::ConnectServer also needs BSTRs as parameters. The sample provided on the documentation page does pass a plain C-style string, so maybe the IWbemLocator interface is more forgiving when presented invalid parameters.
I have found two solutions:
1.Remove WBEM_FLAG_RETURN_IMMEDIATELY flag.
_bstr_t objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
hRes = pSvc->DeleteInstance(objPath, 0, NULL, NULL);
2.Pass IWbemCallResult for result.
_bstr_t objPath(L"CommandLineEventConsumer.Name='{709782F3-E860-488E-BD8A-89FBC8C1495C}'");
CComPtr<IWbemCallResult> pRes = NULL;
hRes = pSvc->DeleteInstance(objPath, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pRes);
Didn't investigate a lot, but it works both ways. Looks like specs aren't 100% correct.

Changing Adapter NetConnectionID using IWBemServices in Visual-C++

I am trying to change the NetConnectionID value of a network adapter by creating an instance of the Win32_NetworkAdapter class using IWBemServices. I am locating the adapter by its DeviceId and then trying to put the new NetConnectionID into the instance.
I am not getting any errors, but the NetConnectionID remains unchanged.
This is what my function currently looks like:
int CIPConfigModule::setAdapterName(CString CAdapterName, unsigned int idAdapter)
{
int bNoErrors = NO_ERROR;
IWbemClassObject *pNewInstance = NULL;
IWbemClassObject *pObjectClass = NULL;
IWbemContext *pCtx = NULL;
IWbemCallResult *pResult = NULL;
// Get the class definition.
string strIndex = std::to_string((long double)idAdapter);
string strIndexCommand = "Win32_NetworkAdapter.DeviceID=\"";
strIndexCommand.append(strIndex).append("\"");
CString CSIndexCommand = strIndexCommand.c_str();
// BSTR PathToClass = SysAllocString(L"Example");
HRESULT hRes = m_pSvc->GetObject((bstr_t)CSIndexCommand, 0, pCtx,
&pObjectClass, &pResult);
if (hRes != WBEM_S_NO_ERROR){
bNoErrors=ERROR_ATTEMPTING_TO_CHANGE_NAME;
return bNoErrors;
}
// Create a new instance.
pObjectClass->SpawnInstance(0, &pNewInstance);
pObjectClass->Release(); // Don't need the class any more
VARIANT v;
VariantInit(&v);
// Set the NetConnectioID property.
V_VT(&v) = VT_BSTR;
V_BSTR(&v) = CAdapterName.AllocSysString();
//V_BSTR(&v) = SysAllocString(L"Testing");
BSTR KeyProp = SysAllocString(L"NetConnectionID");
pNewInstance->Put(KeyProp, 0, &v, 0);
SysFreeString(KeyProp);
VariantClear(&v);
// Other properties acquire the 'default' value specified
// in the class definition unless otherwise modified here.
// Write the instance to WMI.
hRes = m_pSvc->PutInstance(pNewInstance, 0, pCtx, &pResult);
pNewInstance->Release();
if (hRes != WBEM_S_NO_ERROR){
bNoErrors=ERROR_ATTEMPTING_TO_CHANGE_NAME;
return bNoErrors;
}
return bNoErrors;
}
Does anyone have an idea of what I could be missing?

How to use the "__ProviderArchitecture" flag of IWbemContext in c?

I've been trying to follow the following MSDN tutorial to query the 64 bit registry provider from a 32 bit application.
Sadly, the examples are all written in VB, and I'm stuck with something.
For C++ developers, the article mentions that...
C++ applications can use the IWbemContext interface with IWbemServices::ExecMethod to communicate the use of a nondefault provider to WMI.
...although, when you look at the sample VB code, the context object is also used in the ConnectServer method:
Set objCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
objCtx.Add "__ProviderArchitecture", 32
Set objLocator = CreateObject("Wbemscripting.SWbemLocator")
Set objServices = objLocator.ConnectServer("","root\default","","",,,,objCtx)
Set objStdRegProv = objServices.Get("StdRegProv")
I've tried reproducing this in VC++:
HRESULT res;
CComPtr<IWbemContext> ctx;
if (!create_registry_redirection_context_(hive, ctx)) {
return false;
}
res = locator_->ConnectServer(CComBSTR(namespace_.c_str()), // Namespace to use
0, // Current security context (username)
0, // Current security context (password)
0, // Use current locale
WBEM_FLAG_CONNECT_USE_MAX_WAIT, // Return if connexion is unsuccessful after 2 minutes
0, // Name of the domain of the user to authenticate
ctx, // Optional context
&service_); // Fill this pointer
The create_registry_redirection_context_ method uses CoCreateInstance to instantiate my context, and I use the following lines to set the architecture:
CComVariant value_arch(64, VT_I4);
ctx->SetValue(L"__ProviderArchitecture", 0, &value_arch);
Now the problem is, the ConnectServer method returns an error (0x80041008 - WMI Invalid Parameter). If I comment out the ctx->SetValue(L"__ProviderArchitecture", 0, &value_arch); line, everything works properly, but of course, I end up querying the 32 bit registry provider.
I've also tried not setting any context during the ConnectServer call, but only during the ExecMethod call (as specified in the MSDN article), but although I don't get any error, I'm still querying the 32 bit provider, and not seeing my 64bit registry keys.
What am I doing wrong?
Thanks in advance for your time.
I know, it's a little bit late, but for archive (and because MS is unable to provide such a sample):
HRESULT hres;
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
m_nExitCode = TCP1;
return FALSE;
}
IWbemContext *pContext = NULL;
hres = CoCreateInstance(CLSID_WbemContext, 0, CLSCTX_INPROC_SERVER, IID_IWbemContext, (LPVOID *) &pContext);
if (FAILED(hres))
{
m_nExitCode = TCP1_2;
return FALSE;
}
VARIANT vArchitecture;
VariantInit(&vArchitecture);
V_VT(&vArchitecture) = VT_I4;
V_INT(&vArchitecture) = 64;
hres = pContext->SetValue(_bstr_t(L"__ProviderArchitecture"), 0, &vArchitecture);
VariantClear(&vArchitecture);
//VARIANT vRequiredArchicture;
//VariantInit(&vRequiredArchicture);
//V_VT(&vRequiredArchicture) = VT_BOOL;
//V_BOOL(&vRequiredArchicture) = VARIANT_TRUE;
//hres = pContext->SetValue(_bstr_t(L"__RequiredArchitecture"), 0, &vRequiredArchicture);
//VariantClear(&vRequiredArchicture);
IWbemServices *pSvc = NULL;
hres = pLoc->ConnectServer(
_bstr_t(L"root\\Microsoft\\SqlServer\\ComputerManagement10"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
pContext, // Context object
&pSvc // pointer to IWbemServices proxy
);
The block with RequiredArchitecture is untested and seems to be unnecessary.
PS: Error-handling needs to be improved!
have you looked at
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393067(v=vs.85).aspx
and tried setting "__RequiredArchitecture" = TRUE as well?
Also have to tried to get it to work in the way that the same code shows ( ie 64 bit to 32 bit) first?

How to change Win32_NetworkAdapter NetConnectionID property using WMI

I am writing a VC++ program which needs to change Network Connection name (eg. "Local Connection").
The Windows's IPHELPER provides API to get network connection, but it doesn't provide write method.
And I know I can use "netsh" to change this network connection name, yes, in English Windows, this method works properly, but in Japanese or Chinese Windows this method can't work properly.
After read some documents in MSDN, I found WMI Win32_NetworkAdapter class has a property "NetConnectionID". This property contains the adapter's network connection name, and its read/write perperty.
http://msdn.microsoft.com/en-us/library/windows/desktop/aa394216(v=vs.85).aspx
NetConnectionID
Data type: string
Access type: Read/write
So, I wrote a WMI code to change this property value.
int RasWmiAdapterSetInfo(void *pWmiAdapterCfg)
{
if(pWmiAdapterCfg)
{
int retVal = -1;
HRESULT hres;
VARIANT varWrite;
RAS_WMI_W32_ADAPTER_PTR *pRasWmiAdapterCfg = (RAS_WMI_W32_ADAPTER_PTR*)pWmiAdapterCfg;
VariantInit(&varWrite);
V_VT(&varWrite) = VT_BSTR;
V_BSTR(&varWrite) = SysAllocString(L"My New Connection 1");
hres = pRasWmiAdapterCfg->pclsObj->Put(L"NetConnectionID", 0, &varWrite, 0);
if(FAILED(hres))
{
printf("RasWmiAdapterSetInfo Failed HR=%08x\n", hres);
goto CLEARUP;
}
retVal = 0;
CLEARUP:
VariantClear(&varWrite);
return retVal;
}
return -1;
}
The return value hres was OK, but this property value was not changed.
IWbemLocator *pLoc;
IWbemServices *pSvc;
IEnumWbemClassObject *pEnumerator;
...
... (ignore some code to init pLoc, pSvc, pEnumerator)
...
IWbemClassObject *pclsObj;
IWbemClassObject *pClass;
pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
pSvc->GetObject( bstr_t(adapterClsName), 0, NULL, &pClass, NULL );
Is anyone can tell me why the pclsObj->Put method did not take effect?
Thanks.
As far as I understand, IWbemClassObject::Put() only updates a class instance's property value in memory. To commit changes to WMI repository, you need to call IWbemServices::PutInstance() to update entire class instance.
So adding something like this to your code should get it to work:
IWbemCallResult* callResult = nullptr;
HRESULT hr = wbemSvc_->PutInstance(pClass, WBEM_FLAG_UPDATE_ONLY,
nullptr, &callResult);
if (SUCCEEDED(hr) && callResult)
callResult->Release();
Hope that helps!