Windows2021. 2. 2. 09:04

서비스 프로젝트를 작성 후 배포하기 위해 서비스를 설치하고 설치 후 바로 서비스가 구동되도록 하는 방법에 대해 설명한다.

 

서비스프로젝트에 설치관련 클래스 추가

  1. 작성한 서비스.cs파일 더블클릭
  2. 도구상자에 ProjectInstaller 선택
  3. ServiceProcessInstaller, ServiceInstaller 생성됨

 

설치 프로젝트 추가

  1. 파일 -> 프로젝트 추가 > Setup Project
  2. 프로젝트 아이콘에 마우스우측 버튼 클릭
  3. Add -> 프로젝트 출력
  4. 추가할 프로젝트를 콤보박스에서 선택하고 기본출력을 선택
  5. View -> 사용자지정작업에서 Install, Commit, Rollback, Uninstall에 각각 마우스우측버튼 클릭 후 '사용자지정작업'으로 작성한 서비스프로젝트 추가
  6. 빌드 수행하면 msi와 setup.exe가 생성됨

 

설치 서비스 실행되도록

  1. ProjectInstaller.Designer.cs코드보기
  2. initializeComponent()에서 서비스설치 관련 파라메터 수정
  • ServiceName, DisplayName, Description
  • StartType : Automatic으로 설정
  • Committed 핸들러 오버라이드 등록

            this.serviceProcessInstaller1.Password = null;

            this.serviceProcessInstaller1.Username = null;

            this.serviceProcessInstaller1.Account = ServiceAccount.LocalSystem;

            this.serviceInstaller1.ServiceName = "My Service";

            this.serviceInstaller1.DisplayName = "My service";

            this.serviceInstaller1.Description = "This is My service";

            this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;

            this.Committed += new System.Configuration.Install.InstallEventHandler(MyServiceWindowsServiceInstaller_Committed);

 

  3. ProjectInstaller.cs 코드보기

  4. Committed 핸들러 오버라이드 수행

  • 서비스 컨트롤러 생성하여 서비스를 시작함.

        void MyServiceWindowsServiceInstaller_Committed(object sender, InstallEventArgs e)

        {

            // Auto Start the Service Once Installation is Finished.

            var controller = new ServiceController("MyService");

            controller.Start();

        }

 

 

Posted by 삼스
Windows2020. 7. 28. 13:56

CEF

 

Chromium Embedded Framework (CEF).

chromium 기반의 브라우저를 내포하는 앱을 만들 수 있는 프레임워크이다.

 

 

BSD-license이고 구글 크로미엄프로젝트 기반으로 2008년에 마샬그린브랫에의해 시작된 오픈소스 프로젝트이다.

크로미엄프로젝트와 다르게 구글 크롭앱개발에 주로 집중하였고 써드파티앰의 내장된 브라우저 사용 케이스에 집중하였다.

CEF는 크로미텀의 하부와 코드의 복잡함으로부터 고수준의 안정화된 API, 특정목적의 배포, 그리고 바이너리 배포를 통해 벗어날수 있게 해준다.

CEF기본 구현은 사용자로부터 조금의 또는 전혀 통합하는 작업을 필요로 하지 않는다.

현재 1억개 이상의 인스턴스가 사용되고 있다. 그 일부가 위키페이지에 정리되어 있다.

 

사용케이스 몇가지는 다음과 같다.

* 기존 네이티브 애플리케이션에 HTML5호환 웹브라우저를 내장

* 가벼운 네이트브 쉘애플리케이션으로 기본적으로 웹기술에 기반하여 개발된 UI를 호스팅

* 자체적인 드로잉프레이뭐크를 사용하는 오프스크린 웹컨텐츠를 렌더링

* 웹속성이나 애플리케이션의 자동화된 테스트를 수행

 

CEF는 수많은 언어와 운영체제를 지원하며 새로운 또는 기존애플리케이션에 쉽게 통합이 가능하다. 성능과 쉬운 사용을 목표로 설계되었다.

기반프레임워크는 C, C++로 네이티브라이브러리로 제공되고 애플리케이션과 크로미엄과 상세한 구현으로부터 분리해준다.

이는 브라우저와 커스텀플러그인, 프로토콜, 자바스크립트 객체 그리고 자바스크립트 확장 등을 포함하는 앱간에 밀접한 통합을 제공한다.

애플리케이션은 리로스로딩을 제어하거나, 네비게이션, 메뉴, 프린트 등을 하면서 구글 크롬웹브라우저와 동일한 성능과 HTML5기술을 사용할 수 있다.

 

수많은 개인과 단체가 시간과 자원을 CEF개발에 기연하고 있지만 더 많은 커뮤니티로부터의 참여는 항상 환영한다.

 

시작하기

 

시작은 튜터리얼 위키페이지에서 CEF 사용에 대한 개요를 읽고 아키텍쳐적인 내용이나 사용이슈에 대한 더 깊은 논의를 하기 위하여 GeneralUsage 위키를 읽어라.

API문서를 살펴보고 지원과 관련된 논의에 대해서는 포럼을 참고하라

 

 

 

CEF Tutorial

 

