InstallShield 2010의 InstallScript MSI Project를 이용한 시리얼번호 검사기능 구현 예
개요 생성된 DLL을 VC++에서 사용하는 예는 여기를 참조하세요
소프트웨어를 설치할 때 흔히 볼 수 있는 인증방법으로 사용되는 시리얼번호 검사기능을 인스톨쉴드에서 설정해 보고자 한다.
시리얼번호를 입력하게 하고 그 번호가 설치하려는 소프트웨어에 부여된 일련번호인지를 검사하기 위한 프로젝트 설정 예이다.
시리얼번호를 검사하는 로직은 DLL 파일에 포함되어 있는 경우를 예로 들려고 하므로 Visual C++ 2010 을 사용할 것이다.
InstallShield 이벤트핸들러인 OnFirstUIBefore 함수안에서 이용자에게 시리얼번호를 입력하도록 하고 그 번호를 받아서 DLL 파일안에 정의된 CheckSerial(char * charSerialNumber) 함수에 전달하면 DLL 함수는 시리얼번호를 검사하여 그 결과를 true, false 로 리턴한다
InstallShield의 SdRegisterUserEx(szTitle, szMsg, svName, svCompany, svSerial) 함수는 이용자로부터 시리얼번호를 입력받을 수 있는데, 이 함수가 실행되면 다이얼로그가 실행되고 이용자는 시리얼번호를 포함하여 몇가지 정보를 입력하게 된다. 이때 svSerial 파라미터에 이용자가 입력한 시리얼번호가 저장되므로 이 함수가 실행된 후에 svSerial 변수의 값을 DLL 함수인 CheckSerial()에 전달하여 검사하면 된다. 다이얼로그는 별도로 준비할 필요는 없다.
순서
- 테스트용 프로그램 완성 (이미 완성된 것으로 간주함)
- 시리얼번호를 받아서 검사하기위한 로직을 포함하는 DLL 파일 생성
- 인스톨쉴드에서 InstallScript MSI Project 에서 DLL파일 등록
- 인스톨쉴드에서 OnFirstUIBefore()를 편집하여 SdRegisterUserEx()함수와 DLL 함수를 호출하는 코드를 작성
- 인스톨쉴드에서 Build Release (setup.exe 생성)
- 인스톨쉴드에서 Run Release (setup.exe 실행 테스트)
Visual Studio 2010에서 시리얼번호 검사용 DLL 파일 생성하기
위의 설정으로 DLL 프로젝트에 구성파일들이 준비되었고 SerialCheckDLL.h, SerialCheckDLL.cpp 파일을 편집하면 된다.
SerialCheckDLL.h 파일을 다음과 같이 편집한다
SerialCheckDLL.h
// SerialCheckDLL.h : SerialCheckDLL DLL의 기본 헤더 파일입니다. // #pragma once #ifndef __AFXWIN_H__ #error "PCH에 대해 이 파일을 포함하기 전에 'stdafx.h'를 포함합니다." #endif #include "resource.h" // 주 기호입니다. // CSerialCheckDLLApp // 이 클래스의 구현을 보려면 SerialCheckDLL.cpp를 참조하십시오. // class CSerialCheckDLLApp : public CWinApp { public: CString strSerial; public: CSerialCheckDLLApp(); void SetSerial(CString strSerial) { this->strSerial = strSerial; }; CString GetSerial(){ return this->strSerial; }; int Authenticate(){ if(this->strSerial=="123456") { return true; }else{ return false; } }; // 재정의입니다. public: virtual BOOL InitInstance(); DECLARE_MESSAGE_MAP() }; extern "C" __declspec(dllexport) int CheckSerial(char* charSerialNumber); // 인스톨쉴드에서 호출될 함수선언
SerialCheckDLL.cpp 파일을 다음과 같이 편집한다
SerialCheckDLL.cpp
// SerialCheckDLL.cpp : 해당 DLL의 초기화 루틴을 정의합니다. // #include "stdafx.h" #include "SerialCheckDLL.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // //TODO: 이 DLL이 MFC DLL에 대해 동적으로 링크되어 있는 경우 // MFC로 호출되는 이 DLL에서 내보내지는 모든 함수의 // 시작 부분에 AFX_MANAGE_STATE 매크로가 // 들어 있어야 합니다. // // 예: // // extern "C" BOOL PASCAL EXPORT ExportedFunction() // { // AFX_MANAGE_STATE(AfxGetStaticModuleState()); // // 일반적인 함수 본문은 여기에 옵니다. // } // // 이 매크로는 MFC로 호출하기 전에 // 각 함수에 반드시 들어 있어야 합니다. // 즉, 매크로는 함수의 첫 번째 문이어야 하며 // 개체 변수의 생성자가 MFC DLL로 // 호출할 수 있으므로 개체 변수가 선언되기 전에 // 나와야 합니다. // // 자세한 내용은 // MFC Technical Note 33 및 58을 참조하십시오. // // CSerialCheckDLLApp BEGIN_MESSAGE_MAP(CSerialCheckDLLApp, CWinApp) END_MESSAGE_MAP() // CSerialCheckDLLApp 생성 CSerialCheckDLLApp::CSerialCheckDLLApp() { // TODO: 여기에 생성 코드를 추가합니다. // InitInstance에 모든 중요한 초기화 작업을 배치합니다. } // 유일한 CSerialCheckDLLApp 개체입니다. CSerialCheckDLLApp theApp; // CSerialCheckDLLApp 초기화 BOOL CSerialCheckDLLApp::InitInstance() { CWinApp::InitInstance(); return TRUE; } extern "C" __declspec(dllexport) int CheckSerial(char* charSerialNumber) //인스톨쉴드에서 호출될 함수 { AFX_MANAGE_STATE(AfxGetStaticModuleState()); CString serialString; //시리얼 번호를 저장할 변수 serialString = (LPCSTR)(LPSTR)charSerialNumber; theApp.SetSerial(serialString); if(theApp.Authenticate() ) { return true; } else { return false; } }
디버깅이 완료되면 최종배포판을 빌드한다. 솔루션구성 콤보박스에서 Release 를 선택하고 다시 빌드하면 된다.
탐색기에 프로젝트 폴더를 찾아보면 다음과 같이 배포용 DLL 파일이 생성되어 있는 것을 확인할 수 있다.
인스톨쉴드의 InstallScript MSI Project에 DLL 파일을 등록한다
배포판 DLL 파일을 선택한다
InstallShield의 프로젝트에 등록된 DLL 파일을 확인한다
InstallScript항목을 선택하여 OnFirstUIBefore()함수를 에디터에 불러온다
InstallShield의 Setup.rul 스크립트 OnFirstUIBefore()함수를 다음과 같이 편집한다.
Setup.rul
//=========================================================================== // // File Name: Setup.rul // // Description: Blank setup main script file // // Comments: Blank setup is an empty setup project. If you want to // create a new project via. step-by step instructions use the // Project Assistant. // //=========================================================================== // Included header files ---------------------------------------------------- #include "ifx.h" // Note: In order to have your InstallScript function executed as a custom // action by the Windows Installer, it must be prototyped as an // entry-point function. // The keyword export identifies MyFunction() as an entry-point function. // The argument it accepts must be a handle to the Installer database. /* export prototype MyFunction(HWND); */ //--------------------------------------------------------------------------- // OnFirstUIBefore // // The OnFirstUIBefore event is called by the framework when the setup is // running in first install mode. By default this event displays UI allowing // the end user to specify installation parameters. //---------------------------------------------------------------------------/* OnFirstUIBefore()함수 안에서 호출될 DLL 파일과 그 함수의 원형을 선언한다
* SerialCheckDLL은 DLL파일의 이름이고, MyDLL은 DLL함수의 이름이다
*/ prototype cdecl INT MyDLL.CheckSerial(BYREF STRING, BYREF STRING); function OnFirstUIBefore() NUMBER nResult, nSetupType, nvSize, nUser; STRING szTitle, szMsg, szQuestion, svName, svCompany, szFile; STRING svSerial, szDLLName; STRING szLicenseFile; BOOL bCustom, bIgnore1, bIgnore2; BOOL bResult, bRet, bLicenseAccepted; begin // TO DO: if you want to enable background, window title, and caption bar title // SetTitle( @PRODUCT_NAME, 24, WHITE ); // SetTitle( @PRODUCT_NAME, 0, BACKGROUNDCAPTION ); // Enable( FULLWINDOWMODE ); // Enable( BACKGROUND ); // SetColor(BACKGROUND,RGB (0, 128, 128)); // Added in InstallShield 15 - Show an appropriate error message if // -removeonly is specified and the product is not installed. if( REMOVEONLY ) then Disable( DIALOGCACHE ); szMsg = SdLoadString( IDS_IFX_ERROR_PRODUCT_NOT_INSTALLED_UNINST ); SdSubstituteProductInfo( szMsg ); MessageBox( szMsg, SEVERE ); abort; endif; nSetupType = TYPICAL; Dlg_SdWelcome: szTitle = ""; szMsg = ""; nResult = SdWelcome(szTitle, szMsg); if (nResult = BACK) goto Dlg_SdWelcome; szTitle = ""; svName = ""; svCompany = ""; Dlg_SdRegisterUser: /* szMsg = ""; szTitle = ""; nResult = SdRegisterUser( szTitle, szMsg, svName, svCompany ); if (nResult = BACK) goto Dlg_SdWelcome; */ Dlg_SdRegisterUserEx://///////////////////////////////////////////////////////////////// szMsg = ""; szTitle = ""; svSerial = ""; szDLLName = SUPPORTDIR^"MyDLL.dll"; bResult = UseDLL(szDLLName); SdRegisterUserEx(szTitle, szMsg, svName, svCompany, svSerial); if(bResult = 0) then bRet = CheckSerial(svName, svSerial); if(bRet = TRUE) then UnUseDLL( szDLLName ); goto Dlg_SdLicense2; else MessageBox("The Serial Number is not valid!", INFORMATION ); UnUseDLL( szDLLName ); goto Dlg_SdRegisterUserEx; endif; else MessageBox("DLL is not found", WARNING); UnUseDLL( szDLLName ); abort; endif; if (nResult = BACK) goto Dlg_SdWelcome; Dlg_SdLicense2: // Display the SdLicense2Rtf dialog. szLicenseFile = SUPPORTDIR^"LICENSE.rtf"; nResult = SdLicense2Rtf ("End User License Agreement", "", "", szLicenseFile, bLicenseAccepted); if(nResult = BACK) then goto Dlg_SdWelcome; else bLicenseAccepted = TRUE; MessageBox ("사용권 계약 조항에 동의하셨습니다 ", INFORMATION); endif; Dlg_SetupType: szTitle = ""; szMsg = ""; nResult = SetupType2(szTitle, szMsg, "", nSetupType, 0); if (nResult = BACK) then goto Dlg_SdRegisterUser; else nSetupType = nResult; if (nSetupType != CUSTOM) then nvSize = 0; FeatureCompareSizeRequired(MEDIA, INSTALLDIR, nvSize); if (nvSize != 0) then MessageBox(szSdStr_NotEnoughSpace, WARNING); goto Dlg_SetupType; endif; bCustom = FALSE; goto Dlg_SQL; else bCustom = TRUE; endif; endif; Dlg_SdAskDestPath: nResult = SdAskDestPath(szTitle, szMsg, INSTALLDIR, 0); if (nResult = BACK) goto Dlg_SetupType; Dlg_SdFeatureTree: szTitle = ""; szMsg = ""; if (nSetupType = CUSTOM) then nResult = SdFeatureTree(szTitle, szMsg, INSTALLDIR, "", 2); if (nResult = BACK) goto Dlg_SdAskDestPath; endif; Dlg_SQL: nResult = OnSQLLogin( nResult ); if( nResult = BACK ) then if (!bCustom) then goto Dlg_SetupType; else goto Dlg_SdFeatureTree; endif; endif; Dlg_SdStartCopy: szTitle = ""; szMsg = ""; nResult = SdStartCopy2( szTitle, szMsg ); if (nResult = BACK) then goto Dlg_SQL;; endif; // Added in IS 2009 - Set appropriate StatusEx static text. SetStatusExStaticText( SdLoadString( IDS_IFX_STATUSEX_STATICTEXT_FIRSTUI ) ); // setup default status Enable(STATUSEX); return 0; end;
위의 과정을 마치고 프로젝트를 저장하면 다음과 같이 DLL함수와 인스톨쉴드의 함수가 Functions 항목 아래에 등록되어 있는 것을 볼 수있다
인스톨쉴드 프로젝트를 빌드한다
프로젝트가 오류 없이 빌드되었는지 빌드 메시지를 확인한다
성공적으로 빌드된 설치파일을 실행하여 시리얼번호 검증이 성공적으로 이루어지는지 확인한다
다음과 같이 설치 중에 시리얼번호를 입력받는 대화창이 실행되고 DLL 파일도 제대로 기능을 하고 있는 것을 확인할 수 있다
시리얼번호 인증에 실패하면 다음과 같은 경고가 출력된다