Tech/iOS 개발2011. 1. 18. 18:39

XCode의 디버거에서는 변수의 retain count를 볼 수가 없어서 불편한데요, 디버깅 중에 볼 수 있는 방법이 없지는 않습니다. gdb의 디버깅 콘솔을 띄워서 다음과 같이 입력하면 theObj의 retain count 가 출력됩니다.

print (unsigned int)[theObj retainCount]


참고로, 재미삼아 위의 문장에서 (unsigned int) 부분을 지우고 실행해보면 '리턴 타입 정보가 없어서 "objc_msgSend" 함수를 호출할 수 없다'는 오류 메시지가 표시됩니다. Objective-C 에서 메시지 표현식이 내부적으로 어떻게 구현되는지 엿볼 수 있는 부분이겠네요. ^^

Posted by wafe

댓글을 달아 주세요

Tech/iOS 개발2011. 1. 18. 16:03


오랜만에 글을 쓰네요. 연말 중에는 정신 없어서 방치했던 개발부 블로그님...

요즘 Xcode를 뚝딱거리느라 -_-; iOS에서 svn을 사용하는 방법을 wafe 과장님께서 알려주셨습니다.
iOS에서 Tortoise SVN처럼 폴더 메뉴를 이용하여 svn에서 업데이트하고, 커밋할 수 있는 놈이 있더군요.
바로 SCPlugin인인데요.

설치 방법과 사용법은 굉장히 간단합니다.

SCPlugin 사이트에서 Download 탭을 누르신 뒤, 가장 최신 설치 파일을 다운로드 받으셔서 설치하시면 됩니다.

설치를 마쳤으면, Application(응용 프로그램)폴더로 이동하여 SCToolbarButton을 마우스로 클릭한 채로 Finder 윈도우 상단으로 드래그 하여 이동시킵니다.



svn에서 체크아웃 받을 폴더로 이동하여, SCToolbarButton을 클릭하면, svn 관련하여 익숙한 메뉴들이 나열됩니다.

여기서 당연히 Checkout을 클릭하시면 되겠죠? ' ㅅ')/
Checkout을 클릭하면, 저장소 주소(Repository URL), 본인 계정(Username, Password), Checkout받을 프로젝트 폴더 경로(Checkout to)를 설정해주시면 됩니다.



모든 설정을 완료한 뒤, Checkout 버튼을 클릭하면 끝!
간단하죠잉 ' ㅅ')/
이후부터는, SCToolbarButton을 통해 업데이트, 커밋을 사용하시면 됨돠.

그럼 저는 이만 뿅!
Posted by 벚꽃손님

댓글을 달아 주세요

  1. 어머 이건 허석 씨가 알려준건데용.

    2011.01.18 18:23 신고 [ ADDR : EDIT/ DEL : REPLY ]
  2. veggar

    가뭄에 단비 같은 글이군요. 감사합니다.^^

    2011.04.11 17:30 [ ADDR : EDIT/ DEL : REPLY ]
  3. 근데 사실 SCPlugin 보다는 svnX 가 좀 더 쓰기 편하고 좋아서 SCPlugin 은 안쓴 지 좀 되었습니다. 그리고 Xcode 4에서는 소스 관리 기능이 Xcode 에 좀 더 확실하게 통합되어서 많이 쓸만해졌습니다. 마지 VisualStudio 에 AnkhSVN 을 설치한 것처럼 말이죠.

    2011.07.21 11:44 신고 [ ADDR : EDIT/ DEL : REPLY ]

Tech/Objective-C 언어2011. 1. 17. 23:55

Objective-C 에 구현된 개체 중심(Object-Oriented, 보통은 객체 지향이라고 하죠) 패러다임 관점은 Smalltalk 언어에서 영향을 받았습니다. 그래서 프로그램이란 개체들과 그 개체들 사이에서 일어나는 메시지 교환으로 바라보는 것이 Objective-C 에서의 OOP 관점입니다.  클래스 조차도 개체로 생각하기 때문에, 클래스에도 메시지를 보낼 수 있게 되어 있습니다. C++의 static method 와 대응되는 개념이라고 볼 수도 있죠. 특정 클래스의 인스턴스를 생성할 때 사용하는 alloc이나 init 같은 것이 대표적입니다.

C++의 메소드 호출과 대응된다고 볼 수 있는 Objective-C의 메시지 표현식은 아래와 같은 형태입니다.

[receiver message]

수신자를 정해서 메시지를 보내는 것이지요. 

클래스에 정의된 함수들을 Objective-C 에서도 동일하게 메소드(method)라고 부릅니다. 개체들이 동작하는 방식을 결정하기 때문에 method(방식)이라고 부르는 것입니다.

[myRectangle display];

