Delphi Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
델파이 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
FreePascal/Lazarus
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
델마당
볼랜드포럼 광고 모집

델파이 팁&트릭
Delphi Programming Tip&Tricks
[43] [팁]DLL 폼을 이미지 저장 창고로 활용하자
Soo [jeensoo] 7149 읽음    2003-06-25 16:33
DLL 폼을 이미지 저장 창고로 활용하자  

필자는 프로그램 개발시 사용되는 이미지 데이터를 효율적으로 관리하는 방법을 고려했습니다.

이유인 즉은 각종 이미지를 그냥 TImage에 그냥 디자인시에 끌어다 붙혀놓고 개발을 하자니
실행화일 사이즈가 대단해 질거 같고, 또 일일히 이미지 화일로 존재시켜 그것을 폼 로딩시
끌어오자니 어딘가 석연치 않고(필자는 숨기기를 좋아하는 습성상.., 아마 모든 사람은 개인만의
것을 소유하거나 장소를 원하는것을 좋아하듯..,)
또 이렇게 되면 프로그램 배포시마다 함께 배포하지니 그것도 여간 난감하지 않을수 없었지요.

다음과 같은 해괴(?)한 발상을 하게됐습니다.
동적 DLL을 사용한 어플리케이션의 이미지 데이타 공유 입니다.

즉 DLL에 임의의 폼을 두고 그 폼에는 TImage 나 TImageList 형태의 콤포넌트를 두고(물론 그 내부에는
Binary Image Data 값이 들어있는 상태로)
이것을 Application(이하 main)이 실행시 동적으로 DLL을 로딩해 필요한 이미지 Data만 main쪽 image에
끌어오고(Copy) DLL은 해체해 버리는 방식을 말하는 것이죠.

그런데 필자의 짧은 머리로 이것을 구현하자니., 여간 어렵지 않더군요.


그래서 맨처음엔 TImage형식의 리턴값을 function을 썼죠

[main쪽..]
type 
    TResGetImage = function (ImgName: string): TImage; stdcall; 

.. 

procedure TfrmMP_MAIN.FormCreate(Sender: TObject); 
var 
   ResGetImage: TResGetImage; 
   CurrInst: THandle; 
   FarPtr: TFarProc; 
begin 
     CurrInst := LoadLibrary('RESOURCE.DLL'); 
     if(CurrInst > 0) then 
     begin 
          try 
             FarPtr := GetProcAddress(CurrInst, 'GetImage'); 
             if(FarPtr <> nil) then 
             begin 
                  ResGetImage := FarPtr; 
                  Image1.Assign(ResGetImage('imgPIC1')); 
                  Image2.Assign(ResGetImage('imgPIC2')); 
                  Image3.Assign(ResGetImage('imgPIC3')); 
             end; 
          finally 
             FreeLibrary(CurrInst); 
          end; 
     end 
     else hMessageDlg('"RESOURCE.DLL" 파일이 존재하지 않습니다', mtError, [mbOk], 0); 
end; 


[DLL쪽]
library RESOURCE; 

uses 
  Forms, ExtCtrls, 
  RESOURCE1 in 'RESOURCE1.pas' {frmIMAGES}; 


var 
  Image: TImage; 

function GetImage(ImgName: string): TPersistent; stdcall; 
var 
   frmIMAGES: TfrmIMAGES; 
begin 
     Application.CreateForm(TfrmIMAGES, frmIMAGES); 
     try 
        Image := TImage.Create(Application); 
        Image.Assign(frmIMAGES.FindComponent(ImgName));        <--- 이것을 Image.Picture로 해보고.., 등등 
     finally; 
        frmIMAGES.Free; 
        Result := Image; 
        Image.Free; 
     end; 
end; 

exports 
   GetImage; 

end. 


그럴싸해 보이지만 여지없이 Memory Allocation 에러가 난납니다.  해답은 여러분이 찾아보세요.
-------------------------------------------------------------------------------------

두번째 방법
프로시저를 통한 TImage형식의 Argument(이하 Arg) 전달
[main쪽]

type 
//    TResGetImage = function (ImgName: string): TPicture; stdcall; 
//    TResGetImage = procedure (ImgName: string; Sender: TObject); stdcall; 
    TResGetImage = procedure  (Main: TApplication; ImgName: string; Sender: TObject); stdcall; 