CEF3로 간단한 앱을 작성하는 방법을 설명한다. CEF3 사용에 대해서는 GeneralUsage위키페이지를 참고하라.

 

시작하기

 

CEF개발을 시작하기 쉽게 해주는 샘플프로젝트를 제공한다. cef-project 웹사이트에 방문하고 단계별로 설명을 따라해보기 바란다.

 

 

커스텀URL 로딩하기

 

샘플프로젝트는 구글 홈페이지를 로딩한다. 다른 url로 변경하려면 커맨드라인에서 다음과같이 수행한다.

 

# Load the local file “c:\example\example.html”

cefsimple.exe --url=file://c:/example/example.html

 

샘플프로젝트에서 cefsimple/simple_app.cc에서 다음라인을 수정하고 다시 컴파일하면 바로 진입한다.

 

// Load the local file “c:\example\example.html”

if (url.empty())

  url = "file://c:/example/example.html";

 

 

애플리케이션 요소들

 

모든 CEF애플리케이션은 다음의 기본 콤포넌트들로 구성된다.

 

1. CEF dynamic library(libcef.dll 윈도우용, libcef.so 리눅스용, Chromium Embedded Framework.framework 맥용)

2. 지원 파일들(*.pak, *.bin, ...)

3. 리소스 (html/js/css, string, ...)

4. 클라이언트 실행파일

 

CEF dynamic library, 지원파일들, 리소스는 모든 CEF 애플리케이션에 존재한다. 바이너리 배포판의 Debug / Release 또는 Resources 디렉토리에 포함된다. 이러한 파일 중 필요한 파일과 제외 할 수있는 파일에 대한 자세한 내용은 이진 배포에 포함 된 README.txt 파일을 참조하십시오. 각 플랫폼에서 필요한 애플리케이션 레이아웃에 대한 자세한 설명은 아래를 참조하십시오.

 

60초 아키텍쳐

 

다음은 이 튜터리얼의 기본적인 중요한 아이템들을 요약한다.

 

* CEF는 멀티프로세스를 사용한다. 메인 애플리케이션 프로세스는 '브라우저' 프로세스로 불린다. 서브프로세스들은 렌더러, 플러그인, GPU등으로 생성해되게 될것이다.

* 윈도우와 리눅스에서는 메인과 서브프로세스가 같이 실행될 수 있다. OS-X는 실행과 서브프로세스의 번들이 분리되어야 한다.

* 대부분의 프로세스는 다중스레드를 갖는다.

* 일부 콜백과 함수들은 개개 프로세스 또는 개개의 스레드를 사용할 수 있다. 콜백이나 함수를 처음 사용하는 경우 헤더파일의 코멘트를 반드시 읽어볼것을 권장한다.

 

소스코드

 

cefsimple애플리케이션은 CEF를 초기화하고 하나의 브라우저팝업윈도우를 생성한다. 애플리케이션은 모든 브라우저윈도우가 닫히면 종료된다. 프로그램 흐름은 다음과 같다.

 

1. OS는 브라우저프로세스를 시작한다. 진입포인트는 main또는 wWinMain

2. 진입포인트 함수는

  프로세스레벨의 콜백을 처리하는 SimpleApp의 인스턴스를 생성한다.

  CEF를 초기화 하고 CEF message loop를 시작한다.

3. CEF가 초기화 되면 SimpleApp::OnContextInitialized가 호출된다. 이 메서드는

  SimpleHandler의 싱글턴 인스턴스를 생성한다.

  CefBrowserHost::CreateBrowser를 사용하여 브라우저윈도우를 생성한다.

4. 모든 브라우저는 SimpleHandler인스턴스를 공유하며 이는 브라우저의 행위를 커스텀하고 관련된 콜백을 처리한다(life span, 로딩상태, 타이틀 표시등...)

5. 브라우저하나가 닫히면 SimpleHandler::OnBeforeClose가 호출된다. 모든 브라우저가 닫히면 CEF message loop가 끝나며 애플리케이션이 종료된다.

 

 

진입포인트 함수

 

이 함수는 CEF와 운영체제 관련된 객체들을 초기화해야 한다. 예를 들어 리눅스에서는 X11 에러헨들러를 인스톨해야 하고, OS-X에서는 필요한 코코아객체들의 할당이 필요하다.

 

* 윈도우 : cefsimple/cefsimple_win.cc

* 리눅스 : cefsimple/cefsimple_linux.cc

* OS-X

  브라우저 프로세스 : cefsimple/cefsimple_mac.mm

  서브 프로세스 : cefsimple/process_helper.mac.cc

 

 

SimpleApp

 

프로세스레벨의 콜백을 처리해야 한다. interface/method로 노출되며 멀티프로세스에 의해 공유되고 각 프로세스만 에서 호출된다.

예를 들어 CefBrowserProcessHandler인터페이스는 브라우저 프로세스에서만 호출된다.

CefRenderProcessHandler인터페이스(샘플에서는 없음)는 렌더프로세스에서만 호출된다.

GetBrowserProcessHandler는 반드시 this를 리턴해야 한다. SimpleApp CefApp CefBrowserProcessHandler를 구현해야 하기 때문이다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

 

* 공유된 구현 : cefsimple/simple_app.h, cefsimple/simple_app.cc

 

