1. IPC - 이름 없는 파이프
more..
//////////////////////////////////////////////////////////////////////////////////////
// 이름없는파이프.cpp - HelloExe 에 파이프를 통해서 인자로 문자열을 보낸다.
//////////////////////////////////////////////////////////////////////////////////////
int main()
{
// 이름 없는 파이프 만들기
HANDLE hRead, hWrite;
// 파이프가 내부적으로 사용할 버퍼크기..
CreatePipe( &hRead, &hWrite, 0, 1024 );
// 파이프를 다른 프로세스에 전달한다.
// 1. 상대가 이미 실해중이라면 DuplicateHandle()로 복사후 메시지를 사용해서 핸들전달.
// 2. 상속을 사용해서 자식프로세스에 전달..
SetHandleInformation( hRead, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT );
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof( si ) };
// 명령형 전달인자를 사용해서 pipe 핸들을 전달해 준다.
char cmd[256] = "";
wsprintf( cmd, "HelloExe.exe %d" , hRead );
BOOL b = CreateProcess( 0, cmd, 0, 0, TRUE, // 상속
CREATE_NEW_CONSOLE, 0, 0, &si, &pi );
if ( b )
{
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hRead );
}
while( 1 )
{
char data[256] = { 0 };
printf( "전달할 data >> " );
gets( data );
// 파이프에 적는다. - 파이프도 결국 파일 이다.
DWORD len;
WriteFile( hWrite, data, 256, &len, 0 );
}
}
//////////////////////////////////////////////////////////////////////////////////////
// HelloExe.cpp - 인자로 문자열을 읽어온다.
//////////////////////////////////////////////////////////////////////////////////////
int main( int argc, char* argv[] )
{
HANDLE hRead = (HANDLE)(atoi(argv[1]));
while( 1 )
{
char buf[256] = { 0 };
DWORD len;
ReadFile( hRead, buf, 256, &len, 0 );
printf( "읽어온 Data : %s\n", buf );
}
}
2. IPC - 이름 있는 파이프
more..
//////////////////////////////////////////////////////////////////////////////////////
// TimeServer.cpp - 클라이언트에 현재시간을 보내줄 서버..
//////////////////////////////////////////////////////////////////////////////////////
DWORD WINAPI TimeServer( void* p )
{
// 최대 생성가능한 스레드는 운영체제마다 다르당~.
HANDLE hPipe = CreateNamedPipe(
"\\\\.\\pipe\\TimeServer", // 이름
PIPE_ACCESS_OUTBOUND, // 출력 전용
PIPE_TYPE_MESSAGE, // 메시지 방식( UDP 방식 )
PIPE_UNLIMITED_INSTANCES, // 최대생성 가능한 Instance - 멀티스레드 관련
1024, 1024, // 입출력 버퍼( 권장 사항~ )
1000, // WaitNamedPipe() 로 대기 가능한 시간
0 ); // 보안
if( hPipe == INVALID_HANDLE_VALUE ) // -1
{
printf( "Fail To CreateNamedPipe\n" );
return 0;
}
/////////////////////////////////////////////////////////////////
while( 1 )
{
// 클라이언트가 접속할 때까지 대기 한다.
BOOL b = ConnectNamedPipe( hPipe, 0 ); // 접속 대기
// 이미 접속된 경우는 성공으로 처리 해준다.
if ( b == FALSE && GetLastError() == ERROR_PIPE_CONNECTED )
b = TRUE;
if( b )
{
SYSTEMTIME st;
GetSystemTime( &st ); // UTC
// pipe는 결국 파일 처럼 취급된다.
DWORD len;
WriteFile( hPipe, &st, sizeof(st), &len, 0 );
FlushFileBuffers( hPipe ); // 버퍼를 비운다.( C의 fflush )
DisconnectNamedPipe( hPipe );
}
}
}
int main()
{
// 멀티 스레드를 지원 할 수 있다.
HANDLE hThread1 = CreateThread( 0, 0, TimeServer, 0, 0, 0 );
HANDLE hThread2 = CreateThread( 0, 0, TimeServer, 0, 0, 0 );
HANDLE hThread3 = CreateThread( 0, 0, TimeServer, 0, 0, 0 );
getch();
}
///////////////////////////////////////////////////////////////////////////////////////
// TimeClient.cpp - 서버에 접속하여 시간을 얻어온다.
///////////////////////////////////////////////////////////////////////////////////////
int main()
{
HANDLE hPipe = CreateFile(
"\\\\.\\pipe\\TimeServer",
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0 );
if( hPipe == INVALID_HANDLE_VALUE )
{
printf( "파이프에 접속할 수 없습니다.\n" );
return 0;
}
/////////////////////
SYSTEMTIME st;
DWORD len;
ReadFile( hPipe, &st, sizeof(st), &len, 0 );
// UTC -> 지역시간으로
::SystemTimeToTzSpecificLocalTime( 0, &st, &st );
// 화면에 출력
char s1[256];
char s2[256];
GetDateFormat( LOCALE_USER_DEFAULT, 0, &st, 0, s1, 256 );
GetTimeFormat( LOCALE_USER_DEFAULT, 0, &st, 0, s2, 256 );
printf( "%s - %s\n", s1, s2 );
getch();
CloseHandle( hPipe );
}
3. IPC - 클립보드
more..
////////////////////////////////////////////////////////////////////////////////////////
// 클리보드 내용 읽어오기.cpp
////////////////////////////////////////////////////////////////////////////////////////
int main()
{
char data[4096] = {0};
if ( OpenClipboard(0))
{
if ( IsClipboardFormatAvailable( CF_TEXT ) )
{
HANDLE hData = GetClipboardData( CF_TEXT );
char* pData = (char*)GlobalLock( hData );
strcpy( data, pData);
GlobalUnlock( hData );
}
CloseClipboard();
}
printf("읽어온 Data : %s\n", data);
}
////////////////////////////////////////////////////////////////////////////////////////
// 클리보드 내용 쓰기.cpp
////////////////////////////////////////////////////////////////////////////////////////
int main()
{
// 클립보드에 data전달하는 방법.
char data[256];
gets( data );
// 클립 보드에 보내기 위한 메모리 할당 - 이동 가능한 Global 메모리
HANDLE hData = GlobalAlloc( GMEM_MOVEABLE, 256);
// Global힙은 동기화를 위해 Lock을 걸어야 한다.
char* pData = (char*)GlobalLock( hData );
strcpy( pData, data );
GlobalUnlock(hData );
if ( OpenClipboard( 0 ) )
{
EmptyClipboard();
SetClipboardData( CF_TEXT, hData );
CloseClipboard();
}
else
{
printf("클립 보드를 열수 없습니다.\n");
}
}
4. IPC - WM_COPYDATA
more..
////////////////////////////////////////////////////////////////////////////////////////
// Send.cpp - WM_COPYDATA , COPYDATASTRUCT 구조체를 LPARAM에 담아서 보낸다.
////////////////////////////////////////////////////////////////////////////////////////
int main()
{
HWND hwnd = FindWindow( 0, "B" );
if ( hwnd == 0 )
{
printf("B를 먼저 실행해 주세요.\n");
return 0;
}
while( 1 )
{
char data[256];
printf( "전송할 메시지 >> " );
gets( data );
// 다른 프로세스로 전송한다.
COPYDATASTRUCT cds;
cds.cbData = sizeof( data ); // 전송할 메모리 크기
cds.dwData = 1; // 식별자 32비트.. 원하는 용도로 사용
cds.lpData = data; // 전송할 메모리 주소
// WM_COPYDATA는 포인터 전달이 가능하다.
SendMessage( hwnd, WM_COPYDATA, 0, (LPARAM)&cds );;
}
}
////////////////////////////////////////////////////////////////////////////////////////
// B.cpp - WM_COPYDATA 메시지를 처리한다.
////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static char* pData;
static char buf[256]; switch( msg )
{
case WM_COPYDATA:
{
COPYDATASTRUCT* p = (COPYDATASTRUCT*) lParam;
if( p->dwData == 1 ) // 식별자 조사
{
// static 변수에 주소 보관 했다면??
// 다시 쓰고자 할때잘못된 값을 읽는다.
pData = (char*)(p->lpData);
// 참조 했던값을 복사해서 보관 해 둔다.
strcpy( buf, (char*)(p->lpData) );
MessageBox( 0, pData, "", MB_OK );
}
}
return 0;
case WM_LBUTTONDOWN:
MessageBox( 0, pData, "", MB_OK );
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc( hwnd, msg, wParam, lParam);
}
5. APCQ 기본.
more..
#define _WIN32_WINNT 0x0501
#define WINVER 0x0501
#include <conio.h>
#include <stdio.h>
#include <windows.h>
#include <process.h>
unsigned __stdcall Thread( void* p )
{
for( int i = 0; i < 5; ++i )
{
printf( "busy...\n" );
Sleep(1000);
}
while( 1 )
{
//APC Q의 함수를 실행한다.
SleepEx( INFINITE, TRUE ); // TRUE : APCQ에 작업이 들어오면 실행하라.
}
return 0;
}
// APC Q에 넣기 위한 함수
void CALLBACK foo( ULONG_PTR param )
{
while( 1 )
{
printf( "foo...\n");
Sleep(1000);
}
}
int main()
{
HANDLE hThread = (HANDLE)_beginthreadex( 0, 0, Thread, 0, 0, 0 );
Sleep(1000); // APCQ를 확인할 시간을 벌어준다. 비어있는가를 확인한다.
QueueUserAPC( foo, hThread, 0 ); // 함수, 실행할 스레드, 파라미터
printf( "main...\n" );
getch();
CloseHandle( hThread );
}
6. IOCP 에 대한 단계별 기본 코드
1) Device Ko 대기 - 쓰기 작업이 끝날 때까지 대기, 누가 끝나는지를 알수가 없다.
more..
int main()
{
HANDLE hFile = CreateFile(
"COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); // 비동기 옵션OVERLAP
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "Com1을 Open 할 수 없습니다.\n" );
return 0;
}
const int sz = 1024;
char data[sz];
DWORD len;
printf( "출력 시작\n" );
// 비동기 작업시 반드시 OVERLAPPED 구조체를 제공해야 한다.
OVERLAPPED ov = { 0 };
ov.Internal = 0;
ov.InternalHigh = 0; // OS가 내부적으로 사용. 항상 0으로
ov.Offset = 0;
ov.OffsetHigh = 0; // Fie Offset
ov.hEvent = CreateEvent( 0, 0, 0, "e1" );
BOOL b = WriteFile( hFile, data, sz, &len, &ov ); // 비동기 구조체를 넘김
if ( b == TRUE )
printf( "동기적으로 쓰기 완료\n" );
else if ( b == FALSE && GetLastError() == ERROR_IO_PENDING )
{
printf( "비동기 작업중\n" );
// 화일은 쓰기를 마치면 Signal이 된다.
// 파일에 여러개의 비동기가 걸리면 문제가 된다.이벤트를 사용 못함...ㅠ_ㅠ
WaitForSingleObject( hFile, INFINITE ); // 장치의 KO가 signal 이 되기를 기다림
printf( "비동기 작업 완료\n" );
}
else
printf( "실패\n" );
CloseHandle( hFile );
getch();
}
2) Event를 대기로 누가 끝나는지, Cancel 을 할수 있다.
more..
int main()
{
HANDLE hFile = CreateFile(
"COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); // 비동기 옵션OVERLAP
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "Com1을 Open 할 수 없습니다.\n" );
return 0;
}
const int sz = 1024;
char data[sz];
DWORD len;
printf( "출력 시작\n" );
// 비동기 작업시 반드시 OVERLAPPED 구조체를 제공해야 한다.
OVERLAPPED ov = { 0 };
ov.Internal = 0;
ov.InternalHigh = 0; // OS가 내부적으로 사용. 항상 0으로
ov.Offset = 0;
ov.OffsetHigh = 0; // Fie Offset
ov.hEvent = CreateEvent( 0, 0, 0, "e1" );
BOOL b = WriteFile( hFile, data, sz, &len, &ov ); // 비동기 구조체를 넘김
if ( b == TRUE )
printf( "동기적으로 쓰기 완료\n" );
else if ( b == FALSE && GetLastError() == ERROR_IO_PENDING )
{
printf( "비동기 작업중\n" );
// 작업당 1개씩 전달된 Event 객체를 대기한다.
HANDLE h[2] = { ov.hEvent, CreateEvent( 0, 0, 0, "CancelEvent") };
DWORD ret = WaitForMultipleObjects( 2, h, FALSE, INFINITE );
if ( ret == WAIT_OBJECT_0+1 )
{
// 비동기를 취소하면 된다.
CancelIo( hFile );
}
printf( "비동기 작업 완료\n" );
}
else
printf( "실패\n" );
CloseHandle( hFile );
getch();
}
3) CALLBACK 함수를 사용한다.
more..
// Callback 함수 사용 APCQ에 넣는다.
// 비동기를 요청한 스레드와 대기하는 스레드를 분리 시켜줘야 한다.
VOID CALLBACK foo( DWORD e, DWORD bytes, OVERLAPPED* p )
{
printf( "작업완료 : %d\n", bytes );
}
int main()
{
HANDLE hFile = CreateFile(
"COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); // 비동기 옵션OVERLAP
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "Com1을 Open 할 수 없습니다.\n" );
return 0;
}
const int sz = 1024;
char data[sz];
DWORD len;
printf( "출력 시작\n" );
// 비동기 작업시 반드시 OVERLAPPED 구조체를 제공해야 한다.
OVERLAPPED ov = { 0 };
ov.Internal = 0;
ov.InternalHigh = 0; // OS가 내부적으로 사용. 항상 0으로
ov.Offset = 0;
ov.OffsetHigh = 0; // Fie Offset
ov.hEvent = CreateEvent( 0, 0, 0, "e1" );
// 비동기 전용함수 - 비동기 작업이 종료되면 현재스레드의 APCQ에 foo가 놓인다.
BOOL b = WriteFileEx( hFile, data, sz, &ov, foo ); // len은 끝나야 알수 있다.
// 얼러터블 상태로 들어가야 APCQ가 실행된다.
SleepEx( INFINITE, TRUE );
CloseHandle( hFile );
getch();
}
4) 비동기 요청 스레드와 비동기 대기 스레드를 분린한다.
more..
// 비동기를 요청한 스레드와 대기하는 스레드를 분리 시켜줘야 한다.
VOID CALLBACK foo( DWORD e, DWORD bytes, OVERLAPPED* p )
{
printf( "작업완료 : %d\n", bytes );
}
DWORD WINAPI IOCPWait( void* p )
{
HANDLE hPort = (HANDLE)p;
DWORD id;
DWORD byte;
OVERLAPPED* pov;
while( GetQueuedCompletionStatus( hPort, &byte, &id, &pov, INFINITE ) )
{
OVERLAPPED_PLUS* p = (OVERLAPPED_PLUS*)pov;
// 화일 ID, 실제 작업한 크기
printf( "작업 완료 : %d %d %d\n", id, byte, p->workID );
}
return 0;
}
int main()
{
HANDLE hFile = CreateFile(
"COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); // 비동기 옵션OVERLAP
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "Com1을 Open 할 수 없습니다.\n" );
return 0;
}
// 입출력 완료 포트(IOCP)를 생성한다.
HANDLE hPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, // IOCP에 연결할 파일
0, // 이미 존재하는 IOCP 핸들
0, // 완료 키
2 ); // IOCP에서 대기할 수 있는 스레드 갯수
//IOCP 와 비동기 파일을 연결한다.
CreateIoCompletionPort( hFile, hPort, 1, 2 );
// 스레드를 생성해서 IOCP를 대기 하게 한다.
HANDLE hThread = CreateThread( 0, 0, IOCPWait, (void*)hPort, 0, 0 );
////////////////////////////////////////////////////////////////////////
const int sz = 1024;
char data[sz];
DWORD len;
printf( "출력 시작\n" );
// 비동기 작업시 반드시 OVERLAPPED 구조체를 제공해야 한다.
OVERLAPPED ov;
memset( &ov, 0, sizeof(ov) );
ov.Internal = 0;
ov.InternalHigh = 0; // OS가 내부적으로 사용. 항상 0으로
ov.Offset = 0;
ov.OffsetHigh = 0; // Fie Offset
ov.hEvent = CreateEvent( 0, 0, 0, "e1" );
// 새로운 스레드를 만들어서 비동기 작업을 대기하게 한다.
HANDLE hThread = CreateThread( 0, 0, Wait, (void*)&ov, 0, 0 );
BOOL b = WriteFile( hFile, data, sz, &len, &ov ); // len은 끝나야 알수 있다.
// 주스레드는 그냥..다른 작업을 계속한다.
printf( "비동기 작업 요청완료\n");
getch();
// 주스레드는 그냥 다른 작업을 계속한다.( 대기는 다른스레드가 하게 한다. )
CloseHandle( hFile );
getch();
}
5)
입출력 완료포트 - 한개의 스레드가 여러개의 비동기 작업을 관리 해준다.!!
more..
// IOCP 에서 완료된 작업을 처리할 스레드
// 작업 ID를 관리하기 위해 OVERLAPPED를 확장한다. 상속 또는 포함
struct OVERLAPPED_PLUS : public OVERLAPPED
{
DWORD workID;
};
DWORD WINAPI IOCPWait( void* p )
{
HANDLE hPort = (HANDLE)p;
DWORD id;
DWORD byte;
OVERLAPPED* pov;
while( GetQueuedCompletionStatus( hPort, &byte, &id, &pov, INFINITE ) )
{
OVERLAPPED_PLUS* p = (OVERLAPPED_PLUS*)pov;
// 화일 ID, 실제 작업한 크기
printf( "작업 완료 : %d %d %d\n", id, byte, p->workID );
}
return 0;
}
int main()
{
HANDLE hFile = CreateFile(
"COM1", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0 ); // 비동기 옵션OVERLAP
if ( hFile == INVALID_HANDLE_VALUE )
{
printf( "Com1을 Open 할 수 없습니다.\n" );
return 0;
}
// 입출력 완료 포트(IOCP)를 생성한다.
HANDLE hPort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE, // IOCP에 연결할 파일
0, // 이미 존재하는 IOCP 핸들
0, // 완료 키
2 ); // IOCP에서 대기할 수 있는 스레드 갯수
//IOCP 와 비동기 파일을 연결한다.
CreateIoCompletionPort( hFile, hPort, 1, 2 );
// 스레드를 생성해서 IOCP를 대기 하게 한다.
HANDLE hThread = CreateThread( 0, 0, IOCPWait, (void*)hPort, 0, 0 );
////////////////////////////////////////////////////////////////////////
const int sz = 1024;
char data[sz];
DWORD len;
printf( "출력 시작\n" );
// 비동기 작업시 반드시 OVERLAPPED 구조체를 제공해야 한다.
OVERLAPPED_PLUS ov;
memset( &ov, 0, sizeof(ov) );
ov.Internal = 0;
ov.InternalHigh = 0; // OS가 내부적으로 사용. 항상 0으로
ov.Offset = 0;
ov.OffsetHigh = 0; // Fie Offset
ov.hEvent = CreateEvent( 0, 0, 0, "e1" );
ov.workID = 1;
BOOL b = WriteFile( hFile, data, sz, &len, &ov ); // len은 끝나야 알수 있다.
OVERLAPPED_PLUS ov2;
memset( &ov2, 0, sizeof(ov2) );
ov2.workID = 2;
BOOL b2 = WriteFile( hFile, data, sz, &len, &ov2 );
// 주스레드는 그냥..다른 작업을 계속한다.
printf( "비동기 작업 요청완료\n");
getch();
// 주스레드는 그냥 다른 작업을 계속한다.( 대기는 다른스레드가 하게 한다. )
CloseHandle( hFile );
getch();
}