C언어에서의 생성자 및 소멸자

  • main 함수 전후로 실행됨
  • OS에서 제공하는 기능은 아니고 어플리케이션 레벨에서 main함수 전후로 실행되기때문에, exit 함수등으로 프로그램 정상종료시 소멸자가 실행되지만 프로세스 강제종료시 소멸자가 실행되지 않음
  • gcc 제공기능이라 비쥬얼 스튜디오에서 안된다.
  • 여러개의 생성자를 사용 가능하며 prioirity로 순서 지정 가능(0 ~ 100번은 예약된 우선순위라 101번부터 지정가능)
#include <stdio.h>

void __attribute__((constructor (101))) my_constructor1(void) {
    printf("this is my_constructor1()\n");
}

void __attribute__((constructor (102))) my_constructor2(void) {
    printf("this is my_constructor2()\n");
}


void __attribute__((destructor (102))) my_destructor1(void) {
    printf("this is my_destructor1()\n");

}

void __attribute__((destructor (101))) my_destructor2(void) {
    printf("this is my_destructor2()\n");
}


int main(void) {

    printf("this is main()\n");

    return 0;
}
/*
this is my_constructor1()
this is my_constructor2()
this is main()
this is my_destructor1()
this is my_destructor2()
*/

'C&C++' 카테고리의 다른 글

Errno Setting Function List  (0) 2024.01.17
CMake Syntax  (0) 2020.01.21
How To Use Unit Test In C++  (0) 2020.01.13
WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08

Functions That Set errno and Return an Out-of-Band Error Indicator

Function Name Return Value Error Value
ftell() -1L  Positive 
fgetpos(), fsetpos() Nonzero Positive
mbrtowc(), mbsrtowcs() (size_t)(-1) EILSEQ
signal() SIG_ERR Positive
wcrtomb(), wcsrtombs() (size_t)(-1) EILSEQ
mbrtoc16(), mbrtoc32() (size_t)(-1)  EILSEQ
c16rtomb(), c32rtomb() (size_t)(-1)  EILSEQ 

 

 

Functions that Set errno and Return an In-Band Error Indicator

Function Name Return Value Error Value
fgetwc(), fputwc() WEOF EILSEQ
strtol(), wcstol() LONG_MIN or LONG_MAX ERANGE
strtoll(), wcstoll() LLONG_MIN or LLONG_MAX ERANGE
strtoul(), wcstoul() ULONG_MAX ERANGE
strtoull(), wcstoull() ULLONG_MAX ERANGE
strtoumax(), wcstoumax() UINTMAX_MAX ERANGE
strtod(), wcstod() 0 or ±HUGE_VAL ERANGE
strtof(), wcstof() 0 or ±HUGE_VALF ERANGE
strtold(), wcstold() 0 or ±HUGE_VALL ERANGE
strtoimax(), wcstoimax() INTMAX_MIN, INTMAX_MAX ERANGE

'C&C++' 카테고리의 다른 글

How to use constructor and destructor in C language  (0) 2024.04.24
CMake Syntax  (0) 2020.01.21
How To Use Unit Test In C++  (0) 2020.01.13
WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08

[CMake 설치]

sudo apt install cmake

 

[CMake 주요 문법]

- 변수

set(변수명 값)

set(변수명 "값1 값2") => 공백있는경우 쿼터사용

set(변수명 값1 값2) => 직렬화

 

- 출력

message(${변수명})

message(문자열)

message(타입 문자열)

* 출력타입

STATUS : 상태 메시지
WARNING : 경고 메시지
SEND_ERROR : 에러 메시지를 출력(Makefile생성X)
FATAL_ERROR : 에러 메시지 출력 후 작업 중단
AUTHOR_WARNING : 디버깅 메시지

- 프로젝트명 

project(sampleProject) => 프로젝트명 설정을 설정하면 CMAKE_PROJECT_NAME이라는 변수에 저장된다.

- CMake 버전관리

CMAKE_MINIMUM_REQUIRED(VERSION x.xx)

