소프트웨어를 설치할 때 흔히 볼 수 있는 인증방법으로 사용되는 시리얼번호 검사기능을 인스톨쉴드에서 설정해 보고자 한다.
시리얼번호를 입력하게 하고 그 번호가 설치하려는 소프트웨어에 부여된 일련번호인지를 검사하기 위한 프로젝트 설정 예이다.
시리얼번호를 검사하는 로직은 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 실행 테스트)
위의 설정으로 DLL 프로젝트에 구성파일들이 준비되었고 SerialCheckDLL.h, SerialCheckDLL.cpp 파일을 편집하면 된다.
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;
}
}
탐색기에 프로젝트 폴더를 찾아보면 다음과 같이 배포용 DLL 파일이 생성되어 있는 것을 확인할 수 있다.
InstallShield의 Setup.rul 스크립트 OnFirstUIBefore()함수를 다음과 같이 편집한다.
//===========================================================================
//
// 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;