4

このコード例に基づいて、非常に単純なサービス アプリケーションを作成しました。

アプリケーションは、通常の実行の一部として、見つかったディレクトリまたはその実行パスにファイルが存在すると想定します。

サービスを「インストール」してから、コントロール パネルのサービス マネージャーからサービスを「開始」すると。開いて読み取るファイルが見つからないため、アプリケーションは失敗します (ファイルがインストールされた実行可能ファイルと同じディレクトリにある場合でも)。

私の質問は、Windows サービスがいつ実行されるかということです。予想される実行パスはどれですか?

「CreateService」を呼び出すと、実行用ではなく、バイナリ用のパス パラメータのみが存在するようです。バイナリを実行する場所を示す方法はありますか?

Windows Vista と Windows 7 でこれを試しました。同じ問題が発生します。

4

5 に答える 5

6

Windowsサービスは、通常のユーザーモードアプリケーションとは異なるコンテキストから実行されるため、作業ディレクトリや相対パスについて何も想定しないのが最善です。作業ディレクトリの違いは別として、サービスは完全に異なる権限のセットなどを使用して実行できます。

サービスに必要なファイルへの絶対パスを使用すると、この問題を完全に回避できます。絶対パスは作業ディレクトリに関係なく同じように解釈されるため、これによりサービスの作業ディレクトリは無関係になります。これを行うにはいくつかの方法があります。

  1. 絶対パスをハードコーディングする-これはおそらく問題を回避するための最も簡単な方法ですが、柔軟性も最も低くなります。この方法は、基本的な開発およびテスト作業にはおそらく問題ありませんが、他の人がプログラムを使い始める前に、もう少し洗練されたものが必要になるでしょう。
  2. 絶対パスを環境変数に保存する-パスを任意の値に設定し、必要に応じて変更できるようになったため、柔軟性がさらに高まります。サービスは、異なる環境変数のセットを使用して異なるユーザーとして実行できるため、このアプローチにはまだいくつかの落とし穴があります。
  3. レジストリに絶対パスを保存する-これはおそらく最も確実な方法です。レジストリからパスを取得すると、すべてのユーザーアカウントで同じ結果が得られます。さらに、これはインストール時に比較的簡単に設定できます。
于 2012-09-03T21:37:40.653 に答える
2

バイナリと同じパスを使用する場合は、バイナリ パスを読み取り、それに応じて変更できます。しかし、これは設計された解決策ではなく、むしろ応急処置です。私があなたなら、システム全体の環境変数を作成してそこに値を保存するか、(さらに良いことに) Windows レジストリを使用してサービス構成を保存します。

ノート:

AdjustTokenPrivileges functionを使用して自分自身にいくつかの権限を追加する必要があります。関数の例をここで見ることができますModifyPrivilege

また、HKEY_CURRENT_USER ではなく、必ず HKEY_LOCAL_MACHINE を使用してください。サービスは異なるユーザー アカウントで実行されているため、HKCU はレジストリ エディターで表示されるものとは異なります。

于 2012-09-03T21:38:48.760 に答える
2

デフォルトでは、Windows サービスの現在のディレクトリは System32 フォルダーです。

有望な解決策は、入力場所の完全なパスを保持する環境変数を作成し、実行時にこの変数からパスを取得することです。

于 2012-09-03T21:22:08.930 に答える
1

今日、私が開発していたいくつかのソフトウェアに必要だったので、この問題を解決しました。

上記の人々が言っ​​たように; ディレクトリを特定のファイルにハードコーディングできますが、それは、ロードする必要のある構成ファイルをそこに配置する必要があることを意味します。

私にとって、このサービスは 50,000 台以上のコンピューターにインストールされていました。サービス実行可能ファイルが実行されているディレクトリからロードするように設計されています。

これで、非システム プロセスとして簡単にセットアップして実行できます (私はほとんどのテストを非システム プロセスとして行いました)。しかし、問題は、あなたが使用した (そして私も使用した) システムラッパーは Unicode フォーマットを使用する (そしてそれに依存する) ため、従来の方法ではうまく機能しないということです。

コードのコメント部分でこれを説明する必要があります。いくつかの冗長性があることはわかっていますが、これを書いたときに動作するバージョンが欲しかっただけです。幸いなことに、GetModuleFileNameA を使用して ASCII 形式で処理できます。

私が使用したコードは次のとおりです。

char buffer[MAX_PATH]; // create buffer
DWORD size = GetModuleFileNameA(NULL, buffer, MAX_PATH); // Get file path in ASCII

std::string configLoc; // make string

for (int i = 0; i < strlen(buffer); i++) // iterate through characters of buffer
{
    if (buffer[i] == '\\') // if buffer has a '\' in it, replace with doubles
    {
        configLoc = configLoc + "\\\\"; // doubles needed for parsing. 4 = 2(str)
    }
    else
    {
        configLoc = configLoc + buffer[i]; // else just add char as normal
    }
}

// Complete location
configLoc = configLoc.substr(0, configLoc.length() - 17); //cut the .exe off the end
                                                          //(change this to fit needs)   
configLoc += "\\\\login.cfg"; // add config file to end of string

ここからは、単純に configLoc を新しい ifsteam に解析して、コンテンツを処理できます。

于 2014-02-19T07:15:16.270 に答える