=> 최소 cmake 버전을 명시한다.

=> 최소 버전보다 낮은 cmake를 사용한다면 에러를 출력한다.

 

- 최종빌드 바이너리

ADD_EXECUTABLE(바이너리명 소스코드명 소스코드명)

 

- 최종빌드 라이브러리

ADD_LIBRARY(라이브러리명 소스코드명)

libcmaketest.a 파일이 생성된다.

SHARED(동적 라이브러리) : DLL(Windows), SO(Linux)

STATIC(정적 라이브러리) : LIB(Windows), A(Linux) => 디폴트

 

- 컴파일러 설정

SET (CMAKE_C_COMPILER "gcc" )

=> 환경변수 설정이 되어있지않다면 절대경로로 지정해준다.

 

- 컴파일 옵션

ADD_COMPILE_OPTIONS(-g -W)

 

- 전처리기 매크로

ADD_DEFINITIONS(-DDEBUG=1)

=> 해당 구문은 cmake파일 어디에 두어도 정상 동작한다.

=> gcc에서 -D 옵션

 

- 헤더파일 디렉토리 추가

INCLUDE_DIRECTORIES(/${PATH}/include)

=> 해당 구문은 cmake파일 어디에 두어도 정상 동작한다.

=> gcc에서 -I 옵션

 

- 라이브러리 디렉토리 지정

LINK_DIRECTORIES(/lib /usr/lib)

=> /lib과 /usr/lib를 링크 시 필요한 라이브러리를 참조하도록

=> gcc에서 -L 옵션

 

- 링크옵션 추가

LINK_LIBRARIES(WS2_32)

=> gcc에서 -l 옵션

 

- 출력디렉토리 지정

SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ../)  => 바이너리

SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ../)   => 동적 라이브러리

SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ../)   => 정적 라이브러리

 

[CMake 샘플 코드]

[CMake를 이용해 프로젝트 빌드]

 

 

 

 

 

* https://cmake.org/

 

CMake

CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefile

cmake.org

* https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/Useful-Variables

 

Useful Variables · Wiki · CMake / Community

CMake Wiki

gitlab.kitware.com

 

'C&C++' 카테고리의 다른 글

How to use constructor and destructor in C language  (0) 2024.04.24
Errno Setting Function List  (0) 2024.01.17
How To Use Unit Test In C++  (0) 2020.01.13
WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08

[C++에서 Visual Studio 2019를 사용해 유닛테스트 진행하는법]

 

기존 프로젝트에 프로젝트 추가(솔루션 - 추가 - 새 프로젝트)

 

기본 단위 테스트 프로젝트 선택

 

해당 솔루션에 유닛테스트 프로젝트가 추가되었고 참조 추가를 해준다.

 

참조 추가는 기존의 메인 프로젝트를 선택.

 

테스트 - 창 - 테스트 탐색기를 활성화 한다.

 

메인 소스코드

 

메인 소스코드의 코드를 include해주고 테스트 케이스 추가 후 테스트 탐색기에서 실행

 * 참고 : https://docs.microsoft.com/ko-kr/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference?view=vs-2019

'C&C++' 카테고리의 다른 글

Errno Setting Function List  (0) 2024.01.17
CMake Syntax  (0) 2020.01.21
WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08
WINAPI Options Using Bit Flag  (0) 2019.08.07
[ConnectEx]
Socket 통신에서 동시다발적으로 여러 클라이언트에게 오는 요청을 효율적으로 처리하기 위해 많은 방법들이 고안되었다.
그 중 대표적으로 리눅스에는 Epoll, 윈도우에는 IOCP가 있다. 
IOCP에는 기존에 있던 소켓 함수들의 확장 모델이 있는데 그 중 하나가 ConnectEx 함수이다.
실제로 국내 모 메신져 회사의 Windows Application도 초기에 connect API를 사용하다가 
패치 후 ConnectEx API를 사용하는것을 보면 상용프로그램에서도 IOCP가 성능을 좌지우지 하는듯 보인다.
 