SimpleHandler

 

브라우저레벨의 콜백을 처리해야 한다. 브라우저 프로세스로부터 실행된다. 샘플에서우리는 CefClient인스턴스를 모든 브라우저에서 사용했다. 하지만 당신의 애플리케이션은 각각 전용의 인스턴스를 사용해도 된다. GeneralUsage 위키페이지나 API 헤더파일을 보고 CefApp과 관련된 인터페이스들에 대해 좀더 많은 정보를 살펴보길 바란다.

 

* 공유된 구현 : cefsimple/simple_handler.h, cefsimple/simple_handler.cc

* 윈도우 : cefsimple/simple_handler_win.cc

* 리눅스 : cefsimple/simple_handler_linux.cc

* OS-X : cefsimple/simple_handler_mac.mm

 

빌드 순서

 

빌드 순서는 플랫폼에 의존한다. binary 배포와 함께 포함된 CMake파일을 찾아서 살펴보면 모든과정에 대한 이해가 가능할것이다. 모든 플랫폼에 공통된 빌드 순서는 일반적으로 다음과 같이 요약될 수 있다.

 

1. libcef_dll_wrapper static library를 컴파일

2. application 소스 컴파일, libcef dynamic library libceff_dll_wrapper static library 링크

3. library와 리소스들을 출력디렉토리에 복사

 

 

윈도우 빌드순서

 

1. libcef_dll_wrapper static library를 컴파일

2. cefsimple.ext 컴파일 및 링크

- 필요한 소스코드 : cefsimple_win.cc, simple_app.cc, simple_handler.cc, simple_handler_win.cc.

- 필요한 라이브러리 : comctl32.lib, shlwapi.lib, rcprt4.lib, libcef_dll_wrapper.lib, libcef.lib, cef_sandbox.lib

cef_sandbox.lib(sandbox지원시) VS 2015 update3으로 빌드된 정적라이브러리이다. 다른 버전의 VS는 컴파일이 안될수도 있다. sandbox를 지원하지 않으면 문제가 없으며 cefsimple_win.cc의 코멘트를 확인하여 비활성화 하면 된다.

- 리소스 파일 : cefsimple.rc

- 매니페스트 파일 : cefsimple.exe.manifest, compatibility.manifest

3. Resource 폴더의 모든 파일 대상폴더로 복사

4. Debug/Release 폴더의 모든 파일 대상폴더로 복사

 

결과 폴더의 구조는 2526 브랜치정도가 보일거다.

 

Application/

    cefsimple.exe  <= cefsimple application executable

    libcef.dll <= main CEF library

    icudtl.dat <= unicode support data

    libEGL.dll, libGLESv2.dll, ... <= accelerated compositing support libraries

    cef.pak, devtools_resources.pak, ... <= non-localized resources and strings

    natives_blob.bin, snapshot_blob.bin <= V8 initial snapshot

    locales/

        en-US.pak, ... <= locale-specific resources and strings

 

Posted by 삼스
Windows2020. 5. 6. 14:56

CEF#에서 alert, confirm, prompt등의 dialog를 커스텀하는 방법에 대해 정리한다.

 

IJsDialogHandler인터페이스를 구현한 객체를 ChromiumWebBrowser의 JsDialogHandler에 대입한다.

 

예)

browser.JsDialogHandler = new MyJsDialogHandler();

 

JsDialogHandler의 구현체에서는 OnJSDialog를 오버라이드 한다.

 

public bool OnJSDialog(IWebBrowser chromiumWebBrowser,

  IBrowser browser, string originUrl, CefJsDialogType dialogType,

  string messageText, string defaultPromptText,

  IJsDialogCallback callback, ref bool suppressMessage) 

{

  switch(dialogType)

  {

    case CefJsDialogType.Alert: // alert

      MessageBox.Show(messageText, "Notice", MessageBoxButton.OK);

      callback.Continue(true);

      return true;

    case CefJsDialogType.Confirm: // confirm

      var result = MessageBox.Show(messageText, "Notice", MessageBoxButton.YesNo, MessageBoxImage.Warning);

      callback.Continue(result == MessageBoxResult.Yes);

      return true;

  }

  return false;

}

Posted by 삼스
Windows2020. 4. 27. 16:20

CEF javascript 연동

.NET에서는 다음과 같이 자바스크립트 메서드를 호출한다.

browser.ExecuteScriptAsync("document.body.style.background = 'red';");

browser.ExecuteJavaScriptAsync("(function(){ document.getElementsByName('q')[0].value = 'CefSharp Was Here!'; document.getElementsByName('btnK')[0].click(); })();");

여러개의 프래임으로 구성된 경우 임의 프레임의 스크립트를 다음과 같이 호출 한다.

browser.GetBrowser().GetFrame("SubFrame").ExecuteJavaScriptAsync("document.body.style.background = 'red';");


그럼 언제 자바스크립트를 호출할 수 있는가?

자바스크립트는 V8Context에서만 호출할 수 있다.
IRenderProcessMessageHandler의 OnContextCreated와 OnContextReleased 가 자바스크립트가 호출될 수 있는 환경의 범위를 제공한다. 각 프레임별로 호출되며 frame.IsMain으로 메인프레임여부를 판가름할 수 있다.

