본문 바로가기

Programming/ActiveX

Vista 호환 ATL기반 다이얼로그 ActiveX 만들기 - 본론 -

최근 시간이 없는 관계로..

포스팅이 늦었다. ㅜㅜ; 그래서 간단하게 정리해보고자 한다.

이후 질문 답 형식으로 완성해야겠다. ㅜㅜ; 요즘 넘 바뻐서. 흑.. ㅜㅜ;

프로젝트 생성이나 컨트롤 추가를 모르시는 분이 계시려나... 혹시 모른다면

나중에 올려드리겠습니다.

워드로 정리 해놓은 것을 옮기느라 보기가 안좋을 수 있습니다. 이해해주세요~

그리고 대부분의 내용은 Devpia나 웹상에서 제가 검색한 내용을 토대로 재작성 하였습니다.

==================================================================

1. ATL Project 생성 (필자는 ApLauncher로 생성)
  - 옵션에서 Support MFC를 체크 하도록 한다.

2. ATL Control 추가 (필자는 ApBroker로 생성)
  - 옵션은 걍 다음 다음으로 넘겨버렸다. -ㅁ-;

3. 레지스트리 수정

ApBroker.rgs 파일 수정


 

HKCR

{

             ApLauncher.ApBroker.1 = s 'ApBroker Class'

             {

                           CLSID = s '{FC9E01FE-A5E6-43FD-A925-9D5F6803E779}'

             }

             ApLauncher.ApBroker = s 'ApBroker Class'

             {

                           CLSID = s '{FC9E01FE-A5E6-43FD-A925-9D5F6803E779}'

                           CurVer = s 'ApLauncher.ApBroker.1'

             }

             NoRemove CLSID

             {

                           ForceRemove {FC9E01FE-A5E6-43FD-A925-9D5F6803E779} = s 'ApBroker Class'

                           {

                                        ProgID = s 'ApLauncher.ApBroker.1'

                                        VersionIndependentProgID = s 'ApLauncher.ApBroker'

                                        ForceRemove 'Programmable'

                                        InprocServer32 = s '%MODULE%'

                                        {

                                                     val ThreadingModel = s 'Apartment'

                                        }

                                        val AppID = s '%APPID%'

                                       

                                        Elevation

                                {

                                          val Enabled = d 1

                                }

                                val LocalizedString = s '@%MODULE%,-101'

                                       

                                        ForceRemove 'Control'

                                        ForceRemove 'ToolboxBitmap32' = s '%MODULE%, 103'

                                        'MiscStatus' = s '0'

                                        {

                                            '1' = s '%OLEMISC%'

                                        }

                                        'TypeLib' = s '{B0EA13FC-8B95-4C3E-9627-D8937BFEDBCD}'

                                        'Version' = s '1.0'

                           }

             }

}


 

주의 : String Table 101번에 대한 String 추가할 것


빨간색으로 된 부분이 추가 되어야 하는 부분이다. 이 때 101번 String은 권한 상승시에 사용된다. (각자 확인 해보세요~)


ApLauncher.rgs 수정

HKCR

{

             NoRemove AppID

             {

                           '%APPID%' = s 'ApLauncher'

                           {

                                val DllSurrogate = s ''

                     }

                           'ApLauncher.DLL'

                           {

                                        val AppID = s '%APPID%'

                           }

             }

}

4. Broker 함수 추가


이제 권한 상승 함수와 실제 다이얼로그를 띄우는 부분을 추가해야 겠다.

프로젝스 프로퍼티창에서
  - IApBroker – Add – Add Method - RunBroker
함수 추가

  - IApBroker – Add – Add Method - RunProgram 함수 추가
  - IsVistaOS 함수와 CoCreateInstanceAsAdmin 함수 추가(어디든 CApBroker 클래스가 참조할 수 있으면 됩니다.
     클래스 함수로 포함시키면 좋겠지요...

 
isVistaOS 함수 착성

BOOL IsVistaOS(void)
{
 BOOL bIsVista = FALSE;
 OSVERSIONINFO sInfo;
 sInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
 
 //
 // OS VERSION CHECK.
 //
 if(::GetVersionEx(&sInfo)) {
  if( sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
   sInfo.dwMajorVersion >= 6 &&
   sInfo.dwMinorVersion >= 0) { // Windows VISTA
   bIsVista = TRUE;

  }
 }

 return bIsVista;
}



CoCreateInstanceAsAdmin 함수 작성

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv, BOOL bAdministrator)

