1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif
 
#include <tchar.h>
#include <windows.h>
 
#include <stdio.h>
#include <stdlib.h>
 
#define MAX_LINE 4096
 
/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
 
/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("ADB 연결 장치");
 
//프로세스를 위한 변수
HANDLE write_in, write_out;
HANDLE read_in, read_out;
 
SECURITY_ATTRIBUTES sec;
 
long unsigned int writen=0;
long unsigned int readn=0;
 
int rtv;
 
char command[80= {0,};
char buffer[MAX_LINE];
 
STARTUPINFO si={0,};
PROCESS_INFORMATION pi;
 
 
int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */
 
    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);
 
    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
 
    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;
 
    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           szClassName,       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );
 
    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);
 
    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 00))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }
 
    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}
 
 
/*  This function is called by the Windows function DispatchMessage()  */
 
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int i=0;
 
    switch (message)                  /* handle the messages */
    {
        case WM_CREATE:
            sec.nLength=sizeof(SECURITY_ATTRIBUTES);
            sec.bInheritHandle=TRUE;
            sec.lpSecurityDescriptor=NULL;
 
            CreatePipe(&write_out,&write_in,&sec,0);
            CreatePipe(&read_in,&read_out,&sec,0);
 
            sprintf(command,"./adb.exe shell",NULL);
 
            si.cb=sizeof(STARTUPINFO);
            si.hStdInput=write_out;
            si.hStdOutput=read_out;
            si.dwFlags=STARTF_USESTDHANDLES;
 
            rtv=CreateProcess(NULL,
                            command,
                            NULL,
                            NULL,
                            TRUE,
                            NULL,
                            NULL,
                            NULL,
                            &si,
                            &pi);
 
            if(!rtv){
                printf("Error");
                PostQuitMessage (0);
            }
 
            break;
        case WM_KEYDOWN:
            strcpy(buffer,"input keyevent 26\n");
            WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
            printf("%s",buffer);
 
            for(i=0;i<MAX_LINE;i++){
                buffer[i]=0x00;
            }
 
            ReadFile(read_in,buffer,sizeof(buffer),&readn,NULL);
            printf("%s",buffer);
            break;
        case WM_DESTROY:
            strcpy(buffer,"exit\n");
            WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
            printf("%s",buffer);
 
            CloseHandle(write_in);
            CloseHandle(write_out);
            CloseHandle(read_in);
            CloseHandle(read_out);
            CloseHandle(pi.hProcess);
 
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }
 
    return 0;
}
 
 
cs


윗 방법을 이용해서 Win32로 GUI을 적용할 수 있게 된다.

여기서, 키보드를 누르면 전원키를 누르는 효과를 가지게 된다.


Timer을 이용해서 ADB의 ScreenCap의 내용을 가지고 오게 할 수도 있다.

Posted by JunkMam
,

 shell에서 input을 사용하면 느끼는게 있을것인데, 그것은 바로, 처리속도의 딜레이가 존재한다.

 물론, sendevent라고 해서 Event(사용자가 행동하는 것)를 이용하여 처리한다고 해도 딜레이는 존재한다.(상대적으로 적하지만, 꼬인다는 점은 문제가 있다.)


 sendevent로 한다면, 알맞는 입력 장치랑 연결해야된다.

 그렇게 하기 위해선 해당 기기의 입력 장치가 뭐있는지 알아봐야되는데, 입력 장치를 찾아오고 해당 입력을 알아보는 명령어가 getevent이다.


 getevent만 쳐서 작동시키면, 모든 이벤트를 받아온다.(모든 이벤트가 뭘 뜻하는지 처음 출력한다.)


 getevent /dev/input/event0


 윗 방식으로 한다면, event0의 입력 값을 받아온다.

Posted by JunkMam
,

ADB로 Screencap으로 RAW파일을 만들었으면, 이제 볼줄 알아야될것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
e0 01 00 00 20 03 00 00 01 00 00 00 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
1e 1e 1e ff 8e 8e 8e ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
8f 8f 8f ff 8f 8f 8f ff 8f 8f 8f ff
cs


가지고 온다면, 이렇게 된다.


여기서 e0 01 00 00 는 가로축 20 03 00 00 은 세로축이된다.

01 00 00 00는 레이아웃으로 1층계열로하는데, screencap은 기본적으로 1계층으로 나온 화면만 다 가져오기 때문에 01 00 00 00 는 고정이라고 보는게 맞다.