... 
... 

procedure TfrmMP_MAIN.FormCreate(Sender: TObject); 
var 
   ResGetImage: TResGetImage; 
   CurrInst: THandle; 
   FarPtr: TFarProc; 
begin 
   //  if(cnt > 0) then exit; 
   //  cnt := 1; 

     CurrInst := LoadLibrary('RESOURCE.DLL'); 
     if(CurrInst > 0) then 
     begin 
          try 
             FarPtr := GetProcAddress(CurrInst, 'GetImage'); 
             if(FarPtr <> nil) then 
             begin 
                  ResGetImage := FarPtr; 
                  ResGetImage(Application, 'imgPIC1', Image1); 
                  showmessage('111'); 
             //     ResGetImage('imgPIC1', Image1.Picture); 
             //    Image1.Picture.Assign(ResGetImage('imgPIC1')); 
             //    Image1.Picture.Assign(Image4.Picture ); 
             //    Image2.Assign(ResGetImage('imgPIC2')); 
             //    Image3.Assign(ResGetImage('imgPIC3')); 
             end; 
          finally 
                  showmessage('111'); 
             FreeLibrary(CurrInst); 
                  showmessage('111'); 
          end; 
     end 
     else hMessageDlg('"RESOURCE.DLL" 파일이 존재하지 않습니다', mtError, [mbOk], 0); 
end; 


[DLL 쪽]
library RESOURCE; 

uses 
  Forms, Classes, Graphics, ExtCtrls, jpeg,   Dialogs, 
  RESOURCE1 in 'RESOURCE1.pas' {frmIMAGES}; 

{$R *.RES} 

procedure GetImage(Main: TApplication; ImgName: string; Sender: TObject); stdcall; 
var 
   frmIMAGES: TfrmIMAGES; 
   Stream: TStream; 
begin 
     Application.Handle := Main.Handle; 
     Application.CreateForm(TfrmIMAGES, frmIMAGES); 
     try 
        showmessage(TImage(Sender).Name); 

        Stream := TStream.Create; 
        frmIMAGES.imgPIC1.Picture.Graphic.SaveToStream(Stream); 
        TImage(Sender).Picture.Graphic.LoadFromStream(Stream); 
     //   TImage(Sender).Picture.Graphic.Assign(frmIMAGES.imgPIC1.Picture.Graphic); 
        showmessage('111111'); 
     //   Result := TempPic; 
     //   frmIMAGES.imgPIC2.Picture.Assign(Result); 
     //   frmIMAGES.ShowModal; 
     finally; 
        frmIMAGES.Free; 
        Stream.Free; 
     end; 
end; 

exports 
   GetImage; 

end. 


    DLL 쪽에서 arg로 받은 TImage를  단순히 프로퍼티나
    TImage(Sender).Picture := nil;
    TImage(Sender).Picture.Graphic.Clear; 는 당연 됩니다.
    그러나 정작 중요한 Assign 이나,  stream은 되지 안죠
    또 메인쪽도..  메인폼이 Creation 시나 Show시 Active시에서도 처리를 해도
    결국엔 런타임시 Access Violation 에러는 여전히..,
    
    그래서 고민고민하다 델마당.., 델파이코리아 델파이개발자 페이지에도 q&a에 올렸지만..
    답은 안나오더군요.
    왜 그럴까요~?  생각해 보세요..
