천객만래 [千客萬來] (It has an interminable succession of visitors)

[강좌] 리소스의 활용 I  


아래에 Resource DLL의 활용 방법에 대한 강좌 요청이 있어서 저의 졸고라도
도움이 되실까 해서 올려 드립니다. 참고가 되시길...

리소스의 활용 I

글: 하영재(vaio91@yahoo.com)
    델파이 코리아(www.delphikorea.com)


들어가며...

델파이 코리아(www.delphikorea.com)나 다른 통신사에서 심심치 않게 올라오는
질문중에 Resource File(이하 리소스 파일이라 한다...^^)의 사용에 대한 질문이
있다. 
리소스 파일을 만들고 사용하는 문제들에 대한 질문들인데, 여러가지 단편적인
내용만을 가지고 조합을 해야 하니 처음 접하는 분들은 어려움을 느낄 수도 있을
것이다. 이런 분들을 위해 리소스 파일에 대한 모든 부분이라고는 할 수 없겠으나
많이 사용되는 부분에 대해서 예제와 더불어 간단하게 나마 천천히 살펴보려 한다. 


지금 부터 살펴볼 내용은 다음과 같다.

- 리소스 파일을 만드는 방법.
- 리소스 DLL을 만드는 방법.
- 위의 리소스 파일을 만드는 방법의 상세 내용
   . Bitmap 리소스 파일을 만들고 사용하기
   . Text 리소스 파일을 만들고 사용하기
   . AVI 리소스 파일을 만들고 사용하기
- 리소스 사용시 주의해야 할 메모리 문제
- 그 외 리소스로 할 수 있는 일들

이상과 같은 내용을 살펴 볼 것인데 아주 기초적인 부분부터 다룰 것이므로 기존에
다뤄본 분들이라면 아낌없이 다른 강좌들에 눈을 돌리자... ^^

이곳에서 사용할 예제는 본인이 직접 작성한 것도 있으나 아주 잘만들어진 예제를
제공하기 위해 Borland TI의 소스도 참고했음을 밝혀둔다. TI의 예제 소스를 사용한
부분에서 미리 발췌 했음을 알리도록 할 것이다.


리소스 파일 만들기...

리소스 파일은 바이너리 형태의 데이터로서 RCDATA라 부른다. 리소스 파일은
Res라는 확장자를 가지게 되는데 이 Res 파일을 만들기 위한 과정을 살펴 보자...

Res 파일을 만들기 위해서는 Text로 작성된 RC 파일이 있어야 한다. 이 RC 파일은
Res 파일에 포함하고자 하는 데이터 이름과 데이터 식별자, 그리고 데이터로 구성된
Text 파일이다. RC 파일의 예를 보자...

- RC 파일의 예

  WAVEFILE WAVE c:\WaveSound.wav

이렇게 작성된 것이 RC 파일이다. 여기서 'WAVEFILE'은 리소스를 호출할 때 쓰일
이름이며 'WAVE'는 이 리소스가 WAVE 파일임을 명시하는 것이며 맨 뒷부분은 실제로
리소스에 적재될 파일의 경로와 파일명으로 구성되어 진다. 
이렇게 텍스트 에디터에서 작성을 한 후 '파일이름.RC'로 저장을 하면 된다. 

여기까지 해서 RC 파일을 작성했으면 이제는 컴파일을 해서 RES 파일로 만드는
과정이 남아 있다. 
리소스 파일을 만들기 위해 델파이에서는 BRCC(Boaland Resource Command Line
Compiler)와 BRCC32를 제공한다. 이 녀석들은 RC 파일을 컴파일해서 RES 파일로
만들어 주는 역할을 한다. 
이름 그대로 Command Line에서 사용하는 녀석들인데 사용방법은 매우 간단하다. 
그저 'BRCC32 파일명.RC' 라고 커맨드 라인에서 입력하기만 하면 RES 파일을 만들어
준다. 이 두녀석은 델파이의 BIN 디렉토리에 존재하며 다른 경로에서 컴파일 하기
위해서는 Path에 이 디렉토리를 추가하여 주어야 한다.
(예: path c:\progra~1\borland\delphi5\bin) 

- RC 파일의 컴파일

   BRCC32 MyRes.RC