[myRectangle setWidth:20.0];

[myRectangle setOriginX: 30.0 y:50.0]

위의 세 가지 메시지 표현식은 인자(argument)를 지정하지 않았거나, 하나 지정했거나, 두 개 지정한 메시지 표현식을 보여주고 있습니다. 각각의 메시지 표현식에서 인자를 뺀 부분, 즉 display, setWidth:, setOriginX:y: 는 개체에 있을 메소드를 선택하는 역할을 하고 있으므로 선택자(selector)라고 합니다. 

참고로, Objective-C 에는 메시지 포워딩이라는 개념이 있는데, 특정 개체에 구현되지 않은 메시지를 받았을 때 다른 개체에게 메시지를 보내서 처리를 위임하는 개념을 의미합니다. COM에도 비슷한 개념이 있는데, 메시지를 주고 받는다는 개념에서 출발한 '메시지 포워딩'이라는 말이 좀 더 이해하기 좋다는 생각이 듭니다. 


'Tech > Objective-C 언어' 카테고리의 다른 글

여러 개의 인자를 받는 메소드  (0) 2011.01.20
메시지, 메소드, 셀렉터에 대해서  (0) 2011.01.17
Posted by wafe

댓글을 달아 주세요

Tech/WPF2010. 10. 29. 17:06
WPF에서 이미지를 보여줄 때 Image 태그의 Source를 설정해서 보여주는 방식을 사용한다.

이때 로딩된 이미지 파일을 삭제하려고 하면, Exception이 발생한다.
이것은 이미지의 Source를 null로 설정하여도 동일하게 발생하는데 
이를 해결하기 위해서 이미지 Source의 Cache 옵션을 설정하여 해결할 수 있다.

1
2
3
4
string filepath = @"c:\test.jpg";
this.img.Source = new BitmapImage(new Uri(filepath, UriKind.RelativeOrAbsolute));
this.img.Source = null;
File.Delete(filepath);
!!!Exception 발생


1
2
3
4
5
6
7
8
9
10
11
string filepath = @"c:\test.jpg";

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.CacheOption = BitmapCacheOption.OnLoad;
bi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
bi.UriSource = new Uri(filepath);
bi.EndInit();
img.Source = bi;

File.Delete(filepath);
정상적으로 파일이 삭제됨
Posted by 자이닉스 일이

댓글을 달아 주세요

  1. 인새값자

    BitmapImage bi = new BitmapImage(new Uri(Path));
    image1.Source = bi;
    bi.BeginInit();
    bi.CacheOption = BitmapCacheOption.OnLoad;
    bi.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
    bi.UriSource = new Uri(Path);
    bi.EndInit();
    File.Delete(Path);

    //아직도 예외가 납니다 혹시 ㅜ.ㅜ 도와주세요

    2011.04.07 13:26 [ ADDR : EDIT/ DEL : REPLY ]

Tech/닷넷 일반2010. 9. 19. 20:28

Visual C++로 데스크톱용 제품을 만들 때에는 주로 MFC 기반으로 작업을 하게 됩니다. 이 때는 모듈화를 할 때 MFC ActiveX를 주로 사용합니다. 이것은 Visual C++ 6.0 시절부터 개발해왔던 습관 때문이라고 볼 수 있겠습니다. MFC 응용프로그램에서든, Internet Explorer에서든 모듈을 사용하는 면에서든 COM 모듈을 쓰는 것과 차이가 없고 만들기는 더 편하니까요.

그런데 서비스는 UI가 제공되지 않는 세션(Session 0)에서 실행됩니다. 따라서 ActiveX 같이 윈도우가 없이는 생성조차 되지 않는 모듈은 사용할 수 없습니다. 서비스가 실행되는 세션에서는 데스크톱 윈도우도 없기 때문에 데스크톱 DC를 이용해서 이미지를 조작한다거나 하는 것도 불가능할 정도이거든요.

하지만 이미 만들어져 있는 수많은 ActiveX 모듈을 생각하면 그냥 그렇구나하며 넘어갈 수는 없죠. 일단 검색을 좀 해보니 윈폼을 쓰면 될 거라는 얘기가 있습니다. 이 정보 하나를 가지고 1차 시도를 해봅니다.

예제로 사용할 ActiveX 모듈은 GDI+를 이용해서 이미지를 JPEG 형식으로 저장해주는 간단한 모듈입니다. TheActiveOne이라는 예제 모듈에는 SaveAsJpg 라는 메서드가 하나 있는데, 아래와 같이 이미지를 저장하는 단순한 메서드입니다. from은 원본 이미지 파일 경로이고, to는 새로 생성될 jpg 이미지 파일의 경로입니다. 

InitializeGdiPlus();

 

