サービスコントロールマネージャー(SCM)
SCMは、サービスのリモート管理(プロシージャの呼び出し)のためにWindowsに実装されたサーバーです。
Windowsでドライバーを実行するために、このドライバーの制御を提供するサービスがそれに割り当てられます。 メッセージがドライバと交換されるシステムでドライバを作成するデバイスと混同しないでください。 このデバイスは、ドライバーの起動後に作成されますが、SCMはドライバーをシステムに提供します。 SCMを使用すると、サービスを追加、削除、開始、または停止できます。
問題の声明
C#でSCMを簡素化するバッファクラスを作成します。
このクラスの外観は非常に簡単に認識できます。
public ref class ServiceControlManager : public IDisposable { public: ServiceControlManager(void); void AddDriver(String^ ServiceName, String^ BinaryPathName); void DeleteDriver(String^ ServiceName); void StartDriver(String^ ServiceName); void StopDriver(String^ ServiceName); protected: ~ServiceControlManager(); !ServiceControlManager(); private: SC_HANDLE SCMHandle; };
SCMオブジェクトの属性のみのハンドルからのコンストラクタ、デストラクタ、ファイナライザ、メインメソッド。 その結果、このクラスのオブジェクトのインスタンスには作成されたSCMオブジェクトが含まれ、メソッドによって作業が簡素化されます。 クラスはバッファーであり、C ++ / cliで実装されているため、それぞれ.NET環境およびC#で動作するように自動的にスケーリングされます。
エラー解決
このクラスの主な問題は、SCMの操作中に発生したエラーコードが返されることです。これは、作業の最初の段階で.NETにより馴染みのある例外に置き換えることが望ましいものです。 これを行うには、同様のクラスを作成できます。
[Serializable] public ref class KernelErrorException : Exception { public: KernelErrorException(void); virtual String^ ToString() override; property virtual String^ Message { String^ get() override; }; property virtual DWORD Errorsource { DWORD get(); }; private: DWORD errorsource; internal: KernelErrorException(DWORD Errorsource); };
ご覧のとおり、このクラスのインスタンスには、属性として、GetLastError()から受け取るコード番号のみが含まれます。 また、インスタンスをSystem :: String型にキャストしようとすると、Windowsを使用してメッセージの説明の全文が表示されます。
このクラスには2つのコンストラクターがあり、最初のコンストラクターはデフォルトで1つです。実行中にエラーコードを保存します。 2番目-エラーコードを引数として受け取ります。 2番目は例外をスローする必要がある場合に使用する必要がありますが、その前に何らかのアクションを実行し、その後GetLastError()コマンドが誤った値を返します。 これを行うには、エラーコードを保存し、アクションを実行してから例外が発生します。 このようなアクションの例を以下に示します。マーシャリングに使用されるPTRをクリアします(将来、このコードに戻ることができないため、例外をスローする前にPTRをクリアする必要があります)。
実装
さらに、メソッドの実装は最も基本的なものになります。
KernelErrorException::KernelErrorException(void) { this->errorsource = GetLastError(); } KernelErrorException::KernelErrorException(DWORD Errorsource) { this->errorsource = Errorsource; }
さらに、メソッドの実装は最も基本的なものになります。
String^ KernelErrorException::Message::get() { LPTSTR message = NULL; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, this->errorsource, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, NULL); String^ messageString = gcnew String(message); LocalFree(message); return messageString; } DWORD KernelErrorException::Errorsource::get() { return this->errorsource; } String^ KernelErrorException::ToString() { return this->Message::get(); }
SCMに割り当てられたメモリをクリアする必要があります
.NETでSCMを使用する場合の2番目の問題:ハンドルSCMは長く存続できません。そうしないと、システムクラッシュが発生します。 したがって、それを使用するときは、プログラマー自身がガベージダンプを処理しないようにする必要があります。 コンストラクタとファイナライザを厳密に記述する必要がありますが、デストラクタでは、Disposeパターンのロジックに従って、ファイナライザは[ありがとうGraD_Kh ]と呼ばれます。 ファイナライザは、非管理オブジェクトのリリースについて説明します。
ServiceControlManager::ServiceControlManager(void) { this->SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (!this->SCMHandle) throw gcnew KernelErrorException(); } ServiceControlManager::~ServiceControlManager() { this->!ServiceControlManager(); GC::SuppressFinalize(this); } ServiceControlManager::!ServiceControlManager() { CloseServiceHandle(this->SCMHandle)); }
主な機能
すべてのメソッドの実装は非常に単純で、その基礎は特定の関連プロシージャへの呼び出しですが、正しい実行には必然的に例外的な状況のすべてのチェックが必要です。
実装
void ServiceControlManager::AddDriver(String^ ServiceName, String^ BinaryPathName) { IntPtr serviceNamePtr = Marshal::StringToHGlobalUni(ServiceName); IntPtr binaryPathNamePtr = Marshal::StringToHGlobalUni(BinaryPathName); SC_HANDLE SCMHandleService = CreateService(this->SCMHandle, (LPCTSTR)serviceNamePtr.ToPointer(), (LPCTSTR)serviceNamePtr.ToPointer(), SERVICE_ALL_ACCESS, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, (LPCTSTR)binaryPathNamePtr.ToPointer(), NULL, NULL, NULL, NULL, NULL); DWORD errorsource = GetLastError(); Marshal::FreeHGlobal(serviceNamePtr); Marshal::FreeHGlobal(binaryPathNamePtr); if (!SCMHandleService) throw gcnew KernelErrorException(errorsource); if (!CloseServiceHandle(SCMHandleService)) throw gcnew KernelErrorException(); } void ServiceControlManager::DeleteDriver(String^ ServiceName) { IntPtr serviceNamePtr = Marshal::StringToHGlobalUni(ServiceName); SC_HANDLE SCMHandleService = OpenService(this->SCMHandle, (LPCTSTR)serviceNamePtr.ToPointer(), SERVICE_ALL_ACCESS); DWORD errorsource = GetLastError(); Marshal::FreeHGlobal(serviceNamePtr); if (!SCMHandleService ) throw gcnew KernelErrorException(errorsource); if (!DeleteService(SCMHandleService)) throw gcnew KernelErrorException(); if (!CloseServiceHandle(SCMHandleService)) throw gcnew KernelErrorException(); } void ServiceControlManager::StartDriver(String^ ServiceName) { IntPtr serviceNamePtr = Marshal::StringToHGlobalUni(ServiceName); SC_HANDLE SCMHandleService = OpenService(this->SCMHandle, (LPCTSTR)serviceNamePtr.ToPointer(), SERVICE_ALL_ACCESS); DWORD errorsource = GetLastError(); Marshal::FreeHGlobal(serviceNamePtr); if (!SCMHandleService) throw gcnew KernelErrorException(errorsource); if (!StartService(SCMHandleService, 0, 0)) throw gcnew KernelErrorException(); if (!CloseServiceHandle(SCMHandleService)) throw gcnew KernelErrorException(); } void ServiceControlManager::StopDriver(String^ ServiceName) { IntPtr serviceNamePtr = Marshal::StringToHGlobalUni(ServiceName); SC_HANDLE SCMHandleService = OpenService(this->SCMHandle, (LPCTSTR)serviceNamePtr.ToPointer(), SERVICE_ALL_ACCESS); DWORD errorsource = GetLastError(); Marshal::FreeHGlobal(serviceNamePtr); if (!SCMHandleService) throw gcnew KernelErrorException(errorsource); SERVICE_STATUS serviceStatus; if (!ControlService(SCMHandleService, SERVICE_CONTROL_STOP, &serviceStatus)) throw gcnew KernelErrorException(); if (!CloseServiceHandle(SCMHandleService)) throw gcnew KernelErrorException(); }
最初の方法は、sysファイルをサービスに関連付け、このサービスをシステムに追加します。 2番目-システムからドライバーを削除し、他の2つ-それぞれサービスを開始および停止します。
C#での使用例:
try { using (ServiceControlManager scm = new ServiceControlManager()) { scm.AddDriver(serviceName, filePath); scm.StartDriver(serviceName); scm.StopDriver(serviceName); scm.DeleteDriver(serviceName); } } catch (Exception ex) { }
コンパイル設定
覚えておくべき最も重要なことは、マネージヒープとアンマネージヒープの間で常にマーシャリングを使用することです。 マーシャリングにはネームスペースにいる必要があることを思い出させてください。
using namespace System::Runtime::InteropServices;
libの登録を忘れないでください:
#pragma comment(lib, "Advapi32.lib")
ライブラリのコンパイル時のプロパティ設定:
あとがき
多くの人は、そのようなアプローチは意味をなさないと主張し、C#では標準ライブラリからのマーシャリング引数を使用する方がはるかに簡単だと主張するかもしれません。 しかし、私の意見では、私の決定はより柔軟です。 また、クラスを自分で調整することにより、重要でない変数を取り除くことができます。 / x64でこれらの関数のDLLImportを設定しようとした人は私を理解するでしょう... /
ユーザーインターフェイスプログラムを備えたGitHubライブラリソース