이렇게 해서 만들어진 Res 파일을 사용하려면 어떻게 해야 하는지 알아보자. 
리소스 파일을 실행파일의 Res에 추가하려면 $R 지시자를 사용하여 추가한다. 
이렇게...

{$R MyRes.RES}

아마 Unit에서 {$R *.DFM} 이렇게 작성된 부분을 보았을 것이다. 이것 또한 DFM
파일을 폼의 리소스에 추가하겠다는 코드이다. 이렇게 추가된 리소스는 한 프로젝트
내의 모든 유닛에서 사용이 가능하다. 

위와 같이 하여 폼에 우리가 작성한 Res 파일을 추가한 후 컴파일을 하면 컴파일
전과 컴파일 후의 실행 파일의 크기가 변한 것을 알 수 있다. 당연한 이야기지만...
실행 파일에 우리가 작성한 리소스 파일을 추가 했으니 추가된 리소스 만큼 폼의
크기가 늘어나는 것은 당연한 것이다. 

마지막 부분에서 생각해 볼 것이지만 리소스 파일을 남발하는 것은 자칫 잘못하면
엄청난 크기의 실행 파일을 만들어 낼 수도 있다는 것을 주의해야 하며 이것은
메모리 낭비로 이어지는 것이다.

이렇게해서 실행 파일에 우리의 리소스를 추가했다면 우리는 이 리소스를 사용을
해야 한다. 어떻게 추가된 리소스를 사용하는지 알아보자. 



리소스의 사용예

우리가 추가한 리소스가 Wave 파일이므로 리소스에서 Wave 파일을 로드하여
연주하는 방법을 살펴보자.
Wave 파일을 연주하려면 PlaySound나 sndPlaySound 같은 API를 이용하여 연주하게
되는데 문제는 Wave가 파일로 존재하지 않는 것이라는 것이다. 리소스에 추가된
Wave를 연주하는데 PlaySoud와 sndPlaySound는 서로 비슷한 방법을 사용해서
연주하게 된다. 본론 부터 말하자면 두 함수 모두 Wave 리소스를 연주하는데는
별다른 차이가 없다. 즉 사용방법이 거의 동일하다는 얘기이다. 이 함수들을
사용하기 전에 MMSYSTEM을 Uses에 추가 해 주어야 한다.

- PlaySound의 경우...

  PlaySound의 경우는 바로 리소스에서 Wave를 연주 할 수 있는데 바로 SND_RESOU   
RCE란 플랙을 이용하면 된다. 
  예를 보자...

  PlaySound('WAVEFILE', hInstance, SND_RESOURCE or SND_ASYNC);

  여기서 WAVEFILE은 처음에 RC 파일을 만들때 맨 앞에 적어 주었던 리소스 이름    
이며 두번째 인자로 필요한 HModule에 현재 Instance를 주면 되며 마지막으로 
  SND_RESOURCE라는 플랙을 추가 함으로써 간단히 Wave리소스를 연주 할 수 있다.

- sndPlaySound의 경우...

  sndPlaySound도 PlaySound와 별다른 차이가 없다. 

  sndPlaySound('WAVEFILE', SND_RESOURCE or SND_ASYNC);

  설명을 따로 할 필요가 없을 정도로 비슷하다. 

리소스에서 바로 연주하는 방법을 살펴 보았는데 다른 방법으론 TResourceStream을
이용해서 연주하는 방법이 있다. 이 때 사용하는 플랙은 SND_MEMORY란 플랙이며
sndPlaySound만을 예로 들어 보겠다. 

- TResourceStream을 이용한 연주...

   var
     Soo: TResourceStream;
   begin
     Soo := TResourceStream.Create(hInstance, 'WAVEFILE', PChar('Wave'));
     sndPlaySound(Soo.Memory, SND_MEMORY or SND_ASYNC);
     Soo.Free;
   end;

   TResourceStream을 생성하는 부분을 살펴 보면... 첫번째 인자로 인스탄스를 
   넘겨준다. 그리고 두 번째로 리소스 이름, 그리고 마지막으로 리소스 타입을
   넘겨주면 된다. 
   이렇게 해서 얻어진 Wave를 sndPlaySound의 SND_MEMORY 플랙을 이용해서 연
   주 할 수 있는 것이다. 
   PlaySound도 거의 동일 하므로 직접 한 번 해 보기를 권한다.


