Remote running msiexec on Win32_Process - c#

The main task of this short part is get some computer name and install on this PC needed software (throught msiexec.exe)
I do this
{
Credential creds = new Credential();
UserAttr UserCredential = new UserAttr();
UserCredential = creds.DefaultFlagsTest();
ConnectionOptions connection = new ConnectionOptions();
connection.Username = UserCredential.UserName;
connection.Password = UserCredential.password;
connection.Authentication = AuthenticationLevel.PacketPrivacy;
connection.Authority = "ntlmdomain:tcc1";
ManagementScope scope = new ManagementScope(
"\\\\"+computerName+"\\root\\CIMV2", connection);
scope.Connect();
ManagementClass classInstance =
new ManagementClass(scope,
new ManagementPath("Win32_Process"), null);
ManagementBaseObject inParams = classInstance.GetMethodParameters("Create");
inParams["CommandLine"] = #"msiexec.exe /qb /m log.mif /i ""\\tcc1-pgh10.tcc1.local\swshare$\Packages\NetAgent_10.0.3361\exec\Kaspersky Network Agent.msi""";
ManagementBaseObject outParams = classInstance.InvokeMethod("Create", inParams, null);
int res = int.Parse(outParams["ReturnValue"].ToString());
if (res == 0)
{
MessageBox.Show(outParams["ReturnValue"].ToString(), "Result");
}
else throw new System.ComponentModel.Win32Exception(res);
Close();
}
the program returns 0, but its doesnt mean that msiexec is complete with the same success. Error is check path of package .. or smth. But what i see in a log file log.mif:
..............................
START ATTRIBUTE
NAME = "Product"
ID = 2
ACCESS = READ-ONLY
STORAGE = SPECIFIC
TYPE = STRING(64)
VALUE = "\tcc1-pgh10.tcc1.local\swshare$\Packages\NetAgent_10.0.3361\exe"
END ATTRIBUTE
..............................
he crops the name of package at 64 symb. The reason is that parametr CommandLine of Win32_Process.Create has this limit.
I don't know how to overcome this...
Win32_Process.Create also has property CurrentDirectory, thath seems can solve this propblem. But he can't process UNC paths.
and i can't do the install directory shorter. it is not right. (And i can say that i've done this. And its worked)
Please, maybe you know how to solve this propblem with a long installation path?
different properties as TARGETDIR or INSTALLDIR set only path TO install, no FROM...

I gave up
START ATTRIBUTE
NAME = "Product"
ID = 2
ACCESS = READ-ONLY
STORAGE = SPECIFIC
TYPE = STRING(64)
VALUE = "\tcc1-pgh10.tcc1.local\swshare$\Packages\NetAgent_10.0.3361\exe"
END ATTRIBUTE
The value is limited to 64symb including path. It will be working properly only on a local machine without using UNC paths

Related

Change service startup type to Delayed (Automatic) in remote machine

