예전에 NSIS를 이용 할 경우 대용량 파일을 배포 할때 오류가 나는 문제에 대해서 포스팅을 한 적이 있다. (자세한 내용은 http://www.yuno.org/295 을 참고)

이 때, 해결 했던 방법 중에서 일반적으로 쓰인 방법이 인스톨러와 데이터 파일을 배포해서 2개 이상의 파일을 이용한 배포였다. 그런데, NSIS 자체에서 압축 기능을 지원하고는 있지만, 별도의 압축 파일을 처리 하는 모듈은 없기 때문에 NSIS의 EXCUTE SCRIPT 를 이용해서 COMMAND로 실행 가능한 7Z 압축 바이너리에 압축 풀 파일을 넘겨주어 압축을 푸는 방식을 이용했다.

즉, INSTALL.EXE 와 DATA.7Z 이렇게 2개의 파일을 배포 하지만 INSTALL.EXE 는 설치 능력은 있지만 DATA.7Z 파일의 압축을 풀 능력이 없기 때문에 INSTALL.EXE 자체에 7Z.EXE 를 함께 넣어서 설치 과정중에 7Z.EXE를 별도의 프로세스로 실행해서 DATA.7Z 를 압축 푸는 방식을 이용했다.

하지만, 이 방식이 조금 문제가 있는 것이.. 표준 스트림 버퍼를 이용을 하고는 있지만 7Z.EXE 에서 출력 되는 메세지가 이유를 알 수 없게 버퍼링이 되어서 현재 상태를 바로 보여 줄 수 없다는 문제가 있다. 다시 말하면, 100개의 파일을 압축 푸는데, 어떤 파일을 압축을 풀었다 라고 100번의 메세지가 DETAIL LOG에 나와야 하지만 어떠한 이유에서 이 출력될 LOG가 버퍼링 되어서 뭉쳐서 나오는 문제가 생긴 것이다.

따라서 설치 시간이 오래 걸리는 대용량 설치의 경우 유저들은 이게 설치가 되는건가? 하는 의구심을 품게 된다. 또한 현재 설치가 어느 정도 진행 됐는지 알 수가 없어서 답답함을 가질 수 밖에 없었다.

우리 회사에서도 NSIS를 이용한 배포를 하는데 항상 저 문제가 마음에 걸렸다. 언젠가 시간이 나면 NSIS용 7Z PLUG-IN을 만들어야겠다! 라고 생각하고 있었는데, 최근에 제작 할 시간이 생겨서 작업을 시작하고 보니.. 이런! 이미 나와있다. -_-

Nsis7z plug-in ( 참고 : http://nsis.sourceforge.net/Nsis7z_plug-in ) 을 이용할 경우 NSIS 인스톨러를 이용해서 압축을 풀 수 있게 된다. 대용량 파일을 압축 풀더라도 버퍼링으로 인한 메세지가 밀려서 나오는 문제도 해결 할 수 있을 뿐만 아니라, 현재 설치 상태를 % 로도 표시를 할 수 있다.

사용법도 매우 간단해서, 기존의 처럼 7Z.EXE 를 더 이상 인스톨러에 포함하지 않아도 된다. 플러그인을 다운 받아서 NSIS7Z.DLL 파일은 NSIS의 PLUGIN 폴더에 넣어주면 준비 끝. 그 다음에는 인스톨 스크립트에 몇줄만 추가 하면 완료다.

이 플러그인을 이용 할 경우 2가지 배포 방법을 이용 할 수 있다.

NSIS는 인스톨러 컴파일을 할때 설치 데이터를 인스톨러에 함께 묶는데, 1) 이때 데이터 파일을 함께 묶는 방법, 그리고 2) 데이터 파일을 인스톨러와 별도로 배포 하는 방법으로 나뉘어 진다

1)의 경우는 작은 용량의 배포에 알맞다.
2)의 경우는 대용량 파일을 배포 할때 알맞다. NSIS가 단일 파일을 2GB 이상 지원 하지 않기 때문에 2GB 이상의 데이터를 배포 한다면 별개의 데이터 파일로 배포 하는 것이 좋다.

또한 비스타에서 실행 파일의 용량이 대용량일 경우 실행 시간이 매우 오래 걸리는 문제가 있는데, 이 문제 역시 별도의 데이터 파일을 이용 할 경우 해결 된다.


자, 그러면 플러그인에서 지원하는 명령어들을 확인해 보자. 이 플러그인은 총 3개의 명령어를 지원한다.

Nsis7z::Extract

제일 기본 적인 압축 해제 명령으로 Nsis7z::Extract "ArchiveName.7z" 와 같은 방식으로 사용을 한다. 압축이 풀리는 동안 인스톨러에 표시 되는 프로그레스바는 정상적으로 그 값을 표시 한다.

참고로, 인스톨러의 실행 위치에 함께 있는 파일을 압축 풀기 위해서는
Nsis7z::Extract "$EXEDIR\ArchiveName.7z" 로 경로를 함께 지정해주어야 한다.

경로를 반환하는 상수들에 대해서는 여기를 참고!

Nsis7z::ExtractWithDetails