여기까지 해서 어떻게 리소스 파일을 생성하는지를 알아보았고 또 어떻게 리소스에
추가된 데이터를 읽어 오는가에 대해 알아 보았다. 
다음번에는 리소스 DLL을 만드는 방법과 만들어진 리소스 DLL을 이용해서 동적으로
리소스를 불러오는 방법... 그리고 각 리소스를 어떻게 사용하는지에 대해 알아
보도록 하겠다. 

노파심에 한 마디 더 하자면... 설명한 부분중 설명이 잘 못된 부분이 있을경우
지적해 주시면 바로 수정 조치 토록 하겠다. 

끝까지 읽어 주신 분들께 경의를 표하며 이만 마치겠다. 



RESEXAM1.ZIP


Posted by SB패밀리

[강좌] 리소스의 활용 II  

리소스의 활용 II

글: 하영재(vaio91@yahoo.com)
    델파이 코리아(www.delphikorea.com)

1편 강좌가 나간후 참 오랜만에 2편 강좌를 올리게 되는군여. 죄송합니다. 먹고살다
보니 이런일이.. 흑...

1편 강좌에서는 리소스를 어떻게 프로그램에 링크시키고 불러오고 하는지에 대해서
알아보았었죠... 이번엔 리소스 DLL을 만들고 사용하는 법에 대해 알아보겠습니다. 
이번 강좌에서는 AVI, Cur, Ico, Bmp, Jpeg, string을 Dll에 넣고 불러오는 방법에
대해 설명하겠습니다. 그리고 시간이 된다면 Resource의 활용법으로 실행파일을
리소스에 넣고 불러오는 방법에 대해서도 알아보도록 하겠습니다.
그럼 시작하죠...(지금 부터 존칭이 생략되는거... 다 아시죠? ^^)

1. 리소스 Dll 만들기

리소스 Dll이란 말 그대로 리소스를 포함하고 있는 Dll을 말한다. 만드는 순서를
먼저 알아보자. 

1. 리소스의 경로를 저장하고 있는 RC 파일을 만든다. 
2. RC 파일을 Brcc32등으로 컴파일 하여 Res 파일로 만든다. 
3. 만들어진 Res 파일을 Dll에 포함시킨다. (폼에 포함시키는것과 동일하다.)
4. Dll을 컴파일 한다. 

2번까지는 지난번 강좌의 내용과 같다. 3번 항목의 Res를 Dll에 포함 시키는 부분은
폼에 하는것과 동일하다고 했는데 아래의 예제를 보면 왜 동일하다는 건지 쉽게
이해가 될 것이다. 


library ResOnly;

{$R Resource.res}

begin
end.

아무런 함수등을 포함하지 않고 오직 리소스만 가지고 있는 Dll의 모습이다. 
Dll에 리소스를 포함시키기 위해서 폼에서 했던것과 마찬가지로 $R 지시자를
사용해서 Dll에 리소스를 포함 시키고 있다. 
일반 Dll과의 차이를 의심하는 분들이 계실지 모르지만... ^^ 동일한 Dll이다. 단지
리소스를 포함하고 있다는 것만이 다를뿐. 이 Dll에도 함수를 넣을 수 있다. 그리고
기존의 Dll에도 리소스를 포함시킬 수도 있다. 

그럼 실제로 예제로 사용할 Dll을 하나 만들어 보자. 


먼저 RC 파일을 만든다. 

[MyRes.RC]

MyBMP BITMAP Delphikorea.bmp
MyAVI AVI Cool.avi 
MyCur CURSOR HandPoint.cur
MyJPG JPEG Window.jpg

저장한 후 컴파일 한다. (리소스를 활용하자 1편 강좌 참고)
컴파일이 완료되면 MyRes.Res 란 리소스 파일이 생성되었을 것이다.
이 리소스 파일을 Dll에 포함시킨다.

[MyResource.dpr]

library MyResource;

{$R MyRes.res}

begin
end.

이 프로젝트 파일을 컴파일 하면 아무런 함수등이 포함되지 않은 순수 리소스 Dll이
완성된다. 물론 이 리소스 Dll엔 일반 Dll과 마찬가지고 함수를 가지고 있을 수도
있다. 단지 리소스 파일을 포함하고 있다는 것만 다를 뿐이다.
이제 남은일은 dll에 포함된 리소스를 하나 하나 꺼내 사용하는 일뿐이다.


