GetTickCount / GetTickCount64와 0x7FFE0000 주솟값
GetTickCount API는 시스템이 시작된 이후 경과한 시간을 밀리초 단위로 반환합니다. (10ms ~ 16ms 단위입니다.)
GetTickCount function
; https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount
위의 문서에도 나와있지만 이것의 반환값이 "DWORD"라서 32비트이므로 경과 시간을 표현하는데 한계가 있습니다. 대략 49.7일이 지나면 다시 0으로 돌아온다는 것이지요. 그래서 이 문제를 해결하려면 GetTickCount64를 쓰라고 권장하고 있습니다.
GetTickCount64 function
; https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-gettickcount64
위의 문서에 추가된 덧글에 보면 (64비트까지 확장되었으므로) 기간이 "585 million years"까지 늘어나기 때문에 기간 걱정은 할 필요가 없어진 것입니다.
새삼스럽게 GetTickCount 이야기를 꺼낸 것은 재미있는 글을 보았기 때문입니다. ^^
GetTickCount를 대체할만한 방법 있을까요?
; http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_QnA_etc&no=3187&z=
위의 덧글 중에 보면 0x7FFE0000라는 주소에 32비트/64비트 윈도우 운영체제 공통으로 KSYSTEM_TIME이라는 구조체 값이 들어 있다는 것입니다.
typedef struct _KSYSTEM_TIME
{
ULONG LowPart;
LONG High1Time;
LONG High2Time;
} KSYSTEM_TIME, *PKSYSTEM_TIME;
C#으로 이 값을 구해 보면 다음과 같이 코딩할 수 있습니다.
using System;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
IntPtr ptr = new IntPtr(0x7FFE0008);
KSYSTEM_TIME systemTime = new KSYSTEM_TIME();
Marshal.PtrToStructure(ptr, systemTime);
Console.WriteLine("LowPart: " + systemTime.LowPart);
Console.WriteLine("HighTime: " + systemTime.HighTime);
Console.WriteLine("High2Time: " + systemTime.High2Time);
ulong fullTime = (ulong)systemTime.LowPart | ((ulong)systemTime.HighTime << 32);
Console.WriteLine("KSYSTEM_TIME: \t\t" + fullTime);
/*
출력결과
LowPart: 2242934063
HighTime: 102
High2Time: 102
KSYSTEM_TIME: 441971822558
*/
}
}
[StructLayout(LayoutKind.Sequential)]
public class KSYSTEM_TIME
{
public uint LowPart;
public int HighTime;
public int High2Time;
}
재미있는 것은 GetTickCount/64 결과와의 비교값입니다.
Console.WriteLine("Environment.TickCount: \t" + Environment.TickCount);
Console.WriteLine("GetTickCount: \t\t" + GetTickCount());
Console.WriteLine("GetTickCount64: \t" + GetTickCount64());
위의 코드를 한꺼번에 실행시키면 다음과 같은 결과를 얻을 수 있습니다.
KSYSTEM_TIME: 442837484163
Environment.TickCount: 44283734
GetTickCount: 44283750
GetTickCount64: 44283750
Environment.TickCount는 내부적으로 GetTickCount를 부를 것이므로 값이 같아야 하지만 JIT 컴파일 시간으로 인해 약간 차이가 벌어지는 것 같습니다. JIT 컴파일로 인한 부작용을 없애기 위해 한번 컴파일된 상태로 호출하면 다음과 같이 마지막 3개의 값이 대개 동일하게 나옵니다.
KSYSTEM_TIME: 442837503715
Environment.TickCount: 44283750
GetTickCount: 44283750
GetTickCount64: 44283750
문제는 KSYSTEM_TIME값과 TickCount의 관계입니다. 위의 값을 보면 KSYSTEM_TIME 값에서 정밀도를 절삭한 유형이 TickCount로 해석되는 듯합니다. 즉, GetTickCount가 49.7일이 지나서 0부터 다시 시작한다면 KSYSTEM_TIME 역시 정밀도만 좋은 것일 뿐 49.7일에 대한 문제는 해결되지 않는 것으로 보입니다. (따라서, KSYSTEM_TIME 값이 GetTickCount에 대한 해결책으로 보이지는 않습니다.)
제가 해당 API를 설계한 사람이 아니어서 더 이상의 해석은 불가능하지만, 암튼 0x7FFE0000 이라는 특수한 주소를 알았다는 사실만으로 오늘은 만족해야겠습니다. ^^
(
첨부한 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]