InstallUtil.exe없이 .NET Windows 서비스 설치
C #으로 작성된 표준 .NET Windows 서비스가 있습니다.
InstallUtil을 사용하지 않고 스스로 설치할 수 있습니까? 서비스 설치 프로그램 클래스를 사용해야합니까? 어떻게 사용해야합니까?
다음과 같이 전화를 걸고 싶습니다.
MyService.exe -install
그리고 호출하는 것과 같은 효과가 있습니다 :
InstallUtil MyService.exe
그렇습니다, 그것은 가능합니다 (즉, 나는 이것을 정확히합니다); 올바른 dll (System.ServiceProcess.dll)을 참조하고 설치 관리자 클래스를 추가하면됩니다.
[RunInstaller(true)]
public sealed class MyServiceInstallerProcess : ServiceProcessInstaller
{
public MyServiceInstallerProcess()
{
this.Account = ServiceAccount.NetworkService;
}
}
[RunInstaller(true)]
public sealed class MyServiceInstaller : ServiceInstaller
{
public MyServiceInstaller()
{
this.Description = "Service Description";
this.DisplayName = "Service Name";
this.ServiceName = "ServiceName";
this.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
}
}
static void Install(bool undo, string[] args)
{
try
{
Console.WriteLine(undo ? "uninstalling" : "installing");
using (AssemblyInstaller inst = new AssemblyInstaller(typeof(Program).Assembly, args))
{
IDictionary state = new Hashtable();
inst.UseNewContext = true;
try
{
if (undo)
{
inst.Uninstall(state);
}
else
{
inst.Install(state);
inst.Commit(state);
}
}
catch
{
try
{
inst.Rollback(state);
}
catch { }
throw;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
ManagedInstaller 클래스 의 InstallHelper 메소드를 살펴보십시오 . 다음을 사용하여 서비스를 설치할 수 있습니다.
string[] args;
ManagedInstallerClass.InstallHelper(args);
이것이 바로 InstallUtil이하는 일입니다. 인수는 InstallUtil과 동일합니다.
이 방법의 장점은 레지스트리를 망칠 필요가 없으며 InstallUtil과 동일한 메커니즘을 사용한다는 것입니다.
관련된 작업량은 사소하지 않지만 항상 좋은 이전 WinAPI 호출로 되돌아 갈 수 있습니다. .NET 서비스는 .NET 인식 메커니즘을 통해 설치 될 필요가 없습니다.
설치하기 위해서:
- 를 통해 서비스 관리자를 엽니 다
OpenSCManager
. CreateService
서비스 등록을 위해 전화 하십시오.- 선택적으로 전화
ChangeServiceConfig2
하여 설명을 설정하십시오. - 로 서비스 및 서비스 관리자 핸들을 닫습니다
CloseServiceHandle
.
제거하려면 :
- 를 통해 서비스 관리자를 엽니 다
OpenSCManager
. - 를 사용하여 서비스를 엽니 다
OpenService
. - 에서
DeleteService
반환 한 핸들 을 호출하여 서비스를 삭제하십시오OpenService
. - 로 서비스 및 서비스 관리자 핸들을 닫습니다
CloseServiceHandle
.
ServiceInstaller
/ 를 사용하는 것보다 이것을 선호하는 주된 이유 ServiceProcessInstaller
는 사용자 정의 명령 줄 인수로 서비스를 등록 할 수 있기 때문입니다. 예를 들어,로 등록한 "MyApp.exe -service"
다음 사용자가 인수없이 앱을 실행하는 경우 서비스를 설치 / 제거 할 UI를 제공 할 수 있습니다.
Reflector를 실행 ServiceInstaller
하면이 간단한 설명에서 누락 된 세부 사항을 채울 수 있습니다.
PS 분명히 이것은 "InstallUtil MyService.exe를 호출하는 것과 같은 효과"를 갖지 않습니다. 특히 InstallUtil을 사용하여 제거 할 수 없습니다. 그러나 이것은 아마도 당신에게 실제적인 엄격한 요구 사항이 아닌 것 같습니다.
서비스를 작성할 때 사용하는 클래스는 다음과 같습니다. 일반적으로 서비스가 호출되지 않을 때 나타나는 대화식 화면이 있습니다. 거기에서 나는 필요에 따라 수업을 사용합니다. 동일한 머신에서 여러 개의 명명 된 인스턴스를 허용하므로 InstanceID 필드
샘플 콜
IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
Inst.Install("MySvc", "My Sample Service", "Service that executes something",
_InstanceID,
// System.ServiceProcess.ServiceAccount.LocalService, // this is more secure, but only available in XP and above and WS-2003 and above
System.ServiceProcess.ServiceAccount.LocalSystem, // this is required for WS-2000
System.ServiceProcess.ServiceStartMode.Automatic);
if (controller == null)
{
controller = new System.ServiceProcess.ServiceController(String.Format("MySvc_{0}", _InstanceID), ".");
}
if (controller.Status == System.ServiceProcess.ServiceControllerStatus.Running)
{
Start_Stop.Text = "Stop Service";
Start_Stop_Debugging.Enabled = false;
}
else
{
Start_Stop.Text = "Start Service";
Start_Stop_Debugging.Enabled = true;
}
수업 자체
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Microsoft.Win32;
namespace MySvc
{
class IntegratedServiceInstaller
{
public void Install(String ServiceName, String DisplayName, String Description,
String InstanceID,
System.ServiceProcess.ServiceAccount Account,
System.ServiceProcess.ServiceStartMode StartMode)
{
//http://www.theblacksparrow.com/
System.ServiceProcess.ServiceProcessInstaller ProcessInstaller = new System.ServiceProcess.ServiceProcessInstaller();
ProcessInstaller.Account = Account;
System.ServiceProcess.ServiceInstaller SINST = new System.ServiceProcess.ServiceInstaller();
System.Configuration.Install.InstallContext Context = new System.Configuration.Install.InstallContext();
string processPath = Process.GetCurrentProcess().MainModule.FileName;
if (processPath != null && processPath.Length > 0)
{
System.IO.FileInfo fi = new System.IO.FileInfo(processPath);
String path = String.Format("/assemblypath={0}", fi.FullName);
String[] cmdline = { path };
Context = new System.Configuration.Install.InstallContext("", cmdline);
}
SINST.Context = Context;
SINST.DisplayName = String.Format("{0} - {1}", DisplayName, InstanceID);
SINST.Description = String.Format("{0} - {1}", Description, InstanceID);
SINST.ServiceName = String.Format("{0}_{1}", ServiceName, InstanceID);
SINST.StartType = StartMode;
SINST.Parent = ProcessInstaller;
// http://bytes.com/forum/thread527221.html
SINST.ServicesDependedOn = new String[] { "Spooler", "Netlogon", "Netman" };
System.Collections.Specialized.ListDictionary state = new System.Collections.Specialized.ListDictionary();
SINST.Install(state);
// http://www.dotnet247.com/247reference/msgs/43/219565.aspx
using (RegistryKey oKey = Registry.LocalMachine.OpenSubKey(String.Format(@"SYSTEM\CurrentControlSet\Services\{0}_{1}", ServiceName, InstanceID), true))
{
try
{
Object sValue = oKey.GetValue("ImagePath");
oKey.SetValue("ImagePath", sValue);
}
catch (Exception Ex)
{
System.Windows.Forms.MessageBox.Show(Ex.Message);
}
}
}
public void Uninstall(String ServiceName, String InstanceID)
{
//http://www.theblacksparrow.com/
System.ServiceProcess.ServiceInstaller SINST = new System.ServiceProcess.ServiceInstaller();
System.Configuration.Install.InstallContext Context = new System.Configuration.Install.InstallContext("c:\\install.log", null);
SINST.Context = Context;
SINST.ServiceName = String.Format("{0}_{1}", ServiceName, InstanceID);
SINST.Uninstall(null);
}
}
}
위의 예제는 실제로 효과가 없었으며 # 1 솔루션으로 포럼에 대한 링크는 파헤쳐 끔찍합니다. 여기에 내가 작성한 클래스가 있으며 (일부) 다른 비트는 이 링크 에서 병합되어 어딘가에 묻혀 있습니다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceProcess;
using System.Runtime.InteropServices;
namespace SystemControl {
class Services {
#region "Environment Variables"
public static string GetEnvironment(string name, bool ExpandVariables=true) {
if ( ExpandVariables ) {
return System.Environment.GetEnvironmentVariable( name );
} else {
return (string)Microsoft.Win32.Registry.LocalMachine.OpenSubKey( @"SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" ).GetValue( name, "", Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames );
}
}
public static void SetEnvironment( string name, string value ) {
System.Environment.SetEnvironmentVariable(name, value);
}
#endregion
#region "ServiceCalls Native"
public static ServiceController[] List { get { return ServiceController.GetServices(); } }
public static void Start( string serviceName, int timeoutMilliseconds ) {
ServiceController service=new ServiceController( serviceName );
try {
TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds );
service.Start();
service.WaitForStatus( ServiceControllerStatus.Running, timeout );
} catch {
// ...
}
}
public static void Stop( string serviceName, int timeoutMilliseconds ) {
ServiceController service=new ServiceController( serviceName );
try {
TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds );
service.Stop();
service.WaitForStatus( ServiceControllerStatus.Stopped, timeout );
} catch {
// ...
}
}
public static void Restart( string serviceName, int timeoutMilliseconds ) {
ServiceController service=new ServiceController( serviceName );
try {
int millisec1=Environment.TickCount;
TimeSpan timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds );
service.Stop();
service.WaitForStatus( ServiceControllerStatus.Stopped, timeout );
// count the rest of the timeout
int millisec2=Environment.TickCount;
timeout=TimeSpan.FromMilliseconds( timeoutMilliseconds-( millisec2-millisec1 ) );
service.Start();
service.WaitForStatus( ServiceControllerStatus.Running, timeout );
} catch {
// ...
}
}
public static bool IsInstalled( string serviceName ) {
// get list of Windows services
ServiceController[] services=ServiceController.GetServices();
// try to find service name
foreach ( ServiceController service in services ) {
if ( service.ServiceName==serviceName )
return true;
}
return false;
}
#endregion
#region "ServiceCalls API"
private const int STANDARD_RIGHTS_REQUIRED = 0xF0000;
private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010;
[Flags]
public enum ServiceManagerRights {
Connect = 0x0001,
CreateService = 0x0002,
EnumerateService = 0x0004,
Lock = 0x0008,
QueryLockStatus = 0x0010,
ModifyBootConfig = 0x0020,
StandardRightsRequired = 0xF0000,
AllAccess = (StandardRightsRequired | Connect | CreateService |
EnumerateService | Lock | QueryLockStatus | ModifyBootConfig)
}
[Flags]
public enum ServiceRights {
QueryConfig = 0x1,
ChangeConfig = 0x2,
QueryStatus = 0x4,
EnumerateDependants = 0x8,
Start = 0x10,
Stop = 0x20,
PauseContinue = 0x40,
Interrogate = 0x80,
UserDefinedControl = 0x100,
Delete = 0x00010000,
StandardRightsRequired = 0xF0000,
AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig |
QueryStatus | EnumerateDependants | Start | Stop | PauseContinue |
Interrogate | UserDefinedControl)
}
public enum ServiceBootFlag {
Start = 0x00000000,
SystemStart = 0x00000001,
AutoStart = 0x00000002,
DemandStart = 0x00000003,
Disabled = 0x00000004
}
public enum ServiceState {
Unknown = -1, // The state cannot be (has not been) retrieved.
NotFound = 0, // The service is not known on the host server.
Stop = 1, // The service is NET stopped.
Run = 2, // The service is NET started.
Stopping = 3,
Starting = 4,
}
public enum ServiceControl {
Stop = 0x00000001,
Pause = 0x00000002,
Continue = 0x00000003,
Interrogate = 0x00000004,
Shutdown = 0x00000005,
ParamChange = 0x00000006,
NetBindAdd = 0x00000007,
NetBindRemove = 0x00000008,
NetBindEnable = 0x00000009,
NetBindDisable = 0x0000000A
}
public enum ServiceError {
Ignore = 0x00000000,
Normal = 0x00000001,
Severe = 0x00000002,
Critical = 0x00000003
}
[StructLayout(LayoutKind.Sequential)]
private class SERVICE_STATUS {
public int dwServiceType = 0;
public ServiceState dwCurrentState = 0;
public int dwControlsAccepted = 0;
public int dwWin32ExitCode = 0;
public int dwServiceSpecificExitCode = 0;
public int dwCheckPoint = 0;
public int dwWaitHint = 0;
}
[DllImport("advapi32.dll", EntryPoint = "OpenSCManagerA")]
private static extern IntPtr OpenSCManager(string lpMachineName, string lpDatabaseName, ServiceManagerRights dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "OpenServiceA", CharSet = CharSet.Ansi)]
private static extern IntPtr OpenService(IntPtr hSCManager, string lpServiceName, ServiceRights dwDesiredAccess);
[DllImport("advapi32.dll", EntryPoint = "CreateServiceA")]
private static extern IntPtr CreateService(IntPtr hSCManager, string lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string lpDependencies, string lp, string lpPassword);
[DllImport("advapi32.dll")]
private static extern int CloseServiceHandle(IntPtr hSCObject);
[DllImport("advapi32.dll")]
private static extern int QueryServiceStatus(IntPtr hService, SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int DeleteService(IntPtr hService);
[DllImport("advapi32.dll")]
private static extern int ControlService(IntPtr hService, ServiceControl dwControl, SERVICE_STATUS lpServiceStatus);
[DllImport("advapi32.dll", EntryPoint = "StartServiceA")]
private static extern int StartService(IntPtr hService, int dwNumServiceArgs, int lpServiceArgVectors);
/// <summary>
/// Takes a service name and tries to stop and then uninstall the windows serviceError
/// </summary>
/// <param name="ServiceName">The windows service name to uninstall</param>
public static void Uninstall(string ServiceName)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect);
try
{
IntPtr service = OpenService(scman, ServiceName, ServiceRights.StandardRightsRequired | ServiceRights.Stop | ServiceRights.QueryStatus);
if (service == IntPtr.Zero)
{
throw new ApplicationException("Service not installed.");
}
try
{
StopService(service);
int ret = DeleteService(service);
if (ret == 0)
{
int error = Marshal.GetLastWin32Error();
throw new ApplicationException("Could not delete service " + error);
}
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Accepts a service name and returns true if the service with that service name exists
/// </summary>
/// <param name="ServiceName">The service name that we will check for existence</param>
/// <returns>True if that service exists false otherwise</returns>
public static bool ServiceIsInstalled(string ServiceName)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect);
try
{
IntPtr service = OpenService(scman, ServiceName,
ServiceRights.QueryStatus);
if (service == IntPtr.Zero) return false;
CloseServiceHandle(service);
return true;
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Takes a service name, a service display name and the path to the service executable and installs / starts the windows service.
/// </summary>
/// <param name="ServiceName">The service name that this service will have</param>
/// <param name="DisplayName">The display name that this service will have</param>
/// <param name="FileName">The path to the executable of the service</param>
public static void InstallAndStart(string ServiceName, string DisplayName,
string FileName)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect |
ServiceManagerRights.CreateService);
try
{
IntPtr service = OpenService(scman, ServiceName,
ServiceRights.QueryStatus | ServiceRights.Start);
if (service == IntPtr.Zero)
{
service = CreateService(scman, ServiceName, DisplayName,
ServiceRights.QueryStatus | ServiceRights.Start, SERVICE_WIN32_OWN_PROCESS,
ServiceBootFlag.AutoStart, ServiceError.Normal, FileName, null, IntPtr.Zero,
null, null, null);
}
if (service == IntPtr.Zero)
{
throw new ApplicationException("Failed to install service.");
}
try
{
StartService(service);
}
finally
{
CloseServiceHandle(service);
}
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Takes a service name and starts it
/// </summary>
/// <param name="Name">The service name</param>
public static void StartService(string Name)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect);
try
{
IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus |
ServiceRights.Start);
if (hService == IntPtr.Zero)
{
throw new ApplicationException("Could not open service.");
}
try
{
StartService(hService);
}
finally
{
CloseServiceHandle(hService);
}
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Stops the provided windows service
/// </summary>
/// <param name="Name">The service name that will be stopped</param>
public static void StopService(string Name)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect);
try
{
IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus |
ServiceRights.Stop);
if (hService == IntPtr.Zero)
{
throw new ApplicationException("Could not open service.");
}
try
{
StopService(hService);
}
finally
{
CloseServiceHandle(hService);
}
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Stars the provided windows service
/// </summary>
/// <param name="hService">The handle to the windows service</param>
private static void StartService(IntPtr hService)
{
SERVICE_STATUS status = new SERVICE_STATUS();
StartService(hService, 0, 0);
WaitForServiceStatus(hService, ServiceState.Starting, ServiceState.Run);
}
/// <summary>
/// Stops the provided windows service
/// </summary>
/// <param name="hService">The handle to the windows service</param>
private static void StopService(IntPtr hService)
{
SERVICE_STATUS status = new SERVICE_STATUS();
ControlService(hService, ServiceControl.Stop, status);
WaitForServiceStatus(hService, ServiceState.Stopping, ServiceState.Stop);
}
/// <summary>
/// Takes a service name and returns the <code>ServiceState</code> of the corresponding service
/// </summary>
/// <param name="ServiceName">The service name that we will check for his <code>ServiceState</code></param>
/// <returns>The ServiceState of the service we wanted to check</returns>
public static ServiceState GetServiceStatus(string ServiceName)
{
IntPtr scman = OpenSCManager(ServiceManagerRights.Connect);
try
{
IntPtr hService = OpenService(scman, ServiceName,
ServiceRights.QueryStatus);
if (hService == IntPtr.Zero)
{
return ServiceState.NotFound;
}
try
{
return GetServiceStatus(hService);
}
finally
{
CloseServiceHandle(scman);
}
}
finally
{
CloseServiceHandle(scman);
}
}
/// <summary>
/// Gets the service state by using the handle of the provided windows service
/// </summary>
/// <param name="hService">The handle to the service</param>
/// <returns>The <code>ServiceState</code> of the service</returns>
private static ServiceState GetServiceStatus(IntPtr hService)
{
SERVICE_STATUS ssStatus = new SERVICE_STATUS();
if (QueryServiceStatus(hService, ssStatus) == 0)
{
throw new ApplicationException("Failed to query service status.");
}
return ssStatus.dwCurrentState;
}
/// <summary>
/// Returns true when the service status has been changes from wait status to desired status
/// ,this method waits around 10 seconds for this operation.
/// </summary>
/// <param name="hService">The handle to the service</param>
/// <param name="WaitStatus">The current state of the service</param>
/// <param name="DesiredStatus">The desired state of the service</param>
/// <returns>bool if the service has successfully changed states within the allowed timeline</returns>
private static bool WaitForServiceStatus(IntPtr hService, ServiceState
WaitStatus, ServiceState DesiredStatus)
{
SERVICE_STATUS ssStatus = new SERVICE_STATUS();
int dwOldCheckPoint;
int dwStartTickCount;
QueryServiceStatus(hService, ssStatus);
if (ssStatus.dwCurrentState == DesiredStatus) return true;
dwStartTickCount = Environment.TickCount;
dwOldCheckPoint = ssStatus.dwCheckPoint;
while (ssStatus.dwCurrentState == WaitStatus)
{
// Do not wait longer than the wait hint. A good interval is
// one tenth the wait hint, but no less than 1 second and no
// more than 10 seconds.
int dwWaitTime = ssStatus.dwWaitHint / 10;
if (dwWaitTime < 1000) dwWaitTime = 1000;
else if (dwWaitTime > 10000) dwWaitTime = 10000;
System.Threading.Thread.Sleep(dwWaitTime);
// Check the status again.
if (QueryServiceStatus(hService, ssStatus) == 0) break;
if (ssStatus.dwCheckPoint > dwOldCheckPoint)
{
// The service is making progress.
dwStartTickCount = Environment.TickCount;
dwOldCheckPoint = ssStatus.dwCheckPoint;
}
else
{
if (Environment.TickCount - dwStartTickCount > ssStatus.dwWaitHint)
{
// No progress made within the wait hint
break;
}
}
}
return (ssStatus.dwCurrentState == DesiredStatus);
}
/// <summary>
/// Opens the service manager
/// </summary>
/// <param name="Rights">The service manager rights</param>
/// <returns>the handle to the service manager</returns>
private static IntPtr OpenSCManager(ServiceManagerRights Rights)
{
IntPtr scman = OpenSCManager(null, null, Rights);
if (scman == IntPtr.Zero)
{
throw new ApplicationException("Could not connect to service control manager.");
}
return scman;
}
#endregion
}
}
서비스를 설치하려면 다음과 같이 InstallAndStart 명령을 실행하십시오.
SystemControl.InstallAndStart(
"apache",
"Apache Web Server",
@"""c:\apache\bin\httpd.exe"" -k runservice"
);
프로그램을 실행하는 계정에 서비스 설치 권한이 있는지 확인하십시오. 프로그램에서 언제든지 '관리자로 실행'할 수 있습니다.
또한 서비스를 설치하거나 제거하지 않는 비 api 액세스에 대한 몇 가지 명령을 포함했지만이를 나열하고 여러 가지를 제어 할 수 있습니다 (시작, 중지, 다시 시작). 서비스를 설치하거나 제거 할 수있는 권한 만 높이면됩니다.
OPENSSL_CONF
또는 과 같은 환경 변수를 가져오고 설정하기위한 몇 가지 명령이 있습니다 TEMP
. 대부분의 경우 매개 변수 및 메서드 이름은 설명이 필요 없습니다.
Windows 서비스로 명령 행 응용 프로그램을 설치하려는 경우 ' NSSM '유틸리티를 사용하십시오. 관련 ServerFault 세부 정보는 여기에 있습니다 .
Process QProc = new Process();
QProc.StartInfo.FileName = "cmd";
QProc.StartInfo.Arguments ="/c InstallUtil "+ "\""+ filefullPath +"\"";
QProc.StartInfo.WorkingDirectory = Environment.GetEnvironmentVariable("windir") + @"\Microsoft.NET\Framework\v2.0.50727\";
QProc.StartInfo.UseShellExecute = false;
// QProc.StartInfo.CreateNoWindow = true;
QProc.StartInfo.RedirectStandardOutput = true;
QProc.Start();
// QProc.WaitForExit();
QProc.Close();
참고 URL : https://stackoverflow.com/questions/255056/install-a-net-windows-service-without-installutil-exe
'Programing' 카테고리의 다른 글
JSON 데이터에서 JSON 스키마를 생성하는 도구 (0) | 2020.06.01 |
---|---|
함수에서 기본 인수 사용 (0) | 2020.06.01 |
ng-repeat 내에서 $ index를 사용하여 클래스를 활성화하고 DIV를 표시하려면 어떻게해야합니까? (0) | 2020.06.01 |
PostgreSQL 명명 규칙 (0) | 2020.06.01 |
mongodb에 날짜 / 시간을 저장하는 가장 좋은 방법 (0) | 2020.06.01 |