이후에 값은 RGBA형태로 들어간다.

8f 8f 8f ff

뒤에 A인 FF는 투명도인데, screencap은 전체적인 것이기 때문에 투명도가 그냥 FF로 최댓값이다.

윗 값이 한 픽셀이기 때문에 int형 일차 배열을 e0 01 00 00 X 20 03 00 00 크기만큼 가져야된다.

여기서 int형인데, Little Endian형태로 가지는걸 알 수 있다.

Big Endian이면, 스마트폰이 아직 안나온 크기가 되니 Little Endian이라는걸 알 수 있을것이다.


윗 방법으로 값을 구해서 32Bit(Alpha 값이 필요 없기 때문에 3Byte(24bit) 색을 사용하면된다.)색을 가지고 와서 쓰면 된다.

Posted by JunkMam
,

 안드로이드 4.0 이상 버전에서는 Screencap이라는 명령어가 shell에 추가 되었다.

 Screencap의 사용법은 다음과 같다.


 screencap -p /sdcard/test.png


 이렇게 하면, sdcard에 test.png라는 파일명으로 png 형태로 정리하게 된다.


 기본적으론 RAW파일로 저장이 되며, 이 RAW파일의 구성은 나중에 설명하겠다.

 일단, png형태로 저장이 되고 JPEG형태나 기타 형태는 저장이 안된다.


 screencap의 도움말을 원하면,


 screencap -h


 이렇게 치면 사용이 가능하다.


 거기서 내용을 보면 다음 같이 출력이 된다.


 usage: screencap [-hp] [FILENAME]

   -h: this message

   -p: save the file as a png.

If FILENAME ends with .png it will be saved as a png.

If FILENAME is not given, the results will be printed to stdout.


 여기서 screencap -p을 사용하면 png 형태로 나오지만, 마지막 문구를 보면 RAW형태를 stdout에 출력되는걸 알 수 있다.


 RAW는 32Bit로 RGBA형태를 취급한다.

 stdout으로 출력되기 때문에 앞서 설명한 PIPE로 연결하면, 도움이 된다.

 만약, RAW을 파일로 출력하기 원한다면 다음같이 사용하면된다.


 screencap /sdcard/test


 이렇게하면 test라는 파일에 RAW로 출력이 된다.

Posted by JunkMam
,

ADB에서 전원을 연결하는 것을 PIPE로 송수신할 경우에 이렇게 처리하게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int Auto_Power(HANDLE,char*,int);
 
int Auto_Power(HANDLE write_in, char* buffer, int writen){
    int i=0;
    for(i=0;i<500;i++){
        strcpy(buffer,"input keyevent 26\n");
        WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
        printf("%s",buffer);
        sleep(5000);
        strcpy(buffer,"input swipe 500 700 90 700\n");
        WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
        printf("%s",buffer);
        sleep(1000);
        strcpy(buffer,"input keyevent 26\n");
        WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
        printf("%s",buffer);
        sleep(1000);
    }
    return 1;
}
 
cs

이렇게 적용 시켜서 PIPE HANDLE과 버퍼, 이상유무를 받을 수 있다.

writen이 있을 필요는 없으나, 이상 유무를 확인하기 위해서 사용하는데 쓸만하다.

Posted by JunkMam
,

 모바일 기기 - 갤럭시 S2(스크린 크기가 작다.)


1
2
3
4
5
6
7
8
for($i=0;$i -le 10$i){
adb.exe shell input keyevent 26;
sleep 5;
adb.exe shell input swipe 500 700 90 700;
sleep 1;
adb.exe shell input keyevent 26;
sleep 1;
}
cs


 이렇게 한다면, 자동으로 잠금 화면을 풀었다가 다시 전원을 끄는 효과를 11번 반복함을 볼 수 있다.


 이것이 안되는 경우에는 Samsung에서 제공하는 통합 USB 드라이버[각주:1]를 설치해야된다.


 흐름은 동일하게 ADB을 이용해서 처리하는 것이다.


 keyevent는 ADB에서 지원하는 키 입력 값을 신호로 보내는 것이다.[각주:2]

 

 Android에서 지원하는 KEYEVENT를 보고 하면 코드가 26번은 KEYCODE_POWER[각주:3]이다.


 즉, 흐름은 다음과 같다. keyevent 26번을 input으로 넣게 만들고, swipe(슬라이드)을 하여 잠금 화면을 푼다.

 sleep은 초당 시간을 뜻하는 것으로 sleep 5; 라는 것은 5초를 대기한다는 뜻이 된다.

  1. 삼성 USB 통합 드라이버 - http://local.sec.samsung.com/comLocal/support/down/kies_main.do?kind=usb(2015-06-18) [본문으로]
  2. Android KEYEVENT - http://developer.android.com/reference/android/view/KeyEvent.html(2015-06-18) [본문으로]
  3. Android KEYCODE_POWER - http://developer.android.com/reference/android/view/KeyEvent.html#KEYCODE_POWER(2015-06-18) [본문으로]