Image image(from);

CLSID jpegClsid;

GetEncoderClsid(_T("image/jpeg"), &jpegClsid);

 

image.Save(to, &jpegClsid, NULL);

 

FinalizeGdiPlus();

이제 서비스 프로젝트를 하나 만듭니다. 이름은 TheAxService라고 지었습니다. 서비스를 만드는 방법은 이전의 포스트를 참고하시기 바랍니다. 윈폼을 쓰면 될 것이라 했으니, TheActiveOneForm이라는 윈폼을 하나만들고 TheActiveOne 모듈을 넣습니다. 그리고 서비스 클래스 쪽에서 이미지 저장 기능을 사용할 수 있도록 메서드를 하나 추가 했습니다.

public long SaveAsJPG(string srcImgPath, string outJpgPath)

{

    return this.axTheActiveOne1.SaveAsJpg(srcImgPath, outJpgPath);

}

서비스 진입점에서 윈폼을 만듭니다. 그리고 UI는 Single Thread Apartment에서 생성되어야 할 것이므로 서비스 진입점에 STAThread Attribute를 붙여줬습니다. 그 결과 Program.cs 의 Main 메서드 내용은 아래와 같이 바뀌었습니다.

[STAThread]

static void Main()

{

    //System.Diagnostics.Debugger.Launch();

 

    ServiceBase[] ServicesToRun;

    ServicesToRun = new ServiceBase[]

       {

             new TheAxService(new TheActiveOneForm())

       };

    ServiceBase.Run(ServicesToRun);

}

TheAxService 클래스의 생성자에 폼을 생성해서 넘겨주고 있는 것을 위의 코드에서 볼 수 있습니다.

자, 그러면 핵심인 TheAxService 클래스의 코드를 보겠습니다.

public partial class TheAxService : ServiceBase

{

    TheActiveOneForm axForm;

 

    public TheAxService(TheActiveOneForm form)

    {

        InitializeComponent();

 

        this.axForm = form;

    }

 

    protected override void OnStart(string[] args)

    {

        this.axForm.Load += new EventHandler(axForm_Load);

        this.axForm.Show();

    }

 

    void axForm_Load(object sender, EventArgs e)

    {

        this.axForm.SaveAsJPG(@"C:\Users\wafe\Pictures\office live 2.PNG", @"C:\Users\wafe\Pictures\office live 2.jpg");

    }

}

OnStart 메서드에서 폼을 Show 하고 있고, Load 이벤트 핸들러에서 이미지 파일을 저장하는 기능을 호출하고 있습니다.

이제 서비스를 빌드해서 설치하고 실행시키면!

네, 잘 안됩니다. Windows 7에서 개발하고 있는 제 경우에는 윈도우 관리자가 이상해져서 Windows Server 2008 처럼 클래식 테마로 바뀌는 등 난리도 아닙니다. -_-;;

지금까지 만든 코드에는 제대로 동작하지 않을만한 문제가 있습니다. 디버거를 통해서 확인해보면, Main 메서드가 호출되는 메인 쓰레드와 OnStart 메서드가 호출되는 쓰레드는 서로 다른 쓰레드입니다. ActiveX를 생성하지 않은 쓰레드에서 ActiveX의 메서드를 호출하는 것은 문제가 있죠.

그렇다고 OnStart 메서드에서 ActiveX를 포함하는 윈폼을 생성하도록 하는 것은 불가능합니다. STA 쓰레드가 되도록 제어할 수 없는 쓰레드에서 OnStart 메서드가 호출되기 때문입니다.

그렇다면 별도의 쓰레드를 생성해서 윈폼을 만들고 이미지 저장을 하도록 하면 어떨까 하는 생각을 하게 되었습니다. 그 내용은 다음 포스트에서 다뤄보도록 하겠습니다.

 

Posted by wafe

댓글을 달아 주세요