2. Dll내의 리소스를 사용하자.

2-1) AVI 리소스의 사용

Dll 내의 AVI 리소스를 사용하기 위해서는 먼저 리소스 Dll을 로드하여야 한다. 
우리는 간단히 LoadLibrary API를 이용해서 Dll을 로드 할 수 있다. 

var
DllHandle: THandle;

...

DllHandle := LoadLibrary('MyResource.dll');

이렇게 해서 얻어진 Dll의 Instance Handle은 앞으로 두고 두고 사용할 것이므로 잘
챙겨 두도록 하자. ^^

로드된 Dll을 이용해서 AVI 리소스를 뽑아내는 것은 정말 정말 간단하다. TAnimate
객체를 사용하여 플레이 할것이기 때문이다. 
우리는 TAnimate 객체의 ResName과 ResHandle 프러퍼티만을 셋팅해 줌으로써 바로
플레이가 가능하다.

먼저 ResHandle을 채워준다.

Animate1.ResHandle := DllHandle;

다음으로 ResName을 준다.

Animate1.ResName := 'MyAVI'; //우리가 저장한 AVI의 이름이다.

마지막으로 플레이...

Animate1.Play(1, Animate1.FrameCount, 0);


2-2) JPEG 리소스의 사용
JPEG는 Resource Type으로 등록되지 않은 녀석이다. 별도로 등록되지 않은 리소스
형식은 RT_RCDATA 형식으로 봐도 무방하다. 바이너리로 저장되었으므로 JPEG
리소스를 로드하기 위해선 TResourceStream이란 메모리 스트림 계열의 스트림을
이용해서 리소스 Dll로 부터 메모리로 JPEG 데이터를 옮겨 올 수 있다.

var
ResStream: TResourceStream;
...

ResStream := TResourceStream.Create(DllHandle, 'MyJPEG', 'JPEG');

여기까지 작업하면 ResStream에 JPEG 파일이 들어온다. 
이것을 담을 JPEG 형식의 객체가 필요하므로 하나 생성하자.(JPEG Unit을
포함시켜야 한다.)

uses
JPEG;

var
JPEG: TJPEGImage;
...

JPEG := TJPEGImage.Create;

JPEG Image를 생성했으면 위에서 로드한 ResStream을 받아온다.

JPEG.LoadFromStream(ResStream);

마지막으로 Image에 JPEG를 넣어준다.

Image1.Picture.Bitmap.Assign(JPEG);

위에서 동적 생성한 객체들을 해제해 준다.

JPEG.Free;
ResStream.Free;



2-3) Cursor 리소스의 사용
Custom Cursor를 사용하기 위해선 먼저 Screen의 Cusors에 리소스를 로드해 줘야
한다. 단, 우리가 사용할 커서의 액세를 위해 유니크한 커서 상수를 하나 미리
선언해 주자.

const
crMyCursor = 10;

이 값은 0보다 큰 값이면 어느 값이나 사용해도 무방하다. 단... ^^ 미리 윈도우에
등록된 Cursor 상수값과는 달라야 한다.
커서를 로드하는데는 LoadCursor란 API를 이용한다.

Screen.Cursors[crMyCursor] := LoadCursor(DllHandle, 'MyCur');

로드가 되었으므로 사용하고저 할 때 이 커서를 불러주면 된다.

Screen.Cursor := crMyCursor;


2-4) Bitmap 리소스의 사용

Bitmap 리소스는 리소스 중에서 사용하기가 가장 쉽다. ^^ 이미 델파이에선
TBitmap에 리소스를 사용하기 위한 모든 준비를 마쳐 놨기 때문이다. 그렇지
않았다면 우리는 일일이 많은 작업을 손수 했어야 할 것이다. 
우리는 리소스의 비트맵을 로드하기 위해 단지 LoadFromResourceName이란 메소드만
호출해 주면 된다.

Image2.Picture.Bitmap.LoadFromResourceName(DllHandle, 'MyBMP');

내부적으로 어떻게 처리되는지 알고 싶은 사람은 Graphics.pas에 정의된
LoadFromResourceName 메소드를 분석해 보자...



