. 초기 설정

당근은 실행파일 혼자서 실행되는 단독 실행파일이며 일체의 부속파일이 없다. 그래서 관리하기는 편리한 이점이 있지만 당근이 실행되기 위한 환경을 만들어주는 설치 프로그램이 따로 없어 스스로 환경을 만든 후 실행해야 한다. 현재 상황에서 당근의 실행 환경을 초기화하는 코드는 SOption::Init 함수이며 이 함수에서 초기화한 Option 구조체의 내용대로 동작한다.

그런데 당근의 옵션 중 bExplorerPopup 옵션은 다른 옵션과는 달리 시스템 구성에 변경을 가한다는 면에서 좀 특수하게 초기화할 필요가 있다. 도구/기본 설정 항목에서 이 값을 자유롭게 변경할 수 있지만 이 값의 디폴트가 TRUE이며 OnCreate에서 이 옵션이 선택되어 있으면 RegisterPopup을 호출하므로 최초 실행시 이 옵션이 무조건 적용되어 버린다. 시스템의 구성에 영향을 주는 옵션임에도 불구하고 사용자의 명시적인 동의를 받지 않았기 때문에 문제가 될 수 있다.

이 옵션의 디폴트를 FALSE로 초기화하고 원할 경우만 설정 대화상자에서 이 옵션을 선택하도록 할 수도 있지만 이렇게 되면 사용자들이 이 기능의 존재 여부를 미처 알지 못할 확률이 높다. 그래서 디폴트는 TRUE로 유지하되 최초 실행시 사용자에게 이 기능의 사용 여부를 선택할 수 있는 기회를 제공하기로 했다. 결국 설치 프로그램이 해야 할 일을 자신이 스스로 하게 되는 셈이다. 최초 실행시 다음 대화상자를 보여준다.

이 대화상자는 초기 설치 옵션을 입력받는 목적 외에도 처음 사용자를 위한 친절한(?) 환영 메시지와 함께 쇼트컷, 아이콘 등을 생성하는 역할을 한다. 최초 실행시 한 번만 나타나므로 사용자를 귀찮게 하지는 않는다. 가운데 에디트에는 처음 사용자를 위한 간단한 안내문을 보여줄 것이며 아래쪽의 세 옵션이 설치 옵션이다. 다음 함수들은 이 설치 동작을 위한 유틸리티 함수들이며 Util.cpp 파일에 작성한다.

 

HRESULT MyCreateShortCut(LPCSTR szSrcFile, LPSTR szLnkFile, LPSTR szArgument, LPSTR szDesc)

{

     HRESULT hr;

     IShellLink *pSl;

     IPersistFile *pPf;

     WCHAR wszLnkFile[MAX_PATH]={0,};

 

     hr=CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,

          IID_IShellLink, (void **)&pSl);

     if (FAILED(hr))

          return E_FAIL;

 

     pSl->SetPath(szSrcFile);

     pSl->SetDescription(szDesc);

     pSl->SetArguments(szArgument);

 

     hr=pSl->QueryInterface(IID_IPersistFile, (void **)&pPf);

     if (FAILED(hr)) {

          pSl->Release();

          return E_FAIL;

     }

 

     MultiByteToWideChar(CP_ACP, 0, szLnkFile, -1, wszLnkFile, MAX_PATH);

     hr=pPf->Save(wszLnkFile, TRUE);

 

     pPf->Release();

     pSl->Release();

     return hr;

}

 

void MyGetSpecialFolderPath(HWND hWnd, int nFolder, TCHAR *szPath)

{

     LPITEMIDLIST pidl;

     LPMALLOC pMalloc;

 

     SHGetSpecialFolderLocation(hWnd,nFolder,&pidl);

     SHGetPathFromIDList(pidl, szPath);

     SHGetMalloc(&pMalloc);

     pMalloc->Free(pidl);

     pMalloc->Release();

}

 

COM 객체를 사용하기 때문에 다소 어려워 보일 수도 있는데 이 함수들에 대해서는 API 정복 37장을 참고하기 바란다. 단순히 사용하기만 한다면 이 함수의 내용은 당장 몰라도 상관없다. 설치 대화상자의 프로시저는 다음과 같이 작성한다.

 

BOOL CALLBACK SetupDlgProc(HWND hDlg,UINT iMessage,WPARAM wParam,LPARAM lParam)