I am using the below code to change the service start in a remote server up type to manual/automatic using C#.
public static void ChangeServiceStartupType()
{
string query1 = "select * from Win32_Service where name = 'myservice' ";
string server = "servername";
ConnectionOptions connectoptions = new ConnectionOptions();
connectoptions.Username = #"username";
connectoptions.Password = "password";
ManagementScope scope = new ManagementScope(#"\\" + server + #"\root\cimv2");
scope.Options = connectoptions;
scope.Connect();
ObjectQuery query = new ObjectQuery(query1);
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
foreach (ManagementObject mo in managementObjectSearcher.Get())
{
string name = mo.Properties["Name"].Value.ToString().Trim().ToLower();
string state = mo.Properties["State"].Value.ToString().Trim();
string startmode = mo.Properties["StartMode"].Value.ToString().Trim();
changemode(mo, "Automatic");
}
}
Here is the changemode method
private static void changemode(ManagementObject mo, string startmode)
{
ManagementBaseObject inParams = mo.GetMethodParameters("ChangeStartMode");
inParams["startmode"] = startmode;
ManagementBaseObject outParams = mo.InvokeMethod("ChangeStartMode", inParams, null);
startmode = mo.Properties["StartMode"].Value.ToString().Trim();
}
When I pass the parameters Manual or Automatic in the changemode(object,startmode parameter) the service start up type changes from automatic to manual and vice-versa. However, I am unable to change it to Automatic(Delayed Start).
I tried Auto-Delayed , Delayed-Auto, Automatic (Delayed Start) How do I achieve this?
If there's a way through ManagementObject than I haven't found it, but it IS possible through the registry:
public void SetDalayedAutoStart(string machineName, string serviceName)
{
using (var regKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName))
{
using (RegistryKey serviceKey = regKey.OpenSubKey(#"System\CurrentControlSet\Services\" + serviceName, true))
{
serviceKey.SetValue("DelayedAutostart", 1, RegistryValueKind.DWord);
}
}
}
Note 1: This is only relevant for services with the Automatic start type
Note 2: The new state will be visible in the Services control (services.msc) only after a restart (don't ask me why)
Looking at the documentation, it doesn't seem to be possible to make the service start with a delay using the ChangeStartMode Win32 method. Doing what you want is fairly straightforward if you use ServiceInstaller, like so:
myServiceInstaller.StartType = ServiceStartMode.Automatic;
myServiceInstaller.DelayedAutoStart = true;
However I'm guessing that isn't an option, so we will have to dig deeper.
The Service class does have a DelayedAutoStart property, but it is read only. If you want to set it to delayed, you're going to have to mess around with P/Invokes. All information I could find points to ChangeServiceConfig2 and this struct.
Alternatively, you can just execute this command it it will have the same effect. However, it isn't really an answer to your question, just a workaround.
sc.exe config myService start= delayed-auto
Finally, check out this (very) long answer by user Kramii, and this by Peter Kelly. Both of them wrote helper classes to make doing this sort of thing a lot easier. I haven't tested them but they look promising.
Sty's answer lets you set a service to automatic delayed at the time of creation; not post deployment.
There is a command line that can do that
sc \\computername config *servicename* start= delayed-auto
I ran this command line in the remote server using WMI Management Class and it works fine.
More documentation on how to remote start a process here

check if a process runs on a remote machine for a certain user

I have to create an executable which checks if a certain process is running for a certain user (a service account) on a remote machine, the input parameters are 3 strings, machine name, user name and process name.
I have the idea to do this using either System.Diagnostics or WMI, just wanted to double check if anybody has another idea like powershell or even a window functionality which could make the task even easier.
since we want to make sure that process is always running on a dedicated server we will configure a scheduled task to execute a small console application which does this check. Not sure if coding it in C# is the best option or am I ignoring a builtin feature of windows server? Thanks!
I'm pretty sure you can accomplish this with tasklist cmd: tasklist /S \\<server> /V > tasklist.txt. this will give you a file you can grep through.
namespace not referenced
using System.Management;
I have ended up by implementing following solution in C#
this retrieves the username without domain name of the user running processName on machineName
public static string GetProcessOwner()
{
try
{
var resultUserName = string.Empty;
ConnectionOptions opt = new ConnectionOptions();
string path = string.Format(#"\\{0}\root\cimv2", machineName);
ManagementScope scope = new ManagementScope(path, opt);
scope.Connect();
var query = new ObjectQuery(string.Format("Select * From Win32_Process Where Name = '{0}'", processName));
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
//return argList[1] + "\\" + argList[0];
resultUserName = argList[0];
}
}
return resultUserName;
}
catch (Exception exc)
{
Debug.WriteLine(exc.Message);
return string.Empty;
}
}
GetOwner can return empty array for remote comp, so it may not work

WMI Namespace "Not Found"

I'm trying to run a bat file remotely (from XP to 2003) and running into a problem connecting to any WMI namespace other than cimv2. The code below hits a "Not Found" exception in the "GetMethodParameters" call. But if I replace "directory" with "cimv2", everything is gravy.
ConnectionOptions theConnection = new ConnectionOptions();
theConnection.Username = conDet.User;
theConnection.Password = conDet.Pwd;
theConnection.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope theScope = new ManagementScope(String.Format(#"\\{0}\root\directory", conDet.Server), theConnection);
theScope.Connect();
ManagementClass processClass = new ManagementClass(theScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
enter code here
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = filename;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
I've checked the security on my machine and the server and the two namespaces have identical security settings. Any ideas what's going on?
Thanks.
you are using a wrong namespace, the Win32_Process WMI class is defined in root\cimv2.
So you must rewrite your code to
ManagementScope theScope = new ManagementScope(String.Format(#"\\{0}\root\cimv2", conDet.Server), theConnection);

How to share remote folders?

I'm working on a .NET class that will be used in tools to manage our Active Directory accounts. Each of our accounts gets a network home directory, which can be located on a couple different servers, depending on what type of account we're working with.
I can create and delete the folders just fine, but I'm running into trouble sharing the folders. I found some code here that seems to be what I want, but it isn't working properly for me. The return value I get is 2, but I'm not sure what that indicates.
This shouldn't be a file permission issue, since I'm running my test application as myself, and I have full control of the folder that I'm trying to share (and each of its parent folders).
Here's my (modified) version of the code:
char[] delim = { '\\' };
// folderPath is a string (UNC path)
string[] drivePath = folderPath.Split(delim);
// Create a ManagementClass object
ManagementClass managementClass = new ManagementClass("Win32_Share");
// Create ManagementBaseObjects for in and out parameters
ManagementBaseObject inParams =
managementClass.GetMethodParameters("Create");
ManagementBaseObject outParams;
// Set the input parameters
inParams["Description"] = "";
inParams["Name"] = drivePath[3];
inParams["Path"] = folderPath;
inParams["Type"] = 0x0; // Disk Drive
// Invoke the method on the ManagementClass object
outParams = managementClass.InvokeMethod("Create", inParams, null);
I've tried outputting the other outParams, but it looks like ReturnValue is all I get.
Is there a different way to share a remote folder that would work better?
I'll answer myself, in case someone else finds this later.
I ended up going with the extremely unsexy answer of using PSExec and NET SHARE:
// Retrieve drive path from AD
char[] delim = { '\\' };
string[] drivePath = userAD.HomeDirectory.Split(delim);
// Configure startup properties of process
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = true;
startInfo.FileName = "C:\\Windows\\System32\\psexec.exe";
// Build arguments for folder on alpha or student
startInfo.Arguments = "\\\\" + serverName + " -s net share " + shareName + "=" folderPath + "$ /GRANT:\"authenticated users\",full";
Process process = new Process();
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
Note that in our environment the program will already be running under a user who has Full Control permissions on the folder being shared. To allow similar for a non-privileged user, you have to specify a name and password in startInfo.Arguments.

Creating share programmatically fails with error 9

ObjectGetOptions options = new ObjectGetOptions();
ManagementPath p = new ManagementPath("\\\\server01\\root" + "\\cimv2:Win32_Share");
// Make a connection to a remote computer.
ManagementScope scope = new ManagementScope("\\\\server01\\root\\cimv2");
scope.Connect();
// Create a ManagementClass object
ManagementClass managementClass = new ManagementClass(scope, p, options);
// Create ManagementBaseObjects for in and out parameters
ManagementBaseObject inParams = managementClass.GetMethodParameters("Create");
ManagementBaseObject outParams;
// Set the input parameters
//inParams["Description"] = String.Empty;
inParams["Name"] = "test";
inParams["Path"] = #folderPath;
inParams["Type"] = 0x0; // Disk Drive
// Invoke the method on the ManagementClass object
outParams = managementClass.InvokeMethod("Create", inParams, null);
// Check to see if the method invocation was successful
if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
{
throw new Exception("Unable to share directory. Error code: " + outParams.Properties["ReturnValue"].Value);
}
}
catch (Exception e)
{
MessageBox.Show(e.Message.ToString());
}
}
I am using the following code to set up a share, but I am always getting a return value of 9 which means invalid name. I am passing a string and have tried to use an explicit string and I still get error 9.
I am creating the share remotely rather than on local machine however. I have tried to make sure I am connecting to the remote WMI provider, but I am not sure if I have been successful.
Any suggestions from WMI gurus and others is greatly appreciated.
Found the answer on another site. The folder path needs to be the local path to the machine the share is created on, not a UNC path like I was using.
I had the same error. In my case though the problem was a trailing backslash. Doing directoryPath.TrimEnd('\') solved the problem.
Return Values
Returns one of the values in the following table or any other value to indicate an error.
0 – Success
2 – Access denied
8 – Unknown failure
9 – Invalid name
10 – Invalid level
21 – Invalid parameter
22 – Duplicate share
23 – Redirected path
24 – Unknown device or directory
25 – Net name not found

Categories

Resources