OnFrameLoadStart에서 DOM에 접근할 수 있으며 로딩이 완료되기 던에 DOM의 스크립트를 실행할 수 있다.

browser.RenderProcessMessageHandler = new RenderProcessMessageHandler();

public class RenderProcessMessageHandler : IRenderProcessMessageHandler {

  // Wait for the underlying JavaScript Context to be created. This is only called for the main frame.
  // If the page has no JavaScript, no context will be created.
  void IRenderProcessMessageHandler.OnContextCreated(IWebBrowser browserControl, IBrowser browser, IFrame frame)
  {
    const string script = "document.addEventListener('DOMContentLoaded', function(){ alert('DomLoaded'); });";

    frame.ExecuteJavaScriptAsync(script);
  }
}

//Wait for the page to finish loading (all resources will have been loaded, rendering is likely still happening)
browser.LoadingStateChanged += (sender, args) =>
{
  //Wait for the Page to finish loading
  if (args.IsLoading == false)
  {
    browser.ExecuteJavaScriptAsync("alert('All Resources Have Loaded');");
  }
}

//Wait for the MainFrame to finish loading
browser.FrameLoadEnd += (sender, args) =>
{
  //Wait for the MainFrame to finish loading
  if(args.Frame.IsMain)
  {
    args.Frame.ExecuteJavaScriptAsync("alert('MainFrame finished loading');");
  }
};

* 스크립트는 프레임레벨에서 수행되고 모든 페이지는 한개 이상의 프레임으로 구성된다.
* IWebBrowser.ExecuteScriptAsync 확장메서드는 하위호환성을 위해 남겨 있으며 main frame에서 js를 수행하는 숏컷처럼 사용가능하다.
* frame이 자바스크립트를 포함하지 않으면 V8Context가 생성되지 않는다.
* frame이 로드된 후 context가 없는 경우 IFrame.ExecuteJavaScriptAsync를 호출하여 V8Context을 생성할 수 있다.
* OnFrameLoadStart가 호출될때 DOM 로딩이 완료되지 않는다.
* IRenderProcessMessageHandler.OnContextCreated/OnContextReleased는 메인프레임에서만 호출된다.

결과를 리턴하는 자바스크립트 메서드의 호출은 ?

//An extension method that evaluates JavaScript against the main frame.
Task response = await browser.EvaluateScriptAsync(script);
//Evaluate javascript directly against a frame
Task response = await frame.EvaluateScriptAsync(script);

자바스크립트는 비동기로 수행되기 때문에 에러메세지, 결과, 성공플래그등을 포함하는 Task를 반환한다. 
자바스크립트가 수행될때 기본적으로 알아야 할 사항은 다음과 같다.

* 언제 호출하는게 가능한지에 대해 알아야 하며 위에서 설명하였다.
* 프레임레벨에서 스크립트가 수행되고 모든 페이지는 하나이상의 프레임으로 구성된다.
* 스크립트는 렌더프로세스내에서 수행되고 성능상의 이유로 IPC를 통해 전달되고 데이터만 반환한다.
* 프리미티브 타입인 int, double, date, bool과 string이 제공된다.
* 결과로 객체가 제공되고 IDictionary<string, object>형태로 접근을 더 용이하게하기 위해 dynamic 키워드가 제공된다.
* 프리미티브타입이나 앞서 얘기한 객체가 IList

형태로 배열이 결과로 제공될 수 있다.
* HTMLCollection같은 배열과 유사한 객체의 경우 Array.from으로 배열로 반환하여 사용될수 없다.
* 반환되는 객체 그래프의 복잡성이 제한이 있다. 자바스크립트 JSON.toStringify()로 자바스크립트 객체를 JSON문자열로 변환하고 다시 .NET객체로 디코디할수 있다(참조: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)

// Get Document Height
var task = frame.EvaluateScriptAsync("(function() { var body = document.body, html = document.documentElement; return  Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ); })();");

//Continue execution on the UI Thread
task.ContinueWith(t =>
{
    if (!t.IsFaulted)
    {
        var response = t.Result;
        EvaluateJavaScriptResult = response.Success ? (response.Result ?? "null") : response.Message;
    }
}, TaskScheduler.FromCurrentSynchronizationContext());

//As stated above, it's best to return only the data you require, these examples demo taking a `HTMLCollection` and returning and returning a simplified representation.

//Get all the span elements and create an array that contains their innerText
Array.from(document.getElementsByTagName('span')).map(x => ( x.innerText));
//Get all the a tags and create an array that contains a list of objects 
//Second param is the mapping function
Array.from(document.getElementsByTagName('a'), x => ({ innerText : x.innerText, href : x.href }));


.NET 클래스를 자바스크립트에 바인딩하는 방법!!

JSB(JavaScript Binding)sms JavaScript와 .NET간의 통신을 가능하게 한다. 동기, 비동기방식 모두 지원한다. Sync버전은 더이상 개발되지 않고 있다.

Async JavaScript Binding

1. 바인딩할 클래스 정의

public class BoundObject {
public int Add(int a, int b) {
return a+b;
}
}

2. JavaScriptObjectRepository로 클래스인스턴스 등록