Tech/닷넷 일반2010. 9. 1. 17:46

    빠른 공유를 위해 내용보다는 이미지를 주로 이용하여 기본적인 사항만을 전해드리도록 하겠습니다.

    서비스 프로젝트 만들기

  • Visual C# > Windows > Windows 서비스를 선택.

  • 솔루션 탐색기에서 Service1.cs 이름을 적절한 이름으로 변경.
  • 솔루션 탐색기에서 Service1.cs 를 오른쪽 클릭하여 코드 보기를 한다. 다음과 같은 메서드에 필요한 작업을 넣으면 된다.
    • 서비스를 시작할 때에는 그리 길지 않은 시간의 타임아웃 제한이 있으므로, 오래 걸리는 작업은 OnStart 에 넣지 않는 것이 좋다.

  • 이벤트 로그는 다음과 같이 기록할 수 있다.

    image

    서비스 설치 기능 추가

  • Service1.cs의 디자인 모드에서 오른쪽 클릭하여 "설치 관리자 추가"를 선택한다.

  • 그러면 ProjectInstaller.cs 가 추가되어 디자인 모드로 표시된다.
  • serviceProcessInstaller1 을 선택하고 속성 패널에서 속성을 설정한다.
    • Account의 기본값은 User로 되어 있는데, 보통 서비스는 LocalSystem을 사용한다.

  • serviceInstaller1 을 선택하고 속성 패널에서 속성을 설정한다.
    • Description : 서비스 관리 목록에서 표시되는 서비스 설명
    • ServiceName : 서비스 관리 목록에서 표시되는 서비스 이름
    • StartType : Atomatic - 시스템 부팅 시 서비스가 시작되도록 설정

    서비스 설치 및 제거

  • 서비스를 빌드한 후, Visual Studio Command Prompt 에서 다음과 같은 명령을 실행한다.
    • installutil "서비스 EXE 경로"

  • 서비스 관리 도구에 추가된 것을 볼 수 있다.

  • 서비스 제거 시에는 /u 옵션을 추가하면 된다.
    • installutil /u "서비스 EXE 경로"

Posted by wafe

댓글을 달아 주세요

  1. installutil 이 있는 폴더 위치는
    C:\Windows\Microsoft.NET\Framework\v2.0.50727
    입니다.

    Visual Studio Command line 이 없는 경우에는 직접 저 경로에 있는 것을 사용하면 됩니다.

    2010.09.05 19:00 신고 [ ADDR : EDIT/ DEL : REPLY ]

Tech2010. 8. 27. 15:29

안녕하세요, 자이닉스의 꽃순이입니다.
(자제하겠습니다.)

최근 저희는 7월달 동안 바쁜 일정 보내다 이제 아주 쬐꼼 여유가 생겨, 블로깅할 떡밥을 물고 왔습니다.

요즘 -_-) 급 C++이랑 데이트 중이라, 울고 있습니다.
최근
wafe 과장님(울면서)작업을 하는 중에 IE 비보호 모드에서 외부 프로그램이 실행될 때, 허용 여부를 묻는 경고창을 어떻게 처리할 것인가에 대한 방법을 찾게 되었습니다.

방법은 총 세 가지인데,
1. 경고창에서 다시 묻지 않는다는 체크 박스를 체크하는 방법
2. IE를 관리자 권한으로 실행시키는 방법
3. 레지스트리에 실행시킬 프로그램을 등록하는 방법

들이 있습니다. 우리는 이 중, 3번을 선택하여 방법을 찾던 중에,
스틸님의 블로그에서
"IE 보호 모드에서 비보호 모드 프로세스 실행" 글을 참조하였습니다.

일단 우리가 레지스트리 키를 등록시킬 위치를 알아야겠죠?
실행 창에서 "regedit(레지스트리 편집기)"를 실행시켜서 왼쪽 탭에서 위치를 찾습니다.
HKEY_CURRENT_USER
- Software
  - Microsoft
    - Internet Explorer
      - Low Rights
        - ElevationPolicy

를 확인해보세요 ^-^) 쏼라쏼라 GUID 키들이 보일 겁니다. 새로운 프로그램의 레지스트리 키를 등록할 위치가 바로 이 곳입니다.
내용 구성이 어떻게 되어 있는지 한 놈 집어서 살펴볼까요?


REG_SZ AppName, REG_SZ AppPath, REG_DWORD Policy 이 세 가지 정보가 있는데요. 이 정보들은 레지스트리 키를 생성하여 등록할 때 채워줘야 할 내용들입니다.

자, 이제 레지스트리 키를 생성하여 등록할 위치와 내용을 채울 정보들이 무엇인지 알았으니, 프로그램을 레지스트리에 등록시켜주는 예제 프로그램을 만들어보도록 합시다.
우선, 관리자 권한으로 Visual Studio를 실행시켜 새로운 MFC 응용 프로그램 프로젝트를 생성한 후,


레지스트리에 실행시킬 프로그램을 등록시켜주는 함수를 생성합니다.

이제부터 사용될 함수와 상수들은 WinReg.h에 정의된 놈들입니다.
일단, 레지스트리 키 관련한 네 가지 함수를 아시면 되는데요.

RegOpenKeyEx : 특정 레지스트리 키를 연다.
RegCloseKey : 특정 레지스트리 키를 닫는다.
RegCreateKeyEx : 새로운 레지스트리 키를 생성한다.
RegSetValueEx : 레지스트리 키의 Value를 설정한다.

