반응형

MFC 로 시리얼 통신하는 코드의 일부이다. 

이번에 공부를 하면서 참고한 코드이다.

WriteByte()함수에서 버퍼크기를 수정하여 사용하면 될듯싶다.


프로그램 종료시 시리얼도 닫아주는걸 잊지말자.


SerialPort.h

class CSerialPort
{
public:
	CSerialPort();
	~CSerialPort();
private:
	HANDLE	m_hComm;
	DCB		m_dcb;
	COMMTIMEOUTS m_CommTimeouts;
	BOOL	m_bPortReady;
	bool	m_bWriteRC;
	bool	m_bReadRC;
	DWORD	m_iBytesWritten;
	DWORD	m_iBytesRead;
	DWORD	m_dwBytesRead;
	BYTE	m_nWriteData[DATA_SIZE];

public:
	void ClosePort();
	bool ReadByte(BYTE &resp);
	bool ReadByte(BYTE* &resp, UINT size);
	bool WriteByte(BYTE *pBuff);
	int SetLevel(int nChannel, int nValue);
	int LightOnOff(int nChannel, bool bOn);
	bool OpenPort(CString portname);
	bool SetCommunicationTimeouts(DWORD ReadIntervalTimeout,
		DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant,
		DWORD WriteTotalTimeoutMultiplier, DWORD WriteTotalTimeoutConstant);
	bool ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity,
		BYTE  Parity, BYTE StopBits);
	BOOL ch_state[4];
	BOOL comm_status = OFF;
};

extern CSerialPort m_serial;



SerialPort.cpp

#include "stdafx.h" #include "SerialPort.h" CSerialPort m_serial; CSerialPort::CSerialPort() { } CSerialPort::~CSerialPort() { } bool CSerialPort::OpenPort(CString portname) { m_hComm = CreateFile(L"//./" + portname, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); if (m_hComm == INVALID_HANDLE_VALUE) { return false; } else return true; } bool CSerialPort::ConfigurePort(DWORD BaudRate, BYTE ByteSize, DWORD fParity, BYTE Parity, BYTE StopBits) { if ((m_bPortReady = GetCommState(m_hComm, &m_dcb)) == 0) { //MessageBox(L"GetCommState Error", L"Error", MB_OK + MB_ICONERROR); CloseHandle(m_hComm); return false; } m_dcb.BaudRate = BaudRate; m_dcb.ByteSize = ByteSize; m_dcb.Parity = Parity; m_dcb.StopBits = StopBits; m_dcb.fBinary = true; m_dcb.fDsrSensitivity = false; m_dcb.fParity = fParity; m_dcb.fOutX = false; m_dcb.fInX = false; m_dcb.fNull = false; m_dcb.fAbortOnError = true; m_dcb.fOutxCtsFlow = false; m_dcb.fOutxDsrFlow = false; m_dcb.fDtrControl = DTR_CONTROL_DISABLE; m_dcb.fDsrSensitivity = false; m_dcb.fRtsControl = RTS_CONTROL_DISABLE; m_dcb.fOutxCtsFlow = false; m_dcb.fOutxCtsFlow = false; m_bPortReady = SetCommState(m_hComm, &m_dcb); if (m_bPortReady == 0) { MessageBox((HWND)"SetCommState Error",NULL, L"Error", MB_OK + MB_ICONERROR); CloseHandle(m_hComm); return false; } return true; } bool CSerialPort::SetCommunicationTimeouts(DWORD ReadIntervalTimeout, DWORD ReadTotalTimeoutMultiplier, DWORD ReadTotalTimeoutConstant, DWORD WriteTotalTimeoutMultiplier, DWORD WriteTotalTimeoutConstant) { if ((m_bPortReady = GetCommTimeouts(m_hComm, &m_CommTimeouts)) == 0) return false; m_CommTimeouts.ReadIntervalTimeout = ReadIntervalTimeout; m_CommTimeouts.ReadTotalTimeoutConstant = ReadTotalTimeoutConstant; m_CommTimeouts.ReadTotalTimeoutMultiplier = ReadTotalTimeoutMultiplier; m_CommTimeouts.WriteTotalTimeoutConstant = WriteTotalTimeoutConstant; m_CommTimeouts.WriteTotalTimeoutMultiplier = WriteTotalTimeoutMultiplier; m_bPortReady = SetCommTimeouts(m_hComm, &m_CommTimeouts); if (m_bPortReady == 0) { MessageBox((HWND)"StCommTimeouts function failed", NULL, L"Com Port Error", MB_OK + MB_ICONERROR); CloseHandle(m_hComm); return false; } return true; } bool CSerialPort::WriteByte(BYTE *pBuff) { m_iBytesWritten = 0; BYTE temp[9] = { NULL }; for (int i = 0; i < 9;i++) { temp[i] = pBuff[i]; } if (WriteFile(m_hComm, temp, 9, &m_iBytesWritten, NULL) == 0) return false; else return true; } bool CSerialPort::ReadByte(BYTE &resp) { BYTE rx; resp = 0; DWORD dwBytesTransferred = 0; if (ReadFile(m_hComm, &rx, 1, &dwBytesTransferred, 0)) { if (dwBytesTransferred == 1) { resp = rx; return true; } } return false; } bool CSerialPort::ReadByte(BYTE* &resp, UINT size) { DWORD dwBytesTransferred = 0; if (ReadFile(m_hComm, resp, size, &dwBytesTransferred, 0)) { if (dwBytesTransferred == size) return true; } return false; } void CSerialPort::ClosePort() { CloseHandle(m_hComm); return; }


