web-dev-qa-db-ja.com

C#を使用してWindowsサービスからEXEプログラムを実行するにはどうすればよいですか?

C#を使用してWindowsサービスからEXEプログラムを実行するにはどうすればよいですか?

これは私のコードです:

System.Diagnostics.Process.Start(@"E:\PROJECT XL\INI SQLLOADER\ConsoleApplication2\ConsoleApplication2\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe");

このサービスを実行すると、アプリケーションが起動しません。
コードの何が問題になっていますか?

42
xoops

これは機能しません。少なくともWindows Vista以降では機能しません。重要な問題は、標準のWindowsアプリケーションではなく、Windowsサービス内からこれを実行しようとしていることです。示したコードは、Windowsフォーム、WPF、またはコンソールアプリケーションでは完全に機能しますが、Windowsサービスではまったく機能しません。

Windowsサービスは、特定のユーザーのコンテキストで実行されていないため、追加のアプリケーションを開始できません。通常のWindowsアプリケーションとは異なり、 サービスは分離セッションで実行されるようになりました であり、ユーザーまたはデスクトップとの対話が禁止されています。これにより、アプリケーションを実行する場所がなくなります。

詳細については、これらの関連する質問への回答をご覧ください。

おそらくこれまでに考え出したように、問題に対する最善の解決策は、サービスではなく標準のWindowsアプリケーションを作成することです。これらは、特定のユーザーが実行するように設計されており、そのユーザーのデスクトップに関連付けられています。これにより、既に示したコードを使用して、必要なときにいつでも追加のアプリケーションを実行できます。

別の可能な解決策は、コンソールアプリケーションがインターフェイスや出力を必要としないと仮定した場合、プロセスをウィンドウを作成しないように指示することです。これにより、Windowsはコンソールウィンドウの作成を要求しなくなるため、プロセスの作成をブロックできなくなります。関連するコードの this answer で関連するコードを見つけることができます。

64
Cody Gray

私はこの記事を試しました コードプロジェクト 、それは私のためにうまく機能しています。私もコードを使用しました。記事はスクリーンショット付きの説明に優れています。

このシナリオに必要な説明を追加しています

コンピューターを起動し、ログオンしようとしています。ログオンすると、システムによって一意のセッションIDが割り当てられます。 Windows Vistaでは、コンピューターに最初にログオンしたユーザーには、OSによってセッションID 1が割り当てられます。次にログオンするユーザーには、セッションID 2が割り当てられます。以降、同様に続きます。タスクマネージャーの[ユーザー]タブから、ログオンしている各ユーザーに割り当てられたセッションIDを表示できます。 enter image description here

ただし、WindowsサービスはセッションID 0で提供されます。このセッションは他のセッションから分離されています。これは最終的に、Windowsサービスが1または2のようなユーザーセッションで実行されているアプリケーションを呼び出すことを防ぎます。

Windowsサービスからアプリケーションを呼び出すには、以下のスクリーンショットに示すように、現在のログインユーザーとして機能するwinlogon.exeからコントロールをコピーする必要があります。 enter image description here

重要なコード

// obtain the process id of the winlogon process that 
// is running within the currently active session
Process[] processes = Process.GetProcessesByName("winlogon");
foreach (Process p in processes)
{
    if ((uint)p.SessionId == dwSessionId)
    {
        winlogonPid = (uint)p.Id;
    }
}

// obtain a handle to the winlogon process
hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);

// obtain a handle to the access token of the winlogon process
if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken))
{
    CloseHandle(hProcess);
    return false;
}

// Security attibute structure used in DuplicateTokenEx and   CreateProcessAsUser
// I would prefer to not have to use a security attribute variable and to just 
// simply pass null and inherit (by default) the security attributes
// of the existing token. However, in C# structures are value types and   therefore
// cannot be assigned the null value.
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);

// copy the access token of the winlogon process; 
// the newly created token will be a primary token
if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, 
    (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, 
    (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup))
    {
      CloseHandle(hProcess);
      CloseHandle(hPToken);
      return false;
    }

 STARTUPINFO si = new STARTUPINFO();
 si.cb = (int)Marshal.SizeOf(si);

// interactive window station parameter; basically this indicates 
// that the process created can display a GUI on the desktop
si.lpDesktop = @"winsta0\default";

// flags that specify the priority and creation method of the process
int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

// create a new process in the current User's logon session
 bool result = CreateProcessAsUser(hUserTokenDup,  // client's access token
                            null,             // file to execute
                            applicationName,  // command line
                            ref sa,           // pointer to process    SECURITY_ATTRIBUTES
                            ref sa,           // pointer to thread SECURITY_ATTRIBUTES
                            false,            // handles are not inheritable
                            dwCreationFlags,  // creation flags
                            IntPtr.Zero,      // pointer to new environment block 
                            null,             // name of current directory 
                            ref si,           // pointer to STARTUPINFO structure
                            out procInfo      // receives information about new process
                            );
9

この目的のためにWindowsタスクスケジューラから使用できます。 TaskScheduler のような多くのライブラリが役立ちます。

たとえば、5秒後に1回実行されるタスクをスケジュールしたいとします。

using (var ts = new TaskService())
        {

            var t = ts.Execute("notepad.exe")
                .Once()
                .Starting(DateTime.Now.AddSeconds(5))
                .AsTask("myTask");

        }

notepad.exeは5秒後に実行されます。

詳細および詳細については、 wiki をご覧ください。