첫번째 방법
//For async object registration (equivalent to the old RegisterAsyncJsObject)
browser.JavascriptObjectRepository.Register("boundAsync", new BoundObject(), true, options);

두번째 방법
browser.JavascriptObjectRepository.ResolveObject += (sender, e) => {
var repo = e.ObjectRepository;
if (e.ObjectName == "boundAsync")
{
BindingOptions bindingOptions = null; //Binding options is an optional param, defaults to null
bindingOptions = BindingOptions.DefaultBinder //Use the default binder to serialize values into complex objects, CamelCaseJavascriptNames = true is the default
bindingOptions = new BindingOptions { CamelCaseJavascriptNames = false, Binder = new MyCustomBinder() }); //No camelcase of names and specify a default binder
repo.Register("boundAsync", new BoundObject(), isAsync: true, options: bindingOptions);
}
};

객체가 JavaScript로 바인딩 된 경우 .Net에 알림을 받으려면 ObjectBoundInJavascript 이벤트 또는 ObjectsBoundInJavascript 이벤트를 구독 할 수 있다 (두 이벤트 모두 명백히 유사 함).

browser.JavascriptObjectRepository.ObjectBoundInJavascript += (sender, e) => {
var name = e.ObjectName;

Debug.WriteLine($"Object {e.ObjectName} was bound successfully.");
};    

3. 스크립트에서 CefSharp.BindObjectAsync 호출

<script type="text/javascript">

(async function() {

  await CefSharp.BindObjectAsync("boundAsync");

  // 

  boundAsync.add(16, 5).then(function (actualResult) {

    const expectedResult = 21;

    assert.equal(expectedResult, actualResult, "Add 16 + 5 resulte in " + expectedResult);

  });

})();

</script>

 

CefSharp.BindObjectAsync가 호출되면 JavascriptObjectRepository가 주어진 이름으로 인스턴스가 등록되어 있는지 확인한다. 만일 등록이 안되어 있다면 ResolveObject이벤트가 발생한다. 파라메터 없이 CefSharp.BindObjectAsync가 호출되면 등록된 경우 바운드가 되고 등록이 안되었으면 ObjectName을 All로 설정하여 ResolveObject가 모두 호출된다.

example) https://github.com/cefsharp/CefSharp.MinimalExample/tree/demo/javascriptbinding

 

 

Posted by 삼스
Windows2013. 9. 13. 11:46


http://msdn.microsoft.com/en-us/library/windows/apps/hh913756.aspx

윈도우 스토어앱을 위한 푸시서비스에 대한 MS문서 정리임


윈도우푸시서비스는 3rd party개발자가 자신의 클라우스서비스를 통해 토스트, 타일, 뱃지 또는 데이터를 전송할 수 있는 방안을 제공한다. 

어떻게 동작하나?

다음 그림이 설명하고 있다.

1. 앱이 Notification Client Platform에 푸시채널을 요청한다.

2. Notification Client Platform은 WNS에 새로운 푸시채널을 질의하여 URI형태로 반환한다.

3. 앱은 푸시채널URI를 얻게 된다.

4. 당신의 클라우드서비스에 이 URI를 전달한다. 이 때 보안은 알아서 하길 바란다.

5. 클라우드서비스에서 업데이트내용을 전송하려면 채널URI로 SSL을 통해 HTTP POST request를 수행한다. 이 과정은 인증이 필요하다.

6. WNS는 request를 받아서 해당 디바이스에 전달한다.


이와 같이 안드로이드 GCM이나 애플의 APNS와 동작구조은 대동소이하다.

앱등록 및 클라우드서비스의 credential얻기

WNS로 푸시를 전송하기 전에 앱을 윈도우 스토어 대시보드에 반드시 등록해야 한다. 그러면 클라우드서비스에서 WNS에 인증할때 사용 가능한 credential을 발급해준다. 이 credential은 Package Security Identifier(SID)와 secret Key를 포함한다. https://appdev.microsoft.com/StorePortals/ko-KR/Home/Index?wa=wsignin1.0 <-여기(Windows Store app development)페이지에 가서 Windows Dev Center -> Dashboard에서 이 과정을수행할 수 있다.

앱과 credential은 쌍으로 관리되며 다른 앱에는 사용될 수 없다.
앱 등록에 대한 더 자세한 사항은 http://msdn.microsoft.com/en-us/library/windows/apps/hh465407.aspx <- 여기를 참조.

푸시채널 요청





Posted by 삼스
Windows2013. 9. 12. 17:54


Bouncy Castle은 자바와 C#을 지원하는

암호화관련 거의 모든 API들이 총 망라되어 있는 오픈소스이다.

아래 주소는 그 중에서 WinRT와 WP8의 오피셜사이트이다.

http://w8bouncycastle.codeplex.com/



Posted by 삼스
Windows2013. 5. 23. 17:31




C DLL이 다음 처럼 2개의 함수를 노출한다고 할 때

DLLFunction int addint(int n1, int n2)

{

   return n1 + n2;

}

DLLFunction int addchar(char* s1, char* s2, char* added)

{

   sprintf(added, "%s%s", s1, s2);

   return strlen(added);

}


C#에서는 다음 코드와 같이 사용 가능하다.