반응형

출처 : http://www.sosori.com/2009/09/%EC%A0%84%EC%B2%98%EB%A6%AC%EB%AC%B8%EC%9D%98-%EC%A2%85%EB%A5%98include-define-ifdef.html

전처리문이란?

실질적인 컴파일 이전에 미리 처리되는 문장을 가리킨다. 선행처리기라고도 한다. 따라서 컴파일러는 사용자가 작성한 코드를 컴파일하기 전에 전처리문에서 정의해 놓은 작업들을 먼저 수행한다.

종류로는 #define, #if, #ifdef, #ifndef, #defined, #undef 등이 있다. 이것은 기존에 있는 방대한 소스 코드를 지우지 않고 활성화 비활성화 하는 데에 가장 많이 이용된다. 즉, 기존에 있는 소스 코드를 건드리지 않는 상태에서 부분적인 컴파일을 하는 것이다.

C의 전처리문이 오는 줄(Line)의 첫 문자는 항상 '#'으로 시작한다. ANSI 표준에 따른 C의 전처리문의 종류가 아래에 나와 있다.

 

- 파일 처리를 위한 전처리문 : #include

- 형태 정의를 위한 전처리문 : #define, #undef

- 조건 처리를 위한 전처리문 : #if, #ifdef, #ifndef, #else, #elif, #endif

- 에러 처리를 위한 전처리문 : #error

- 디버깅을 위한 전처리문 : #line

- 컴파일 옵션 처리를 위한 전처리문 : #pragma 

 

조건 처리를 위한 전처리문은 어떤 조건에 대한 검사를 하고 그 결과를 참(0 이 아닌 값) 또는 거짓(0)으로 돌려준다.

#if : ...이 참이라면

#ifdef : ...이 정의되어 있다면

#else : #if나 #ifdef에 대응된다.

#elif : "else + if"의 의미

#endif : #if, #ifdef, #infdef 이 끝났음을 알린다.

 

이하 각 전처리문의 상세를 설명한다.

 

#include

헤더 파일과 같은 외부 파일을 읽어서 포함시키고자 할 때 사용된다. 이때의 파일은 이진파일(Binary file)이 아닌 C의 소스파일과 같은 형태의 일반 문서파일을 말한다.

 

    #include <stdio.h>        /* 이 위치에 stdio.h라는 파일을 포함시킨다. */

    #include "text.h"           /* 이 위치에 text.h라는 파일을 포함시킨다. */

 

