새로 만든 다이얼로그 박스를 메뉴를 통해 불러내는 기능을 만들었으므로 이제 실제 다이얼로그 박스를 조작하면 통신 설정이 적용되도록 프로그램밍 해보겠습니다.
그런데 한가지 고민해봐야 할 문제가 있습니다. 메뉴를 통해 ComSettingsDialog 를 불러냈을 때 현재 기억된 설정값을 표시해줘야 하며, ComSettingsDialog 다이얼로그 조작을 통해 설정값을 바꾸고 OK 버튼을 눌러 창을 닫았을 때 바뀐 설정값들을 상위 (다이얼로그 생성을 ) 호출한 쪽 클래스로 넘겨줘야 합니다.
이렇게 하려면 이 값들을 기억하고있는 변수를 만들어줘야 하며 ComSettingsDialog 다이얼로그를 호출할 때 이 변수를 넘겨줘야 하고 다이얼로그를 닫았을 때 바뀐 변수를 받아내서 실제 설정을 바꾸는데 참조할 수 있게끔 해야 합니다.
이를 위해 필효한 값들을 포함할 Class 자료형을 정의하고 이 자료형을 변수로 만들어 (인스턴스화) 값이 저장되게 만들겠습니다.
ComSettingsDialog.h 파일 상단에 아래처럼 CComInfo 라는 새로운 클래스를 정의합니다.
class CComInfo
{
public:
UINT32 PortNum; // 포트번호 값이 저장됨
CSerial::EBaudrate Baudrate; // 보레이트 값이 저장됨
CSerial::EDataBits DataBits; // 데이터비트 값이 저장됨
CSerial::EParity Parity; // 패리티비트 값이 저장됨
CSerial::EStopBits StopBits; // 스톱비트 값이 저장됨
CSerial::EHandshake Handshake; // 플로우컨트롤 값이 저장됨
};
|
위 클래스가 통신설정에 필요한 값을 클래스로 정의한 자료형입니다. 클래스명은 CComInfo 라고 이름지었으며 포트번호 및 보레이트 값, 기타 통신설정에 필요한 값들을 멤버변수로 넣어놨습니다.
보레이트, 데이터비트 등 포트번호를 뺀 다른 멤버변수들의 자료형은 Serial.h 파일 안에 CSerial 클래스 내 자료형으로 정의 돼 있습니다. 이 자료형을 차용하여 새로 정의한 CComInfo 의 멤버변수로 선언한 것이니 Serial.h 파일을 인클루드 선언을 해 줘야 하지만 이 파일을 인클루드 하는 SerialMFC.h 파일이 인클루드 선언 돼 있으므로 컴파일에 문제가 없을것입니다.
여러분이 여기에다 더 필요한 값을 추가할 수도 있습니다. ComSettingsDialog 의 다이얼로그 UI 화면에 CTS, DTR 포트들의 On/Off 제어 선택란을 넣고 싶으시면 CComInfo 클래스에도 이에대한 값을 추가시켜주면 될것입니다. On/Off 값이니 각 포트당 BOOL 값을 기억하는 변수를 선언해주면 되겠네요.
이제 이 자료형으로 멤버변수를 만들겠습니다. 이 변수가 이 프로그램에서 시리얼 통신 관련 설정 옵션들을 기억하게 될 것입니다.
도큐먼트 클래스인 CComConstructorDoc 클래스 선언부에 다음과 같이 멤버변수를 추가합니다.
public:
CComInfo m_ComInfo;
|
그리고 CComInfo 자료형을 참조할 수 있게 CComConstructorDoc.h 상위에 아래와 같이 ComSettingsDialog.h 파일을 인클루드 선언해줍니다.
#include "ComSettingsDialog.h";
|
이제 m_ComInfo 라는 이 통신 설정변수는 도큐먼트 클래스에서 관리되므로 도큐먼트가 새로 만들어 질 때 통신 설정값의 기본값이 매겨지도록 코딩해줍니다.
이것은 도큐먼트 클래스의 가상 멤버함수인 OnNewDocument 에서 해줍니다.
BOOL CComConstructorDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
m_ComInfo.nPortNum = 1;
m_ComInfo.eBaudrate = CSerial::EBaud9600;
m_ComInfo.eDataBits = CSerial::EData8;
m_ComInfo.eParity = CSerial::EParNone;
m_ComInfo.eStopBits = CSerial::EStop1;
m_ComInfo.eHandshake = CSerial::EHandshakeOff;
return TRUE;
}
|
이제 이 통신설정 변수는 MFC 프레임 워크의 도큐먼트 클래스에 정의하였으므로 GetActioveDocument() 혹은 GetDocument() 를 호출함으로서 이 프로그램 코드 어디에서든 이 변수를 참조할 수 있습니다.
이로서 MFC 프레임 워크 구조에 입각해 프로그램에서 필요한 주요 변수들을 도큐먼트 클래스에 선언하고 다른 코드영역에서 도큐먼트 클래스를 불러와 변수들을 참조하고 조작하게끔 만들것입니다.
다음 링크는 MFC 프레임워크가 설명된 MSDN 사이트입니다.
https://msdn.microsoft.com/ko-kr/library/k9kb0kba.aspx
이전 포스트 내용에서는 접속 시 포트번호와 보레이트 저장 변수를 CMainFrame 에 두었었는데 이젠 필요 없으므로 지우고 초기값 대입 코드도 지웁니다.
CMainFrame::OnCreate 멤버함수를 찾아가서 아래의 코드를 지우거나 #if 0 문으로 가둬서 비활성화 시킵니다.
그렇게 하면 OnCreate 함수를 클래스 위저드로 처음 생성했을 때의 동작과 같아지므로 아예 이 함수를 제거해도 됩니다.
#if 0
if(CSerial::CheckPort(_T("COM1")) == CSerial::EPortAvailable)
m_nPort = 1;
else if(CSerial::CheckPort(_T("COM2")) == CSerial::EPortAvailable)
m_nPort = 2;
else if(CSerial::CheckPort(_T("COM3")) == CSerial::EPortAvailable)
m_nPort = 3;
else if(CSerial::CheckPort(_T("COM4")) == CSerial::EPortAvailable)
m_nPort = 4;
else
m_nPort = 1;
m_eBaudrate = CSerial::EBaud9600; // 9600 bits/sec
#endif
|
그리고 이전에 코딩했었던 메뉴의 접속 기능이 실제 실행되는 부분인 CMainFrame::OnConnect 함수에서 위 도큐먼트 클래스에 만들어준 변수값을 참조하여 접속하게끔 코드를 바꿔줍니다.
void CMainFrame::OnConnect()
{
CString szPort;
CComConstructorDoc* pDoc = (CComConstructorDoc*)GetActiveDocument();
ASSERT(pDoc);
#if 1
szPort.Format("COM%d", pDoc->m_ComInfo.nPortNum);
if (m_Serial.Open(szPort,this) != ERROR_SUCCESS)
{
szPort += _T(" 포트를 열 수 없습니다.");
AfxMessageBox((LPCTSTR)szPort,MB_ICONSTOP|MB_OK);
//GetParent()->PostMessage(WM_CLOSE);
return;
}
m_Serial.Setup(pDoc->m_ComInfo.eBaudrate, pDoc->m_ComInfo.eDataBits, pDoc->m_ComInfo.eParity, pDoc->m_ComInfo.eStopBits);
m_Serial.SetupHandshaking(CSerial::EHandshakeOff);
//m_Serial.SetEventChar(EVENT_CHAR);
#else
szPort.Format("COM%d", m_nPort);
if (m_Serial.Open(szPort,this) != ERROR_SUCCESS)
{
szPort += _T(" 포트를 열 수 없습니다.");
AfxMessageBox((LPCTSTR)szPort,MB_ICONSTOP|MB_OK);
GetParent()->PostMessage(WM_CLOSE);
return;
}
m_Serial.Setup(m_eBaudrate, CSerial::EData8, CSerial::EParNone, CSerial::EStop1);
m_Serial.SetupHandshaking(CSerial::EHandshakeOff);
//m_Serial.SetEventChar(EVENT_CHAR);
#endif
}
|
위 작업한 파일 MainFrm.cpp 상단에 아래와 같이 도큐먼트 클래스를 사용할 수 있게끔 도큐먼트의 헤더파일을 인클루드 선언해주세요.
#include "ComConstructorDoc.h";
|
소스를 보면 CMainFrame 에서 정의해 사용하였던 포트번호와 보레이트 저장 변수를 이용하는 대신 도큐먼트 클래스의 m_ComInfo 변수를 참조하게끔 고쳤습니다.
#if 1 의 안쪽이 새로 바뀐 내용이고 #else 문 다음이 이전 남아있던 코드입니다. 코드작업시 한정된 내용을 수정할 때 기능적으로 다르게 대체할 경우 많이 사용하는 방법입니다.
#if 문을 안쓰고 이전코드를 지워줘도 됩니다만 이전 코딩했던 작업들이 기억할만 하다고 판단하여 이렇게 해놓으면 나중에 코드를 찾아볼 때 어떻게 작업내용이 바뀌게 됐는지 알아볼 수 있는것이 장점입니다만 너무 남용하면 코드가 난잡해지므로 자신이 알아볼 수 있을 정도만 사용해야 겠네요.
다시 코드를 보면 GetActiveDocument() 라는 함수를 호출하였는데 이 함수가 현재 사용되는 도큐먼트 인스턴스를 메인프레임 클래스에서 참조하고 싶을 때 사용하는 함수입니다.
도큐먼트의 포인터를 가져왔으니 위에서 도큐먼트에 추가해줬던 m_ComInfo 변수의 접근과 조작이 가능해집니다.
메인프레임이 아닌 다른 클래스 코드영역에서 도큐먼트를 가져오려면 어떻게 하면 될까요? 메인프레임의 인스턴스를 가져온 다음 도큐먼트 인스턴스를 가져오면 되겠네요. 이것은 통신설정 다이얼로그에 대한 코딩을 할 때 다룰 것입니다. 통신설정 다이얼로그 안에서도 m_ComInfo 변수를 참조하여 표시하려면 도큐먼트 클래스를 접근해야 하니까요.
아래는 MFC 프레임워크에서의 클래스인 메인프레임, 도큐먼트 그리고 뷰 간 각 서로의 인스턴스를 참조할 때의 API 함수를 적어보았습니다. MFC 를 하게되면 굉장히 많이 사용 하게되니까요.
MFC 프레임워크 클래스간 인스턴스 참조법
SDI 형태
– CMainFrame *pFrame = (CmainFrame *) AfxGetMainWnd();
– CTestApp *pApp = (CtestApp *) AfxGetApp();
– CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd(); CTestDoc *pDoc = (CTestDoc *)pFrame->GetActiveDocument();
– CTestDoc *pDoc = ((CMainFrame *)AfxGetMainWnd())->GetActiveDocument();
– CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd(); CTestView *pView = (CTestView *)pFrame->GetActiveView();
– CTestView *pView = ((CMainFrame *)AfxGetMainWnd())->GetActiveView();
MDI 형태
– CMainFrame *pFrame = (CMainFrame *)AfxGetMainWnd(); CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame();
– CChildFrame *pChild = ((CMainFrame *)AfxGetMainWnd())->GetActiveFrame();
– CMainFrame *pFrame = (CMainFrame)AfxGetMainWnd(); CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame(); CMdiTestDoc *pDoc = (CMdiTestDoc *)pChild->GetActiveDocument();
– CMdiTestDoc *pDoc = (((CMainFrame *)AfxGetMainWnd())->GetActiveFrame())->GetActiveDocument();
– CCainFrame *pFrame = (CMainFrame)AfxGetMainWnd(); CChildFrame *pChild = (CChildFrame *)pFrame->GetActiveFrame(); CMdiTestView *pView = (CMdiTestDoc *)pChild->GetActiveView();
– CMdiTestView *pView = (((CMainFrame *)AfxGetMainWnd())->GetActiveFrame())->GetActiveView(); |
여기까지 작업한 것 만으로도 메뉴의 [접속]과 [접속해제] 기능이 잘 동작할 것입니다. 대신 m_ComInfo 변수의 초기값이 적용되니 현재로는 COM 포트 1만 사용할 수 있고 바꿀 수는 없는 상태입니다.
Leave a Reply
로그인을 해야 댓글을 남길 수 있습니다.