[ConnectEx 사용법]
ConnectEx API는 기존 API들 처럼 DLL에 정의되어있지 않다. 
런타임시 WSAIoctl API를 통해 ConnectEx를 메모리에 생성한 후 함수 포인터를 가져와 사용해야한다.
 
[ConnectEx 함수 포맷]
 
[ConnectEx 후킹]
먼저 ConnectEx API를 생성해준다.
이후 WSAIoctl을 후킹하고 인자를 검사하여 함수 포인터를 얻겠다는 의미로 dwIoControlCode 인자가 SIO_GET_EXTENSION_FUNCTION_POINTER 인지 확인하고
어떠한 API를 생성할것인지를 알 수 있는 lpvInBuffer 인자가 WSA_CONNECTEX 인지 검사한 후에 조건에 만족한다면 
ConnectEx를 생성하겠다는 것으로 판단할 수 있으므로 실제 ConnectEx의 주소가 들어갈 포인터에 MyConnectEx 함수의 주소를 대입해주면 된다.
LPFN_CONNECTEX Org_Connectex;//해당 함수포인터는 ConnectEx의 원본함수가 된다.
 
BOOl GetConnectEx(){
    GUID guid = WSAID_CONNECTEX;
    DWORD dwBytes;
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        TCHAR buf[100];
        _stprintf(buf, TEXT("error : %x"), GetLastError());
        OutputDebugString(buf);
        return FALSE;
    }
    int rc = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
        &guid, sizeof(guid),
        &Org_Connectex, sizeof(Org_Connectex),
        &dwBytes, NULL, NULL);
    if(!rc){
        return TRUE;
    }
    else{
        return FALSE;
    }
}
int WSAAPI MyWSAIoctl(
    SOCKET s,
    DWORD dwIoControlCode,
    LPVOID lpvInBuffer,
    DWORD cbInBuffer,
    LPVOID lpvOutBuffer,
    DWORD cbOutBuffer,
    LPDWORD lpcbBytesReturned,
    LPWSAOVERLAPPED lpOverlapped,
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
) {
    int ret = ((PFMyWSAIoctl)OrgWinAPI[WSAIOCTL])(
        s, 
        dwIoControlCode, 
        lpvInBuffer, 
        cbInBuffer, 
        lpvOutBuffer, 
        cbOutBuffer, 
        lpcbBytesReturned, 
        lpOverlapped, 
        lpCompletionRoutine
    );
    if (dwIoControlCode == SIO_GET_EXTENSION_FUNCTION_POINTER && CheckGUID(lpvInBuffer, WSAID_CONNECTEX)) {
        // WSAIoctl의 인자를 검사해 ConnectEx API를 생성하려는 호출인지 판단한다.
        *(DWORD*)lpvOutBuffer = (DWORD)MyConnectex;
        //정상적으로 생성된 ConnectEx의 주소가 들어있는 포인터를 MyConnectEx함수의 주소로 지정해준다.
    }
    return ret;
}
BOOL WSAAPI MyConnectex(
    SOCKET s,
    const sockaddr* name,
    int namelen,
    PVOID lpSendBuffer,
    DWORD dwSendDataLength,
    LPDWORD lpdwBytesSent,
    LPOVERLAPPED lpOverlapped
) {
    BOOL ret = ((PFMyConnectex)Org_Connectex)(
        s, 
        name,
        namelen, 
        lpSendBuffer, 
        dwSendDataLength, 
        lpdwBytesSent, 
        lpOverlapped
    );
    OutputDebugString(TEXT("ConnectEx API Hooking Success"));
 
    return ret;
}
int main(){
    if(GetConnectEx()){//ConnectEx API를 생성한다.
        hook_code(TEXT("Ws2_32.dll"), TEXT("WSAIoctl"), MyWSAIoctl);
        //WSAIoctl API를 후킹한다.
    }
}

'C&C++' 카테고리의 다른 글

