Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
(시리즈 글이 8개 있습니다.)
.NET Framework: 614. C# - DateTime.Ticks의 정밀도
; https://www.sysnet.pe.kr/2/0/11082

.NET Framework: 827. C# - 인터넷 시간 서버로부터 받은 시간을 윈도우에 적용하는 방법
; https://www.sysnet.pe.kr/2/0/11883

스크립트: 33. JavaScript와 C#의 시간 변환
; https://www.sysnet.pe.kr/2/0/12849

Windows: 204.  Windows 10부터 바뀐 QueryPerformanceFrequency, QueryPerformanceCounter
; https://www.sysnet.pe.kr/2/0/13035

.NET Framework: 1997. C# - nano 시간을 가져오는 방법
; https://www.sysnet.pe.kr/2/0/13036

스크립트: 47. 파이썬의 time.time() 실숫값을 GoLang / C#에서 사용하는 방법
; https://www.sysnet.pe.kr/2/0/13308

닷넷: 2143. C# - 시스템 Time Zone 변경 시 이벤트 알림을 받는 방법
; https://www.sysnet.pe.kr/2/0/13413

닷넷: 2309. C# - .NET Core에서 바뀐 DateTime.Ticks의 정밀도
; https://www.sysnet.pe.kr/2/0/13803




C# - 인터넷 시간 서버로부터 받은 시간을 윈도우에 적용하는 방법

다음과 같은 질문이 있군요. ^^

인터넷 시간을 불러와 pc에 적용 시키고 싶습니다.
; https://www.sysnet.pe.kr/3/0/5152

코드를 보면, 공개된 time.nist.gov 시간 서버(NTP: Network Time Protocol)로부터 데이터를 받아와 처리하고 있습니다.

/*
c:\temp> telnet time.nist.gov 13
59442 21-08-16 14:28:19 50 0 0 585.3 UTC(NIST) *

Connection to host lost.
*/
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;

