1

I'm writing a function in C# that queries WMI, uses the objects returned from WMI as parameters to a method in a different WMI class.

private void InstallUpdates()
{
    ManagementScope sc = new ManagementScope(@"\\.\root\ccm\clientsdk");
    ManagementClass c = new ManagementClass(@"CCM_SoftwareUpdatesManager");
    ManagementObjectSearcher s = new ManagementObjectSearcher("SELECT * FROM CCM_SOFTWAREUPDATE WHERE COMPLIANCESTATE=0 AND EVALUATIONSTATE < 2");
    c.Scope = s.Scope = sc;

    ManagementObjectCollection col = s.Get();
    List<ManagementObject> lUpdates = new List<ManagementObject>();

    //Install each update individually and track progress
    int index = 1;
    foreach (ManagementObject o in col)
    {


        object[] args = { o };

        object[] methodArgs = { args, null };

        lblCurrentAction.Text = "Now running method: Install Updates " + o.Properties["Name"].Value + " EvalState=" + (UInt32)o.Properties["EvaluationState"].Value;

        c.InvokeMethod("InstallUpdates",methodArgs);

        lblCurrentUpdate.Text = "Now Installing Update " + index + " of " + col.Count + ": " + o.Properties["name"].Value;

        UInt32 intProgress = (UInt32)o.Properties["PercentComplete"].Value;

        UInt32 evalState = (UInt32)o.Properties["EvaluationState"].Value;

        lblCurrentAction.Text = lblCurrentAction.Text + " EvalState: " + evalState;

        while (evalState <=  7)
        {
            progressBar1.Value = (intProgress <= 100) ? (int)intProgress : 100;
            evalState = (UInt32)o.Properties["EvaluationState"].Value;
            intProgress = (UInt32)o.Properties["PercentComplete"].Value;
            lblCurrentAction.Text = lblCurrentAction.Text + " EvalState: " + evalState;
        }

        ++index;

    }





}

I pasted the entire function for reference, but the problem lines are #1,2, and 4 inside the foreach loop. From the documentation here the method takes as a parameter an array of ccm_softwareupdate objects, which I've successfully queried from a different class (and am running a foreach on the collection), so I know that the objects exist.

Any, as these are system updates, I'd like to install them one at a time, at least during testing, but when I pass a single object array to the method

object[] args = { o };

c.InvokeMethod("InstallUpdates", args);

I get a cast error:

unable to cast object of type 'system.management.managementobject' to system.array

So somewhere it's obviously seeing my array as only one object. I know it's not making it to the WMI method because I don't see the update starting to install.

From reading on the internet, I've also tried what's in the function now:

object[] args = { o };

object[] methodArgs = { args, null };

c.InvokeMethod("InstallUpdates", methodArgs);

The key here was to create a second array that holds the first array and a null value as the second value. This actually works and the WMI method is invoked, but it never returns from the method, the code just hangs. Switching the arguments around

object[] methodArgs = { null, args };

Reveals that it actually hangs on the null argument, because here the update never starts installing. I've also tried this as a sanity check

object[] args = { o, o };

c.InvokeMethod("InstallUpdates", args);

But I get the same casting error, so I must be on the right track with the double array method. Also, using

object[] methodArgs = { args, 0};

or

object[] methodArgs = { args };

Doesn't work.

To reiterate, I'm looking for a way to pass an array to a WMI Method using C#.


Update

This powershell script does the same thing, and actually works. The only difference is that it's initial array has more than one object, but that shouldn't matter.

    #    '=================================================================== 
#    ' DISCLAIMER: 
#    '------------------------------------------------------------------- 
#    ' 
#    ' This sample is provided as is and is not meant for use on a  
#    ' production environment. It is provided only for illustrative  
#    ' purposes. The end user must test and modify the sample to suit  
#    ' their target environment. 
#    '  
#    ' Microsoft can make no representation concerning the content of  
#    ' this sample. Microsoft is providing this information only as a  
#    ' convenience to you. This is to inform you that Microsoft has not  
#    ' tested the sample and therefore cannot make any representations  
#    ' regarding the quality, safety, or suitability of any code or  
#    ' information found here. 
#    '  
#    '=================================================================== 

# This is a simpple get of all instances of CCM_SoftwareUpdate from root\CCM\ClientSDK 
$MissingUpdates = Get-WmiObject -Class CCM_SoftwareUpdate -Filter ComplianceState=0 -Namespace root\CCM\ClientSDK 

# The following is done to do 2 things: Get the missing updates (ComplianceState=0)  
# and take the PowerShell object and turn it into an array of WMI objects 
$MissingUpdatesReformatted = @($MissingUpdates | ForEach-Object {if($_.ComplianceState -eq 0){[WMI]$_.__PATH}}) 

# The following is the invoke of the CCM_SoftwareUpdatesManager.InstallUpdates with our found updates 
# NOTE: the command in the ArgumentList is intentional, as it flattens the Object into a System.Array for us 
# The WMI method requires it in this format. 
$InstallReturn = Invoke-WmiMethod -Class CCM_SoftwareUpdatesManager -Name InstallUpdates -ArgumentList (,$MissingUpdatesReformatted) -Namespace root\ccm\clientsdk 
4

1 回答 1

1

我在寻找触发 CCM 代理安装更新的 C# 方法时遇到了这个问题。这是我在生产应用程序中运行的内容。

using (var searcher = new ManagementObjectSearcher(string.Format(@"\\{0}\root\CCM\ClientSDK", strComputerName), string.Format("SELECT * FROM CCM_SoftwareUpdate WHERE Name=\"{0}\"", strUpdateName)))
foreach (var obj in searcher.Get())
    using (var mInv = new ManagementClass(string.Format(@"\\{0}\root\CCM\ClientSDK", strComputerName), "CCM_SoftwareUpdatesManager", null))
        mInv.InvokeMethod("InstallUpdates", new object[] { new ManagementBaseObject[] { obj } });
于 2019-02-22T19:50:13.463 回答