'<...>'를 사용할 때와 '"..."'를 사용할 때의 차이점은 '<...>'는 컴파일러의 표준 포함 파일 디렉토리(또는 사용자가 별도로 지정해 준)에서 파일을 찾는 것을 기본으로 한다.  그리고 "..."'를 사용했을 때는 현재의 디렉토리를 기본으로 파일을 찾게 된다. 아예 디렉토리를 같이 지정할 수도 있다.

 

    #include <C:\MYDIR\MYHEAD.H>

    #include "C:\MYDIR\MYHEAD.H"

 

#define

상수값을 지정하기 위한 예약어로 매크로라고 부른다. 구문의 상수로 치환한다.

또한 #define은 함수 역활과 비슷하게 아래와 같이 쓰일 수 있다.

#define SUM(x) ((x) = (x) + (x))

동작원리는 함수와 같다. 말 그대로 main소스에서 SUM을 호출하면 옆에 있는 더하기 코드가 치환되는 것이다.

 

#define으로 정의할 수 있는 것은 숫자만이 아니다.

#define MYNAME "Young Hee"

printf(MYNAME);

라고 한다면 이는 전처리기에 의해

printf("Young Hee");와 같이 전개되는 것이다.

 

이렇게 #define으로 정의된 것은 일반적인 변수와는 다르다. 그 차이는 명백한데, 예를 들어서

#define MYNAME "Turbo"

char my_name[] = "Turbo"

의 두가지 경우가 있다고 하자. "MYNAME"은 전처리문으로 "my_name"은 문자형 배열 변수로 정의되었다.

printf(MYNAME);

printf(MYNAME);

printf(my_name);

printf(my_name);

이것을 전처리한 상태는 다음과 같이 될 것이다.

printf("Turbo");

printf("Turbo");

printf(my_name);

printf(my_name);

이런 결과에서 우리가 유추해 볼 수 있는 것은 전처리 명령을 사용했을 경우 "Turbo"라는 동일한 동작에 대해서 두개의 똑같은 문자열이 사용됐고, 변수를 사용했을 경우에는 하나의 문자열을 가지고 두번을 사용하고 있다는 것이다. 결과적으로 이런 경우에는 전처리문을 사용했을 경우 메모리 낭비를 가져 온다는 것을 알 수 있다.

 

#undef

#define으로 이미 정의된 매크로를 무효화한다.

#define ADD(a, b) (a + b)

#undef ADD(a, b)

라고 하면 앞으로 사용되는 ADD(...)는 'undefined symbol'이 되어 에러 처리 된다.

 

#if ~ #endif

#if 구문은 if랑 아주 비슷하다. 이것은 어떠한 구문을 컴파일 할지 안할지를 지정할 수 있다.
#define A 1
#if A
    source code.....
#endif
위 source code 부분은 컴파일이 된다. if문에서와 같이 참, 거짓을 구분하여 컴파일이 된다. 위에서 A값은 1 즉 0보다 큰 수이기 때문에 참인 것이다.

직접 아래와 같이 하면 거짓이기 때문에 source code 부분은 컴파일이 되지 않는다.

#if 0

    source code....

#endif

 

#ifdef ~ #endif
컴파일 할 때

#define MYDEF               /* MYDEF는 값은 가지지 않았지만 어쨋든 정의는 되었다 */

#ifdef YOURDEF              /* 만약 YOURDEF가 정의되어 있다면... */

    #define BASE 10         /* BASE == 10 */

#elif MYDEF                 /* 그외에 MYDEF가 정의되었다면... */

    #define BASE 2       /* BASE == 2 */

#endif
와 같이 쓰면 BASE는 상수 2로 치환되어 
전처리기가 컴파일러에게 넘겨준다.

#ifndef 헤더명_H__ ~ #endif

