저작권에 대한 공지
이 문서에 포함된 모든 글과 소스 코드는 작자인 저 김성동의 지적 재산이지만 무단으로 배포하거나 책이나 강의 교재등을 위한 복제 등 저작권을 저해하는 행위를 제외하고는 자유롭게 사용할 수 있습니다.
또한 이 페이지에 대한 링크는 자유롭게 할 수 있으나 전문을 전제하는 일은 허용하지 않습니다.


3. 콤포넌트 개발의 고급 기술

3.1. 개요

이전 장에서 몇가지 콤포넌트를 개발해 보았다. 하지만 아직 정복해야할 산은 남아 있다. 콤포넌트를 개발해서 혼자서 사용한다면 아마도 이번 장에서 얘기하게 될 내용이 그리 중요하게 생각되지 않을 수도 있다. 하지만 콤포넌트를 개발해서 다른 사람이 사용하도록 배포하거나 상용으로 판매하게 된다면 상황은 달라진다. 오브젝트 인스펙터가 처리하지 못하는 프로퍼티를 쉽게 조작할 수 있도록 해 주는 프로퍼티 에디터나 복잡한 콤포넌트의 프로퍼티들을 좀 더 쉽고 직관적으로 조작할 수 있도록 폼 디자이너에서 콤포넌트를 더블 클릭하거나 콤포넌트 콘텍스트 메뉴에서 실행되는 콤포넌트 에디터(TeeChart 콤포넌틀를 폼에 올려 놓고 마우스 오른쪽 버튼을 눌러 보라)도 제공해 주는 것이 좋을 것이다. 그리고 콤포넌트를 설치해서 사용하는 사용자들은 콤포넌트 도움말이 델파이 IDE와 완벽하게 통합되어 동작하기를 원할 것이다.
이번 장에서는 이런 고급 기법들에 대해서 자세하게 알아 보겠다. 그리 어려운 일은 아니니 너무 걱정하지 않아도 된다.

3.2. 고급 RTTI(Run-Time Type Information)

3.2.1. 개요
1장에서 RTTI의 is 연산자와 as 연산자에 대해 알아 보았다. 하지만 이것이 RTTI의 모든 것은 아니다. RTTI는 컴파일러가 코드 세그먼트에 추가해 주는 정보로서 Published 프로퍼티나 클래스에 대한 심볼 정보들이다. RTTI 관련 함수나 프로시저는 TypInfo.pas에 정의되어 있다. 하지만 RTTI는 다른 내용들과 달리 델파이 도움말이나 문서에서 비교적 잘 다루고 있지 않다. 왜냐하면 이 정보들은 델파이 IDE가 정상적으로 동작하는데 중요한 역할을 하고 있고 컴파일러와 밀접한 연관을 가지고 있어서 컴파일러가 업그레이드될 때마다 RTTI의 기능이 변경될 소지가 많기 때문이다.
이번 절에서는 콤포넌트 개발 뿐만이 아니고 어플리케이션 개발시에도 아주 유용한 RTTI 함수나 프로시저에 대해 자세하게 알아 보도록 하겠다.
3.2.2. 열거형 관련 RTTI 함수
열거형은 델파이에서 많이 사용하는 데이터 형 중의 하나이다. 델파이에서 사용하고 있는 많은 열거형 중에서 콤포넌트 팔레트 Win32 페이지에 있는 TAnimate 콤포넌트의 CommonAVI 프로퍼티 형인 TCommonAVI를 예로 들어보자. TCommonAVI는 ComCtrls.pas에 아래와 같이 선언되어 있다.

TCommonAVI = (aviNone, aviFindFolder, aviFindFile, aviFindComputer, aviCopyFiles,
    aviCopyFile, aviRecycleFile, aviEmptyRecycle, aviDeleteFile);


RTTI에서는 열거형과 관련된 두가지 함수를 제공하는데 하나는 열거형의 각 멤버를 문자열로 반환해 주는 GetEnumName 함수와 지정한 문자열을 가지고 열거형내에서의 순서를 반환해 주는 GetEnumValue 함수를 제공한다.
GetEnumName 함수의 원형은 아래와 같다.

function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;