여기서 요점은 c data type과 C# data type을 매칭하는것인데. 이 때 메모리를 할당하여 포인터를 전달하거나 struct 데이터를 전달할 경우 Marsal 클래스를 사용해야 한다. (참조 : http://rainiac.com/dev/index.php/c-interop-c%EA%B3%BC-c-api%EC%9D%98-%EC%83%81%ED%98%B8%EC%9A%B4%EC%98%81/)


using System.Runtime.InteropServices; 


// 빈 페이지 항목 템플릿에 대한 설명은 http://go.microsoft.com/fwlink/?LinkId=234238에 나와 있습니다.



namespace DllTestApp1

{

    class Dll

    {

        [DllImport("Win32Project1.dll")]

        public static extern int addint(int n1, int n2);

        [DllImport("Win32Project1.dll")]

        public static extern int addchar(StringBuilder s1, StringBuilder s2, StringBuilder s3);   

    }



    /// <summary>

    /// 자체에서 사용하거나 프레임 내에서 탐색할 수 있는 빈 페이지입니다.

    /// </summary>

    public sealed partial class MainPage : Page

    {

        public MainPage()

        {

            this.InitializeComponent();

        }


        /// <summary>

        /// 이 페이지가 프레임에 표시되려고 할 때 호출됩니다.

        /// </summary>

        /// <param name="e">페이지에 도달한 방법을 설명하는 이벤트 데이터입니다. Parameter

        /// 속성은 일반적으로 페이지를 구성하는 데 사용됩니다.</param>

        protected override void OnNavigatedTo(NavigationEventArgs e)

        {

        }




        // add int

        private void Button_Click_1(object sender, RoutedEventArgs e)

        {

            int sum = Dll.addint((int)10, (int)200);

        }


        // add char

        private void Button_Click_2(object sender, RoutedEventArgs e)

        {


            StringBuilder aa = new StringBuilder("aa");

            StringBuilder bb = new StringBuilder("bb");

            StringBuilder added = new StringBuilder();

            Dll.addchar(aa, bb, added);

        }

    }

}


Posted by 삼스
Windows2013. 1. 2. 15:49


MVC나 MVVC모델을 사용하여 화면과 데이터를 분리하여 개발할것을 권장한다. 좋은 메트오앱은 view model을 얼마나 잘 적용하였느냐가 관건이다.

여기서는 Data로써 GroceryItem을 정의한다. 따라서 다음과 같이 GroceryItem.cs파일을 작성한다.


name, store, quantity변수가 있고 각각 getter, setter가 정의되어 있는데 특히 setter에는 내부적으로 NotifiPropertyChanged가 호출되고 있으며 PropertyChanged라는 이벤트 핸들러를 호출하고 있다. 

여기서 중요한것이 데이터들에 observable속성을 이런식으로 부여할 수 있다는 것이다. 메트로UI의 특성중에 데이터바인딩도 아주 좋은 특징중 하나이다. observable한 속성을 준 데이터가 변경이 되면 UI에 바로 반영되도록 할수 있다.

System.ComponentModel.INotifyPropertyChanged인터페이스로 observable속성을 부여할 수 있고 이 속성이 변경될때마다  PropertyChangedEventHandler 이벤트로 화면에 표시되도록 할 수 있다. 

ViewModel클래스를 하나더 추가 하였는데 이 클래스(ViewModel.cs)는 사용자데이터와 애플리케이션의 상태정보를 포함한다.



GroceryItem의 컬렉션을 관리하는게 주임무인 클래스이다. ObservableCollection객체로 관리되고 있다. 그외에 stroeList, zipCode, 그리고 현재 선택된 아이템의 인덱스(selectedItemIndex)로 구성된다.

GroceryItem이 Observable이므로 데이터가 변경되면 자동으로 UI에도 반영되기를 기대한다. 여기서 사용한 ObservableCollection은 System.Collections.ObjectModel 네임스페이스에 해당되고 이 클래스는 기본적인 콜렉션의 특성과 리스트아이템이 추가, 삭제, 변경될때 이벤트를 발생시킨다.

ViewModel클래스는 INotifyPropertyChanged를 또한 구현하고 있는데 이는 view model에도 두가지 변수에 대해 observable속성을 주었기 때문이다. HomeZipCode와 SelectedItemIndex가 그것인데 이 정보가 변경될때도 이벤트가 발생하게 된다.

페이지를 새로 정의하여 그 페이지를 맨처음 화면에 띄우고자 한다. Page폴더에 ListPage라는 페이지를 정의할것인데 이 페이지가 처음화면에 뜨도록 하기 위해서는 App.xaml.cs에서 약간의 변경이 필요하다.


위 코드의 rootFrame.Navigate(typeof(Pages.ListPage));가 그 부분이다.


데이터관점에서 설명하기 위해 UI가 아닌 ListPage.xaml.cs의 구현코드먼저 설명하겠다.

ViewModel객체를 가지고 있으며 Store를 4가지, 그리고 GroceryList를 4가지 추가하였다. 여기서 가장 중요한 부분은 this.DataContext = viewModel; 부분이다. 메트로UI의 가장 큰 장점은 데이터바인딩을 통해 UI에 바로 적용되는 것이며 이를 위해 데이터의 소스를 지정하는 것이 중요하다 이 작업을 하는 것이 DataContext를 지정하는 것이다.