새로운 레지스트리 키를 등록시킬 위치가 있는지 확인부터 합시다.
RegOpenKeyEx를 이용하여 HKEY_CURRENT_USER\\Software\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy 키가 존재하는지 확인할 수 있습니다.

long rtn = 0;					// 레지스트리 관련 작업의 수행 성공/실패 여부를 갖는 변수
HKEY rtnKey = NULL;				// 레지스트리 관련 작업의 결과물인 레지스트리 키 정보를 갖는 변수

rtn = RegOpenKeyEx(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy"), 0, KEY_ALL_ACCESS, &rtnKey);

RegOpenKeyEx의 반환값인 rtn이 0이면 레지스트리 키가 존재하는 것이고, 1이면 존재하지 않다는 것입니다.
Open한 레지스트리 키의 정보는 rtnKey에 담겨집니다.

그리고 새로운 레지스트리 키를 생성하기 위해 열었던 레지스트리 키를 닫습니다.

RegCloseKey(rtnKey);

닫은 후, 새로운 레지스트리 키를 생성해보도록 하죠.
예를 들어, calc.exe 라는 외부 프로세스가 실행된다는 것을 가정하고, 레지스트리에 새로이 등록한다고 했을 때,

DWORD dwDisp; // 레지스트리 키를 생성할 때, 상태 정보

CString guid = CreateGUID();    // GUID 키 생성
CString newRegKey;
newRegKey.Format(_T("SOFTWARE\\Microsoft\\Internet Explorer\\Low Rights\\ElevationPolicy\\%s"), guid);

rtn = RegCreateKeyEx(HKEY_LOCAL_MACHINE, newRegKey
	,0, NULL, REG_OPTION_NON_VOLATILE,  KEY_ALL_ACCESS, NULL, &rtnKey, &dwDisp);

dwDisp에 관한 설명이 조금 이상합니다만; 부가적인 설명을 덧붙이자면, RegCreateKeyEx를 이용하여 새로운 레지스트리 키를 생성할 때, 1이라는 값을 가지고 있으면, 생성할 레지스트리 키가 존재하지 않은 상태에서 생성된 상태라는 것을 의미하고, 2라는 값을 가지고 있으면, 이미 레지스트리 키가 존재하고 Open 되었다는 것을 의미합니다.
새로운 GUID를 생성하여 레지스트리 키로 등록합니다.

이제 새로운 레지스트리 키를 생성했으니, 레지스트리 키에 필요한 정보(AppName, AppPath, Policy)를 채워줍니다.

CString appName(_T("calc.exe"));
RegSetValueEx(rtnKey, _T("AppName"), 0, REG_SZ, (BYTE*)(appName.GetBuffer()), (appName.GetLength() + 1) * 2);
appName.ReleaseBuffer();

CString appPath(_T("C:\\Windows\\System32"));
RegSetValueEx(rtnKey, _T("AppPath"), 0, REG_SZ, (BYTE*)(appPath.GetBuffer()), (appPath.GetLength() + 1) * 2);
appPath.ReleaseBuffer();

DWORD dwPolicy = 3;
RegSetValueEx(rtnKey, _T("Policy"), 0, REG_DWORD, (BYTE*)(&dwPolicy), sizeof(dwPolicy));

AppName에는 외부 프로그램의 이름을 채웁니다. 저는 calc.exe를 채웁니다 ^-^)
AppPath에는 외부 프로그램의 경로를 채우고, Policy에는 3을 채우면 됩니다.

위의 작업을 모두 마쳤으면, 다시 레지스트리 키를 닫는 것으로 끝납니다.
프로그램을 실행시키고, 레지스트리 키에 calc.exe가 등록되어졌는지 확인해볼까요?


위의 사진처럼 레지스트리 편집기를 확인해보았을 때, 새로이 생성된 GUID 이름의 레지스트리 키가 생성되었고, 내용들이 채워져 있는 것을 확인하실 수 있을 것입니다.
이제 IE를 비보호 모드에서 실행시켰을 때에도, calc.exe를 실행시킬 것인지 허용 여부를 묻는 경고창이 뜨지 않을 것입니다 ^-^)

그럼 이만, 뿅!
Posted by 벚꽃손님

댓글을 달아 주세요

Tech/Silverlight2010. 6. 2. 12:04

내가 하고자 하는 것은, Rectangle이 이동하는 애니메이션을 갖고 있는 UserControl만 있는 프로젝트를 만들고, 그 프로젝트의 결곽물인 .xap 파일을 다른 프로젝트에서 동적으로 로딩하여 애니메이션을 볼 수 있도록 하는 것이다.

이 과정에서 나는 MEF를 이용할 것이다. 동적으로 .xap파일을 로딩하는 방법은 MEF말고도 있다. WebClient를 이용하여 .xap파일을 열어 Assembly 정보를 얻는 방법도 그 중 하나이다.