헤더파일이 겹치는 것을 막기 위한 일종의 매크로이다. 예를 들어 헤더파일에 어떤 클래스의 인터페이스 선언을 넣었다고 하자. 이 클래스 인터페이스에서 다른 파일의 프로토타입이 필요해서 다른 A 파일을 include 하고 있는데 이 헤더 파일을 include 하는 파일에서 A라는 헤더 파일을 이미 include 하고 있다면 두번 define한 것이 된다. 그러면 SYNTEX 에러가 난다. 그래서 그런 것을 막는 방법의 하나로 #ifndef을 사용한다. 이전에 include되어 있으면 #endif쪽으로 점프해버려 결국 한번 선언되는 것이다.

#include  <stdio.h>    ------ (a)

#include  <stdio.h>    ------ (b)
이러허게 두번 썼다고 하자. 그런데 앞에 이미 include를 했는데 밑에 또 한다면 문제가 된다. 컴파일러가 검사해야할 코드량도 많아진다. 그래서 stdio.h에는

#ifndef STDIO_H__

#define STDIO_H__

가 선언되어 있다. 만약 STDIO_H가 선언되어 있지 않다면 선언한다는 뜻이다. 그 뒤 (b)에서는 이미 (a)쪽에서 STDIO_H__ 을 선언한 상태이기 때문에 전처리기 쪽에서 무시해버린다. 그러므로 컴파일러는 (a)만 검사한다.

 

#defined

define이 여러 개 되어 있는지를 검사할 때 쓴다. 이것은 여러 개를 동시에 검사 할 수 있다.

#if #defined A || #defined B

 

#ifdef와 #if defined의 차이

#ifdef는 정의가 되어 있는지를 테스트 하기 때문에, 한번에 여러 개를 사용할 수 없다. 형식이

#ifdef name

처럼, 하나의 토큰이 되어야 하기 때문이다.

 

여러 개가 정의되어 있는지를 테스트 하기 위해서는

#if defined(MACRO1) || defined(MACRO2)

처럼 사용한다.

#if는 ||로 중첩해서 사용할 수 있다. 형식이, #if expression이므로, C 표현이 올 수 있다.

#if MACRO1 || MACRO2

처럼 사용해도 된다.

 

#error

소스 라인에 직접 에러 메세지를 출력한다. 전처리기가 #error 문을 만나면 그 즉시 컴파일을 중단하고 다음과 같은 에러 메시지를 출력한다.

ERROR : XXXXX.c ########: Error directive: 내용

    - XXXXX.c --> 현재 컴파일 중인 파일명

    - ####### --> 전처리기가 #error 문을 만난 시점에서의 행 번호(헤더 포함)

 

#ifdef __LARGE__

#error This program must be compiled in LARGE memory model!

#endif

이 내용은 만일 프로그램이 LARGE 모델이 아니라면 "#error" 뒤에 표시된 메세지를 출력하고 컴파일을 중지하게 된다.

 

#line

이 명령은 소스 코드의 행번호를 지정하기 위한 것으로 주로 컴파일러에 의해 미리 정의된 __LINE__과 함께 사용된다. C에서는 베이식과 같은 행번호를 사용하지 않는다. 하지만 디버깅을 하다 보면 "행번호를 알 수 있으면 좋을텐데"하는 경우가 있다. 예를 들자면 어떤 표현들이 있고, 그 표현들 중의 어딘가에 잘못된 부분이 있는 것 같은데 정확히 그 표현이 있는 행을 찾지 못한다면 "#line"을 사용함으로써 그 일을 보다 수월하게 할 수 있게 된다.

__LINE__과 __FILE__을 각각 행번호와 파일명으로 변경한다.

#include <stdio.h>

 

#define DEBUG

 

void main(void)

{

    int count = 100;

 

    #line 100               /* 다음 줄번호를 100으로 설정한다 */

                               /* <-- 이 줄의 번호가 100이다 */

    #ifdef DEBUG        /* <-- 이 줄의 번호가 101이다 */

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

 

    count = count * count - 56;

    #ifdef DEBUG

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

 

    count = count / 2 + 48;

    #ifdef DEBUG

    printf("line:%d, count = %d\n", __LINE__, count);

    #endif

}