이것으로 할 얘긴 거의 다 한것 같다. 한 가지 빠진것이 있다면 Icon 파일에 대한
것인데 이것또한 별로 설명할 것이 없으므로 직접 한 번 해 보시길... ^^
마지막 단계로 로드한 Dll을 해제 할 차례이다.

FreeLibrary(DllHandle);

폼의 종료에 넣어 두면 될 것이다.


자... 이제 보너스를 드릴 차례이다. ^^

앞에서도 얘기 했듯이 Exe 파일도 리소스에 포함시켜서 사용할 수 있다. 눈치채신
분도 있으시겠지만 별 특별한건 없다. 그저 TResourceStream을 이용하는 것이다. 
그 외에 MIDI 파일도 넣어보고 String 데이터도 한 번 넣어 보자. 

[MyExeRc.rc]

MyNotePad EXEFILE c:\windows\NotePad.exe
MyMIDI MIDI xi_shui_chang_liu.mid

STRINGTABLE 
{
1, "하영재 바보! 윽... 앞으론 이런 예문은 만들지 않으렵니다."
2, "델파이 코리아 만세!!! (www.delphikorea.com)"
}

위 에서 설명한대로 컴파일 후 Dll에 포함시켜서 다시 컴파일 한다.
그럼 Dll이 만들어 질 것이다. 여기서 Dll이름은 MyExeDll.dll로 했다. 

먼저 dll을 로드 한다.

var
DllHandle: THandle;
...

DllHandle := LoadLibrary('MyExeDll.dll');
...

[Exe 파일 저장하고 실행하기]

var
ExeRes: TResourceStream;
begin
if DllHandle = 0 then Exit;

ExeRes := TResourceStream.Create(DllHandle, 'MyNotePad', 'EXEFILE');
ExeRes.SaveToFile('Note.exe');
WinExec(PChar('Note.exe'), SW_SHOW);
ExeRes.Free;
end;

설명은 생략한다. 위에서 모두 했으므로... ^^

[MIDI 파일 저장하고 연주하기]

var
MidiRes: TResourceStream;
begin
if DllHandle = 0 then Exit;

MidiRes := TResourceStream.Create(DllHandle, 'MyMIDI', 'MIDI');
MidiRes.SaveToFile('MyMIDI.Mid');

MediaPlayer1.FileName := 'MyMidi.mid';
MediaPlayer1.Open;
MediaPlayer1.Play;
MidiRes.Free;
end;

여기서 MIDI의 연주는 MediaPlayer를 이용했다. 역쉬 설명 생략... ^^

[String Resource 사용하기]

리소스내의 String을 읽어오기 위해 LoadString 함수를 이용한다.

var
FText : pchar;
begin
if DllHandle = 0 then Exit;

GetMem(FText, 1025);

LoadString(DllHandle, 1, FText, 1024);
ShowMessage(string(FText));
FreeMem(FText);
end;

마찬가지로 사용한 Library를 해제해 준다.

FreeLibrary(DllHandle);


보너스 끝... ^^



3. 마치며...

언제나 그렇듯이 좀 좋은 강좌를 쓸수 없을까 늘 고민을 하게 된다. 이번에도
아쉽지만 이쯤에서 마무리를 지을까 한다. 다음번에 여유가 된다면 Icon
Library에서 Icon을 읽고 또 원하는 Icon만 모아서 Icon Libarary를 만드는 방법에
대해 강좌를 해 볼까 한다. (물론 원하시는 분들이 계시다면...^^)
다음에 더 좋은 강좌로 찾아 뵐 것을 약속드리며 이만 줄이겠다. 



 
Posted by SB패밀리
델파이 RLINK32: out of memory 
델파이 컴파일을 하다보면 RLINK32 에러가 발생하는 경우가 있다.

이 경우는 코드상의 에러가 아니어서 설정 변경을 통해서 에러를 수정할 수가 있다.
아래의 방법들을 시도해 보는 것으로 에러를 수정할 수가 있다.


1 리소스명을 짧게 한다.
2. dll 파일을 더 작은 리소스 수로 여러개로 나눈다.
3. 이미지 리소스를 여러개의 strips(icons, glyphs, etc)으로 결합한다.
4. 만약 icon 파일에 128x128 크기의 형식이 포함되어 있다면 이것을 제거하면 확실하게 수정된다.




 
Posted by SB패밀리