또한 List의 아이템이 변경되었을때 이벤트핸들러가 추가되어 있다. 여기서 viewModel.SelectedItemIndex에 인덱스를 업데이트한다. 이 작업은 내부적으로 observable 속성의 영향으로 PropertyChanged이벤트가 호출되면서 화면을 업데이트 하게 된다.


ListPage의 XAML코드는 다음과 같다.


Grid의 Background속성에 AppBackgroundColor라는 StandardStyle.xaml에 정의되지 않은 속성을 부여하였다. 이는 별도의 스타일을 추가로 정의하여 변경함을 의미한다. 이렇게 사용자가 원하는데로 스타일속성을 재정의하여 사용이 가능하다. 그리고 2개의 컬럼을 정의하여 화면은 두갈래로 나누었고 각 칼럼별로 row를 두개를 두어서 화면은 4분할 하였다.

좌측레이아웃은 row두개를 합쳐서 하나로 만들었다.

<StackPanel Grid.RowSpan="2"> 

이 패널은 헤더를 표시하는 TextBlock과 List뷰를 포함한다. 

우측레이아웃은 StackPanel이 두개가 준비되어있다. Grid.Column과 Grid.Row속성으로 위치를 조정할 수 있다.

디자인화면은 데이터가 동적이기 때문에 볼수 없다. ListView는 리스트내의 아이템들을 표시할것인데 다음과 같이 3가지 속성을 줌으로써 가능하다.


ItemSource속성은 어디에서 데이터들을 가져와야 하는지를 지정한다.

ItemTemplate은 ItemSource의 데이터들을 어떻게 화면에 표시할것인지를 결정한다. 이 템플릿도 별도의 스타일로 미리 지정된 스타일을 지정하는 방식으로 처리된다.

마지막으로 리스트아이템이 변경되었을 때 의 이벤트 핸들러를 추가해준다.


이제는 상세내역과 아이템이 선택되지 않았을 때 등을 표시하기 위한 페이지의 추가방법을 설명한다.

모든 컨트롤들과 코드를 하나의 파일안에 구현할 필요는 없다. 프로젝트를 관리하기 쉽게 하기 위해 여러개의 페이지를 만들고 필요에 따라 화면에 띄우면 된다. 간단한 데모로 NoItemSelected.xaml을 새로운 페이지로 프로젝트에 추가한다.


이 페이지는 Frame control을 통해서 화면에 보여질 수 있는데 위에서 ListPage.xaml의 Item Detail부분을 다음과 같이 Frame속성을 추가하여 x:Name을 지정하면 동적으로 코드레벨에서 특정 페이지를 표시할 수 있다.


<Frame x:Name="ItemDetailFrame">으로 프레임속성을 부여하였고 코드에서 ItemDetailFrame.Navigate(typeof(NoItemSelected)); 로 동적으로 페이지를 표시하였다.


동적으로 다른 페이지를 레이아웃에 집어넣어 표시되게 할 수 있다. ItemDetail.xaml을 새로운 페이지로 정의하여 데모를 보자.



페이지의 변경은 viewModel의 SelectedItemIndex가 변경될때마다 해주면 된다.

ListPage.xaml.cs의 생성자부분을 다음과 같이 수정한다.

SelectedItemIndex가 -1이 아니면 viewModel을 파라메터로 하여 ItemDetailFrame의 Page를 변경한다.

여기서 viewModel을 Navigate메서드에 인자로 넣었음을 중요하게 인지해야 한다.

이 인자는 ItemDetail page에서 OnNavigatedTo를 통해서 전달되어진다.


SetItemDetail메서드는 선택된 아이템정보를 화면에 표시하는 함수이다.

HandleItemChange메서드는 사용자가 입력한 정보를 아이템데이터에 업데이트 하는 함수이다.


Posted by 삼스
Windows2013. 1. 2. 15:23


XAML을 잘 몰라도 된다. 아주 쉽다. 안드로이드 개발자라면 안드로이드 레이아웃으로 화면 만드는것과 유사하다고 생각하면 된다.

다음은 BlankPage.xaml에 버튼두개를 배치하는 예이다.



Grid안에 StackPanel이 있고 정중앙에 배치하였다. StackPanel에는 버튼두개를 정의하였고 첫번째 버튼은 "FirstButton" 두번째 버튼은 이름없이 정의하였다. 이 이름은 이 버튼객체에 접근할때 사용된다. 버튼의 각종 속성과 스타일을 설정할 수 있다. 이 모든 작업은 VS의 design툴에서 쉽게 속성의 변경및 추가가 가능하다.

위 작업을 하면 다음과 같은 화면이 뜨게 된다.


첫번째 버튼을 우르면 호출되는 이벤트 핸들러는 이미 xaml에 정의되어 있다. Click="ButtonClick"이 해당되는데 이 이벤트를 처리하는 코드는 BlankPage.xaml.cs에 다음과 같이 추가가능하다.


위코드는 버튼의 라벨텍스트를 콘솔에 디버그문자열로 찍어준다.