{

             BIND_OPTS3 bo;

             WCHAR  wszCLSID[50];

             WCHAR  wszMonikerName[300];

 

             StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0]));

             HRESULT hr = S_OK ;

 

             if( bAdministrator)

                           hr = StringCchPrintfW(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);

             else

                           hr = StringCchPrintfW(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:HighestAvailable!new:%s", wszCLSID);

 

             if (FAILED(hr))

                           return hr;

             memset(&bo, 0, sizeof(bo));

             bo.cbStruct = sizeof(bo);

             bo.hwnd = hwnd;

             bo.dwClassContext  = CLSCTX_LOCAL_SERVER;

             return CoGetObject(wszMonikerName, &bo, riid, ppv);

}

 


 

RunBroker 함수 작성

STDMETHODIMP CApBroker::RunBroker(void)

{

             AFX_MANAGE_STATE(AfxGetStaticModuleState());

 

             HRESULT                                                                  hr;

             HWND                                                          hwnd = NULL;

             IApBroker*            objElevator = NULL;           

             const GUID                                                   guidCurrentObject = CLSID_ApBroker;

             const IID                                         iidCurrentObject = IID_IApBroker;

 

             CoInitialize(NULL);

            

             if(!IsVistaOS()) {

                           //

                           // VISTA 가아닌경우.

                           //

                           RunProgram();

 

                           return S_OK;

             }

 

             //

             // 권한상승(ELEVATION)

             //

             hr = CoCreateInstanceAsAdmin(NULL, guidCurrentObject, iidCurrentObject, (void**)(&objElevator), TRUE);

 

             //

             // EXECUTE

             //

 

             if(SUCCEEDED(hr)) {

                           // do work.

                           objElevator->RunProgram();

 

                           // release.                       

                           objElevator->Release();

 

                           return S_OK;

             } else {

                           //

                           // Elevation 실패.

                           //

                           CString sErr;

                           sErr.Format(_T("CoCreateInstanceAsAdmin failed! HResult: %x"), hr);

                           ::MessageBox(NULL, sErr, _T("ApBroker") , MB_OK);

             }

            

             return S_OK;

}


위에서 Vista인 경우와 아닌 경우로 나뉘어 Vista인 경우 권한 상승 후 프로그램을

실행하고 있습니다. 이부분을 유의해서 보시면 됩니다.
 

RunProgram 함수 작성

STDMETHODIMP CApBroker::RunProgram(void)

{

             AFX_MANAGE_STATE(AfxGetStaticModuleState());

             // TODO: Add your implementation code here

             CDlgMain m_DlgMain;

             m_DlgMain.DoModal();

 

             return S_OK;

}

 

자 지금까진 공통적인 부분이었고 저기 CDlgMain m_DlGmain; 부분을 봅시다.
필자는 다이얼로그를 하나 추가해서 연결해 두었습니다.

그리고 CApBroker::RunProgram 안에서 호출하고 있습니다.

따라서 기존 MFC 프로그래밍 하는 것처럼 코딩하시고 권한 상승 부분만 ATL을 이용합니다.

필자는 ATL을 해본적도 없고 MFC를 좀 하던차에 ActiveX를 만들어야 했기 때문에

좀 편하게 코딩해보가 이런 꼼수를 사용했습니다.
(더 확실한 것은 아예 모두 MFC로 만드는 것이겠지요. 필자는 하다하다 포기했습니다.
 방법을 아시는 분은 링크좀.. ㅡㅡ;)


5. 안정성 코드 추가

안정성 코드는 이 엑티브 엑스를 정말로 실행해도 됩니까? 하는 것을 빼게 해줍니다.
그냥 빨간 부분 추가만 해주시면 됩니다.