#pragma

컴파일 옵션의 지정. 컴파일러 작성자에 의해서 정의된 다양한 명령을 컴파일러에게 제공하기 위해서 사용되는 지시어이다. 컴파일러의 여러가지 옵션을 명령행 상에서가 아닌 코드상에서 직접 설정한다.  #pragma는 함수의 바로 앞에 오며, 그 함수에만 영향을 준다. Turbo C는 9개의 #pragma문(warn, inline, saveregs, exit, argsused, hdrfile, hdrstop, option, startup)을 지원하고 있다.

 

#pragma inline : 컴파일할 때 어셈블러를 통해서 하도록 지시한다.즉, 인라인 어셈블리 코드가 프로그램에 있음을 알려준다. (명령행 상에서 '-B' 옵션)

#pragma saveregs : 이 홉션은 휴즈 메모리 모델에 대해 컴파일된 함수에게 모든 레지스터를 저장하도록 한다.

#pragma warn : 이 지시어는 Turbo C에게 경고 메시지 옵션을 무시하도록 한다.

 

#pragma warn -par

이는 매개 변수(parAMETER)가 사용되지 않았다고 경고(warnING)를 내지 못하도록 한다. 이와 반대되는 표현은

#pragma warn +par

이다.

경고의 내용 앞에 (+)를 사용하면 경고를 낼 수 있도록 설정하고, (-)를 사용하면 경고를 내지 못하도록 하는 것은 모든 경고에 대해 동일하다. 명령행에서는 "-wxxx"로 경고를 설정하고 "-w-xxx"로 경고를 해제한다. 경고의 종류는 무척 많은데 자주 사용되는 것을 아래에 나타냈다. 모든 것을 알고 싶다면 컴파일러 User's Guide의 명령행 컴파일러 부분을 참고하시기 바란다.

 

par : 전해진 파라미터가 사용되지 않음

rvl : void 형이 아닌 함수에서 리턴값이 없음

aus : 변수에 값을 할당했으나 사용하지 않았음

voi : void 형 함수에서 리턴값이 사용되었음

sig : 부호비트(MSB)가 고려되지 않은 형변환(type-castion)에서 부호 비트를 소실할 수 있음

 

C의 predefined macro

__FILE__a string that holds the path/name of the compiled file
__LINE__an integer that holds the number of the current line number
__DATE__a string that holds the current system date
__TIME__a string that holds the current system time
__STDC__defined as the value '1' if the compiler conforms with the ANSI C standard
__cplusplusdetermines if your compiler is in C or C++ mode. Usually used in headers

 

예)

#include <stdio.h>  