--------------------------------------------------------------------------------------
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .

    결국 후배를 꼬득여 test 해보게 했지요.
    그런데 이것저것 해본 후배는 30분이 지난뒤 갑자기 된다며 전화가 오더군요.
    오케바리~~~   그!러!나   후배왈 FreeLibrary 부분을 주석처리후에 컴파일해서 실행된다는 거였습니다.
    허~~~~~~~~~~걱..     저는 또 고민하지 않을수 없었습니다.
    물론 후배에게 문제를 던지기 이전에도 혹시 arg로 넘겨받은 TImage의 메소드들중
    Assign이나 LoadFromFile LoadFrom Stream과 같은 해당 TImage의 Picture의 메모리 참조에
    영향을 주는 메소드들이 결국 DLL쪽의 힙이나, 메모리를 할당하여 처리후 이것을
    Arg로 넘겨받은 TImage의 Picture에 참조 시키는것은 아닌가 이런 의구심은 가지고 있었는데..,
    결국 이것이 현실로 나타난것이죠.

    그래서 결국엔 또 한반의 난관에 빠지지 않을수가 없었습니다.
    그래서 애라 몰겠다 차라리 Access db에 몰아넣고 Ado로 불러다 쓰기로 방향전환을 했다가..
    그래도 들어간 시간이 아까워 오기가 생기더군요...

    그래서 Windows Api 함수들을 찾다가 결국 클립보드와 GlobalAlloc을 찾았지만..
    GlobalAlloc 같은 경우는 쓰기가 여간 복잡하지 않더군요..  거기다
    서로 다른 두 Application이(Main과 DLL쪽) 있는 상황에서는 더욱 복잡해 보일것 같았습니다.
    그래서 결국 클립보드를 활용하게 됐습니다.
    다행이 TImage.Picture.SaveToClipboardFormat 과 TImage.Picture.LoadFromClipboardFormat Method를
    사용하기로 결정하고 다음과 같이 TClipImg라는 Data Type을 정의하고
    이것을 매개로 이미지를 복사하는데 성공했습니다
    다음이 완성된 소스 입니다.
    그럼 오늘도 즐프~~ 하시 기를 빌며....,  이만  꾸~~~~~~~~~뻑..
------------------------------------------------------------------------------------------------------


[Main쪽]
type 
    TClipImg = record 
        AFormat: Word; 
        AData: THandle; 
        APalette: HPALETTE; 
   end; // 클립보드 이미지 데이터.. 

type 
    TResGetImage = function (ImgName: string): TClipImg; stdcall; 

procedure TfrmMP_MAIN.FormActivate(Sender: TObject); 
var 
   ResGetImage: TResGetImage; 
   CurrInst: THandle; 
   FarPtr: TFarProc; 
   ClipImg: TClipImg; 
begin 
     if(cnt > 0) then exit; 
     cnt := 1; 

     CurrInst := LoadLibrary('RESOURCE.DLL'); 
     if(CurrInst > 0) then 
     begin 
          try 
             FarPtr := GetProcAddress(CurrInst, 'GetImage'); 
             if(FarPtr <> nil) then 
             begin 
                  ResGetImage := FarPtr; 
                  ClipImg := ResGetImage('imgPIC1'); 
                  Image1.Picture.LoadFromClipboardFormat(ClipImg.AFormat, ClipImg.AData, ClipImg.APalette); 
                  showmessage('111'); 
             //     ResGetImage('imgPIC1', Image1.Picture); 
             //    Image1.Picture.Assign(ResGetImage('imgPIC1')); 
             //    Image1.Picture.Assign(Image4.Picture ); 
             //    Image2.Assign(ResGetImage('imgPIC2')); 
             //    Image3.Assign(ResGetImage('imgPIC3')); 
             end; 
          finally 
             FreeLibrary(CurrInst); 
          end; 
     end 
     else hMessageDlg('"RESOURCE.DLL" 파일이 존재하지 않습니다', mtError, [mbOk], 0); 
end; 


[DLL쪽]
library RESOURCE; 

uses 
  Forms, Classes, Graphics, ExtCtrls, jpeg,   Dialogs, Windows, 
  RESOURCE1 in 'RESOURCE1.pas' {frmIMAGES}; 

{$R *.RES} 

type 
   TClipImg = record 
       AFormat: Word; 
       AData: THandle; 
       APalette: HPALETTE; 
   end; 

function GetImage(ImgName: string): TClipImg; stdcall; 
var 
   frmIMAGES: TfrmIMAGES; 
   img: TClipImg; 
begin 
     Application.CreateForm(TfrmIMAGES, frmIMAGES); 
     try 
        TImage(frmIMAGES.FindComponent(ImgName)).Picture.SaveToClipboardFormat(img.AFormat, img.AData, img.APalette); 
        showmessage('111111'); 
     finally; 
        frmIMAGES.Free; 
        Result := img; 
     end; 
end; 

exports 
   GetImage; 

end. 

+ -

관련 글 리스트
43 [팁]DLL 폼을 이미지 저장 창고로 활용하자 Soo 7149 2003/06/25
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.