【業務用】管理下にあるPCへファイルを配布する(C#)

大量のPCへファイルを配布したい

工場内にある大量のPCを管理していく中で、パッチのファイル等を適用したい。
但し、対象のPCを利用するのはPCが使い慣れた人間ではないので、ファイル共有サーバーに各自がアクセスして、自分でファイルを持ってきてもらうなどはできれば避けたい。

つまりは、こちらからファイルを配布して、できれば実行までしたい。
という要望がきっかけでした。

工場内でシステムを作っていると、こんな状況に遭遇したことはないでしょうか?

バッチを組んでしまってもいいのですが、管理用データベースからデータを取得して実行したい等を想定してのC#での実行です。

※ある程度管理が行き届いている(IPアドレスやPC名が管理されている、利用できるローカルユーザー等が適切に設定されているなど)前提での話です。また、セキュリティの設定上必ず実行できるとは限りません。


※この先のソースコードはご自由にご利用していただいて問題ありませんが、各自の責任でお願いします。問題や不利益等が発生したとしても当サイトは関知しません。

PCの電源が入っているかを調べる

ファイルを配布しようにも相手側のPCの電源がONになっていないとなにもできません。
なので、まずはPingを打って結果を取得します。

/// <summary>
/// 対象PCの電源がONになっているかをPingを実行することにより確認します
/// </summary>
/// <param name="PCName">対象となるPC名を設定します。※IPアドレスを指定しても問題ありません</param>
/// <returns>true:電源がON,false:電源のONが確認できない</returns>
public bool CheckPowerON(string PCName)
{
    bool ret = false;

    //Pingを打って結果を取得
    System.Net.NetworkInformation.Ping pin = new System.Net.NetworkInformation.Ping();
    System.Net.NetworkInformation.PingReply pinRep = pin.Send(PCName);

    if(pinRep.Status == System.Net.NetworkInformation.IPStatus.Success)
    {
        ret = true;     //replyがあった場合はPCの電源が点いているとみなす
    }

    return ret;
}

アクセス権限を設定する

PCの電源が入っていることが確認できたので、そのPCへアクセスする権限をコマンドラインから設定します。
net useコマンドを利用して設定します。

/// <summary>
/// 対象PCのアクセス権限を設定します。
/// </summary>
/// <param name="PCName">対象となるPC名を設定します。※IPアドレスを指定しても問題ありません</param>
/// <param name="Domain">ユーザーのドメイン名を指定します。空文字を指定した場合、ローカルユーザーとして認証を試みます</param>
/// <param name="UserName">ユーザー名を指定します。ドメインユーザーの場合はDomainの設定が必須です</param>
/// <param name="Password">認証に用いるパスワードを指定します</param>
/// <param name="SharedFolderName">対象PCの共有フォルダ名を指定します。フルコントロールユーザーで実行したい場合は、空文字を指定します。</param>
/// <returns>true:認証が成功、false:認証に失敗</returns>
public bool SetAccessAuthority(string PCName,string Domain, string UserName, string Password, string SharedFolderName)
{
    bool ret = false;
    string command = "";

    System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
    System.Diagnostics.Process proc;

    //コマンドプロセスの実行準備
    procInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
    procInfo.RedirectStandardInput = false;     //リダイレクト入力はしない
    procInfo.RedirectStandardOutput = true;     //リダイレクト出力を受け取る
    procInfo.UseShellExecute = false;

    procInfo.CreateNoWindow = true;     //コマンドの入力画面の表示はしない

    //共有フォルダ名が明示されていない場合は、フルコントロールのアクセス設定をする
    if(SharedFolderName == "")
        command = @"/c net use \\" + PCName + @"\ipc$ " + Password + @"/user:";
    else
        command = @"/c net use \\" + PCName + @"\" + SharedFolderName + " " + Password + @" /user:";

    //ドメインユーザーが指定されている場合はドメインをユーザー名の頭に設定する
    if (Domain != "")
        command += Domain + @"\" + UserName;
    else
        command += PCName + @"\" + UserName;        //ローカルユーザー


    procInfo.Arguments = command;

    //コマンドを実行する
    proc = System.Diagnostics.Process.Start(procInfo);
    proc.WaitForExit();     //処理が完了するまで待機

    if (proc.ExitCode != 0)
        ret = true;
    else
        ret = false;

    proc.Dispose();

    return ret;
}

ここまで出来てしまえば、あとはコピーするだけなのでC#のSystem.IO.File.Copy等を利用してやればいいわけです。
パッチを適用させたい場合などは、普段利用されているユーザーのスタートアップにパッチ等を起動するbatファイルを仕込んでおけるならさらに管理は楽になりますね。

アクセス権限の解除をする

コピー等が完了したら、アクセス権限の解除をしてしまいましょう。

/// <summary>
/// アクセス権限を設定したPCのアクセス権限を解除します
/// </summary>
/// <param name="PCName">対象となるPC名を設定します。※IPアドレスを指定しても問題ありません</param>
/// <param name="SharedFolderName">共有設定されたフォルダ名を指定します。フルコントロールユーザーで実行していた場合は、空文字を指定します。</param>
public void ReleaseAccessAuthority(string PCName, string SharedFolderName)
{
    string command = "";

    System.Diagnostics.ProcessStartInfo procInfo = new System.Diagnostics.ProcessStartInfo();
    System.Diagnostics.Process proc;

    //コマンドプロセスの実行準備
    procInfo.FileName = System.Environment.GetEnvironmentVariable("ComSpec");
    procInfo.RedirectStandardInput = false;     //リダイレクト入力はしない
    procInfo.RedirectStandardOutput = true;     //リダイレクト出力を受け取る
    procInfo.UseShellExecute = false;

    procInfo.CreateNoWindow = true;     //コマンドの入力画面の表示はしない

    //共有フォルダ名が明示されていない場合は、フルコントロールのアクセス
    if (SharedFolderName == "")
        command = @"/c net use \\" + PCName + @"\ipc$ /delete";
    else
        command = @"/c net use \\" + PCName + @"\" + SharedFolderName + @" /delete";


    procInfo.Arguments = command;

    //コマンドを実行する
    proc = System.Diagnostics.Process.Start(procInfo);
    proc.WaitForExit();     //処理が完了するまで待機
    proc.Dispose();
}

まとめ

以下のようにして上記関数を利用できます。

string log = ""; //実行結果格納用

string PCName = "PCName";      //PC名
string DomainName = "";        //ドメイン名
string UserName = "UserName";  //ユーザー名
string Password = "Password";  //パスワード
string SharedFolderName = "";  //共有フォルダ名

if (CheckPowerON(PCName))
{
    if (SetAccessAuthority(PCName, DomainName, UserName, Password, SharedFolderName))
    {
        //ファイルをコピーします。
        //フルコントロールアクセスで指定した場合は、
        //PCName + @"\c$"のあとにアクセスしたいフォルダのフルパスを指定します。 ※Cドライブ内のフォルダにアクセスしたい場合
        System.IO.File.Copy(SourceFileName, @"\\" + PCName + @"\" + SharedFolderName);
        log += PCName + ":ファイルのコピーに成功しました。" + Environment.NewLine;

        //コピーが完了したので開放処理
        ReleaseAccessAuthority(PCName, SharedFolderName);
    }
    else
    {
        log += PCName + ":アクセス権限の設定に失敗しました。" + Environment.NewLine;
    }
}
else
{
    log += PCName + ":電源がONになっていることを確認できませんでした。" + Environment.NewLine;
}

MessageBox.Show(log);

最後に

アクセス制限を設定してしまえば、他にもコマンドを利用して遠隔でPCのシャットダウンなども行えるので管理はしやすくなるかもしれません。


お仕事の片手間で大量のPCを管理している方の負担が少しでも楽になれば幸いです。

投稿日:

執筆者:

このサイトの管理者について

1980年代生まれ
石川県在住

メーカー2社経験後に退社

1年程度自由に在職中はできなかったことにチャレンジ

その後コロナの影響でなかなか仕事が見つかりませんでしたが、無事就職

現在はシステム会社に勤務中

PAGE TOP