Posted by JunkMam
,

 ADB Shell을 설정하고 처리하는 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <stdio.h>
#include <stdlib.h>
 
#include <conio.h>
 
#include <windows.h>
 
#define MAX_LINE 4096
 
int main(int argc, char** argv)
{
    HANDLE write_in, write_out;
    HANDLE read_in, read_out;
 
    SECURITY_ATTRIBUTES sec;
 
    long unsigned int writen=0;
    long unsigned int readn=0;
 
    int rtv;
 
    char command[80= {0,};
    char buffer[MAX_LINE];
 
    STARTUPINFO si={0,};
    PROCESS_INFORMATION pi;
 
    //pthread
    pthread_t read_pthread;
    pthread_t write_pthread;
 
    sec.nLength=sizeof(SECURITY_ATTRIBUTES);
    sec.bInheritHandle=TRUE;
    sec.lpSecurityDescriptor=NULL;
 
    CreatePipe(&write_out,&write_in,&sec,0);
    CreatePipe(&read_in,&read_out,&sec,0);
 
    sprintf(command,"./adb.exe shell",NULL);
 
    si.cb=sizeof(STARTUPINFO);
    si.hStdInput=write_out;
    si.hStdOutput=read_out;
    si.dwFlags=STARTF_USESTDHANDLES;
 
    rtv=CreateProcess(NULL,
                      command,
                      NULL,
                      NULL,
                      TRUE,
                      NULL,
                      NULL,
                      NULL,
                      &si,
                      &pi);
 
    if(!rtv){
        printf("Error");
        return -1;
    }
 
    int i=0;
    int n = 0;
    char tmpkey=0;
 
    char filenames[100];
 
    FILE *fp=NULL;
 
    while(1){
 
        writen=0;
        readn=0;
 
        for(i=0;i<MAX_LINE;i++){
            buffer[i]=0x00;
        }
 
        //ReadFile(read_in,buffer,sizeof(buffer),&readn,NULL);
        //ReadFile(read_in,buffer,sizeof(buffer),&readn,FILE_FLAG_OVERLAPPED);
        ReadFile(read_in,buffer,sizeof(buffer),&readn,NULL);
 
        if(!strncmp(buffer,"exit",4)){
            break;
        }
 
        printf("%s",buffer);
 
        for(i=0;i<MAX_LINE;i++){
            buffer[i]=0x00;
        }
 
        fgets(buffer,MAX_LINE,stdin);
        //WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
 
        if(!strncmp(buffer,"exit\n",5)){
            WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
            break;
        }
        WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
 
    }
 
    while(system("start ./adb.exe kill-server"));
 
    CloseHandle(write_in);
    CloseHandle(write_out);
    CloseHandle(read_in);
    CloseHandle(read_out);
 
    return 0;
}
 
cs


 이렇게하여, 출력이 가능하다.

 PIPE가 이상 없이 되기 위해서 설정하는 방법도 있다.

Posted by JunkMam
,

 ADB을 이용해서 안드로이드 자동화를 하기 위해선 명령어를 알 수 있어야 될 것이다.


 기본적으로 안드로이드의 액티비티를 작동시키기 위해서는 명령어는 am(Activity Menager)을 사용해야된다.


 예) am start -n com.android.activity/.Main


 이렇게 작동시킨다.


 규칙은 am start -n [Package Name]/.[Class Name][각주:1]이 된다.


 Package Name과 Class Name을 사용해야된다.


 이 Package Name과 Activity Name을 얻기 위해서는 명칭을 알아야 될 것이다.


 이걸 알아내기 위해선, 액티비티를 실행 했을때 알 수 있다.(다른 방법은 아직 조사 중이다.)


 명령어는 다음과 같다.


 예) dumpsys activity activities


 이렇게 하면, activity에 관련된 것으로, 메모리로 올라와 있는걸 설정되어 있다.


 한 번이라도 실행 되면, 자동으로 메모리 어딘가에 올라가 있으며, 이걸 확인 하기 위해서 sumpsys을 이용한다.


 그래서, am start을 실행 시키기 전에 dumpsys로 여는 방법이 중요하다.

 단, Root가 되어야지만 실행이 가능한 액티비티도 존재하기 때문에 잘 분석해서 사용해야 될 것이다.


 dumpsys의 내용을 나중에 기록한다.

  1. 정확하겐 Activity Name이다. [본문으로]
