用Delphi建立服務程式 |
日期:2005年11月29日 作者:sunmohe 人氣: 3154 檢視:[大字型 中字型 小字型] |
Windows 2000/XP和2003等支援一種叫做"服務程式"的東西.程式作為服務啟動有以下幾個好處:
(1)不用登陸進系統即可執行. (2)具有SYSTEM特權.所以你在程式管理器裡面是無法結束它的. 筆者在2003年為一公司開發機頂盒專案的時候,曾經寫過課件上傳和媒體服務,下面就介紹一下如何用Delphi7建立一個Service程式. 執行Delphi7,選擇選單File-->New-->Other--->Service Application.將生成一個服務程式的框架.將工程儲存為ServiceDemo.dpr和Unit_Main.pas,然後回到主框架.我們注意到,Service有幾個屬性.其中以下幾個是我們比較常用的: (1)DisplayName:服務的顯示名稱 (2)Name:服務名稱. 我們在這裡將DisplayName的值改為"Delphi服務演示程式",Name改為"DelphiService".編譯這個專案,將得到 ServiceDemo.exe.這已經是一個服務程式了!進入CMD模式,切換致工程所在目錄,執行命令"ServiceDemo.exe /install",將提示服務安裝成功!然後"net start DelphiService"將啟動這個服務.進入控制面版-->管理工具-->服務,將顯示這個服務和當前狀態.不過這個服務現在什麼也幹不了,因為我們還沒有寫程式碼:)先"net stop DelphiService"停止再"ServiceDemo.exe /uninstall"刪除這個服務.回到Delphi7的IDE. 我們的計劃是為這個服務新增一個主視窗,執行後工作列顯示程式的圖示,雙擊圖示將顯示主視窗,上面有一個按鈕,點選該按鈕將實現Ctrl+Alt+Del功能. 實際上,服務程式莫認是工作於Winlogon桌面的,可以開啟控制皮膚,檢視我們剛才那個服務的屬性-->登陸,其中"允許服務與桌面互動 "是不打鉤的.怎麼辦?呵呵,回到IDE,注意那個布林屬性:Interactive,當這個屬性為True的時候,該服務程式就可以與桌面互動了. File-->New-->Form為服務新增視窗FrmMain,單元儲存為Unit_FrmMain,並且把這個視窗設定為手工建立.完成後的程式碼如下: unit Unit_Main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs, Unit_FrmMain; type TDelphiService = class(TService) procedure ServiceContinue(Sender: TService; var Continued: Boolean); procedure ServiceExecute(Sender: TService); procedure ServicePause(Sender: TService; var Paused: Boolean); procedure ServiceShutdown(Sender: TService); procedure ServiceStart(Sender: TService; var Started: Boolean); procedure ServiceStop(Sender: TService; var Stopped: Boolean); private { Private declarations } public function GetServiceController: TServiceController; override; { Public declarations } end; var DelphiService: TDelphiService; FrmMain: TFrmMain; implementation {$R *.DFM} procedure ServiceController(CtrlCode: DWord); stdcall; begin DelphiService.Controller(CtrlCode); end; function TDelphiService.GetServiceController: TServiceController; begin Result := ServiceController; end; procedure TDelphiService.ServiceContinue(Sender: TService; var Continued: Boolean); begin while not Terminated do begin Sleep(10); ServiceThread.ProcessRequests(False); end; end; procedure TDelphiService.ServiceExecute(Sender: TService); begin while not Terminated do begin Sleep(10); ServiceThread.ProcessRequests(False); end; end; procedure TDelphiService.ServicePause(Sender: TService; var Paused: Boolean); begin Paused := True; end; procedure TDelphiService.ServiceShutdown(Sender: TService); begin gbCanClose := true; FrmMain.Free; Status := csStopped; ReportStatus(); end; procedure TDelphiService.ServiceStart(Sender: TService; var Started: Boolean); begin Started := True; Svcmgr.Application.CreateForm(TFrmMain, FrmMain); gbCanClose := False; FrmMain.Hide; end; procedure TDelphiService.ServiceStop(Sender: TService; var Stopped: Boolean); begin Stopped := True; gbCanClose := True; FrmMain.Free; end; end. 主視窗單元如下: unit Unit_FrmMain; interface uses Windows, Messages, SysUtils, Variants, Classes, ShellApi, Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls; const WM_TrayIcon = WM_USER + 1234; type TFrmMain = class(TForm) Timer1: TTimer; Button1: TButton; procedure FormCreate(Sender: TObject); procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); procedure FormDestroy(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } IconData: TNotifyIconData; procedure AddIconToTray; procedure DelIconFromTray; procedure TrayIconMessage(var Msg: TMessage); message WM_TrayIcon; procedure SysButtonMsg(var Msg: TMessage); message WM_SYSCOMMAND; public { Public declarations } end; var FrmMain: TFrmMain; gbCanClose: Boolean; implementation {$R *.dfm} procedure TFrmMain.FormCreate(Sender: TObject); begin FormStyle := fsStayOnTop; SetWindowLong(Application.Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW); gbCanClose := False; Timer1.Interval := 1000; Timer1.Enabled := True; end; procedure TFrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose := gbCanClose; if not CanClose then begin Hide; end; end; procedure TFrmMain.FormDestroy(Sender: TObject); begin Timer1.Enabled := False; DelIconFromTray; end; procedure TFrmMain.AddIconToTray; begin ZeroMemory(@IconData, SizeOf(TNotifyIconData)); IconData.cbSize := SizeOf(TNotifyIconData); IconData.Wnd := Handle; IconData.uID := 1; IconData.uFlags := NIF_MESSAGE or NIF_ICON or NIF_TIP; IconData.uCallbackMessage := WM_TrayIcon; IconData.hIcon := Application.Icon.Handle; IconData.szTip := Delphi服務演示程式; Shell_NotifyIcon(NIM_ADD, @IconData); end; procedure TFrmMain.DelIconFromTray; begin Shell_NotifyIcon(NIM_DELETE, @IconData); end; procedure TFrmMain.SysButtonMsg(var Msg: TMessage); begin if (Msg.wParam = SC_CLOSE) or (Msg.wParam = SC_MINIMIZE) then Hide else inherited; // 執行預設動作 end; procedure TFrmMain.TrayIconMessage(var Msg: TMessage); begin if (Msg.LParam = WM_LBUTTONDBLCLK) then Show(); end; procedure TFrmMain.Timer1Timer(Sender: TObject); begin AddIconToTray; end; procedure SendHokKey;stdcall; var HDesk_WL: HDESK; begin HDesk_WL := OpenDesktop (Winlogon, 0, False, DESKTOP_JOURNALPLAYBACK); if (HDesk_WL <> 0) then if (SetThreadDesktop (HDesk_WL) = True) then PostMessage(HWND_BROADCAST, WM_HOTKEY, 0, MAKELONG (MOD_ALT or MOD_CONTROL, VK_DELETE)); end; procedure TFrmMain.Button1Click(Sender: TObject); var dwThreadID : DWORD; begin CreateThread(nil, 0, @SendHokKey, nil, 0, dwThreadID); end; end. 補充: (1)關於更多服務程式的演示程式,請訪問以下Url:http://www.torry.net/pages.php?id=226,上面包含了多個演示如何控制和管理系統服務的程式碼. (2)請切記:Windows實際上存在多個桌面.例如螢幕傳輸會出現白屏,可能有兩個原因:一是系統處於鎖定或未登陸桌面,二是處於螢幕保護桌面.這時候要將當前桌面切換到該桌面才能抓屏. (3)關於服務程式與桌面互動,還有種動態切換方法.大概單元如下: unit ServiceDesktop; interface function InitServiceDesktop: boolean; procedure DoneServiceDeskTop; implementation uses Windows, SysUtils; const DefaultWindowStation = WinSta0; DefaultDesktop = Default; var hwinstaSave: HWINSTA; hdeskSave: HDESK; hwinstaUser: HWINSTA; hdeskUser: HDESK; function InitServiceDesktop: boolean; var dwThreadId: DWORD; begin dwThreadId := GetCurrentThreadID; // Ensure connection to service window station and desktop, and // save their handles. hwinstaSave := GetProcessWindowStation; hdeskSave := GetThreadDesktop(dwThreadId); hwinstaUser := OpenWindowStation(DefaultWindowStation, FALSE, MAXIMUM_ALLOWED); if hwinstaUser = 0 then begin OutputDebugString(PChar(OpenWindowStation failed + SysErrorMessage(GetLastError))); Result := false; exit; end; if not SetProcessWindowStation(hwinstaUser) then begin OutputDebugString(SetProcessWindowStation failed); Result := false; exit; end; hdeskUser := OpenDesktop(DefaultDesktop, 0, FALSE, MAXIMUM_ALLOWED); if hdeskUser = 0 then begin OutputDebugString(OpenDesktop failed); SetProcessWindowStation(hwinstaSave); CloseWindowStation(hwinstaUser); Result := false; exit; end; Result := SetThreadDesktop(hdeskUser); if not Result then OutputDebugString(PChar(SetThreadDesktop + SysErrorMessage(GetLastError))); end; procedure DoneServiceDeskTop; begin // Restore window station and desktop. SetThreadDesktop(hdeskSave); SetProcessWindowStation(hwinstaSave); if hwinstaUser <> 0 then CloseWindowStation(hwinstaUser); if hdeskUser <> 0 then CloseDesktop(hdeskUser); end; initialization InitServiceDesktop; finalization DoneServiceDesktop; end. 更詳細的演示程式碼請參看:http://www.torry.net/samples/samples/os/isarticle.zip (4)關於安裝服務如何新增服務描述.有兩種方法:一是修改登錄檔.服務的詳細資訊都位於HKEY_LOCAL_MACHINE\SYSTEM\ ControlSet001\Services\下面,例如我們剛才那個服務就位於HKEY_LOCAL_MACHINE\SYSTEM\ ControlSet001\Services\DelphiService下.第二種方法就是先用QueryServiceConfig2函式獲取服務資訊,然後ChangeServiceConfig2來改變描述.用Delphi實現的話,單元如下: unit WinSvcEx; interface uses Windows, WinSvc; const // // Service config info levels // SERVICE_CONFIG_DESCRIPTION = 1; SERVICE_CONFIG_FAILURE_ACTIONS = 2; // // DLL name of imported functions // AdvApiDLL = advapi32.dll; type // // Service description string // PServiceDescriptionA = ^TServiceDescriptionA; PServiceDescriptionW = ^TServiceDescriptionW; PServiceDescription = PServiceDescriptionA; {$EXTERNALSYM _SERVICE_DESCRIPTIONA} _SERVICE_DESCRIPTIONA = record lpDescription : PAnsiChar; end; {$EXTERNALSYM _SERVICE_DESCRIPTIONW} _SERVICE_DESCRIPTIONW = record lpDescription : PWideChar; end; {$EXTERNALSYM _SERVICE_DESCRIPTION} _SERVICE_DESCRIPTION = _SERVICE_DESCRIPTIONA; {$EXTERNALSYM SERVICE_DESCRIPTIONA} SERVICE_DESCRIPTIONA = _SERVICE_DESCRIPTIONA; {$EXTERNALSYM SERVICE_DESCRIPTIONW} SERVICE_DESCRIPTIONW = _SERVICE_DESCRIPTIONW; {$EXTERNALSYM SERVICE_DESCRIPTION} SERVICE_DESCRIPTION = _SERVICE_DESCRIPTIONA; TServiceDescriptionA = _SERVICE_DESCRIPTIONA; TServiceDescriptionW = _SERVICE_DESCRIPTIONW; TServiceDescription = TServiceDescriptionA; // // Actions to take on service failure // {$EXTERNALSYM _SC_ACTION_TYPE} _SC_ACTION_TYPE = (SC_ACTION_NONE, SC_ACTION_RESTART, SC_ACTION_REBOOT, SC_ACTION_RUN_COMMAND); {$EXTERNALSYM SC_ACTION_TYPE} SC_ACTION_TYPE = _SC_ACTION_TYPE; PServiceAction = ^TServiceAction; {$EXTERNALSYM _SC_ACTION} _SC_ACTION = record aType : SC_ACTION_TYPE; Delay : DWORD; end; {$EXTERNALSYM SC_ACTION} SC_ACTION = _SC_ACTION; TServiceAction = _SC_ACTION; PServiceFailureActionsA = ^TServiceFailureActionsA; PServiceFailureActionsW = ^TServiceFailureActionsW; PServiceFailureActions = PServiceFailureActionsA; {$EXTERNALSYM _SERVICE_FAILURE_ACTIONSA} _SERVICE_FAILURE_ACTIONSA = record dwResetPeriod : DWORD; lpRebootMsg : LPSTR; lpCommand : LPSTR; cActions : DWORD; lpsaActions : ^SC_ACTION; end; {$EXTERNALSYM _SERVICE_FAILURE_ACTIONSW} _SERVICE_FAILURE_ACTIONSW = record dwResetPeriod : DWORD; lpRebootMsg : LPWSTR; lpCommand : LPWSTR; cActions : DWORD; lpsaActions : ^SC_ACTION; end; {$EXTERNALSYM _SERVICE_FAILURE_ACTIONS} _SERVICE_FAILURE_ACTIONS = _SERVICE_FAILURE_ACTIONSA; {$EXTERNALSYM SERVICE_FAILURE_ACTIONSA} SERVICE_FAILURE_ACTIONSA = _SERVICE_FAILURE_ACTIONSA; {$EXTERNALSYM SERVICE_FAILURE_ACTIONSW} SERVICE_FAILURE_ACTIONSW = _SERVICE_FAILURE_ACTIONSW; {$EXTERNALSYM SERVICE_FAILURE_ACTIONS} SERVICE_FAILURE_ACTIONS = _SERVICE_FAILURE_ACTIONSA; TServiceFailureActionsA = _SERVICE_FAILURE_ACTIONSA; TServiceFailureActionsW = _SERVICE_FAILURE_ACTIONSW; TServiceFailureActions = TServiceFailureActionsA; /////////////////////////////////////////////////////////////////////////// // API Function Prototypes /////////////////////////////////////////////////////////////////////////// TQueryServiceConfig2 = function (hService : SC_HANDLE; dwInfoLevel : DWORD; lpBuffer : pointer; cbBufSize : DWORD; var pcbBytesNeeded) : BOOL; stdcall; TChangeServiceConfig2 = function (hService : SC_HANDLE; dwInfoLevel : DWORD; lpInfo : pointer) : BOOL; stdcall; var hDLL : THandle ; LibLoaded : boolean ; var OSVersionInfo : TOSVersionInfo; {$EXTERNALSYM QueryServiceConfig2A} QueryServiceConfig2A : TQueryServiceConfig2; {$EXTERNALSYM QueryServiceConfig2W} QueryServiceConfig2W : TQueryServiceConfig2; {$EXTERNALSYM QueryServiceConfig2} QueryServiceConfig2 : TQueryServiceConfig2; {$EXTERNALSYM ChangeServiceConfig2A} ChangeServiceConfig2A : TChangeServiceConfig2; {$EXTERNALSYM ChangeServiceConfig2W} ChangeServiceConfig2W : TChangeServiceConfig2; {$EXTERNALSYM ChangeServiceConfig2} ChangeServiceConfig2 : TChangeServiceConfig2; implementation initialization OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo); GetVersionEx(OSVersionInfo); if (OSVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT) and (OSVersionInfo.dwMajorVersion >= 5) then begin if hDLL = 0 then begin hDLL:=GetModuleHandle(AdvApiDLL); LibLoaded := False; if hDLL = 0 then begin hDLL := LoadLibrary(AdvApiDLL); LibLoaded := True; end; end; if hDLL <> 0 then begin @QueryServiceConfig2A := GetProcAddress(hDLL, QueryServiceConfig2A); @QueryServiceConfig2W := GetProcAddress(hDLL, QueryServiceConfig2W); @QueryServiceConfig2 := @QueryServiceConfig2A; @ChangeServiceConfig2A := GetProcAddress(hDLL, ChangeServiceConfig2A); @ChangeServiceConfig2W := GetProcAddress(hDLL, ChangeServiceConfig2W); @ChangeServiceConfig2 := @ChangeServiceConfig2A; end; end else begin @QueryServiceConfig2A := nil; @QueryServiceConfig2W := nil; @QueryServiceConfig2 := nil; @ChangeServiceConfig2A := nil; @ChangeServiceConfig2W := nil; @ChangeServiceConfig2 := nil; end; finalization if (hDLL <> 0) and LibLoaded then FreeLibrary(hDLL); end. unit winntService; interface uses Windows,WinSvc,WinSvcEx; function InstallService(const strServiceName,strDisplayName,strDescription,strFilename: string):Boolean; //eg:InstallService(服務名稱,顯示名稱,描述資訊,服務檔案); procedure UninstallService(strServiceName:string); implementation function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler; asm PUSH EDI PUSH ESI PUSH EBX MOV ESI,EAX MOV EDI,EDX MOV EBX,ECX XOR AL,AL TEST ECX,ECX JZ @@1 REPNE SCASB JNE @@1 INC ECX @@1: SUB EBX,ECX MOV EDI,ESI MOV ESI,EDX MOV EDX,EDI MOV ECX,EBX SHR ECX,2 REP MOVSD MOV ECX,EBX AND ECX,3 REP MOVSB STOSB MOV EAX,EDX POP EBX POP ESI POP EDI end; function StrPCopy(Dest: PChar; const Source: string): PChar; begin Result := StrLCopy(Dest, PChar(Source), Length(Source)); end; function InstallService(const strServiceName,strDisplayName,strDescription,strFilename: string):Boolean; var //ss : TServiceStatus; //psTemp : PChar; hSCM,hSCS:THandle; srvdesc : PServiceDescription; desc : string; //SrvType : DWord; lpServiceArgVectors:pchar; begin Result:=False; //psTemp := nil; //SrvType := SERVICE_WIN32_OWN_PROCESS and SERVICE_INTERACTIVE_PROCESS; hSCM:=OpenSCManager(nil,nil,SC_MANAGER_ALL_ACCESS);//連線服務資料庫 if hSCM=0 then Exit;//MessageBox(hHandle,Pchar(SysErrorMessage(GetLastError)),服務程式管理器,MB_ICONERROR+MB_TOPMOST); hSCS:=CreateService( //建立服務函式 hSCM, // 服務控制管理控制程式碼 Pchar(strServiceName), // 服務名稱 Pchar(strDisplayName), // 顯示的服務名稱 SERVICE_ALL_ACCESS, // 存取權利 SERVICE_WIN32_OWN_PROCESS or SERVICE_INTERACTIVE_PROCESS,// 服務型別 SERVICE_WIN32_SHARE_PROCESS SERVICE_AUTO_START, // 啟動型別 SERVICE_ERROR_IGNORE, // 錯誤控制型別 Pchar(strFilename), // 服務程式 nil, // 組服務名稱 nil, // 組標識 nil, // 依賴的服務 nil, // 啟動服務帳號 nil); // 啟動服務口令 if hSCS=0 then Exit;//MessageBox(hHandle,Pchar(SysErrorMessage(GetLastError)),Pchar(Application.Title),MB_ICONERROR+MB_TOPMOST); if Assigned(ChangeServiceConfig2) then begin desc := Copy(strDescription,1,1024); GetMem(srvdesc,SizeOf(TServiceDescription)); GetMem(srvdesc^.lpDescription,Length(desc) + 1); try StrPCopy(srvdesc^.lpDescription, desc); ChangeServiceConfig2(hSCS,SERVICE_CONFIG_DESCRIPTION,srvdesc); finally FreeMem(srvdesc^.lpDescription); FreeMem(srvdesc); end; end; lpServiceArgVectors := nil; if not StartService(hSCS, 0, lpServiceArgVectors) then //啟動服務 Exit; //MessageBox(hHandle,Pchar(SysErrorMessage(GetLastError)),Pchar(Application.Title),MB_ICONERROR+MB_TOPMOST); CloseServiceHandle(hSCS); //關閉控制程式碼 Result:=True; end; procedure UninstallService(strServiceName:string); var SCManager: SC_HANDLE; Service: SC_HANDLE; Status: TServiceStatus; begin SCManager := OpenSCManager(nil, nil, SC_MANAGER_ALL_ACCESS); if SCManager = 0 then Exit; try Service := OpenService(SCManager, Pchar(strServiceName), SERVICE_ALL_ACCESS); ControlService(Service, SERVICE_CONTROL_STOP, Status); DeleteService(Service); CloseServiceHandle(Service); finally CloseServiceHandle(SCManager); end; end; end. (5)如何暴力關閉一個服務程式,實現我們以前那個"NT工具箱"的功能?首先,根據程式名稱來殺死程式是用以下函式: uses Tlhelp32; function KillTask(ExeFileName: string): Integer; const PROCESS_TERMINATE = 01; var ContinueLoop: BOOL; FSnapshotHandle: THandle; FProcessEntry32: TProcessEntry32; begin Result := 0; FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); FProcessEntry32.dwSize := SizeOf(FProcessEntry32); ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32); while Integer(ContinueLoop) <> 0 do begin if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) = UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) = UpperCase(ExeFileName))) then Result := Integer(TerminateProcess( OpenProcess(PROCESS_TERMINATE, BOOL(0), FProcessEntry32.th32ProcessID), 0)); ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32); end; CloseHandle(FSnapshotHandle); end; 但是對於服務程式,它會提示"拒絕訪問".其實只要程式擁有Debug許可權即可: function EnableDebugPrivilege: Boolean; function EnablePrivilege(hToken: Cardinal; PrivName: string; bEnable: Boolean): Boolean; var TP: TOKEN_PRIVILEGES; Dummy: Cardinal; begin TP.PrivilegeCount := 1; LookupPrivilegeValue(nil, pchar(PrivName), TP.Privileges[0].Luid); if bEnable then TP.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED else TP.Privileges[0].Attributes := 0; AdjustTokenPrivileges(hToken, False, TP, SizeOf(TP), nil, Dummy); Result := GetLastError = ERROR_SUCCESS; end; var hToken: Cardinal; begin OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, hToken); result:=EnablePrivilege(hToken, SeDebugPrivilege, True); CloseHandle(hToken); end; 使用方法: EnableDebugPrivilege;//提升許可權 KillTask(xxxx.exe);//關閉該服務程式. |
用Delphi建立服務程式
相關文章
- node中建立服務程式
- windowsSevice程式和topshelf程式建立服務對比Windows
- 建立Linux服務Linux
- Windows手工建立服務方法Windows
- WCF 服務應用程式與 服務庫之間的區別
- 用Delphi編寫DelTree程式 (轉)
- 使用C#建立安裝Windows服務程式(乾貨)C#Windows
- Yao:一個用於建立Web服務和儀表板的低程式碼引擎Web
- 2.8.1.4 建立資料庫服務資料庫
- python如何建立web服務PythonWeb
- Windows服務建立及安裝Windows
- 使用.NET Core建立Windows服務Windows
- Node.js 建立MySql服務Node.jsMySql
- 程式設計師必備技能:一鍵建立windows 服務程式設計師Windows
- 用附加到程式的方法除錯服務程式碼除錯
- .NET應用程式管理服務AMS設計
- 用C語言編寫windows服務程式C語言Windows
- 使用Go語言建立WebSocket服務GoWeb
- 使用 Go 語言建立 WebSocket 服務GoWeb
- 關於使用Topshelf建立服務
- 第19章 建立RESTful Web服務RESTWeb
- 用Delphi編寫安裝程式(1) (轉)
- 將windows應用程式註冊為windows服務Windows
- 控制檯程式使用ABP框架應用層服務框架
- DNS服務應用DNS
- Oracle 服務端程式Oracle服務端
- 領域服務與應用服務的職責
- 領域服務和應用服務的差別?
- Delphi實用程式設計經驗二 (轉)程式設計
- 使用C#建立windows服務續之使用Topshelf優化Windows服務C#Windows優化
- abp(net core)+easyui+efcore倉儲系統——建立應用服務(五)UI
- 4.2.1.10 為您的應用程式計劃服務
- Oracle ebs應用相關服務和程式總結Oracle
- Spring Cloud的應用程式—上下文服務SpringCloud
- 銀彈谷:低程式碼開發助力快速建立金融服務平臺
- Qt服務應用操作QT
- 【Azure 應用服務】Azure App Service 自帶 FTP服務APPFTP
- 4.2.11 使用 SRVCTL 建立資料庫服務資料庫