GetEnumName 함수의 첫번째 인자는 열거형의 형 정보(TypeInfo)에 대한 포인터이며 두번째 인자는 문자열로 변환하고 싶은 멤버의 열거형내에서의 순서이다.
GetEnumValue 함수의 원형은 아래와 같으며 첫번째 인자는 GetEnumName과 같이 형 정보에 대한 포인터이며 두번째 인자는 열거형 멤버를 나타내는 문자열이다.

function GetEnumValue(TypeInfo: PTypeInfo; const Name: string): Integer;


두 함수 모두 열거형의 형 정보에 대한 포인터를 첫번째 인자로 사용한다. 이 포인터는 TypeInfo 함수를 사용하면 쉽게 얻을 수 있다. TypeInfo 함수의 원형은 아래와 같고 인자는 컴파일러가 생성한 RTTI 포인터를 구하고 싶은 형의 지시자(Type Identifier)를 사용하면 된다.

function TypeInfo(TypeIdent): Pointer;


따라서 열거형 TCommonAVI의 aviFindFile 멤버에 대한 문자열을 얻고 싶으면 아래와 같이 하면 되며 반대의 경우도 마찬가지이다.

var
  TypeStr : string;
  TypeOrd : Integer;
begin
  TypeStr := GetEnumName(TypeInfo(TCommonAVI), Ord(aviFindFile));
  ShowMessage(TypeStr);
  TypeOrd := GetEnumValue(TypeInfo(TCommonAVI), TypeStr);
  ShowMessage(IntToStr(TypeOrd));
end;


3.2.3. 프로퍼티 관련 RTTI 함수
델파이 폼 디자이너에서 폼이나 폼이 소유하고 있는 콤포넌트를 선택하면 오브젝트 인스펙터는 선택된 콤포넌트의 Published 프로퍼티 리스트를 구하고 각각의 프로퍼티와 연결된 프로퍼티 에디터를 생성한다. 그렇다면 오브젝트 인스펙터는 어떻게 콤포넌트의 프로퍼티 리스트를 구할까?
콤포넌트의 프로퍼티 리스트는 GetPropList 함수를 통해서 구할 수 있다. GetPropList 함수의 원형은 아래와 같다.

functionGetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList): Integer;


첫번째 인자는 GetEnumName, GetEnumValue에서와 마찬가지로 형 정보에 대한 포인터이다. 하지만 GetEnumName이나 GetEnumValue에서처럼 TypeInfo 함수를 써서 PTypeInfo 인자를 구하지 않고 GetPropList는 객체에 대해 사용하는 것이기 때문에 콤포넌트의 ClassInfo 프로퍼티를 넣어 주어야 한다.
두번째 인자는 추출할 프로퍼티의 종류를 지정한다. TTypeKinds는 프로퍼티 종류를 나타내는 집합형으로서 아래와 같이 선언되어 있다.

type
  TTypeKind = (tkUnknown, tkInteger, tkChar, tkEnumeration, tkFloat,
    tkString, tkSet, tkClass, tkMethod, tkWChar, tkLString, tkWString,
    tkVariant, tkArray, tkRecord, tkInterface, tkInt64, tkDynArray);
  TTypeKinds = set of TTypeKind;

const
  tkAny = [Low(TTypeKind)..High(TTypeKind)];
  tkMethods = [tkMethod];
  tkProperties = tkAny - tkMethods - [tkUnknown];


마지막 인자인 PPropList는 TPropInfo의 배열인 TPropList에 대한 포인터이다. TPropList는 최대 16380개의 프로퍼티 정보를 가질 수 있도록 선언되어 있다. TPropList는 프로퍼티의 이름과 프로퍼티 형 정보를 가지고 있는 구조체이다.
GetPropList 함수는 PPropList에 구해진 프로퍼티의 개수를 반환한다.

GetPropList 함수의 사용한 간단한 프로젝트 PropList를 하나 만들어 보자. 아래 그림은 PropList 의 실행 화면이다.

그림 3-1 GetPropList 예제


소스 코드를 보면 폼이 생성될 때 폼이 소유하고 있는 콤포넌트 리스트를 콤보 박스에 추가해 준다.