CMake Syntax  (0) 2020.01.21
How To Use Unit Test In C++  (0) 2020.01.13
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08
WINAPI Options Using Bit Flag  (0) 2019.08.07
WINAPI CreateProcess Suspend State  (0) 2019.07.11
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){
    switch (fdwReason)  {
    case DLL_PROCESS_ATTACH:
        LogFileOpen();
        break;
    case DLL_PROCESS_DETACH:
        LogFileClose();
        break;
    }
    return TRUE;
}
 
나는 해당 DLL을 로드한 프로세스가 종료될때 까지 다른 프로세스가 로그파일에 접근하지 못하도록 DLL이 로드될때 파일을 열고 DLL이 언로드될때 파일 핸들을 닫아주도록 구현을 하려고 했다.
그러나 위 방식대로 구현을 하니 DOS 명령 kill의 경우나 Procexp로 프로세스를 죽이는 경우에는 로그파일이 제대로 생성되지 않았다.
즉 외부 프로세스가 강제로 해당 DLL을 로딩중인 프로세스를 끄려고 할 경우 DLL_PROCESS_DETACH로 들어가지 않는것을 확인했다.
 
MSDN에서는 FreeLibrary API를 통해 가상메모리에서 언로드 될때 그리고 프로세스가 종료될때 DLL_PROCESS_DETACH에 들어온다고 정의한다.
그래서 당연히 나는 어떤식으로든 프로세스가 종료되면 DLL_PROCESS_DETACH로 들어갈거라고 생각했다.
그러나 MSDN에서 정의하는 프로세스가 종료되는 순간은 지극히 정상종료를 의미하는것 같다.
 
프로세스가 정상적으로 종료될때 메인스레드에 진입하는 메인함수들(main, winmain, dllmain…)이 반환되면 CRT(C/C++ RunTime Library)에 제어권이 돌아가고 OS에서 메인스레드 종료작업 후 메인함수의 리턴값을 가지고 ExitProcess API를 호출한다.
이 ExitProcess API에서 해당 프로세스에 로드된 DLL들에 DLL_PROCESS_DETACH를 인자로 dllmain을 호출한다고 한다. 
그러나 TerminateProcess API를 통해 프로세스를 죽이면 CRT로 제어권이 안돌아갈 뿐더러 어떠한 메인스레드 종료작업도 하지못하고 그냥 강제로 종료된다.
 
 
MSDN에서 또한 TerminateProcess를 사용한다면 DLL에서 사용되는 데이터를 보장받지 못할것이라고 말한다.
 
결론은 타 프로세스가 TerminateProcess를 통해 프로세스를 종료시키면 DLL_PROCESS_DETACH로 안들어간다.
 
VOID WaitThreadStart() {
    hEvent = OpenEvent(EVENT_ALL_ACCESS, TRUE, TEXT("finish"));
    hThread = (HANDLE)_beginthreadex(NULL, 0, WaitForExit, NULL, 0, (unsigned*)& dwThreadID);
}
UINT WINAPI WaitForExit(LPVOID lpParam){
    WaitForSingleObject(hEvent, INFINITE);
 
    ExitProcess(1);
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){
    switch (fdwReason)  {
    case DLL_PROCESS_ATTACH:
        LogFileOpen();
        WaitThreadStart();
        break;
    case DLL_PROCESS_DETACH:
        LogFileClose();
        OutputDebugString(TEXT("finish"));
        break;
    }
    return TRUE;
}
 
내 문제에서는 DLL을 인젝션 해주는 에이전트 프로세스에서 CreateEvent로 이벤트를 만들고 종료를 원하는 순간 SetEvent를 통해 해당 DLL을 로드한 모든 프로세스가 종료되도록 함으로써 
DLL을 로드한 모든 프로세스가 실행중인 동안에는 타 프로세스의 로그파일 접근을 막았지만 이건 내 문제에서 이벤트를 통한 해결방법이다.
 
DLL_PROCESS_DETACH를 너무 믿지 말자.
 

'C&C++' 카테고리의 다른 글