동적으로 .xap를 로딩하는 방법은 지난 포스팅에 소개했었다. (예제 따라해보기지만...)
우선 처음으로 할 일은 Rectangle이 이동하는 애니메이션을 갖고 있는 UserControl만 있는 프로젝트를 만드는 일이다.
나는 간단하게 Rectangle을 생성한 뒤, Rectangle이 좌측에서 우측으로 이동하면서 색깔이 변하는 Storyboard를 만들었다.
그 다음엔 애니메이션을 재생하고 있는 UserControl을 통째로 Export한다.

[Export("Animation", typeof(UserControl))]
public partial class AnimationControl : UserControl

"Animation"이라는 특정 이름과 UserControl 타입으로 외부에 노출한다는 것을 명시해주자. 그리고 빌드 한 후, .xap 결과물을 Import할 프로젝트의 ClientBin 폴더에 복사한다.
Import할 프로젝트로 이동하여, Import 짝꿍을 만들어주자. 지난 포스팅에도 말했었지만, 외부 .xap파일을 로딩하는 경우이므로 [ImportMany]를 사용한다.

[ImportMany("Animation", typeof(UserControl), AllowRecomposition = true)]
public IEnumerable<UserControl> AnimationCtrl { get; set; }

위와 같이 정의하면, AnimationCtrl에 아까 Export한 AnimationControl이 들어갈 것이다. 내가 원하는 것은 AnimationControl을 현재 프로젝트의 LayoutRoot에 추가하여 애니메이션을 보는 것이기 때문에, AnimationCtrl을 foreach문으로 돌려 AnimationControl의 정보를 추출해낸다. 이 작업을 .xap파일 다운로드가 완료되면 수행하도록 해준다.

Package.DownloadPackageAsync(new Uri("RectangleAnimation.xap", UriKind.Relative), (s, p) => 
{    
    catalog.AddPackage(p);    
    foreach (var item in AnimationCtrl)    
    {        
        this.LayoutRoot.Children.Add(item);    
    }
});

여기까지 했다면, 내가 찾던 UserControl이 현재 화면에 보이고, 애니메이션이 실행될 것이다. 난 여기에서 그치지 않고, 애니메이션 실행이 완료된 후, 메시지 박스를 호출하도록 하겠다. 그러기 위해선, 몇 줄 더 첨가해야한다.

Package.DownloadPackageAsync(new Uri("RectangleAnimation.xap", UriKind.Relative), (s, p) => 
{
     catalog.AddPackage(p);

     foreach (var item in AnimationCtrl)
     {
          EventInfo eInfo = (item.GetType()).GetEvent("Completed");
          Type handlerType = eInfo.EventHandlerType;
          Delegate d = Delegate.CreateDelegate(handlerType, null, this.GetType().GetMethod("OnCompleted"));

          eInfo.AddEventHandler(item, d);

          this.LayoutRoot.Children.Add(item);
          }
     }
);

분홍색 박스 친 부분들이 추가된 부분이다. 난 애니메이션을 포함하고 있는 UserControl 내에서 미리 애니메이션 실행이 완료되면 외부로 Completed 이벤트를 알리도록 만들어 놓았다. 그리고 외부에서 EventInfo를 이용하여, Completed로 정의된 이벤트 정보를 얻도록 한 후, Completed 이벤트가 발생하면, OnCompleted 함수를 호출하도록 연결하였다.

public void OnCompleted(object sender, EventArgs e)
{
    MessageBox.Show("Animation Complete");
}

그 이후, 간단하게 OnCompleted 에서 메시지 박스를 호출하면 끝!
참 쉽죠잉.

Posted by 벚꽃손님

댓글을 달아 주세요

  1. 이거 소스 . 없나요?;;;

    2010.07.15 20:25 [ ADDR : EDIT/ DEL : REPLY ]
    • 대략 너무 늦은 답장 죄송합니다...
      이미 지금 시점이면, 해결책을 찾으셨을 것 같아 ㅠ_ㅠ 흐르는 눈물을 닦으며 지금에야 덧글을 발견했단 말씀뿐이 못 드리겠습니다.

      이로써, 블로그 관리가 안되어있단 사실이 드러났...

      현재 소스는 따로 올려놓지 않은 상태이지만, 메일을 적어두시거나 요청하시면 소스를 올려두도록 노력하겠습니다.

      2010.08.27 16:48 신고 [ ADDR : EDIT/ DEL ]

