<데이터 형변환>

현재 프로그램 상에서 에디터 창으로 입력받은 아스키 데이터 20바이트를 헥사값 10바이트로 바꿔서 저장한 후 전송하려 합니다. 예를 들면 '3', '2' (ASCII - 2 byte)를 입력받아 헥사값 '32'로 저장하려는 것이지요(HEXA - 1 byte).

'3' '2' 'A' '2' '1' ....'9' '9' '0' (화면상에서 20자리 데이터 입력)
'32' 'A2' 1 .... '90' (실제적으로 저장되는 값은 10자리 헥사값의 데이터)

10자리로 정의된 헥사값은 다시 문자열 버퍼에 저장되서 전송돼야 하는데 간단한 문제를 제가 너무 어렵게 생각하고 있는지도 모르겠습니다. 방법을 알려주세요.

다음은 다이얼로그의 에디트 컨트롤에서 헥사값을 입력받아 RS232 포트로 날리는 내용의 코드입니다.

헥사값 입력받기

다이얼로그의 에디트 박스 컨트롤에서 특정한 문자만 입력받게 합니다.

[1] 다이얼로그 리소스에서 에디트 박스를 하나 만듭니다.

[2] 클래스 위저드에서 에디트 박스 컨트롤 변수를 하나 설정합니다.

[3] 클래스 위저드에서 에디트 박스 컨트롤의 EN_CHANGE 함수를 만들고 다음과 같이 코딩합니다.

void CMyDlg::OnChangeEditAddress()
{
     // send this notification unless you override the CDialog::OnInitDialog()
     // function to send the EM_SETEVENTMASK message to the control
     // with the ENM_CHANGE flag ORed into the lParam mask.

     int i, j, len, nbytes, pos1, pos2;
     char *line, *newline;

     m_EBCaddress.GetSel(pos1, pos2);
     len = m_EBCaddress.GetWindowTextLength();
     line = new char[len+1];
     newline = new char[len+1];
     nbytes = m_EBCaddress.GetWindowText(line, len+1);

     // 아래부분은 헥사값만 체크하는데
     // 필요할 경우 조건문을 바꾼다.
     for(i = j = 0; i <= nbytes; i++) {     // null 포함
          if(isxdigit(line[i]) :: line[i] == NULL)
               newline[j++] = line[i];
     }

     if(strcmp(line, newline) != 0) {
          m_EBCaddress.SetWindowText(newline);     // 여기서 재귀호출
          if(pos1 - (i - j) >= 0) pos1 = pos1 - (i - j);
          if(pos2 - (i - j) >= 0) pos2 = pos2 - (i - j);
          m_EBCaddress.SetSel(pos1, pos2);     // hh1rr
     }

     delete line;
     delete newline;
}

헥사 데이터 날리기

 void CSerialDlg::OnButtonSendHexa()
{
     int i, n, c;
     CString string, temp;

     UpdateData(TRUE);

     // 헥사 고르기
     n = m_EDsendHexa.GetLength();
     if(n == 0)
          return;
     
     for(i=0; i<n; i++) {
          c = m_EDsendHexa.GetAt(i);
          if(isxdigit(c)) {
               temp.Format("%c", c);
               string += temp;
          }
     }

     m_EDsendHexa = string;
     m_EDsendHexa.MakeUpper();
     UpdateData(FALSE);

     // sendbuf
     BYTE sendbuf[MAX_SEND_SIZE];
     int nSize;

     string = m_EDsendHexa;
     // 2 char 가 하나의 hexa 값으로 날아감
     nSize = string.GetLength()/2;
     for(i=0; i<nSize; i++) {
          temp = string.Left(2);
          string = string.Right(string.GetLength()-2);
          
          sscanf(temp, "%x", &c);
          sendbuf[i] = c;
     }

     if(!m_COM.Write(sendbuf, nSize)) {
          temp.Format("%s Write Error !!", m_port);
          AddListBox(&m_LBCstatus, temp);
          return;
     }
}

<VC++에서 시리얼 포트로 데이터 비트 발생>

 시리얼 포트로 데이터 비트를 발생시켜야 하는데 어떻게 해야 하는지 모르겠습니다. 비주얼 C++ 프로그램은 처음이라 전혀 감이 안옵니다. 특정 프로토콜이 적용되는 부분으로 PC에서 특정품(52비트)의 비트를 시리얼 방식으로 순차적으로 발생시켜 RS-232로 보내줘야 합니다. 그 다음으로 다른 컨버터에 의해 데이터 변환이 이뤄지도록 하려 합니다. RS-232 비트 단위로 시리얼 데이터를 발생시켜주는 법을 알려주세요.

 다음과 같은 순서로 하면 됩니다.