{

     TCHAR szPath[MAX_PATH];

     TCHAR szLink[MAX_PATH];

 

     switch(iMessage)

     {

     case WM_INITDIALOG:

          SetDlgItemText(hDlg,IDC_IS_MESSAGE,"당근은 텍스트를 보고 편집할 수"

              " 있는 텍스트 편집 프로그램입니다.\r\n\r\n"

              "처음 실행하셨으므로 다음 초기 설정을 선택해 주십시오.이 대화상자는 최초 "

              "실행시 한 번만 나타나며 이후부터 도구 메뉴의 기본설정 명령으로 모든 옵션을"

              " 변경할 수 있습니다. 기본으로 주어진 옵션이 가장 무난합니다.\r\n\r\n"

              "감사합니다.");

          CheckDlgButton(hDlg,IDC_IS_POPUP,BST_CHECKED);

          return TRUE;

     case WM_COMMAND:

          switch (LOWORD(wParam))

          {

          case IDOK:

          case IDCANCEL:

              if (IsDlgButtonChecked(hDlg,IDC_IS_POPUP)==BST_CHECKED) {

                   RegisterPopup(TRUE);

              }

 

              if (IsDlgButtonChecked(hDlg,IDC_IS_MAKEICON)==BST_CHECKED) {

                   MyGetSpecialFolderPath(hDlg, CSIDL_DESKTOP, szLink);

                   lstrcat(szLink, "\\당근.lnk");

                   GetModuleFileName(g_hInst,szPath,MAX_PATH);

                   MyCreateShortCut(szPath, szLink, "", "당근");

              }

 

              if (IsDlgButtonChecked(hDlg,IDC_IS_REGMENU)==BST_CHECKED) {

                   MyGetSpecialFolderPath(hDlg, CSIDL_PROGRAMS, szPath);

                   lstrcat(szPath, "\\당근");

                   CreateDirectory(szPath,NULL);

 

                   lstrcpy(szLink, szPath);

                   lstrcat(szLink, "\\당근.lnk");

                   GetModuleFileName(g_hInst,szPath,MAX_PATH);

                   MyCreateShortCut(szPath, szLink, "", "당근");

              }

              EndDialog(hDlg,0);

              return TRUE;

          }

          return FALSE;

     }

     return FALSE;

}

 

WM_INITDIALOG에서 안내 메시지를 보여주며 탐색기 팝업메뉴 옵션을 미리 선택해놓아 권장옵션임을 표시한다. IDOK 버튼을 클릭하면 선택한 옵션값을 읽어 환경을 초기화한다. 탐색기 팝업메뉴 등록은 미리 만들어 둔 RegisterPopup 함수를 호출하며 나머지 두 옵션은 직접 폴더와 쇼트컷을 만들어야 한다. 쇼트컷 생성과 특수 폴더 작성에 대해서도 API 정복 37장을 참조하기 바란다.

이 대화상자는 일종의 안내 대화상자이기 때문에 취소 버튼은 없으며 타이틀바의 닫기 버튼을 클릭하여도 IDOK를 누른 것과 동일하게 취급한다. 설치 대화상자는 메인 윈도우가 만들어지기 전에 실행되어야 하므로 WinMain에서 호출한다.

 

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance

            ,LPSTR lpszCmdParam,int nCmdShow)

{

    CoInitialize(NULL);

     hMenu1=LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENU1));

     hMenu2=LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENU2));

     ....

     RegisterClassEx(&WndClassEx);

 

    if (SHRegReadInt(SHCU,KEY"Setting","StartAction",1000)==1000) {

        DialogBox(g_hInst, MAKEINTRESOURCE(IDD_SETUP), HWND_DESKTOP, SetupDlgProc);

    }

 

     hWnd=CreateWindow(lpszClass,"당근",WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,

          CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,

          NULL,(HMENU)NULL,hInstance,NULL);

     ....

    CoUninitialize();

     return (int)Message.wParam;

}

 

쉘 함수들은 COM 인터페이스를 사용하므로 CoInitialize로 쉘 라이브러리를 초기화했다. 레지스트리에서 StartAction 값을 읽어 보고 이 값이 정의되어 있지 않으면 처음 실행된 것으로 간주하고 설치 대화상자를 보여준다. 두 번째부터는 StartAction 키가 존재하므로 이 대화상자가 나타나지 않는다. 레지스트리 편집기에서 Dangeun 서브키를 삭제한 후 실행하면 설치 대화상자가 나타날 것이다.