기본 압축 해제 명령에 % 표시 기능을 추가 한 명령.
Nsis7z::ExtractWithDetails "DATA.7z" "Installing package %s..." 와 같이 2번째 파라미터에 스트링을 넘겨 주면 그에 알맞게 % 표시를 해준다. 위의 명령을 예로 들면 Installing package %s... 을 Installing package 퍼센트% ( 현재 용량 / 전체 용량 ) 으로 표시해준다.

Nsis7z::ExtractWithCallback

마지막으로 ExtractWithCallback의 경우 압축을 다 푼후 지정한 콜백 함수를 호출한다. 콜백을 호출 해줄 뿐 명령 자체는 Extract와 동일하다.

GetFunctionAddress $R9 CallbackFunction  ; CallbackFunction의 호출 주소를 $R9에 저장
Nsis7z::ExtractWithCallback "Data.7z" $R9 ; Data.7z의 압축 해제 후 $R9 주소 함수 호출


위의 3개 함수를 이용하면 각 현재 진행 상태를 안정적으로 표시 할 수 있다. 다만, 이 플러그인의 조금 아쉬운 점은 압축 풀고 있는 파일명을 표시 안해주는 것이다. 하지만, 이 플러그인은 코드가 공개 되어 있으므로 조금만 수정하면 현재 압축 해제 파일명을 DetailPrint 를 이용하여 표시하는 것은 큰 문제가 되지 않을 것이라 생각된다.

posted by Yuno.org
  • Gerbera 2011.02.18 11:32 신고

    NSIS 로 setup 배포할려고하는데 질문있어서 글올립니다.
    현재 배포시키려는 데이터 용량은 10기가 정도인데요
    용량이 크다보니깐 dvd 로 굽는데도 10기가를 나눠서 굽어야하는 문제점이 발생하네요
    분할 압축을 하여 7z 플러그인 명령어는 분할압축데이터에는 먹히질 않는거 같고;
    ExecWait 명령으로 7z 분할 압축 된것을 풀려고 시도를 해봤는데 dvd 로 나누어서 압축 데이터를 넣다보니 압축해제가 되질 않네요
    NSIS로 데이터 10 기가를 dvd 3장으로 나누어서 배포 할 수 있는 방법은 없을까요?

    • Yuno 2011.02.18 12:02 신고

      두가지 방법이 있을것 같습니다.

      처음에는 7z 플러그인 자체를 수정 하는 방법..

      7z 의 플러그인의 코드가 공개 되어 있습니다~ 분할압축데이터에 적용이 가능하도록 플러그인을 수정하는게 제일 깔끔해 보일 것 같습니다.

      두번째는 데이터가 10기가라고는 하지만, 데이터가 실제로는 분할 되어 있다는 가정을 한다면 DVD 여러장에 맞도록 데이터를 분할해서 임의로 압축하고 (파일을 수동으로 나눠서 압축 한다고 생각하시면 될듯)

      NSIS 에서 디스크 1, 2, 3.. 등에 해당하는 스크립트를 따로 제작해서 1번 압축이 끝나면 2번을 넣어 달라는 메세지 형식으로 하는 방법이 있을 것 같습니다.


게임 케릭터 미리 보기 작업을 하느라 처음으로 ASP.NET C#을 이용해서 그래픽 파일을 브라우저로 전송하는 코드를 작성할 일이 생겼다.

ASP.NET을 처음 만져봐서 각종 정보를 찾아 보니 든든한 클래스가 많이 존재했다. Bitmap, ImageConvert, Color 등..

따라서 게임 그래픽 데이터 파일을 로드하여 Bitmap 에 넣고 각종 변환 작업 (색 변경, 조합, 이미지 머지 등) 수행 후에 BMP로 저장 또는 Response.OutputStream으로 바로 이진 전송을 해도 오류가 나는게 아닌가.

오류도 당황스럽게 GDI+ 일반 오류. (A generic error occurred in GDI+) 가 발생 하는게 아닌가..

뭐가 문제란 말이냐!! GIF나 JPG로는 잘 저장이 되는데! 심지어 파일로도 저장이 잘 되는데 BMP나 PNG, TIFF 등 이미지 손실이 없을 것만 같은 것들은 모조리 저장이 안된다니!?

한참을 고민하다가 해결 방법을 찾았다. 바로 목표 스트림으로 출력하는게 아니고 별도의 메모리 스트림을 거쳐서 찍으니까 모든게 해결. ... 쳇;

뭔가 별도의 포멧으로 저장 할때는 스트림에서 읽었다 썼다를 반복이라도 하는건가!?

------------------------------
  Bitmap Image;

  Image = new Bitmap (320,240);

  Image에 대한 그래픽 처리 blah blah..

  Response.Clear();
  Response.ContentType="image/bmp";
  Image.Save( Response.OutputStream, ImageFormat.Bmp);
  Image.Dispose();

- 출력 오류 GDI+ 일반 오류 발생.

------------------------------

  Bitmap Image;
  MemoryStream memStream = new MemoryStream();

  Image = new Bitmap (320,240);

  Image에 대한 그래픽 처리 blah blah..

  Response.Clear();
  Response.ContentType="image/bmp";
  Image.Save( memStream, ImageFormat.Bmp);
  memStream.WriteTo( Response.OutputStream );
  Image.Dispose();

- 성공


posted by Yuno.org