namespace ConsoleApp_test
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct SYSTEMTIME
        {
            public short wYear;
            public short wMonth;
            public short wDayOfWeek;
            public short wDay;
            public short wHour;
            public short wMinute;
            public short wSecond;
            public short wMilliseconds;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetSystemTime(ref SYSTEMTIME st);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetLocalTime(ref SYSTEMTIME st);

        static void Main(string[] args)
        {
            Console.WriteLine(Process.GetCurrentProcess().Id);
            while (true)
            {
                string responseText = null;

                try
                {
                    using (var client = new TcpClient("time.nist.gov", 13))
                    using (var streamReader = new StreamReader(client.GetStream()))
                    {
                        Thread.Sleep(5000);

                        // 인터넷 시간 불러오기
                        responseText = streamReader.ReadToEnd(); // "59442 21-08-16 14:28:19 50 0 0 585.3 UTC(NIST) *"
                        var utcDateTimeString = responseText.Substring(7, 17);

                        if (DateTime.TryParseExact(utcDateTimeString, "yy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime utcDateTime) == false)
                        {
                            Console.WriteLine(responseText);
                            continue;
                        }

                        var localDateTime = utcDateTime.ToLocalTime();

                        // pc에 적용하기
                        SYSTEMTIME st = new SYSTEMTIME();
                        st.wYear = (short)localDateTime.Year;
                        st.wMonth = (short)localDateTime.Month;
                        st.wDay = (short)localDateTime.Day;
                        st.wHour = (short)localDateTime.Hour;
                        st.wMinute = (short)localDateTime.Minute;
                        st.wSecond = (short)localDateTime.Second;
                        st.wMilliseconds = (short)localDateTime.Millisecond;

                        bool result = SetLocalTime(ref st);
                        if (result == false)
                        {
                            int lastError = Marshal.GetLastWin32Error();
                            Console.WriteLine(lastError);
                        }

                        Console.WriteLine($"Response: {responseText}, DateTime: {localDateTime}");
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception: " + e.Message + "\n(" + responseText + ")");
                }
            }
        }
    }
}

하지만, time.nist.gov로부터 시간은 잘 받아왔는데 윈도우 운영체제의 시간을 설정하는 SetSystemTime/SetLocalTime Win32 API 호출 시 결과가 false를 반환합니다. 이런 경우 Marshal.GetLastWin32Error 메서드를 이용하면 Win32 API가 왜 실패했는지를 알 수 있는데, 위의 경우에는 1314 값으로 이것을 Visual Studio의 "Tools" / "Error Lookup" 도구에서 살펴보면,

errlookup_1.png

"A required privilege is not held by the client."라는 설명이 나옵니다. 역시 왜 이런 식의 오류가 발생했는지는 문서를 보면 됩니다.

The calling process must have the SE_SYSTEMTIME_NAME privilege. This privilege is disabled by default. The SetSystemTime function enables the SE_SYSTEMTIME_NAME privilege before changing the system time and disables the privilege before returning.


그렇습니다. 운영체제의 시간을 변경하려면 호출 프로세스의 권한에 SE_SYSTEMTIME_NAME 특권이 있어야 하는데 윈도우의 일반 사용자 권한에는 그 특권이 없기 때문에 1314 오류가 발생하는 것입니다. 따라서 이 프로그램을 관리자 권한으로 실행하면 문제는 간단하게 해결됩니다.

참고로, 특권에 대해서는 예전에 쓴 글이 있습니다.

SeCreateGlobalPrivilege 특권과 WCF NamedPipe
; https://www.sysnet.pe.kr/2/0/1806

일반적으로 관리자 권한의 경우 다행히 SE_SYSTEMTIME_NAME 특권이 기본적으로 활성화된 상태이므로 일부러 설정할 필요까지는 없습니다. 만약 관리자 권한에서도 SE_SYSTEMTIME_NAME 특권이 없었다고 가정한다면, 다음의 NuGet 라이브러리를 이용해,

Win32.TokenPrivileges
; https://github.com/trondr/Win32.TokenPrivileges

다음과 같은 식으로 강제 활성화 후 SetSystemTime/SetLocalTime API를 호출하면 됩니다.

using (new AdjustPrivilege(PrivilegeName.SeSystemtimePrivilege))
{
    Console.WriteLine("Privileges should now be granted.");
    Console.WriteLine($"SeSystemtimePrivilege: {PrivilegeProvider.HasPrivilege(null, currentProcess, PrivilegeName.SeSystemtimePrivilege)}");

    bool result = SetLocalTime(ref st);
    if (result == false)
    {
        int lastError = Marshal.GetLastWin32Error();
        Console.WriteLine(lastError);
    }
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/10/2023]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12621정성태5/1/202119424.NET Framework: 1053. C# - 특정 레지스트리 변경 시 알림을 받는 방법 [1]파일 다운로드1
12620정성태4/29/202123283.NET Framework: 1052. C# - 왜 구조체는 16 바이트의 크기가 적합한가? [1]파일 다운로드1
12619정성태4/28/202123199.NET Framework: 1051. C# - 구조체의 크기가 16바이트가 넘어가면 힙에 할당된다? [2]파일 다운로드1
12618정성태4/27/202121447사물인터넷: 58. NodeMCU v1 ESP8266 CP2102 Module을 이용한 WiFi UDP 통신 [1]파일 다운로드1
12617정성태4/26/202118321.NET Framework: 1050. C# - ETW EventListener의 Keywords별 EventId에 따른 필터링 방법파일 다운로드1
12616정성태4/26/202117984.NET Framework: 1049. C# - ETW EventListener를 상속받았을 때 초기화 순서파일 다운로드1
12615정성태4/26/202115300오류 유형: 712. Microsoft Live 로그인 - 계정을 선택하는(Pick an account) 화면에서 진행이 안 되는 문제
12614정성태4/24/202119934개발 환경 구성: 570. C# - Azure AD 인증을 지원하는 ASP.NET Core/5+ 웹 애플리케이션 예제 구성 [4]파일 다운로드1
12613정성태4/23/202118220.NET Framework: 1048. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (2) 관리 코드파일 다운로드1
12612정성태4/23/202117824.NET Framework: 1047. C# - ETW 이벤트의 Keywords에 속한 EventId 구하는 방법 (1) PInvoke파일 다운로드1
12611정성태4/22/202116118오류 유형: 711. 닷넷 EXE 실행 오류 - Mixed mode assembly is build against version 'v2.0.50727' of the runtime
12610정성태4/22/202116090.NET Framework: 1046. C# - 컴파일 시점에 참조할 수 없는 타입을 포함한 이벤트 핸들러를 Reflection을 이용해 구독하는 방법파일 다운로드1
12609정성태4/22/202119452.NET Framework: 1045. C# - 런타임 시점에 이벤트 핸들러를 만들어 Reflection을 이용해 구독하는 방법파일 다운로드1
12608정성태4/21/202119980.NET Framework: 1044. C# - Generic Host를 이용해 .NET 5로 리눅스 daemon 프로그램 만드는 방법 [9]파일 다운로드1
12607정성태4/21/202116425.NET Framework: 1043. C# - 실행 시점에 동적으로 Delegate 타입을 만드는 방법파일 다운로드1
12606정성태4/21/202123054.NET Framework: 1042. C# - enum 값을 int로 암시적(implicit) 형변환하는 방법? [2]파일 다운로드1
12605정성태4/18/202118456.NET Framework: 1041. C# - AssemblyID, ModuleID를 관리 코드에서 구하는 방법파일 다운로드1
12604정성태4/18/202116184VS.NET IDE: 163. 비주얼 스튜디오 속성 창의 "Build(빌드)" / "Configuration(구성)"에서의 "활성" 의미
12603정성태4/16/202117948VS.NET IDE: 162. 비주얼 스튜디오 - 상속받은 컨트롤이 디자인 창에서 지원되지 않는 문제
12602정성태4/16/202118819VS.NET IDE: 161. x64 DLL 프로젝트의 컨트롤이 Visual Studio의 Designer에서 보이지 않는 문제 [1]
12601정성태4/15/202117874.NET Framework: 1040. C# - REST API 대신 github 클라이언트 라이브러리를 통해 프로그래밍으로 접근
12600정성태4/15/202118306.NET Framework: 1039. C# - Kubeconfig의 token 설정 및 인증서 구성을 자동화하는 프로그램
12599정성태4/14/202118678.NET Framework: 1038. C# - 인증서 및 키 파일로부터 pfx/p12 파일을 생성하는 방법파일 다운로드1
12598정성태4/14/202119644.NET Framework: 1037. openssl의 PEM 개인키 파일을 .NET RSACryptoServiceProvider에서 사용하는 방법 (2)파일 다운로드1
12597정성태4/13/202118543개발 환경 구성: 569. csproj의 내용을 공통 설정할 수 있는 Directory.Build.targets / Directory.Build.props 파일
12596정성태4/12/202117805개발 환경 구성: 568. Windows의 80 포트 점유를 해제하는 방법
... 46  47  48  49  50  51  52  53  [54]  55  56  57  58  59  60  ...