サービスでのメモリマップの使用


8

サービスとして(を使用して-service)スイッチとしても実行できるアプリケーションを作成しました。コマンドプロンプトからサービスを実行している場合、これは問題なく完全に機能します(真のサービスとして実行されていないときにコンソールからデバッグできるように何かを設定しています)。しかし、それを真のサービスとして実行しようとした後、アプリケーションを使用して既存のメモリマップを開くと、エラーが発生します...

指定されたファイルが見つかりません。

サービスとして、またはコンソールで実行する方法:

[STAThread]
static void Main(string[] args)
{
    //Convert all arguments to lower
    args = Array.ConvertAll(args, e => e.ToLower());

    //Create the container object for the settings to be stored
    Settings.Bag = new SettingsBag();

    //Check if we want to run this as a service
    bool runAsService = args.Contains("-service");

    //Check if debugging
    bool debug = Environment.UserInteractive;

    //Catch all unhandled exceptions as well
    if (!debug || debug)
    {
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    }

    if (runAsService)
    {
        //Create service array
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new CRSService()
        };

        //Run services in interactive mode if needed
        if (debug)
            RunInteractive(ServicesToRun);
        else
            ServiceBase.Run(ServicesToRun);
    }
    else
    {
        //Start the main gui
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainGUI());
    }
}

私のアプリケーションには、サービス側とアプリケーション側があります。アプリケーションの目的は、単にサービスを制御することです。私はメモリマッピングファイルを使用してすべての制御を行っています。ただし、アプリケーションを真のサービスとして実行すると、デバッグログから正しい名前とアクセス設定でメモリマップファイルが作成されます。また、必要な場所にファイルが作成されることも確認できます。コンソールでデバッグするときと同じように、すべてがサービスでまったく同じように機能しているようです。ただし、私のアプリケーション(サービスではなくアプリケーションとして実行した場合)は、メモリマップファイルが見つからないことを示しています。エラーのファイル名パスも破棄しているので、正しい場所を探していることがわかります。

メモリマップを開く方法(エラーがスローされる場所):

m_mmf = MemoryMappedFile.OpenExisting(
    m_sMapName,
    MemoryMappedFileRights.ReadWrite
);

注:サービスは、Visual Studioを実行しているのと同じアカウントで実行されています。例として、以下の画像は、タスクマネージャー、services.msc gui、および現在識別されているアカウントを示しています。

ここに画像の説明を入力してください

サービスが作成した後に、クライアントアプリケーションにメモリマップファイルを表示するにはどうすればよいですか?真のサービスとして実行するのではなく、コンソールサービスとして実行すると機能するのはなぜですか?


@Amy VS2017以降は使用できないため、Visual Studioのバージョンは重要だと思います。たとえば、回答がVS2019では機能したが2017では機能しなかった場合、回答は受け入れられません。それが理にかなっていると思います。
Arvo Bowen

「VS2017より高いものは使用できません」というのは、VSが責任を負うべき理由ではありません。C#と.Net Frameworkのどのバージョンを使用していますか?それらを質問に追加してください。質問があるときVSタグにのみ適用されるべきであるについて VS.
エイミー

サービスは、どのアカウントで実行するように登録されていますか?そのシステムアカウントの場合、問題はハンドルと名前がそれらにアクセスするためのユーザーアカウントから保護されている可能性があります。
Joel Lucsy

3
名前の前に「Global \\ MyName」のようにグローバルを付けましたか?これは、docs.microsoft.com / en
Joel Lucsy

2
さて、それが問題です。サービスとして実行するときに機能させるには、その名前を@ "Global \ myappsvr_mm_toggles"にする必要があります。サービスは、ログインしているユーザーセッションとは異なるセッションで実行され、Windowsはカーネルオブジェクトの名前空間をセッション用に分離しているため、相互に干渉することはできません。グローバル名前空間にオプトインしない限り。両方のプログラムが同じセッションで実行されたため、テスト時に機能しました。
ハンスパッサント

回答:


5

WindowsサービスはSessionで分離して実行され0ますが、コンソールアプリケーションはユーザーセッションで実行されるため、それらが互いに通信するには、Global\他のセッションからアクセスできるように、メモリマップファイルを名前空間に作成する必要があります。例えば

var file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", ...

また、ファイルに適切なアクセス権を設定して、すべてのユーザーがアクセスできるようにする必要もあります。

この記事を読むことをお勧めします。永続化されていないメモリマップファイルの実装WindowsサービスとのIPCスタイルの通信を公開します。これは、上記をより詳細に説明し、アクセス許可の設定例などを示しています。


上記のリンクからコピーされたソースコード:

ミューテックス、ミューテックスセキュリティ、MMFセキュリティポリシーの作成

bool mutexCreated;
Mutex mutex;
MutexSecurity mutexSecurity = new MutexSecurity();
MemoryMappedFileSecurity mmfSecurity = new MemoryMappedFileSecurity();

mutexSecurity.AddAccessRule(new MutexAccessRule(new SecurityIdentifier(WellKnownSidType.WorldSid, null), 
MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));
mmfSecurity.AddAccessRule(new AccessRule<MemoryMappedFileRights>("everyone", MemoryMappedFileRights.FullControl, 
AccessControlType.Allow));

mutex = new Mutex(false, @"Global\MyMutex", out mutexCreated, mutexSecurity);
if (mutexCreated == false) log.DebugFormat("There has been an error creating the mutex"); 
else log.DebugFormat("mutex created successfully");

MMFを作成して書き込む

MemoryMappedFile file = MemoryMappedFile.CreateOrOpen(@"Global\MyMemoryMappedFile", 4096, 
MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, mmfSecurity, 
HandleInheritability.Inheritable);

using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor()) {
   string xmlData = SerializeToXml(CurrentJobQueue) + "\0"; // \0 terminates the XML to stop badly formed 
issues when the next string written is shorter than the current

    byte[] buffer = ConvertStringToByteArray(xmlData);
    mutex.WaitOne();
    accessor.WriteArray<byte>(0, buffer, 0, buffer.Length);
    mutex.ReleaseMutex();
    }

MMFからの読み取り

using (MemoryMappedFile file = MemoryMappedFile.OpenExisting(
   @"Global\MyMemoryMappedFile", MemoryMappedFileRights.Read)) {

     using (MemoryMappedViewAccessor accessor =
         file.CreateViewAccessor(0, 0, MemoryMappedFileAccess.Read)) {
         byte[] buffer = new byte[accessor.Capacity];

         Mutex mutex = Mutex.OpenExisting(@"Global\MyMutex");
         mutex.WaitOne();
         accessor.ReadArray<byte>(0, buffer, 0, buffer.Length);
         mutex.ReleaseMutex();

         string xmlData = ConvertByteArrayToString(buffer);
         data = DeserializeFromXML(xmlData);
       }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.