런타입에 버튼객체의 속성을 변경하여 모양을 변경할 수 있다. 첫번째버튼에 x:Name="FirstButton" 으로 버튼객체의 이름을 지정하였는데 이 이름을 사용하여 바로 버튼객체의 속성이나 메소드를 호출할 수 있다.







Posted by 삼스
Windows2012. 12. 27. 13:41



Implementing a Contract


Suspending과 Resuming은 생명주기 이벤트일 뿐아니라 Contract시스템의 일부로도 사용된다. 여기서는 SearchContract의 적용예를 설명할 것이다.


먼저 manifest에 선언을 하나 해주어야 한다. package.appxmanifest를 열어서 Declaration tab으로 가면 유효한 contract list가 나올것이다. Search를 선택하고 Add버튼을 눌러라. Search constract의 속서은 변경하지 않겠다.


Search contract의 목적은 시스템의 검색기능을 애플리케이션까지 확장하기 위해서이다. 여기서는 grocery list의 아이템들 중에서 검색된 첫번째 정보를 찾아줄것이다. 이는 검색을 위해서 많은 작업을 요하지 않으며 나는 ViewModel에 검색을 위핸 메서드 SearchAndSelect를 추가할 것이다.


using System.Collections.Generic;

using System.Collections.ObjectModel;

using System.ComponentModel;

namespace MetroGrocer.Data {

  public class ViewModel : INotifyPropertyChanged {

    private ObservableCollection<GroceryItem> groceryList;

    private List<string> storeList;

    private int selectedItemIndex;

    private string homeZipCode;

    private string location;

    public ViewModel() {

      groceryList = new ObservableCollection<GroceryItem>();

      storeList = new List<string>();

      selectedItemIndex = -1;

      homeZipCode = "NY 10118";

      location = "Unknown";

}

    public void SearchAndSelect(string searchTerm) {

      int selIndex = -1;

      for (int i = 0; i < GroceryList.Count; i++) {

        if (GroceryList[i].Name.ToLower().Contains(searchTerm.ToLower())) {

          selIndex = i;

          break;

} }

      SelectedItemIndex = selIndex;

    }

// ...properties removed for brevity...
public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged(string propName) {

      if (PropertyChanged != null) {

        PropertyChanged(this, new PropertyChangedEventArgs(propName));

} }

} }

이 메서드는 사용자가 입력한 문자열을 인자로 받아 grocery list에서 찾은 후 첫번째로 매칭된 아이템의 인덱스를 선택한다. 매치가 안되면 -1이다. SelectedItemIndex가 observable이기 때문에 아이템 검색은 그 아이넴을 선택하고 그 결과 애플리케이션 레이아웃에 선택된 아이템의 정보가 표시될것이다.

이를 위해 ListPage.xaml.cs에서 선택된 아이템으로 표시되도록 일부 수정해야 한다.


protected override void OnNavigatedTo(NavigationEventArgs e) {

  viewModel = (ViewModel)e.Parameter;

  ItemDetailFrame.Navigate(typeof(NoItemSelected));

  viewModel.PropertyChanged += (sender, args) => {

    if (args.PropertyName == "SelectedItemIndex") {

  groceryList.SelectedIndex = viewModel.SelectedItemIndex;

      if (viewModel.SelectedItemIndex == -1) {

        ItemDetailFrame.Navigate(typeof(NoItemSelected));

        AppBarDoneButton.IsEnabled = false;

      } else {

        ItemDetailFrame.Navigate(typeof(ItemDetail), viewModel);

        AppBarDoneButton.IsEnabled = true;

      }

} };

...


Contract관련 생명주기

Application class의 몇몇 메서드를 override함으로써 contract에 대응되는 생명주기 이벤트를 처리할 수 있다. 다음은 OnSearchActivated 메서드의 구현예이다. 이는 사용자가 이 앱을 타겟으로 검색할때 이 메서드가 호출된다.


using System.Threading;

using System.Threading.Tasks;

using MetroGrocer.Data;

using Windows.ApplicationModel;

using Windows.ApplicationModel.Activation;

using Windows.UI.Core;

using Windows.UI.Xaml;

using Windows.UI.Xaml.Controls;

namespace MetroGrocer {

  sealed partial class App : Application {

    private ViewModel viewModel;

    private Task locationTask;

    private CancellationTokenSource locationTokenSource;

    private Frame rootFrame;

    public App() {

      this.InitializeComponent();

viewModel = new ViewModel();
// ...test data removed for brevity...

      this.Suspending += OnSuspending;

      this.Resuming += OnResuming;

      StartLocationTracking();

    }

    protected override void OnLaunched(LaunchActivatedEventArgs args) {

      // Create a Frame to act navigation context and navigate to the first page

      rootFrame = new Frame();

      rootFrame.Navigate(typeof(Pages.MainPage), viewModel);

      // Place the frame in the current Window and ensure that it is active

      Window.Current.Content = rootFrame;

      Window.Current.Activate();

}

protected override void OnSearchActivated(SearchActivatedEventArgs args) {

 viewModel.SearchAndSelect(args.QueryText);

}

//...other methods removed for brevity

} }


이것이 search contract를 지원하기 위한 작업의 전부이다. 

Posted by 삼스