Tech/Silverlight2010. 5. 12. 16:28

  이전에 XAP 로딩하는 문제로 MEF를 살펴본 적이 있었는데, 그 때에는 잘 이해하지 못해 포기했었다.
  그러다, 이제와서 다시 살펴 볼 기회가 생겨 MEF에 대해 조사하게 되었다.

  이제사 간신히 예제 하나를 따라잡기 해 본 수준(?)이라고 하겠다.

  다음의 자료에서 굉장히 간단하게 MEF 예제를 놓고 설명한 동영상 자료가 있다.
  Silverlight MEF 자료 동영상

  MEF로 동적으로 .xap 파일을 로딩하는 것만 할 수 있는 것은 아니지만, 이 부분에 대해서만 중점적으로 설명하도록 하겠다.

  우선 MEF는 Silverlight 4에서 포함되어 나올 예정이다. 현재 3버전에는 없기 때문에 따로 파일을 받아야만 쓸 수 있다. 나는 MEF Preview 6 버전을 이용하여 설명하도록 하겠다.
  MEF Preview 6
  위의 사이트에는 MEF Preview 6에 관련된 정보들에 대한 설명이 갖추어져 있다. 파일을 다운 받기 위해서는, 다음과 같이 "the CodePlex site" 링크 주소를 클릭하여 다운받으면 된다.



  파일 다운로드를 다 받은 후, 압축을 해제한다.
  자, 이제 준비는 되었다.

  우선 동영상 예제때로 따라해보도록 하겠다.

  첫 번째로 해야할 일은 .dll을 참조시키는 것이다. 아까 압축 해제한 폴더에서 MEF_Preview_6\bin\SL3 폴더 아래로 이동하면, System.ComponentModel.Composition.dll 파일이 있을 것이다. 이 .dll 파일을 참조시켜야 한다.

  그 다음은 Catalog 객체를 생성해야 하는데, 우선 System.ComponentModel.Composition.dll을 개체 브라우저로 확인해보자. 여러 가지 차암~ 많다. 그 중 Catalog객체만 보면, AggregateCatalog, AssemblyCatalog, TypeCatalog, ComposableCatalog, PackageCatalog들이 있다. (이 이외에도 다른 Catalog들이 있다.) 아직 이 Catalog들의 각각의 역할을 확인하지 못했다.
  나는 이제부터 PackageCatalog를 사용할 것이다.

var catalog = new PackageCatalog();
catalog.AddPackage(Package.Current);

  Package.Current는 현재 실행 중인 어셈블리의 Package이다. 이렇게 PackageCatalog를 생성하여 현재 실행 중인 어셈블리의 Package를 추가한 뒤, CompositionContainer를 생성한다.

var container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);

  위와 같이 CompositionContainer를 만들어 이전에 생성한 PackageCatalog를 담는다. 그 후, 지금 내가 사용하고 있는 클래스 객체와 연결하기 위해 CompositionBatch를 생성하여, Compose를 하는 이 세 줄을 container.ComposeParts(this); 한 줄로 축약시킬 수 있다. 그럼 이후부턴 생성한 CompositionContainer를 통해 Export되는 요소들을 접근할 수 있게 된다. 무슨 의미냐 하면,

  MEF 에서는 외부로 노출시켜야 할 요소들에게 태그처럼 [Export]를 명시해준다. 예를 들어, Name이라는 이름의 Property를 외부에 노출시켜야 하는 경우, 다음과 같이 정의한다.

[Export("Name")]
public string Name
{
     get
     {
          return "Incheon";
     }
}

 "Name"이라는 특정 이름으로 Name 속성이 외부로 노출되었다. 이 Name 속성을 접근하고자 하는 경우에는, 아까 생성했던 CompositionContainer를 통해 접근할 수 있다. 예를 들어, CompositionContainer.GetExport<T>()를 사용하면 된다. 하지만 이런 식의 접근보다, 바로 접근할 수 있는 짝꿍을 만들어 주는 것이 더 효율적이다. [Export]의 짝꿍은 [Import]이다. 한 마디로 Export된 속성을 받아서 쓰는 입장 쪽에서 Import로 정의된 속성을 만들어주면 된다.

[Import("Name")]
public string Name { get; set; }

  이렇게 정의한 후에, 프로그램을 실행해보면, CompositionContainer의 Compose가 실행되자마자 Import로 정의된 Name 속성 값으로 Export로 정의된 Name 속성의 value가 들어가는 것을 확인할 수 있다. 그 다음부터는 간단하게 Name 속성을 그대로 사용하면 된다.

  자, 문제는 -_- 얘네들을 어떻게 쪼물딱 해서 .xap를 동적으로 로딩하는가에 대한 문제이다.

  우선 새로운 프로젝트를 생성하여, 클래스를 추가하고, 외부로 노출시킬 요소들을 만들자.