void main(void)  
{  
    printf("The path/name of this file is %s\n", __FILE__);  
    printf("The current line is %d\n", __LINE__);  
    printf("The current system date is %s\n", __DATE__);  
    printf("The current system time is %s\n", __TIME__);  

    #ifdef __STDC__  
        printf("The compiler conforms with the ANSI C standard\n");  
    #else  
        printf("The compiler doesn't conform with the ANSI C standard\n");  
    #endif  
    #ifdef __cplusplus  
        printf("The compiler is working with C++\n");  
    #else  
        printf("The compiler is working with C\n");  
    #endif  

 

  프로그래머들 마다 코딩 스타일(암시적 약속)이 있다. 보통 매크로, const 변수는 대문자로 적는 것이 원칙이다. 매크로 함수와 일반함수, 매크로 대상체(object-like macro)와 일반 변수를 구분하기 쉽게 해주는 것이기 때문이다.

#define STDIO_H_

왜 뒤에 _를 붙였을까? 이것도 하나의 암시적 약속이다. 컴파일러 제작회사는 매크로를 정의할 때 사용자들과 이름이 충돌이 나지 않게 하기 위해서 대부분 _를 뒤어 덧붙인다. 또한 _를 하나 혹은 두 개 연속으로 시작하는 것은 컴파일러 내부에서 사용하는 매크로라는 성격이 강하다. 물론 강제적인 뜻은 없으며 단지 관습상 그렇다. 왜 이것이 관습이 되었나 하면 보통 매크로 변수 이름이나 함수 이름을 지을 때 뒤에 _를 붙이지 않기 때문이다. 그래서 함수 제작자들이 _를 단골로 붙였다.


반응형

출처 : http://codens.info/678

쓰레드 생성

- AfxBeginThread 권장 

- CWinThread::m_bAutoDelete 

- 기본은 TRUE, 자동으로 개체(핸들포함)가 삭제됨 <- 권장

- FALSE로 설정하려면 CREATE_SUSPENED 로 시작해서 설정후 ResumeThread()해줘야함

- 해제시 핸들을 닫으면 안되고 CWinThread개체를 delete 해야 함


- AfxEndThread()는 강제 종료 함수로 권장 안함


- CreateThread() 권장 안함



* 쓰레드 종료 대기

    - WaitForSingleObject 사용 권장


    - GetExitCodeThread 는 bAutoDelete = TRUE시 문제 있음, 

- CreateThread()때만 사용, 하지만 권장 안함



//=======================================
//AfxBeginThread

//시작
CWinThread * CSimpleTestDlg::MFC_TrdStart( bool _bAutoDel)
{
    char *str = "MFC_TrdStart"; g_pstr[0]=str;m_pstr[0] = str;
    //쓰레드 생성
    m_bStop=false;
    CWinThread *pWThd=0;
    //
    if( _bAutoDel == false){
        _DbgStr(_T("MFC_TrdStart- 자동삭제=false"));
        //값을 바꾸기위해서는 멈춤->설정->시작
        pWThd = AfxBeginThread( MFC_TrdFunc, this, 0, 0, CREATE_SUSPENDED  );//멈춤    
        pWThd->m_bAutoDelete = FALSE; //기본값은 TRUE, 설정
        pWThd->ResumeThread();//시작
    }else{
        _DbgStr(_T("MFC_TrdStart- 자동삭제=true"));
        pWThd = AfxBeginThread( MFC_TrdFunc, this, 0, 0, 0  );//<- 권장
    }

    if( !pWThd )
        return 0;

    m_hThread = pWThd->m_hThread;
    
    _DbgStr(_T("MFC_TrdStart- 끝=%X"), m_hThread);
    return pWThd;
}

//쓰레드 트리거 함수
UINT __cdecl CSimpleTestDlg::MFC_TrdFunc( LPVOID pParam )
{
    char *str = "MFC_TrdFunc";g_pstr[1]=str;
    CSimpleTestDlg *pClass = (CSimpleTestDlg *)pParam;
    pClass->MFC_TrdProc();
    _DbgStr(_T("쓰레드 종료 MFC_TrdFunc"));//MFC_TrdProc()에 AfxEndThread()가 있으면 여기까지 실행이 안됨
    return 1;
}

//실제 쓰레드 동작
int CSimpleTestDlg::MFC_TrdProc()
{
    char *str = "MFC_TrdProc";g_pstr[2]=str; m_pstr[2] = str;
    while(1)
    {
        if( m_bStop ){
            _DbgStr(_T("쓰레드 종료신호 MFC_TrdProc"));
            break;
        }

        Sleep(1000);
        _DbgStr(_T("쓰레드 실행중 MFC_TrdProc"));
    }

    _DbgStr(_T("쓰레드 종료 MFC_TrdProc-1-%d"), (UINT)GetTime() );
    //AfxEndThread(0);//쓰레드가 강제 종료됨, 사용하면 안됨 TerminateThread()와 같음
    _DbgStr(_T("쓰레드 종료 MFC_TrdProc-2-%d"), (UINT)GetTime() );//
    
    return 1;
}


//===============================================
//쓰레드 종료를 기다린다
//WaitForSingleObject 사용 권장


// CreadThread()로 시작한 쓰레드만 대기, AfxBeginThread() 로 시작한 쓰레드는 사용하면 안됨
int WaitThreadEndH(HANDLE _hThread, int _nWaitMilSec ,  LPCTSTR _sMsg, bool _bClose)
{
    if( !_hThread ) return 0;
    _DbgStr(_T("WaitThreadEnd-시작-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    //bFinishThread == TRUE; // 종료를 위한 변수설정 후 3초간 대기
    
    DWORD dwExit=0;
    DWORD dwRetCode = WaitForSingleObject(_hThread, _nWaitMilSec);
    if(dwRetCode == WAIT_OBJECT_0) // 정상종료
    {
        _DbgStr(_T("WaitThreadEnd-정상 종료-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    }
    else if(dwRetCode == WAIT_TIMEOUT) // 타임아웃
    {
        //::TerminateThread(_hThread, 0 );
        _DbgStr(_T("WaitThreadEnd-강제종료-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    }    

    if( _bClose){  
        //AfxBeginThread()로 생성된 쓰레드는 핸들을 닫으면 안된다. CWinThread를 delete 해야한다.
        //m_bAutoDelete == TRUE 일때는 호출하면 안된다
        CloseHandle( _hThread );
    }

    _DbgStr(_T("WaitThreadEnd-끝-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    return 1;
}


//AfxBeginThread() 로 시작한 쓰레드는 이함수로 종료해야 함
int WaitThreadEndW(CWinThread *_pWinThread, int _nWaitMilSec, LPCTSTR _sMsg)
{
    if( !_pWinThread ) return 0;
    HANDLE _hThread = _pWinThread->m_hThread;
    _DbgStr(_T("WaitThreadEnd(W)-시작-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    //bFinishThread == TRUE; // 종료를 위한 변수설정 후 3초간 대기
    bool bAutoDel = _pWinThread->m_bAutoDelete;
    DWORD dwExit=0;
    DWORD dwRetCode = WaitThreadEndH( _pWinThread->m_hThread, _nWaitMilSec, _sMsg, false  );//핸들을 닫으면 안된다.
    
    //m_bAutoDelete = FALSE의 경우 스레드의 삭제
    if(bAutoDel==false)
    {
        GetExitCodeThread(_pWinThread->m_hThread, &dwExit);
        _DbgStr(_T("WaitThreadEnd(W)-쓰레드 클래스 삭제(code=%d)", dwExit));
        
        //if( _bClose) CloseHandle( _hThread );//하면 안됨클래스 해제시 자동 해제 된다.
        
        delete _pWinThread;  
    }    

    _DbgStr(_T("WaitThreadEnd(W)-끝-%s-%X-%d"),_sMsg, _hThread, GetTickCount() );
    return 1;
}
//===================================================================


반응형

DoDataExchange로 MFC 컨트롤과 변수가 서로 연결된 상태에서 


변수에 현재 컨트롤 값을 반영하려면 UpdateData(TRUE), 


변수의 값을 컨트롤에 반영하려면 UpdateData(FALSE) 이다.

반응형




프로젝트 설정 > 구성 속성 > C/C++ > 미리 컴파일된 헤더 > 미리 컴파일된 헤더


에서 미리 컴파일된 헤더를 사용 안함으로 설정

반응형

원래 c와 c++은 string이라는 똑똑한 자료구조형을 compiler차원에서 지원하고 있지 않습니다.


그대신 가장 많이 사용하는 string을 어떻게 저장해야 할지에 대해 고심한 결과...

결국 배열의 끝에 '\0'또는 0 또는 NULL값을 넣어 string을 표현하도록 했습니다.

결국 가장 적은 용량의 string처리와 가장 골치아픈 string처리가 탄생하는 순간이였죠.


어쨌거나 요점은...

Windows에서는 이런 string처리를 위해서 char* 형을 그대로 쓰기 보다는 LPCSTR등의 표현으로 

대치해 사용함으로써, 개발의 편의성을 돕고 있습니다.


* LPSTR, LPCSTR

LP는 long pointer를 나타내는 약어로서 16bit시절의 윈도우의 유산입니다.

과거 windows3.1까지의 시절에는 포인터는 모두 16bit였고, 24bit 메모리를 long pointer라는 것을 통해서 

extended memory라는 이름으로 관리했었거든요..

현재 LP(long pointer)는 .Net에서는 64bit pointer를, VC++6.0과 그 이전 버전에서는 32bit pointer를 나타냅니다.


C는 constant, 즉 함수의 내부에서 인자값을 변경하지 말라는 뜻입니다.


STR은 내부적으로는 char형 배열에 null값 종료를 의미하고 있죠.


LPSTR = long pointer string = char *

LPCSTR = long pointer constant string = const char *

결과적으로는 맨 마지막과 같은 형이라는 거죠.




* LPWSTR, LPCWSTR

W 이넘은 wide char를 나타냅니다. 쉽게 말하면 unicode죠..

한글 조합형 코드도 아니고...unicode를 나타냅니다.


LPWSTR = long pointer wide string = w_char *

LPCWSTR = long pointer constant wide string = const w_char *


//이하 미정리

* LPCTSTR

LPCTSTR = long pointer constant t_string = const tchar *

- t_char('티캐릭터'라고 읽습니다.)


마이크로소프트가 세계 각국에 제품을 판매하면서..각국의 언어에 맞추어 개발하는 것에 환멸을 느끼다가..

드디어 windows를 unicode기반으로 개발하는 작업에 착수했습니다.


그런데... 문제는 char는 1Byte이고 wide char는 2Byte이므로..포인터 연산을 많이하는 c, c++코드는 호환성에 치명적인 문제가 있었죠. 그래서 컴파일러가 precompile option을 보고. 환경에 맞게 동작하는 코드를 작성할 수 있는 새로운 변수 모양의 Macro를 선언하게 되었습니다.

그것이 바로 TCHAR, t_char라는 변수죠.

이놈들은 자신의 운영체제가 

multi-byte환경이면, char형으로, unicode환경이면, w_char, wide char형으로 type casting됩니다.


그래서... 보통 windows 9x, 2000계열의 환경이라면,

LPTSTR = LPSTR = char *

LPCTSTR = LPCSTR = const char *가 됩니다.


그런데..

아마 저 코드에서..

(LPSTR)(LPCTSTR) 형변환을 할때 자세히 보면..

const 라는 키워드만 떼내는거지요...

그러니까 사실은 (char *)(const char *)와 같은 말입니다.

웃기는 형변환이죠..

그럼 없어도 될까요?^^


없으면 당연히 오류가 나게됩니다.

왜냐면...(LPSTR)CString을 하면.... CString형 자료의 맨 처음 주소부터 char * 형으로 형변환하기 때문이죠.

CString형은 앞의 16Byte를 자료형을 표현하기 위해서 사용하기 때문에, 여기서부터 형 변환을 해주면 엉뚱한 값이 표현되게 됩니다.


따라서 MFC에서 지원하는 CString class는 LPCTSTR라는 함수를 통해서 일단 안전하게 const char * 형으로 바뀐 자료형을 얻어오게 하는거죠.


CString myString;

(LPCTSTR)myString;이라고 해주면..

myString내부의 string 값을 꺼내오게 도와주는 연산자 또는 함수를 사용하게 된겁니다.

즉 (LPCTSTR)이란 놈이 반환값이 const char* 인 함수입니다.

정확하게 표현하면 operator overloading이라는 거지요.


결과적으로 (LPSTR)(LPCTSTR)myString은

myString의 내부 string 자료를 함수를 통해 자료를 꺼내온뒤에, char* type으로 안전하게 바꾸어주는 역할을 하게 되는 거지요.


참고로, 함수의 인자가 char * 인곳에 const char* 형을 넣으면 컴파일 오류가 발생하기 때문에 (LPSTR)을 한번더 앞에 써주어서 강제 type casting을 한 것입니다.

+ Recent posts