ApLauncher.cpp 수정

  - CreateComponentCategory 함수 추가
  - RegisterCLSIDInCategory 함수 추가

HRESULT CreateComponentCategory(CATID catid, WCHAR* catDescription)
{

    ICatRegister* pcr = NULL ; // interface pointer
    HRESULT hr = S_OK ;

    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
   NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
 if (FAILED(hr)) {
  return hr;
 }

    // Make sure the HKCR\Component Categories\{..catid...}
    // key is registered
    CATEGORYINFO catinfo;
    catinfo.catid = catid;
    catinfo.lcid = 0x0409 ; // english

 // Make sure the provided description is not too long.
 // Only copy the first 127 characters if it is
 int len = (int)wcslen(catDescription);
 if (len>127)
  len = 127;
    wcsncpy_s(catinfo.szDescription, catDescription, len);
 // Make sure the description is null terminated
 catinfo.szDescription[len] = '\0';

    hr = pcr->RegisterCategories(1, &catinfo);
 pcr->Release();
 
 return hr;
}


 

HRESULT RegisterCLSIDInCategory(REFCLSID clsid, CATID catid)
{
// Register your component categories information.
    ICatRegister* pcr = NULL ;
    HRESULT hr = S_OK ;
    hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
   NULL, CLSCTX_INPROC_SERVER, IID_ICatRegister, (void**)&pcr);
    if (SUCCEEDED(hr))
    {
       // Register this category as being "implemented" by
       // the class.
       CATID rgcatid[1] ;
       rgcatid[0] = catid;
       hr = pcr->RegisterClassImplCategories(clsid, 1, rgcatid);
    }

    if (pcr != NULL)
        pcr->Release();
 
 return hr;
}


 빨간 부분 추가

STDAPI DllRegisterServer(void)

{

    // registers object, typelib and all interfaces in typelib

    HRESULT hr = _AtlModule.DllRegisterServer();

#ifdef _MERGE_PROXYSTUB

    if (FAILED(hr))

        return hr;

    hr = PrxDllRegisterServer();

#endif

 

             /////////////////////////////////////////////////////////////////////////////   

             // 안전성코드추가SAFE FOR SCRIPTING.

             /////////////////////////////////////////////////////////////////////////////   

             const CATID CATID_SafeForScripting =

             { 0x7dd95801, 0x9882, 0x11cf, { 0x9f, 0xa9, 0x00, 0xaa, 0x00, 0x6c, 0x42, 0xc4 } };

             const CATID CATID_SafeForInitializing =

             { 0x7dd95802, 0x9882, 0x11cf, { 0x9f, 0xa9, 0x00, 0xaa, 0x00, 0x6c, 0x42, 0xc4 } };

             REFCLSID clsidCurrentActiveX = CLSID_ApBroker;

            

 

             CoInitialize( NULL );

 

             if (FAILED( CreateComponentCategory(CATID_SafeForScripting, L"Controls that are safely scriptable") )) {

                           return ResultFromScode(SELFREG_E_CLASS);

             }

 

             if (FAILED( CreateComponentCategory(CATID_SafeForInitializing, L"Controls safely initializable from persistent data") )) {

                           return ResultFromScode(SELFREG_E_CLASS);

             }

 

             if (FAILED( RegisterCLSIDInCategory(clsidCurrentActiveX, CATID_SafeForScripting) )) {

                           return ResultFromScode(SELFREG_E_CLASS);

             }

 

             if (FAILED( RegisterCLSIDInCategory(clsidCurrentActiveX, CATID_SafeForInitializing) )) {

                           return ResultFromScode(SELFREG_E_CLASS);

             }

 

             return hr;

}

================================================================
에고에고 힘들다.  있는거 가져다 붙이는 것도 힘드네요.

다시 말씀드리자면 이 방법은 MFC 이용하여 권한 상승을 하지는 않습니다.

정확히는 ATL 기반에서 권한 상승하고 MFC를 쓰는 것이지요.

포스팅이 너무 늦어버렸네요..