[1] 사용할 시리얼 포트를 오픈해줘야 합니다. 이는 CreateFile() API를 사용하면 되며, 파라미터에 관한 설명은 MSDN을 참조하세요.

[2] 통신 환경 설정을 해줘야 합니다. DCB struct에 사용될 환경들(baudrate, data bit..) 등을 설정해주고 SelfCommState() API를 사용해 이를 셋팅합니다.

[3] SetCommMask() API를 사용해 Input 버퍼에서 감지할 이벤트들을 설정합니다. 그 SetupComm() API를 사용해 Input/Output 버퍼를 설정하고, PurgeComm() API로 이를 초기화합니다(이 정도면 통신상 필요한 환경 설정은 대강 갖추었다고 볼 수 있습니다).

[4] 이제 해야 할 일은 데이터를 발생시키는 것입니다. 이는 비교적 간단합니다. WriteFile() API를 사용하면 원격 시스템(Remote System)으로 데이터가 전송됩니다. 위의 함수 파라미터 중 첫째 인자가 처음에 사용했던 CreateFile의 리턴값(Return Value)으로 이는 열린 포트에 대한 핸들러인데, WriteFile()은 이를 가지고 해당 포트로 데이터를 전송합니다.
 나머지 파라미터들은 전송할 데이터, 데이터 길이 따위들인데 비교적 간단합니다. 마지막 인자가 Overlapped struct pointer로 이것은 CreateFile()로 포트를 오픈했을 때 이를 Overlapped로 Create했으면 포트에 대한 Read/Write 시 발생하는 이벤트들을 Overlapped가 관장하게 되는데 그때 사용하는 Overlapped I/O struct pointer를 말합니다. 하지만 Overlapped로 Create하지 않았으면 아무 생각없이 NULL을 사용하면 됩니다.

 

<CtrlList에서 컬럼 고정시키기>

 CtrlList에서 컬럼을 고정시키려고 하는데 codeguru에 나와 있는 대로 해 보았지만 잘 안됩니다. 방법을 알려주세요.

ListCtrl은 컬럼 크기를 바꾸기 전에 자신의 부모 윈도우에게 통지(notification) 메시지를 보냅니다. 그래서 우리는 이 통지 메시지를 먼저 처리할 수 있는 메시지 핸들러를 만들면 되지요. 이 통지 메시지를 처리하기 위해서는 OnNotify() 함수를 오버라이드하면 됩니다.

BOOL CTestListCtrl::OnNotify(WPARAM wParam, LPARAM lParam,
     LRESULT* pResult)
{
     switch (((NMHDR *)lParam->code) {
     case HDN_BEGINTRACKW :        // 마우스 트래킹시 발생하는 통보 메시지
     case HDN_BEGINTRACKA :         //
          *pResult = TRUE;                  // 컬럼 크기 조절 방지
          return TRUE;                        // 메시지 처리못하게 바로 리턴
     }

     return CTestListCtrl::OnNotify(wParam, lParam, pResult);
}

 이렇게 하면 컬럼 크기 조정을 방지할 수 있습니다. 윈도우 프로그래밍은 대부분의 구조가 메시지 전달방식 구조이기 때문에 모든 컨트롤의 메시지들을 잘 파악하고 있으면 프로그래머 마음대로 이벤트들을 조절할 수 있습니다. MFC를 사용하면 이 메시지들의 흐름을 잘 파악하기 힘듭니다. 왜냐하면이 메시지에 응답하는 콜백함수들이 캡슐화되어 있기 때문이죠. 그래서 API를 공부하면 이 메시지의 흐름을 파악하기 쉽습니다. 어차피 MFC도 API를 객체지향적으로 포장한 클래스이므로 내부적으로는 API 함수와 동일합니다.

 

<투명한 뷰 만들기>

 SDI에서 투명한 뷰를 만들어 보려고 하는데 잘 안됩니다. 방법을 알려주세요.

창을 투명하게 하기 위해서는

BOOL CTestView::PreCreateWindow(CREATESTRUCT& cs)
{
     cs.dwExStyle |= WS_EX_TRANSPARENT;
     . . .
}

위와 같이 윈도우의 확장스타일을 추가하고

BOOL CTestView::OnEraseBkgnd(CDC* pCD)
{
     return TRUE;
}

이렇게 변경하면 실제로 투명한 창이 구현됩니다.

 

- the end of this article -