How To Use Unit Test In C++  (0) 2020.01.13
WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI Options Using Bit Flag  (0) 2019.08.07
WINAPI CreateProcess Suspend State  (0) 2019.07.11
Incremental Linking  (0) 2019.07.10
비트 플래그 (Bit Flag)
비트 플래그란 한 비트를 플래그로 한 비트에 하나의 상태가 참이냐 거짓이냐를 따지는 것이다.
메모리는 바이트 단위이므로 최소 8비트를 가진다. 이 말은 8개의 조건이 참이냐 거짓이냐를 가질수 있다는 말이다.
0000 0000 에서 한 비트를 플래그로 생각했을때 1이면 on, 0이면off이 된다.
EX)
0000 0001 = 0x01 = 사과
0000 0010 = 0x02 = 딸기
0000 0100 = 0x04 = 참외
0000 1000 = 0x08 = 자두
0001 0000 = 0x10 = 수박
0010 0000 = 0x20 = 복숭아
0100 0000 = 0x40 = 바나나
1000 0000 = 0x80 = 배
 
옵션이 이렇게 있을때 사과와 자두 그리고 복숭아를 골랐다고 표현한다면 
(0000 0001) | (0000 1000) | (0100 0000) = (0100 1001) 
이런식으로 표현할 수 있다.
 
Windows API에서 제공하는 다양한 옵션들 또한 대부분 비트 플래그를 통해 지정된다.
 
NtCreateFile에서 DesiredAccess 파라미터를 예로 들어보겠다.
<winnt.h> 헤더의 정의를 보면 ACCESS_MASK는 DWORD 타입이고 DWORD는 4바이트 자료형이기 때문에 최대 32가지의 옵션을 지정할 수 있다.
위 목록은 MSDN에서 제공하는 ACCESS_MASK flag 목록의 일부이고
아래의 매크로는 <winnt.h> 헤더에서 실제로 옵션들을 정의하는 코드의 일부다.
NtCreateFile 호출시 보통 여러개의 옵션을 OR 연산으로 지정해주기 때문에 위 비트플래그 설명을 본다면 이해가 된다.
나는 NtCreateFile 후킹시 인자로 들어온 DesiredAccess의 값에 FILE_WRITE_ATTRIBUTES 옵션이 들어왔는지를 알아야 했다.
WinAPI에서 옵션이 어떠한 원리로 지정되는지 몰라서 처음에는 이 기능을 제공하는 WinAPI가 있나보다 하고 엄청 찾아봤다.
하지만 비트플래그 원리를 통해 옵션이 지정된다는 것을 알고 간단히 비트마스크 연산을 통해 해결했다.

'C&C++' 카테고리의 다른 글

WINAPI How To Hook ConnectEx  (0) 2019.11.06
WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08
WINAPI CreateProcess Suspend State  (0) 2019.07.11
Incremental Linking  (0) 2019.07.10
WINAPI VirtualAlloc and VirtualAllocEx Difference  (0) 2019.07.09
[프로세스 시작전에 DLL Injection을 하는 방법]
 
CreateProcess API를 suspend 상태로 실행시키면 메모리에 프로세스가 올라가지만
suspend 상태이기 때문에 메인스레드가 실행은 되지 않는다.
이때 프로세스에 DLL Injection을 해주고 완료가 되면 ResumeThread API를 통해 대기중인 메인스레드를 실행시킨다.
 
프로세스 대기 상태
suspend 상태에서 메모리를 보면 아무것도 없다.
위 코드를 보면 suspend상태에서 5초후 실행된다.
이때 notepad.exe에서 필요한 dll이 로딩되지 않는 이유는 프로세스가 올라갈 메모리만 할당되고
프로세스 내 메인 스레드가 실행되지 않기 때문에 notepad.exe의 IAT에 있는 dll들이 올라가지 않지만
Suspend 상태가 해제되면 메인스레드가 실행되고 notepad.exe가 정상적으로 실행되며 DLL 또한 로딩된다.
 