public class NameContainer
{
    [Export("Names")]
    public string name1 = "name1";
    [Export("Names")]
    public string name2 = "name2";
    [Export("Names")]
    public string name3 = "name3";
}

  위와 같이, NameContainer라는 이름의 클래스를 생성한 뒤, "Names"라는 이름으로 외부에 노출시킬 아이들을 몇 놈 만들었다. 나는 Names로 노출된 아이들을 ListBox에 정렬하여 표현할 생각이다. 우선 내가 본래 있던 프로젝트로 돌아와 Import 짝꿍을 만들자. 나는 [Import] 대신 [ImportMany]를 사용할 것이다. [ImportMany]를 사용하는 경우 중엔 Export 되는 상대가 여러놈일 때가 있다. 하지만, 지금의 경우에는 .xap파일을 로드하여 쓰기 때문이라고 생각하는 것이 맞을 것이다.

[ImportMany("Names", AllowRecomposition = true)]
public ObservableCollection<string> Names { get; set; }

  정확한지는 모르지만, AllowRecomposition 속성은 CompositionContainer와 연결되어있는 Export들의 상태가 변경될 때마다 Update 하는 것을 허락하는 지를 나타내는 것 같다. Recomposition에 대한 설명은 다음 싸이트에 있다. 
  Recomposition and Constructors

  ImportMany로 값을 얻는 경우에는 배열이나 Collection 형태 등으로 얻어야 한다. 나는 Names라는 이름의 ObaservableCollection에 "Names"로 Export되는 아이들을 넣을 것이다.
  문제는, Export로 정의된 요소들은 모두 다른 프로젝트에 있다. 내가 원하는 것은 Export 요소들을 담고 있는 프로젝트의 .xap 결과물만을 이용해서 내가 만들어 놓은 ListBox에 출력시키고 싶다. 이런 경우에는, Export 요소들을 담고 있는 프로젝트의 .xap 결과물을 내가 본래 작업하는 프로젝트의 Web 프로젝트 폴더 내에 있는 ClientBin 폴더 아래에 복사한다. 그 후, CompositionContainer 객체를 생성하기 전에,

Package.DownloadPackageAsync(new Uri("NameContainerProject.xap", UriKind.Relative), (s, p) => catalog.AddPackage(p));
  Package.DownloadPackageAsync를 이용하여, 새로이 생성했던 프로젝트의 .xap 결과물이 위치하고 있는 경로파일을 매개변수로 넘겨지면, .xap을 다운로드 하여 Package로 만들어준다. 그 후에 CompositionCatalog에 추가하면 된다.

  이제, Names에 내용물들이 채워졌을 것이다. ListBox에 정렬시켜보자.

NameListBox.ItemsSource = Cities;

  이제 -ㅁ-) 실행시켜 보면, ListBox에 Names에 있던 요소들이 모두 출력되어 있는 것을 확인할 수 있을 것이다.
  여기서 끗!

... 이라고 하고 싶지만, 내가 원하는 형태는 애니메이션이 있는 UserControl을 Export시키고, .xap를 로드하여 UserControl을 Import로 정보를 얻어  애니메이션을 볼 수 있는 것이다. 이에 대해서는, 다음에 포스팅하도록 하겠다.
Posted by 벚꽃손님

댓글을 달아 주세요

  1. 와~ MEF 정리한 내용이다! 고생 많았고 다음 편도 기대할게요~ ^^

    2010.05.12 23:41 신고 [ ADDR : EDIT/ DEL : REPLY ]


인스톨러 빌드

a.   릴리즈 구성

상단의 메뉴에서 Build -> Release Wizard를 선택하여, 인스톨러의 배포 방식을 설정하고, 인스톨러를 작성한다.

 

 

i.              배포할 이름을 지정한다.

 

ii.             인스톨러를 배포할 미디어를 선택한다.

 

iii.            단일 설치파일을 생성할 것인지 선택한다.

 

iv.            설치시 암호를 사용할 것인지 선택한다.

 

v.             프로그램이 지원하는 플랫폼을 설정한다.

 

vi.            지원할 언어를 선택한다.

vii.           인스톨러에 포함될 feature들을 선택한다.

 

viii.          feature들을 어떤식으로 미디어에 저장할 것인지 선택한다.

 

ix.            어떤 UI를 사용할 것인지 선택한다.

다음의 몇가지 과정들은 그냥 넘어가도 상관없다. 온라인 설치나, 서명 등을 설정하는 과정이다.

 

x.             릴리즈 설정 종료

하단의 Build the Release를 체크 후 마치면, 바로 빌드가 된다.

Posted by 비회원

댓글을 달아 주세요

  1. 감사

    감사합니다!!!!!

    2010.08.28 06:54 [ ADDR : EDIT/ DEL : REPLY ]