procedure TfrmPropList.FormCreate(Sender: TObject);
var
  nIndex : Integer;
begin
  for nIndex := 0 to Pred(ComponentCount) do
  begin
    cbComponent.Items.Add(Components[nIndex].Name);
  end;
end;


그리고 콤보 박스에서 콤포넌트를 선택하면 선택된 콤포넌트의 프로퍼티 리스트를 리스트 박스에 추가해 주도록 되어 있다.

procedure TfrmPropList.cbComponentChange(Sender: TObject);
var
  nIndex, nCount : Integer;
  PropList : PPropList;
  CompName : string;
begin
  if cbComponent.ItemIndex <> -1 then
  begin
    PropList := Allocmem(SizeOf(TPropList));
    try
      CompName := cbComponent.Items[cbComponent.ItemIndex];
      lstProperty.Items.Clear;
      nCount := GetPropList(FindComponent(CompName).ClassInfo, tkAny, PropList);
      for nIndex := 0 to Pred(nCount) do
      begin
        lstProperty.Items.Add(PropList[nIndex]^.Name);
      end;
    finally
      Freemem(PropList);
    end;
  end;
end;


GetPropList를 사용하지 않고 콤포넌트의 지정한 프로퍼티 하나에 대한 정보를 구하고 싶을 때는 GetPropInfo 함수를 사용한다. GetPropInfo는 TPropInfo 구조체에 대한 포인터인 PPropInfo를 반환하는데 TPropInfo 구조체는 아래와 같이 선언되어 있다.

  PPropInfo = ^TPropInfo;
  TPropInfo = packed record
    PropType: PPTypeInfo;
    GetProc: Pointer;
    SetProc: Pointer;
    StoredProc: Pointer;
    Index: Integer;
    Default: Longint;
    NameIndex: SmallInt;
    Name: ShortString;
  end;


그리고 TPropInfo 구조체의 멤버인 PPTypeInfo는 아래와 같이 선언되어 있다.

  PPTypeInfo = ^PTypeInfo;
  PTypeInfo = ^TTypeInfo;
  TTypeInfo = record
    Kind: TTypeKind;
    Name: ShortString;
  end;


GetPropInfo와 GetXXXProp, SetXXXProp 함수들을 이용하면 실행 시에 콤포넌트의 프로퍼티를 문자열로 참조할 수가 있다. 예를 들어

var
  PropInfo : PPropInfo;
begin
  PropInfo := GetPropInfo(Form1.ClassInfo, 'Caption');
  ShowMessage('Form1의 Caption --> ' + GetStrProp(Form1, PropInfo));
end;


이와 같은 경우 문자열 상수 'Caption' 을 이용해서 Form1의 Caption 프로퍼티를 조작하고 있다.
GetXXXProp, SetXXXProp은 오버로드된 함수로서 예와 같이 PropInfo를 인자로하는 것도 있지만 단순히 프로퍼티 이름을 문자열로 받아서 처리해주는 함수도 선언되어 있다. 이와 같은 함수를 이용하면 폼 실행 시에 오브젝트 인스펙터 기능을 하는 도구를 만들어 볼 수도 있을 것이다.
그 밖에 프로퍼티와 관련된 유용한 RTTI 함수를 아래 표에 나타내었으니 참고하기 바란다.

표 3-1 프로퍼티와 관련된 함수
함수설명
IsPublishedProp콤포넌트에 지정한 이름을 가진 프로퍼티가 있는지 조사한다.
IsStoredProp지정한 프로퍼티가 폼 파일에 저장되는지를 조사한다.
PropIsType지정한 프로퍼티의 형이 지정한 형(TTypeKind)이면 참을 반환한다.
PropType지정한 프로퍼티의 형(TTypeKind)을 반환한다.





저작권에 대한 공지
이 문서에 포함된 모든 글과 소스 코드는 작자인 저 김성동의 지적 재산이지만 무단으로 배포하거나 책이나 강의 교재등을 위한 복제 등 저작권을 저해하는 행위를 제외하고는 자유롭게 사용할 수 있습니다.
또한 이 페이지에 대한 링크는 자유롭게 할 수 있으나 전문을 전제하는 일은 허용하지 않습니다.