프로세스 실행 후

 

프로세스 suspend 상태에서 DLL Injection을 통해 IAT를 동적으로 후킹하면 프로세스 시작전 후킹을 할 수 있다.

'C&C++' 카테고리의 다른 글

WINAPI DLL_PROCESS_DETACH  (1) 2019.08.08
WINAPI Options Using Bit Flag  (0) 2019.08.07
Incremental Linking  (0) 2019.07.10
WINAPI VirtualAlloc and VirtualAllocEx Difference  (0) 2019.07.09
Features And Uses Of STL Container In C++  (0) 2018.11.04
 

해당 코드를 실행하면 함수 포인터를 출력한다.
하지만 해당 주소에 가보면 실제 함수 프롤로그가 아닌 실제 주소로 점프하도록 하는 코드가 있다.
MyFunc 함수 이외에도 위 주소 근처를 보면 해당 프로그램에서 사용하는 모든 함수들로 점프하는 코드들이 있다.
원래는 함수포인터는 함수의 시작주소를 갖는게 일반적이지만 위 모습은 일반적이지 않은 모습이다.
이 문제에 대한 원인은 (문제는 아니지만…) 증분 링크 옵션때문이다.
증분링크(Incremental Link)란 
 
매번 컴파일할 때 마다 링킹과정이 계속 반복 되는데 처음 컴파일 할 때 함수 주소들을 테이블에 보관하고 
다음 컴파일시 수정된것만 다시 계산하여 좀더 빠르게 링킹을 하도록 하는 기능이다.
증분링킹이 진행되면 초기 ilk파일이 생기고 저 ilk 파일을 토대로 빠른 링킹과정이 진행된다.
저 파일에 문제가 생기면 Visual Studio는 경고를 하고 비증분 링킹으로 전환한다.
 
warning LNK4076: invalid incremental status file '*.ilk'; linking nonincrementally
프로젝트 속성에 들어가 증분링크 옵션을 꺼두고 다시 컴파일한다.
증분링크 옵션을 꺼두고 다시 컴파일하니 함수 프롤로그가 잘 나타나는것을 확인할 수 있다.

 

'C&C++' 카테고리의 다른 글

WINAPI Options Using Bit Flag  (0) 2019.08.07
WINAPI CreateProcess Suspend State  (0) 2019.07.11
WINAPI VirtualAlloc and VirtualAllocEx Difference  (0) 2019.07.09
Features And Uses Of STL Container In C++  (0) 2018.11.04
Modern C++  (1) 2018.10.02
LPVOID VirtualAlloc( LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
 
LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
 
함수 원형에서 확인 가능하듯이 차이점은 프로세스 핸들이 인자로 들어가느냐 안들어가느냐에 있다.
 
다시말해 VirtualAlloc 함수는 내 프로세스에, VirtualAllocEx 함수는 타 프로세스에도 메모리 공간을 할당할 수 있다.
 
[인자 설명]
LPVOID lpAddress
  • 할당할 메모리의 절대주소
  • NULL을 인자로 주면 시스템에서 알아서 할당
 
SIZE_T dwSize
  • 할당할 메모리의 크기
  • NULL을 인자로 주면 하나의 페이지 크기만큼 할당
 
DWORD flAllocationType
  • 할당 방법 지정
  • MEM_COMMIT : 물리 메모리의 할당을 확정
  • MEM_RESERVE : 물리 메모리의 할당 없이 예약만 한다
  • ETC : 나머지 옵션은 MSDN
 
DWORD flProtect
 
Return Value
  • 성공시 할당한 메모리 주소를 반환
  • 실패시 NULL 반환
 

'C&C++' 카테고리의 다른 글

WINAPI CreateProcess Suspend State  (0) 2019.07.11
Incremental Linking  (0) 2019.07.10
Features And Uses Of STL Container In C++  (0) 2018.11.04
Modern C++  (1) 2018.10.02
WINAPI How To Load Windows Tray Icon  (0) 2018.09.08

+ Recent posts