Posted by JunkMam
,

 ADB랑 연결한다고 해도, PIPE로 통신해야된다.

 방법은 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 
    if(!rtv){
        printf("Error");
        return -1;
    }
 
    int i=0;
    int n = 0;
    char tmpkey=0;
 
    char filenames[100];
 
    FILE *fp=NULL;
 
    while(1){
 
        writen=0;
        readn=0;
 
        for(i=0;i<MAX_LINE;i++){
            buffer[i]=0x00;
        }
 
        //ReadFile(read_in,buffer,sizeof(buffer),&readn,NULL);
        //ReadFile(read_in,buffer,sizeof(buffer),&readn,FILE_FLAG_OVERLAPPED);
        ReadFile(read_in,buffer,sizeof(buffer),&readn,NULL);
 
        if(!strncmp(buffer,"exit",4)){
            break;
        }
 
        printf("%s",buffer);
 
        for(i=0;i<MAX_LINE;i++){
            buffer[i]=0x00;
        }
 
        fgets(buffer,MAX_LINE,stdin);
        //WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
 
        if(!strncmp(buffer,"exit\n",5)){
            WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
            break;
        }
        WriteFile(write_in,buffer,strlen(buffer),&writen,NULL);
 
    }
 
cs


 이렇게 하면, WriteFile로 처리될 수 있다.

 ReadFile로 하면, 파이프로 적혀진 버퍼를 읽어들인다.

 WriteFile로 하면, 버퍼에 기록한다.


 이렇게 해서 ADB SHELL을 통신할 수 있다.

Posted by JunkMam
,

 안드로이드 자동화에서 PIPE을 STDIN[각주:1]과 STDOUT[각주:2]을 하는 방법을 알아봐야된다.

 이 방법을 기록한 블로그가 현재 검색이 되지 않아서 MS측에서 제공하는 것을 작성할려고 한다.[각주:3]


 여기에서 작동하는 방법을 이용하면, STDIN과 STDOUT을 받아서 처리할 수 있게 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
    HANDLE write_in, write_out;
    HANDLE read_in, read_out;
 
    SECURITY_ATTRIBUTES sec;
 
    long unsigned int writen=0;
    long unsigned int readn=0;
 
    int rtv;
 
    char command[80= {0,};
    char buffer[MAX_LINE];
 
    STARTUPINFO si={0,};
    PROCESS_INFORMATION pi;
 
    //pthread
    pthread_t read_pthread;
    pthread_t write_pthread;
 
    sec.nLength=sizeof(SECURITY_ATTRIBUTES);
    sec.bInheritHandle=TRUE;
    sec.lpSecurityDescriptor=NULL;
 
    CreatePipe(&write_out,&write_in,&sec,0);
    CreatePipe(&read_in,&read_out,&sec,0);
 
    sprintf(command,"./adb.exe shell",NULL);
 
    si.cb=sizeof(STARTUPINFO);
    si.hStdInput=write_out;
    si.hStdOutput=read_out;
    si.dwFlags=STARTF_USESTDHANDLES;
 
    rtv=CreateProcess(NULL,
                      command,
                      NULL,
                      NULL,
                      TRUE,
                      NULL,
                      NULL,
                      NULL,
                      &si,
                      &pi);
 
 
cs


 윗 같이 사용할 경우에, 입/출력 버퍼를 PIPE에 연결하여 처리할 수 있게 된다.

 이렇게 하면, ADB의 SHELL의 입/출력을 다른 쪽으로 연결되어서 처리하게 된다.

  1. STDIN(Standard Input) : 기본적으로 사용자가 키보드에 입력하는 것을 받아들이는 입력 버퍼 [본문으로]
  2. STDOUT (Standard Output) : 기본적으로 모니터에서 출력되는 것을 저장하는 출력 버퍼. [본문으로]
  3. https://support.microsoft.com/ko-kr/kb/190351(2015-06-13) [본문으로]
Posted by JunkMam
,