そのアセンブリで必要なクラスとメソッドがわかっている場合は、次のように自分で呼び出すことができます。

        Assembly assembly = Assembly.LoadFrom("yourApp.exe");
        Type[] types = Assembly.GetTypes();
        foreach (Type t in types)
        {
            if (t.Name == "YourClass")
            {
                MethodInfo method = t.GetMethod("YourMethod",
                BindingFlags.Public | BindingFlags.Instance);
                if (method != null)
                {
                    ParameterInfo[] parameters = method.GetParameters();
                    object classInstance = Activator.CreateInstance(t, null);

                    var result = method.Invoke(classInstance, parameters.Length == 0 ? null : parameters);

                    break;
                }
            }

        }
4
Rahmat Anjirabi

Windows XPでは、Windowsサービスから.exeを非常にうまく実行できます。過去に自分でやったことがあります。

Windowsサービスのプロパティで[デスクトップとの対話を許可する]オプションをオンにしていることを確認する必要があります。それが行われない場合、実行されません。

これらのバージョンは追加のセキュリティ特権を必要とするため、エラーをスローする可能性があるため、Windows 7またはVistaでチェックインする必要がありますが、直接または間接的に達成できると確信しています。 XP自分でやったように確信しています。

2
Depinder Bharti

最初に、システムアカウントで実行されるWindowsサービスを作成します。このサービスは、現在アクティブなユーザーのセッション内でインタラクティブプロセスを生成する役割を果たします。この新しく作成されたプロセスはUIを表示し、完全な管理者権限で実行されます。最初のユーザーがコンピューターにログオンすると、このサービスが開始され、Session0で実行されます。ただし、このサービスが生成するプロセスは、現在ログオンしているユーザーのデスクトップで実行されます。このサービスをLoaderServiceと呼びます。

次に、winlogon.exeプロセスは、ユーザーのログインおよびログアウト手順を管理します。コンピューターにログオンするすべてのユーザーは、一意のセッションIDと、セッションに関連付けられた対応するwinlogon.exeプロセスを持つことになります。さて、前述したように、LoaderServiceはSystemアカウントで実行されます。また、コンピューター上の各winlogon.exeプロセスがシステムアカウントで実行されることを確認しました。 SystemアカウントはLoaderServiceプロセスとwinlogon.exeプロセスの両方の所有者であるため、LoaderServiceはwinlogon.exeプロセスのアクセストークン(およびセッションID)をコピーし、Win32 API関数CreateProcessAsUserを呼び出してプロセスを起動できます。ログオンしているユーザーの現在アクティブなセッション。コピーされたwinlogon.exeプロセスのアクセストークン内にあるセッションIDは0より大きいため、そのトークンを使用して対話型プロセスを起動できます。

これを試してください。 2ビットと64ビットの両方のアーキテクチャでVista UACを破壊する

2
Elshan

ほとんどの賛成票でのトップアンサーは間違っていませんが、それでも私が投稿するものの反対です。私は言うそれは完全に動作しますexeファイルを起動し、あなたはこれを行うことができます任意のユーザーのコンテキスト。論理的には、ユーザーインターフェイスを使用したり、ユーザー入力を求めたりすることはできません...

私のアドバイスは次のとおりです。

  1. ユーザーの操作なしで、開始時にサービスが行うべきことを実行する単純なコンソールアプリケーションを作成します。特に(現在).NET Coreを使用できないため、Windowsサービスプロジェクトタイプを使用しないことをお勧めします。
  2. コードを追加して、サービスから呼び出すEXEを開始します

起動する例plink.exe。出力を聞くこともできます:

var psi = new ProcessStartInfo()
{
    FileName = "./Client/plink.exe", //path to your *.exe
    Arguments = "-telnet -P 23 127.0.0.1 -l myUsername -raw", //arguments
    RedirectStandardError = true,
    RedirectStandardOutput = true,
    RedirectStandardInput = true,
    UseShellExecute = false,
    CreateNoWindow = true //no window, you can't show it anyway
};

var p = Process.Start(psi);
  1. NSSM(Non-Sucking Service Manager)を使用して、そのコンソールアプリケーションをサービスとして登録します。 NSSMはコマンドラインで制御でき、サービスを構成するためのUIを表示するか、コマンドラインで構成できます。そのユーザーのログインデータがわかっている場合、そのユーザーのコンテキストでサービスを実行できます。

LocalSystemアカウントを取得しました。これはデフォルトであり、Local Service以上のものです。特定のユーザーのログイン情報を入力しなくても正常に機能しました。より高いアクセス許可が必要な場合にできる「デスクトップとの対話をサービスに許可する」チェックボックスもチェックしていません。

Option to allow service to interact with desktop

最後に、一番上の答えが私の答えとは正反対だと言うのがどれだけ面白いかを言いたいのですが、それでも私たち二人は正しいのです。あなたが今言っても、Windowsサービスプロジェクトタイプではできない場合-あなたはできますが、以前にこれを持っていましたが、インストールは大ざっぱで、NSSMを見つけるまでは意図しないハッキングのようなものでした。

0
CodingYourLife

.exeを別の場所にコピーしていると思います。これは私が推測する問題かもしれません。 exeをコピーすると、その依存関係はコピーされません。

だから、あなたができることは、すべての依存DLLをGACに入れて、すべての.net exeがアクセスできるようにすることです

そうでない場合は、exeを新しい場所にコピーしないでください。環境変数を作成して、C#でexeを呼び出すだけです。パスは環境変数で定義されているため、C#プログラムからexeにアクセスできます。

更新:

以前は、c#.netコードから.exeファイルを実行しようとしていたc#.net 3.5プロジェクトで、ある種の同じ問題が発生しました。機能)およびexeアプリケーションで使用していたDLLメソッド。最後に、同じアプリケーションを別のプロジェクトとして同じソリューションに作成することでこれを解決し、そのプロジェクト出力を展開プロジェクトに追加しました。このシナリオによると、私が答えた、彼が望んでいるものではない場合、私は非常に残念です。

0
SharpUrBrain