2008. 12. 23. 00:32

스마트클라이언트 서명키 만들기

개인인증서 만드는 명령어 참고 주소:
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.VisualStudio.v80.ko/dv_fxdeploy/html/a05b5f2f-d1f2-471a-8096-8b11f7554265.htm

스마트 클라이언트에서 클릭원스 매니페스트 서명 인증키가 제공되는데 이것은 기본적으로
테스트 인증서 입니다.

이 인증서는 생성 후 1년 밖에 사용하지 못합니다.

즉 1년 이후로는 만기가 되기 때문에 새로운 버전이 배포 되어도 업데이트가 이루어지지 않고
오류가 발생하여 아예 실행이 되지 않습니다.

더군다나 이렇게 생성된 테스트 인증서는 만기날짜 조정이 불가능 합니다.

그렇기 때문에 날짜가 조정가능한 한 인증서를 만들기 위해서는 cmd 창에서 아래 코드를 실행하면
개인인증서가 만들어 집니다.

makecert -r -pe -n "CN=HEC" -b 01/11/2006 -e 10/12/2100 -sky exchange -ss my


출처
http://www.setisigns.net/94

Posted by 굿데이
2008. 12. 22. 17:55
현재.. 대부분의 글들이 스마트클라이언트에 대한 것 들입니다.

ActiveX로 된 업/다운로드 컴포넌트들에 대한 약간은 답답함(?)에서 비롯된

자체개발욕구 때문에 닷넷으로 개발가능한 기술들에 대한 정보를 모으는 중입니다.

물론 웹하드에 사용할 컴포넌트 이므로 나스와도 무관하진 않군요.

만들수 있을지.. 언제 만들지는 모르지만.. 그냥 지금은 이렇게 의욕에 넘처 자료구하고,

테스트 해보는 중입니다.

언제쯤 개발 가능 하려나.. 이놈에 허접 ㅠㅠ
Posted by 굿데이
2008. 12. 22. 11:29

IE임베디드 스마트클라이언트를 테스트겸 작성해보면서 겪었던 문제점과 해결방법을
간단하게 정리해보겠습니다.

혹시나, 저와 같은 문제를 겪으실지도 모르는 분들을 위해.. 팁으로 알아두셨으면 합니다.

몇개 안되지만.. 도움이 되었으면 합니다. ^^



1. 스마트클라이언트에서 소켓 라이브러리 사용시 지연문제

2. DLL 추가 검색 문제

3. DLL 중복 다운로드 문제

4. 기타문제



저로 하여금 난감스럽게 만들었던 문제들입니다. 혹시나 해당되시는 분들이 있으시다면..
도움이 되길 바랍니다.

참고로 원리와 이유는 배제하고 해결법만 기재하겠습니다. 원리와 이유에 대해 자세히 알고자
하시는 분은.. 아래 사이트를 참고하시기 바랍니다.


"유경상"님과 "정성태"님의 글이 잘 정리되어있습니다.


http://taeyo.net/ (ASP.NET(외부)란에 정성태님의 스마트클라이언트 관련 게시물)

http://www.simpleisbest.net/archive/2006/01/02/360.aspx (유경상님의 닷넷블로그입니다.(추천))


그외, 데브피아 (강좌&팁 / 질답게시판)





1. 스마트클라이언트에서 소켓 라이브러리 API사용시 지연문제의 해결 ---------------------------


   http://www.devpia.com/forum/BoardView.aspx?forumname=csharp_lec&page=1&no=1059&code=

   링크로 직접보시는게 가장 적절한듯 싶어 링크를 걸어 놓습니다.


   주 내용은, IE임베디드 형태의 스마트클라이언트에서 소켓 라이브러리 API를 사용하기 전에

   System.Configuration.ConfigurationSettings.GetConfig("system.net/settings");
   구문을 먼저 삽입하라 입니다.


   본 내용은 강좌는 아니고 팁이므로 원리에 대해서는 설명하지 않겠습니다.


   * 일부 PC에서는 해당 구문의 삽입여부와 무관하게 정상적으로 동작하는것을 확인했습니다만
   역시나 넣어주는것이 안전합니다.




2. DLL추가 검색문제-----------------------------------------------------------
   스마트클라이언트 배포시 Fiddler를 통해 모니터링 하시다 보시면.



위와 같이 이미 DLL를 검색하고 다운로드 했음에도 불구하고 여기저기 다른곳을 검색하며,
파일을 찾을수 없다는 404에러를 뿌립니다. 이 문제의 해결책은 다음과 같습니다.

  일단 스마트클라이언트의 Config파일을 작성해야 합니다. 파일명은 "XXXX.dll.config" 입니다.  
 *.dll.config가 되야하므로 이점 주의하시기
바랍니다. config파일의 내용은 없어도 무방합니다.

  config파일은 배포할 스마트클라이언트 dll파일과 같은 곳에 위치해야합니다.


  웹파일에 다음구문을 추가합니다.  
  <link rel = "Configuration" href = "SmartClient_DLL/SmartClient.dll.config">


  * 참조경로 주의.


  역시나 이유와 원리에 대해서는 설명하지 않도록 하겠습니다. "정성태"님의 글에서는 문제점만 나타내었고.
  해결책이 제시되어있지 않지만 "유경상"님의 블로그에는 위와 같이 하는 이유에 대해
  자세히 설명되어있습니다. 해당 블로그를 참고 하시기 바랍니다.


  위와 같이 처리했다면, 배포시 여러군데서 중복검색하는 부하를 줄일 수 있습니다.

  해결된 모습입니다.


 


   


   

3. DLL 중복 다운로드 문제 ---------------------------------------------------------


   2번의 문제 해결을 읽어보시다가 발견하셨으리라 생각합니다.

   그림을 보시면   dll과 DLL 이렇게 확장자의 대소문자를 바꿔서 두번 다운로드 된것을 보실수 있을겁니다.


   이 문제에 대한 이유는 아쉽게도 찾을 수 없었습니다.

   

   허나, 데브피아 질답게시판에서 "유경상"님이 답변해주신 게시물을 발견했습니다.
   그곳에 해결책이 기재되어있더군요.


   해결책은 웹소스파일에서


   <OBJECT id="SC" style="WIDTH: 740px; HEIGHT: 544px"
   classid="SmartClient_DLL/SmartClient.dll#SmartClient.OTIS_WebViewer"  VIEWASTEXT VIEWASTEXT>
   </OBJECT>


   dll로 소문자기재를 하지말고, DLL로 대문자를 사용하라는 것이었습니다. (이유는 아쉽게도 모릅니다.)


   <OBJECT id="SC" style="WIDTH: 740px; HEIGHT: 544px"
   classid="SmartClient_DLL/SmartClient.DLL#SmartClient.OTIS_WebViewer"  VIEWASTEXT VIEWASTEXT>
   </OBJECT>



    중복다운로드를 해결한 모습입니다.


   




4. 기타 문제들...----------------------------------------------------------------------


   1) 컨트롤이 웹상에서 보이지만 동작하지 않을때


      >>  권한집합을 FullTrust로 준다음 제대로 동작하는지 재확인한다. 정상적이라면 소스코드중 권한을
   넘어서는 것이 없는지 확인하고
 권한의 범위를 재설정해본다.





   2) FullTrust권한을 주었음에도 불구하고, 컨트롤에 웹에서 보이기만 할뿐 동작하지 않을때.


       >> 프레임워크 1.1이 맞는지 확인한다. 상위버젼이 설치되어 있지 않은지 확인하고 만약 그렇다면


       C:\Program Files\Internet Explorer 폴더에 iexplore.exe.config 파일을 새로 생성하고,


       <?xml version="1.0" encoding="utf-8" ?>

          <configuration>

                <startup>

                      <requiredRuntime version="v1.1.4322"/>

                </startup>

           </configuration>


        이와 같이 익스플로러 실행시 사용할 프레임워크의 버젼을 지정해준다.





    3) 변경된 내용이 즉각 적용되지 않을때.


       >> 내용을 수정했다면, AssemblyInfo.cs파일의 어셈블리 버젼을 바꿔보자.


       >> 새로고침을 하지 말고 익스플로러를 완전히 종료하고 새로 열어보자.










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


이 외에도 여러가지 문제점이 있었지만.. 막상 쓸려니 기억이 나질 안네요...(에휴 바본가봐.ㅜ.ㅜ)

다시한번 말씀드리지만. 이글은 강좌는 아니므로.. 중요한 원리와 이유에 대한 언급을 하지않았습니다.

훨씬 좋게 정리된 글들이 있으므로 그것을 참고하시기를 바라는 바입니다.



다만 직접 해보면서 겪었던 문제들에 대한 해결책들이 여기저기 흩어져 있었기에..
그것을 모아놓은 글이라 생각해주세요.


그럼 이만 끝냅니다. ^^


출처

http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=18&MAEULNo=8&no=1163&ref=1163
Posted by 굿데이
2008. 12. 22. 10:25

Smart Client (PrintPreviewDialog와 OpenFileDialog의 사용)

스마트 클라이언트란 말 그대로 똑똑한 클라이언트라 할 수 있다.
기존의 ActiveX컨트롤과 비슷한 기능을 하지만 그보다 훨씬 간단히 접근 할 수 있다. 물론 그렇게 만만한 놈은 아니다.
지금부터 스마트 클라이언트를 이용하여 웹브라우저에서 인쇄 및 파일 업로드 기능을 작성해 보도록 하자.

기존의 <input type=file>태그를 통하여 파일업로드를 할 경우 기본 컨트롤의 모양을 보기 좋게 바꾸기는 상당히 어려운 점이 많이 있었다.

하지만 스마트 클라이언트를 이용하면 간단히 이미지를 클릭할때 OpenFileDialog를 띄울 수 있다.

그럼 예제를 시작해 보도록 하자!!


using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace RichWinForm
{
 //외부 스크립트의 호출을 위한 인터페이스
 public interface IPrintControlCOMIncomming
 {
  void show(string oData);
  string showDialog();
 }

 //외부 스크립트에서 public메서드를 호출하기위한
 [ClassInterface(ClassInterfaceType.None)]

//유저컨트롤과 인터페이스(외부의 스크립트에서 public메서드 호출을 위해)를 상속받는다.
 public class Print: System.Windows.Forms.UserControl, IPrintControlCOMIncomming
 {
  private System.Windows.Forms.Button btnFile;
  System.Windows.Forms.TextBox textBox1;
  System.Windows.Forms.Button button1;

  //미리보기 다이얼로그 인스턴스 생성
  PrintPreviewDialog ppd = new System.Windows.Forms.PrintPreviewDialog();

  //파일선택 다이얼로그
  OpenFileDialog ofd = new OpenFileDialog();

  //미리보기의 문서 인스턴스 생성
  PrintDocument pd = new PrintDocument();
 
  //초기화
  private void InitializeComponent()
  {
   this.button1 = new System.Windows.Forms.Button();
   this.textBox1 = new System.Windows.Forms.TextBox();
   this.btnFile = new System.Windows.Forms.Button();
   this.SuspendLayout();
   //
   // button1
   //
   this.button1.BackColor = System.Drawing.SystemColors.Control;
   this.button1.Font = new System.Drawing.Font("돋움", 12F,  System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
   this.button1.Location = new System.Drawing.Point(16, 16);
   this.button1.Name = "button1";
   this.button1.Size = new System.Drawing.Size(104, 23);
   this.button1.TabIndex = 0;
   this.button1.Text = "인쇄하기(윈폼)";
   this.button1.Click += new System.EventHandler(this.button1_Click);
   //
   // textBox1
   //
   this.textBox1.Location = new System.Drawing.Point(16, 56);
   this.textBox1.Name = "textBox1";
   this.textBox1.Size = new System.Drawing.Size(216, 21);
   this.textBox1.TabIndex = 1;
   this.textBox1.Text = "";
   //
   // btnFile
   //
   this.btnFile.BackColor = System.Drawing.SystemColors.Control;
   this.btnFile.Font = new System.Drawing.Font("돋움", 12F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
   this.btnFile.Location = new System.Drawing.Point(112, 104);
   this.btnFile.Name = "btnFile";
   this.btnFile.Size = new System.Drawing.Size(120, 23);
   this.btnFile.TabIndex = 3;
   this.btnFile.Text = "파일 업로드(윈폼)";
   this.btnFile.Click += new System.EventHandler(this.btnFile_Click);
   //
   // Print
   //
   this.BackColor = System.Drawing.SystemColors.ControlLight;
   this.Controls.Add(this.btnFile);
   this.Controls.Add(this.textBox1);
   this.Controls.Add(this.button1);
   this.Name = "Print";
   this.Size = new System.Drawing.Size(248, 176);
   this.ResumeLayout(false);

  }
 
  //생성자
  public Print()
  {
   InitializeComponent();
   this.pd.PrintPage += new PrintPageEventHandler(this.OnPrintPage);
  }
 
  //페이지를 설정하는 이벤트 메서드
  private void OnPrintPage(object obj, PrintPageEventArgs e)
  {
   string text = "앗싸!!! 된다...";
   System.Drawing.Font printFont = new System.Drawing.Font("돋움", 12, System.Drawing.FontStyle.Regular);

   e.Graphics.DrawString(text, printFont, System.Drawing.Brushes.Black, 0, 0);
  }
 
  //윈폼 버튼 클릭시
  private void button1_Click(object sender, System.EventArgs e)
  {
   //문서의 이름 설정
   pd.DocumentName="tt";
   //미리보기 다이얼로그에 문서 지정
   ppd.Document = pd;
   //다이얼로그 출력
   ppd.ShowDialog();
  }

  #region IPrintControlCOMIncomming 멤버

  //외부 스크립트 이벤트에 의하여 호줄 되는 메서드
  public void show(string oData)
  {
   //외부에서 데이터를 가져와서
   //버튼의 이름으로 설정
   textBox1.Text = oData;
   //문서의 이름 설정
   pd.DocumentName="tt";
   //미리보기 다이얼로그에 문서 지정
   ppd.Document = pd;
   //다이얼로그 출력
   ppd.ShowDialog();
  }

  //웹폼 버튼 선택 시 파일 경로 출력
  public string showDialog()
  {
   if(ofd.ShowDialog() == DialogResult.OK)
   {
    return ofd.FileName;
   }
   return null;
  }
  #endregion
 

  //윈폼 버튼 선택 시 파일 경로 출력
  private void btnFile_Click(object sender, System.EventArgs e)
  {
   if(ofd.ShowDialog() == DialogResult.OK)
   {
    textBox1.Text = ofd.FileName;
   }
  }
 }
}


dll을 호출하는 HTML 코드

기존의 ActiveX와 동일하게 <OBJECT>태그를 통하여 dll을 사용한다.

<%@ Page language="c#" Codebehind="MyForm.aspx.cs" AutoEventWireup="false" Inherits="RichWinForm.WebForm1" %>
<HTML>
 <HEAD>
  <title>WebForm1</title>
  <meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
  <meta content="C#" name="CODE_LANGUAGE">
  <meta content="JavaScript" name="vs_defaultClientScript">
  <meta content="
http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
  <script language="javascript">
  function printClick()
  {
   MyForm.PrintPrev.show("외부에서 들어온 데이터 입니다.");
  }
  function openFile()
  {
   Output.innerText = MyForm.PrintPrev.showDialog();
  }
  </script>
  <script for="Slider" event="Scroll (source,args)" language="javascript">
   Output.innerText = MyForm.Slider.Value;
  </script>
 </HEAD>
 <body>
  <form id="MyForm" method="post" runat="server">
   <OBJECT id="PrintPrev" height="200" width="300" classid="http:Print.dll#RichWinForm.Print" VIEWASTEXT>
   </OBJECT>
   <br>
   <br>
   웹 버튼으로 인쇄해보자 :
   <input type="button" value="미리보기" onclick="printClick()">
   <br>
   <br>
   웹 버튼으로 파일업로드 :
   <input type="button" value="파일찾기" onclick="openFile()">
   <asp:Button id="btnSave" runat="server" Text="저장"></asp:Button>
  </form>
  <span id="Output" runat="server">0</span>
 
 </body>
</HTML
>

Posted by 굿데이
2008. 12. 22. 10:00

Smart Client


  Smart Client는 자동 Update 기능을 손쉽게 구현할 수 있는 기능이다.  자동 Update 기능은 개념은 다르지만 이미 V3, 아래아 한글 등 많은 프로그램에서 기능을 구현해 놓았다.

 Smart Client 기능은 타 응용 프로그램에서는 새버젼의 파일이 있을 경우 사용자에게 Update 할것인지를 선택하게 하나, Smart Client는 실행시 자동으로 Update된다.

  이 기능을 응용하면 웹 브라우저에서 실행되는 것 처럼 구성할 수도 있다.

   웹 브라우저에 포함되어 실행되는 프로그램은 기존에 ActiveX나 JAVA로 프로그램하여 구성하였다. 그러나 이 경우 사이트 방문자가 모르는 사이 프로그램이 설치되어 많은 문제를 야기 시켰다. 이를 해결하기 위해 Windows XP SP2이상에서는 보안 경고를 내보내고 있긴하나 완전한 해결책은 아니다.

  따라서 C#에서는 웹 브라우저에 포함되어 실행되는 형태는 지원하지 않는다. 따로 응용 어플리케이션을 다운 받아 실행할 수 있는 Smart Client 프로그램을 제작하여 배포함에 의해 웹 브라우저와는 별도의 화면으로 실행하도록 한다.

  이것은 필자가 C#을 접하면서 기대한 것과는 사뭇다르다. 필자는 ActiveX 같은 프로그램을 훨씬더 손쉬운 방법으로 제작할 수 있으리라 기대를 했었다. 그러나 Smart Client를 이용하면 ActiveX로 구성한 것과 비슷한 효과를 줄 수는 있다.

   응용 어플리케이션을 일반적으로 작성하여 IIS에 등록을 하고, 이 응용 어플리케이션을 자동으로 Update 할 수 있는 Smart Client 프로그램을 작성하고 배포하여 실행 하면 IIS 서버에 등록된 응용 어플리케이션을 다운 받아 실행하는 방식을 사용한다.

  이제 실제 Smart Client 프로그램을 제작해 보자. 여기서 사용할 응용 어플리케이션은  DLL과 Web Service에서 설명한 계산기를 이용할 것이다.

   기본 프로그램 제작에서 설명한 방식으로 프로젝트를 수동으로 하나 만든다. 프로젝트 이름은 CalcSmartClient로 한다. 참조에 System.dll과 System.Windows.Form.dll을 추가하고 CalcSmartClient.cs를 만든후 다음과 같이 코딩을 한다.

using System;

using System.Windows.Forms;

using System.Reflection;


class ShapeSmartClient

{

    public static void Main()

    {   

        try

        {

            Assembly assembly = Assembly.LoadFrom(

                "http://localhost/Calculator.exe");


            Type type = assembly.GetType("Calculator.MainForm");

            type.InvokeMember("Main",

                BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,

                null, null, null);

        }

        catch(Exception ex)

        {

            MessageBox.Show("서버 에러입니다. 네트워크를 확인 한 후 실행하세요");

        }

    }

}


이제 응용 어플리케이션을 약간 수정하여 IIS에 등록해보자. 어플리케이션 수정은 간단하다. 기본 프로그램 제작에서 잠시 언급했었는데 Main 함수 앞에 public으로 선언하는 것이다.

또한 IIS에 등록하는 것도 간단하다. http://localhost에 해당하는 디렉토리(설정을 변경하지 않았으면 c:\inetpub\wwwroot가 그 위치이다)에 실행파일(dll을 사용하면 dll 파일도 같이)을 복사하기만 하면 된다.

1. 실행파일의 위치를 설정한다.

            Assembly assembly = Assembly.LoadFrom(

                "http://localhost/Calculator.exe");

2. Main을 포함한 클래스 이름을 설정한다. 클래스 이름은 namespace.class의 형태로 입력한다.

            Type type = assembly.GetType("Calculator.MainForm");

3. Main을 호출한다.

            type.InvokeMember("Main",

                BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod,

                null, null, null);


이것이 Smart Client 제작의 전 과정이다.


   이제 제작한 SmartClientClient를 실행 해보자.

   Calculator가 잘 실행 될 것이다.

   탐색기를 실행하여 C:\WINDOWS\assembly로 가면 왼쪽 Tree에 Download가 있을 것이다. 좀 특수한 구조로 되어 있어 실제 오른쪽 파일 보이는 곳에는 존재하지 않은 디렉토리이다. Download를 선택하면 다음과 같이 나타난다.

 Calculator가 여러게 있는 것을 볼 수 있다. 실제 CalcSmartClient.exe를 실행하면 이곳에 Calculator.exe를 찾아 존재하지 않거나, 파일의 크기, 날짜, Version을 비교하여 다르면 서버로부터 다운 받아 실행한다. 참고로 실행 파일에서 사용하는 dll 파일을 그 루틴이 load 될 때(처음 사용될 때, 계산기 프로그램에서는 처음으로 = 키를 누를때) 자동으로 복사된다.

  여기서 우리는 Calculator 제작시 버전에 대해 전혀 신경쓰지 않았다. Calculator 프로젝트를 열어 Assembly.cs를 열어 보자.

  [assembly: AssemblyVersion("1.0.*")]라는 부분을 볼 수 있을 것이다. 마지막에 *는 자동으로 버전이 입력되도록 한 것이다. 이렇게 설정하면(초기 설정) 1.0.으로 시작하면서 나머지는 자동으로 입력이될 것이다. 자동으로 입력이 되는 부분이 Soruce를 수정하여 빌드를 할 때 마다 바뀌어 버전이 부여된다. 참고로 이 부분을 포함하여 전체 버전을 수동으로 입력하여도 된다. SmartClient를 이용하여 배포하는 경우에는 필요없는 다운로드를 피하기 위해 수동으로 관리하는 것이 좋다.


   이제 인터넷에서 실행되는 프로그램 처럼 보이게 해보자. Smart Client 프로그램 입력에서 http://localhost/Calculator.exe라고 입력 했던 부분 중 localhost 대신 실제 IP를 입력하여 빌드 하고 생성된 실행파일인 CalcSmartClient.exe를 http://localhost/의 위치로 복사한다.

   HTML 파일(여기서는 test.htm)을 하나 만들어 다음과 같이 입력한다.

<html>

<body>

<a href="CalcSmartClient.exe">실행</a>

</body>

</html>

  같은 PC에서건 외부 PC에서건 test.htm 파일을 인터넷 익스플로어에서 열면 다음과 같이 화면에 나타난다.

   

  여기에 DLL과 Web Service에서 설명한 보안 설정 법과 앞으로 설명할 보안 설정법을 표시하는 것이 좋다. 이것은 단순 HTML 작성법으로 작성해서 실행 주변에 Link 걸어 주면 되므로 설명하지 않는다.

  실행을 선택하면 다음과 같은 대화 상자가 생성된다.

  실행을 선택하면 다음과 같이 보안 경고 대화 상자가 생성된다.

   실행 버튼을 누르면 실행이 된다.

   실제 인터넷에서 실행되는 프로그램처럼 보이고자 한다면 위 두 버튼을 안나오게 하면 될 것이다. 이것은 웹서버를 신뢰할 수 있는 사이트로 등록함에 의해 이루어 진다.

  인터넷 익스플로어를 실행하여 도구 메뉴의 부메뉴 인터넷 옵션을 선택하여 생성된 대화 상자에서 보안 탭을 누르면 다음과 같이 화면에 나타난다.

  사이트를 선택하면 다음과 같은 대화 상자가 생성된다.

   영역에 웹 사이트 추가에 http://서버 IP의 형태로 입력하고, 이 영역에 있는 모든 사이트에 대해 서버 확인(https:) 필요 체크 버튼을 해제하고 추가를 선택한 후 확인을 누르면 신뢰할 수 있는 사이트로 등록이 된다.

  이것은 ActiveX와는 완전히 다르다. ActiveX는 인터넷 익스플로어에 포함되어 실행되나 이 프로그램은 인터넷에서 실행되는 것 처럼 보이지만 인터넷 익스플로어와는 전혀 별개로 동작한다. 앞에서도 이야기 했지만 Microsoft 사에서는 보안 문제 때문에 ActiveX 와 같은 형태의 프로그램을 버릴려고 하고 있다. ActiveX와 비교해 단점은 일부는 일반 HTML 화면과 응용 어플리케이션 화면을 같이 보여 줄수 없고 별도의 화면으로 띄워야 한다는 것이다. 일반적으로 프로그램은 응용 어플리케이션을 독립적으로 띄워도 상관없는 것이 대부분이다.


다운로드

Posted by 굿데이
2008. 12. 20. 05:14

사용자 삽입 이미지
































플로우 차트만 보면 무척 간단하다.

업데이트할 내용이 있는지 검사해서 있으면 하고 없으면 말고. 이게 끝이다.


자동업데이트 기능은 인터넷의 혜택으로 인해 요즘 프로그램에선 필수 이다.

예제 보고 간단하게 함 따라 해봤는데 나름대로 구미에 맞게 수정하다 보니 제법 그럴싸해졌다.

첨엔 ini 파일을 사용했는데 그거보단 레지스트리가 난거 같아서 레지스트리에 업데이트 정보를 저장해서 사용한다.

DB와 연동해서 프로그램별로 버전관리가 가능하게 했다.



업데이트 관리 DB – 업데이트 프로그램 통합 관리

3개의 테이블 사용 프로그램 관리, 업데이트 관리, 업데이트 로그

프로그램 아이디로 구분 하나의 자동업데이트 프로그램을 이용해 모든 프로그램에 적용하여 사용할 수 있다.

설정파일의 프로그램 아이디로 업데이트 관리 테이블을 검색하고 업데이트 내용을 가져와서 해당 파일들을 다운로드 받은 후 업데이트 결과를 업데이트 로그 테이블에 저장해준다.

각 프로그램 단위로 업데이트 상황을 파악할 수 있다.


업데이트 파일 검색

버전 정보를 날짜 형식으로 지정해서 현재 버전 보다 높은 버전이 있는지 검색

SELECT TOP 1 IDX, ID, VERSION, CONTENTS FROM UPDATE_LIST WHERE ID=@ID AND VERSION>@VERSION ORDER BY VERSION ASC

업데이트 로그 등록

업데이트할 모든 파일을 다운로드 받은 후 로그 기록

INSERT INTO UPDATE_LOG (UPDATE_IDX, IP, DOWN_DATE) VALUES (@IDX, @IP, GETDATE())






DB는 아무거나 상관없다.

처음에는 프로그램에서 직접 디비에 접속을 했는데 업데이트 로그 기록하려고 클라이언트의 IP정보를 어떻게 받아오나 고민하다가

좀더 효과적인 방법을 찾았다.

프로그램에서 디비에 직접 접속하는게 아니고 특정 웹페이지로 접속을 해서 업데이트 정보를 가져온다.

가져온 정보로 업데이트 하고 다시 웹페이지를 접속해서 로그를 저장하면 된다.

이렇게 하면 우선 프로그램에 디비 접속 정보가 없어도 되기 때문에 좀더 보안에 안전하다.

그리고 약간의 유연성이 더 있을 것 같다.


프로그램이 처음 실행될때 업데이트를 체크하도록 했는데 시작 프로그램에 등록해 놓으니 프로그램이 먼저 시작하고 나서 나중에

인터넷에 연결이 되는 상황이 발생한다.

인터넷 연결이 나중에 되더라도 프로그램이야 잘 돌아가지만 업데이트는 전혀 안되는 상황이 생길 수 있다.

그래서 프로그램 실행 중간에 한번씩 업데이트 체크하는 부분을 추가해야 된다.



08.08.06 보완사항

메인 프로그램에서 10초에 한번씩 작업을 진행하는데 타이머 위치가 잘못 되서 특정 예외 상황 발생시 거의 무한 루프에 빠져버리는 문제점을 발견했다.

몇일 쉬다가 다시 코드를 보니 원인 찾는데 넘 오래 걸렸다.

타이머 시작 위치 변경 해서 해결 했다.


40대의 컴에서 동시에 작업을 하다 보니 디비 서버가 뻗어버렸다.

디비서버 동시 접속수 늘려주고 디비 작업 후 연결 닫는 구문을 모두 추가해주었다.


메인 프로그램에서 자동업데이트 체크하는 부분하고 마무리 작업 처리하는 부분만 추가해주면 일단 마무리.





자동업데이트 소스

using System;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Web;
using Microsoft.Win32;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;
using System.ComponentModel;

namespace Keyword_Search_Starter
{
    public partial class Starter : Form
    {
private string sLocalFilePath; // 실행 경로 private string sServerFilePath; // 다운받을 서버주소 private string sResultMsg; // 업데이트 체크 결과 메세지(Error, OK) private string sID = null; // 프로그램 아이디 private string sIDX = null; // 업데이트 리스트 키값 private string sAuto = null; // 자동업데이트 유무(1, 0) private string sVersion = null; // 업데이트 버전 - 6자리날짜(080803) private string sStartProgram = null; // 업데이트 후 실행할 프로그램 private string sUserIP = null; // 접속 아이피 로그기록 private int iUpdateCount = 0; // 파일 다운로드 숫자 - 전송완료 될 때마다 증가함 private string sRegKey = "Software\\Pharos"; private string sUpdateAddr = "http://blog.naver.com/inidu2/Upload/UpdateListView.aspx"; public Starter() { InitializeComponent(); } #region Starter_Load 레지스트리 정보 읽기 private void Starter_Load(object sender, EventArgs e) { sLocalFilePath = AppDomain.CurrentDomain.BaseDirectory; // 레지스트리 설정 내용 확인 RegistryKey reg = Registry.LocalMachine; reg = reg.CreateSubKey(sRegKey); sID = (string)reg.GetValue("ID", null); sStartProgram = (string)reg.GetValue("StartProgram", null); sVersion = (string)reg.GetValue("Version", null); sAuto = Convert.ToString(reg.GetValue("Auto", null)); if (reg.GetValue("UpdateAddr", null) != null) { sUpdateAddr = Convert.ToString(reg.GetValue("UpdateAddr", null)); } lbTitle.Text += " ID : " + sID; GetUpdateList(); } #endregion #region GetUpdateList() 업데이트 리스트 가져오기 private void GetUpdateList() { string path = sUpdateAddr + "?id=" + sID + "&version=" + sVersion; try { HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(path); HttpWebResponse hwrp = (HttpWebResponse)hwr.GetResponse();
Stream strm = hwrp.GetResponseStream(); StreamReader sr = new StreamReader(strm); listView1.Items.Clear(); int line = 1, n = 1; string temp = null; while (sr.Peek() > -1) { temp = sr.ReadLine(); switch (line) { case 1: sResultMsg = temp; break; // 결과 메시지 case 2: sUserIP = temp; break; // 접속 IP case 3: sIDX = temp; break; // 업데이트 리스트 키 case 4: sVersion = temp; break; // Version case 5: sServerFilePath = temp; break; // Path default: // File List listView1.Items.Add(n.ToString()); listView1.Items[n - 1].SubItems.Add(temp); listView1.Items[n - 1].SubItems.Add("전송대기"); n++; break; } line++; } sr.Close(); strm.Close(); } catch { } finally { } // 업데이트 항목이 없으면 무시하고 프로그램 실행 if (listView1.Items.Count == 0) { AppStart(); } else { btnStart.Enabled = false; DownLoad(); } } #endregion #region DownLoad() 다운로드 protected void DownLoad() { this.Cursor = Cursors.WaitCursor; // 다운로드할 파일이 있으면 다운로드 if (listView1.Items.Count > 0) { for (int i = 0; i < listView1.Items.Count; i++) { string serverFile = sServerFilePath + listView1.Items[i].SubItems[1].Text; string localFile = sLocalFilePath + listView1.Items[i].SubItems[1].Text; try { WebClient webClient = new WebClient(); listView1.Items[i].SubItems[2].Text = "전송중"; webClient.DownloadFileCompleted +=
new AsyncCompletedEventHandler(Completed); webClient.DownloadProgressChanged +=
new DownloadProgressChangedEventHandler(ProgressChanged); webClient.DownloadFileAsync(new Uri(serverFile), @localFile); listView1.Items[i].SubItems[2].Text = "전송완료"; } catch (Exception ex) { listView1.Items[i].SubItems[2].Text = "전송에러"; MessageBox.Show(serverFile + "\r\n" + ex.Message + "\r\n" + ex.StackTrace); } } } this.Cursor = Cursors.Default; } #endregion private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) { progressBar1.Value = e.ProgressPercentage; lbState.Text = e.ProgressPercentage.ToString() + "%"; } #region Completed 전송완료 private void Completed(object sender, AsyncCompletedEventArgs e) { iUpdateCount++; if (iUpdateCount == listView1.Items.Count) { this.UpdateLog(); this.AppStart(); } } #endregion #region AppStart() 프로그램 실행 private void AppStart() { try { string AppPath = AppDomain.CurrentDomain.BaseDirectory; System.Diagnostics.Process ps = new System.Diagnostics.Process(); ps.StartInfo.FileName = AppPath + sStartProgram; ps.Start(); // 타이머 작업으로 업데이트 감시 Application.Exit(); } catch (Exception ex) { MessageBox.Show(ex.Message + "\r\n" + ex.StackTrace); } } #endregion #region UpdateLog() 업데이트 로그 작성 및 버전 정보 업데이트 private void UpdateLog() { // 레지스트리 설정 내용 확인 RegistryKey reg = Registry.LocalMachine; reg = reg.CreateSubKey(sRegKey); reg.SetValue("Version", sVersion); reg.SetValue("LastUpdate", DateTime.Now); // 업데이트 로그 string path = sUpdateAddr + "?mode=log&idx=" + sIDX; HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(path); HttpWebResponse hwrp = (HttpWebResponse)hwr.GetResponse(); } #endregion private void btnStart_Click(object sender, EventArgs e) { DownLoad(); } } }

[출처] 자동 업데이트|작성자 파로스

Posted by 굿데이
2008. 12. 20. 05:05
1. 소스를 정리하였습니다.

아래 표는 각 프로젝트와 그에 속한 클래스에 대한 설명입니다.

Hau 자동 업데이터 컴퍼넌트 프로젝트. 아래 두 프로젝트에 의해 참조됨.
     AutoUpdater 자동 업데이터 컴퍼넌트
     RemoteFile 웹서버의 파일을 표현
     TransferingInfo 전송 상태를 표현
     UpdateListDataSet 업데이트 파일 목록을 읽고 쓰기 위한 스키마
Hau.Sample 자동 업데이터 컴퍼넌트를 이용하는 샘플 프로젝트
     NomAutoUpdater 자동 업데이터 컴퍼넌트를 이용하여 구현된 NoM의 자동 업데이터
Hau.UpdateListEditor 업데이트 파일 목록 편집기 프로젝트
    UpdateListEditorForm 업데이트 파일 목록 편집기

2. AutoUpdater에 LocalRoot 라는 속성이 추가되었습니다.

이전 버전에는 다운로드 위치가 항상 AutoUpdater 컴퍼넌트가 사용된 실행파일과 같은 위치였지만, 이제는 LocalRoot 속성을 지정하면 아무 곳에나 다운로드를 받을 수 있게 되었습니다.


3. 업데이트 파일 목록 편집기에서 제외 정규식과, 그리드의 편집 기능이 없어졌습니다.


4. 업데이트 파일 목록 편집기 '마지막 수정 시각 1초 증가' 버튼이 추가되었습니다.

이 기능은 기존 업데이트 목록을 열어서 특정 파일만 최신 버전으로 지정하는 경우에 유용하게 사용할 수 있습니다.

1초가 증가된 행의 배경색은 베이지로 변경됩니다. 또한 한번 1초가 증가된 행은 더 이상 증가되지 않습니다. (논리적으로 2초 이상을 증가시킬 이유가 없습니다.)


5. 업데이트 파일 목록 편집기의 그리드에서 Delete 키를 누르면 행이 두 개씩 삭제되던 버그를 수정하였습니다.




출처
http://kimgwajang.tistory.com/135

Posted by 굿데이
2008. 12. 20. 05:03

http://kimgwajang.tistory.com/category/HAU
에 있는 포스트를 참조하십시오.


김과장! 이게 뭐야?

자동 업데이터 입니다.

클릭온스 같은 거야?

네. 훨씬 더 단순한 로직이긴 하지만 결과적으로 비슷한 일을 합니다.

그런데 '클릭원스' 라고 발음해야 하는 것 아닌가요? 클릭(click) 한 번(once)으로 설치가 된다는 의미일테니 '원스'가 맞지 않겠습니까? '온스'는 ounce 라는 질량 단위가 있긴 하지만 클릭과는 상관이 없잖아요.

따지기는, 양놈들이 뭐라고 하든 국내에서는 클릭온스로 통해. 그리고 유명한 XX님도 클릭온스라고 발음해.

그건 그렇고, 왜 만든거야?

부장님도 아시다시피, 제가 클릭원스와 링크(LINQ) 매니아지 않습니까?

링크도 XX님은 '링큐'라고 하던데.

'링크'가 맞을 것 같아요. 왜냐 하면...

아니 됐고, 하던 이야기나 마저 해봐.

클릭원스가 나오기 전에, 그러니까 닷넷 1.X 시절에 HAU의 초기 버전을 만들어서 사용하곤 했었는데요. 클릭원스가 등장하면서 부터는 클릭원스만 애용하고 있습니다.

어떤 사람들은 클릭원스가 필드에 맞지 않는 쓸모 없는 기술이라고도 이야기하는데요. Smart Client Deployment with ClickOnce라는 책을 읽고 나서는 클릭원스는 제가 알고 있는 것보다 훨씬 많은 일을 할 수 있다는 것을 알게 되었습니다.

하지만 클릭원스를 사용하기 위해서는 몇 가지 요구사항, 즉 배포 서버에 IIS와 프론트페이지 서버 익스텐션이 설치되어 있어야 하는데, 필드에서는 상황에 따라선 이런 요구 사항이 충족되지 않는 경우가 종종 있었습니다.

그래서 클릭원스를 사용하는 것이 배포의 베스트 프랙티스라고 생각은 하지만, 클릭원스를 사용할 수 없는 환경도 있으니까 대안으로 웹 서버를 이용한 단순한 자동 업데이터를 만들게 된 것입니다.

웹 서버만 있으면 된다고? 리눅스에 아파치가 설치된 서버도 돼?

네 그렇습니다.

사장님이 좋아하시겠네.

사장님 좋아하시라고 만든 건 아니고요.

김과장이 만든 자동 업데이터는 어떤 장점이 있는데? 가볍다거나 무료다라는 것 말고. 그건 보통 완성도가 떨어지는 소프트웨어에 관습적으로 붙이는 말이니까.

풍부한 이벤트를 제공합니다.

HAU는 물리적으로 컴퍼넌트(System.ComponentModel.Component)로 되어 있는데 이 컴퍼넌트를 윈도우 폼이나 사용자 정의 컨트롤에 올려 놓아 사용할 수 있습니다. HAU가 다양한 이벤트를 제공하기 때문에 HAU를 사용하여 자동 업데이터를 개발하는 개발자는 자유로운 UI를 구현할 수 있습니다.

또?

계층 구조로 된 파일의 자동 업데이트를 지원합니다.

또?

업데이트 체크를 위한 웹 서버에의 요청을 최소화 하여 속도가 빠릅니다.

예컨데, A, B, C 세 개의 파일이 서버에 있고, 이 중 A와 B가 최신 버전이면, HAU는 세 파일에 대해서 각각 웹 서버에 요청을 보내 최신 버전 여부를 확인하지 않고, 업데이트 파일 목록를 구하는 한 번의 요청으로 최신 버전 여부를 판단합니다.

만일 배포된 파일이 200개 라면, 매 사용자가 프로그램을 실행할 때 마다 최신 버전이 있든 없든 항상 웹 서버에 200번의 요청을 보내야 하는 오버헤드가 발생할 수 있습니다.

업데이트 파일 목록이 뭐야? 어떻게 만드는 거야?

서버에 배포된 파일의 정보(경로, 크기, 수정 시각)를 저장하고 있는 XML 파일입니다.

업데이트 파일 목록의 XML 스키마는 너무나 단순하지만 직접 작성하기에는 번거로운 일이라서, 업데이트 파일 목록 편집기를 같이 만들었습니다. 이를 이용하면 업데이트 파일 목록을 쉽게 만들 수 있습니다.

더 있어?

음... 이런 말씀은 안드리려고 했는데, 가볍고 무료입니다.

좀 구차하지 않나?

소스가 단순하고 직관적입니다.

무슨 근거로?

닷넷 프레임웍 2.0에는 WebClient 라는 클래스가 추가됨으로 해서 HTTP 프로토콜을 사용한 다운로드가 무척 간단해졌습니다. 이를 직접 구현한 HAU의 이전 버전에 비하면 몇 백 줄 이상이 줄어들었습니다.

그리고 처음부터 공개를 목적으로 작성한 소스이기 때문에 최대한 다른 개발자가 쉽게 파악하기 용이한 구조로 작성하고 주석을 성실하게 달았습니다. 쓰레드에 대한 약간의 지식만 있으면 누구나 쉽게 커스터마이징이 가능할 것으로 자신합니다.

김과장이 보내준 비주얼 스튜디오 프로젝트에 들어 있는 파일들은 다 뭐야?

AutoUpdater는 자동 업데이트 기능을 구현한 컴퍼넌트 입니다. 따라서 업데이트 진행 상황을 표시하는 UI 같은 것은 포함되어 있지 않습니다. 대신 AutoUpdater이 노출하는 이벤트에 대한 핸들러를 작성하여 UI를 붙일 수 있습니다.

SampleForm는 바로 이러한 UI를 구현하는 예제로 만든 폼입니다.

마지막으로 UpdateListEditor는 업데이트 파일 목록 편집기입니다.

어떻게 사용하는 거야? 그림과 함께 상세하게 설명해줘봐.

NoM의 배포를 예로 들어 설명해보겠습니다.

NoM의 배포에 필요한 파일의 목록은 아래와 같습니다.

파란색은 파일, 빨간색은 폴더를 나타내는 계층구조를 가지고 있습니다.

참고로 dummy 폴더는 NoM의 배포에 필요하지는 않지만 설명의 편의 상 추가한 폴더입니다.

파일들을 모두 D:\NoM 폴더에 모아 두고 이후 작업을 진행합니다.

업데이트 목록 만들기

먼저 위 파일들을 가지고 업데이트 목록을 만들어야 합니다.

소스에 있는 UpdateListEditor를 실행합니다.

쌩뚱맞게 '제외 정규식'은 뭐야?

정규식을 입력하면, 그에 매치되는 파일은 업데이트 목록에서 제외하는 기능입니다.

지금은 제외할 파일이 없으니까 비워 두고 디렉토리 읽기를 클릭합니다.

폴더 찾아보기 대화상자가 열리면 D:\NoM를 선택합니다.

필요하다면 그리드에서 목록을 수정할 수 있지만, 지금은 바로 '저장하기'를 클릭하여 이 목록을 XML파일로 저장합니다.

D:\NoM\UpdatingList.xml로 저장합니다.

이제 업데이트 목록 파일이 만들어졌습니다.

배포 파일을 웹서버로 업로드 하기

위 파일들을 계층 구조를 그대로 유지한채로 웹서버에 올립니다. 위에서 만든 UpdatingList.xml 파일도 같이 올려야 합니다.

여기서는 http://www.dolsan.org/nullnull/Nom에 업로드한 것으로 하겠습니다.

여기까지가 배포 서버를 준비한 과정이었습니다. 지금부터는 배포 서버를 이용하여 자동으로 업데이트를 하는 응용 프로그램을 만들도록 하겠습니다.

자동 업데이터 응용프로그램 만들기

비주얼 스튜디오에서 새 윈폼 응용프로그램을 만듭니다.

소스에 있는 Framework.AutoUpdater.dll에 대한 참조를 추가한 후 도구상자에서 AutoUpdater 컴퍼넌트를 선택하여 폼으로 끌어다 놓습니다.

autoUpdater1의 속성 중 RootUri를 http://www.dolsan.org/nullnull/Nom로 UpdateListFileName을 UpdatingList.xml로 설정합니다.

구현할 UI에 따라 autoUpdater1의 이벤트 핸들러를 적절하게 구현합니다.

자세한 내용은 소스에 포함된 SampleForm을 참조하시기 바랍니다.

각 이벤트의 의미는 다음과 같습니다.

이벤트

의미

UpdateCompleted 업데이트가 끝났음
UpdatableListFound 업데이트 할 파일의 목록이 발견되었음
UpdatableFileFound 업데이트 할 파일이 발견되었음
FileTransfering 개별 파일의 전송이 시작되려고 함
FileTransfered 개별 파일의 전송이 완료되었음
UpdateProgressChanged 업데이트 진행 상황이 변경되었음

물론 이 중에서 가장 중요한 이벤트는 업데이트가 끝났음을 알리는 UpdateCompleted라고 할 수 있겠습니다.

이에 대한 이벤트 핸들러는 일반적으로 다음과 같이 구현될 것입니다.

NoM.exe를 실행하고 HAU는 종료하는 코드입니다.

시간이 지나 NoM이 업데이트 되면, 업데이트 목록 파일 만들기와 웹서버로 업로드하기를 반복하면 됩니다.

아 그렇군. 김과장 수고했어.

네.

Posted by 굿데이
2008. 12. 20. 03:11
지난번 HttpHelper 클래스를 이어 이번에는 WebClient를 이용한 Downloader 클래스를 만들어 보도록 하겠습니다.

다들 아시겠지만 Downloader 클래스는 실버라이틑 1.1에는 있었지만 실버라이트 2가 나오면서 사라지고 WebClient로 대체가 되었습니다.

물론 바뀐 WebClient가 사용방법이 어려운건 아니지만 좀 더 사용하기 편하게 하기 위해서 WebClient를 이용한 Downloader 클래스를 작성해 보았습니다.

WebClient를 이용한 Downloader 클래스 만들기

1. Downloader 클래스 작성

Downloader 클래스도 HttpHelper 클래스와 마찬가지로 기본 클래스, 이벤트, 다운로드 구분자로 구성 되어 있습니다.

사용할 네임스페이스

using System.Net;
using System.IO;

다운로드 구분

public enum DownloadKind
{
    Text,
    Binary
}

다운로드 구분은 크게 2가지로 텍스트와 바이너리로 구성이 됩니다. 텍스트는 txt, xml, html, js, cs, asp, aspx 같은 순수 텍스트로만 이루어진 파일이며, 바이너리는 wmv, zip, exe 같은 동영상이나 압축파일 등등 텍스트를 제외한 모든 파일이라고 생각해도 좋을거 같습니다.

이벤트 핸들러

public delegate void DownloadCompleteEventHandler(DownloadCompleteEventArgs e);
public class DownloadCompleteEventArgs : EventArgs
{
    public Stream ResultStream { get; set; }
    public string ResultString { get; set; }
    public DownloadCompleteEventArgs(Stream result)
    {
        this.ResultStream = result;
    }
    public DownloadCompleteEventArgs(string result)
    {
        this.ResultString = result;
    }
}
public delegate void DownloadErrorEventHandler(DownloadErrorEventArgs e);
public class DownloadErrorEventArgs : EventArgs
{
    public string Error { get; set; }
    public DownloadErrorEventArgs(string error)
    {
        this.Error = error;
    }
}

이벤트 핸들러는 크게 다운로드 완료 이벤트와 다운로드 에러 이벤트로 구성됩니다.

다음은 기본 클래스의 구성 입니다.

전역

WebClient       m_WebClient;
DownloadKind m_DownloadKind;
Uri                 m_DownloadUri;
public event DownloadCompleteEventHandler DownloadComplete;
private void OnDownloadComplete(DownloadCompleteEventArgs e)
{
    if (this.DownloadComplete != null)
    {
        this.DownloadComplete(e);
    }
}
public event DownloadErrorEventHandler DownloadError;
private void OnDownloadError(DownloadErrorEventArgs e)
{
    if (this.DownloadError != null)
    {
        this.DownloadError(e);
    }
}

다운로드에 필요한 변수와 이벤트를 발생 시키기 위한 내용입니다.

생성자

public Downloader(Uri downloadUri, DownloadKind kind)
{
    m_WebClient = new WebClient();
    m_DownloadKind = kind;
    m_DownloadUri  = downloadUri;
    if (m_DownloadKind == DownloadKind.Binary)
    {
        m_WebClient.OpenReadCompleted += new OpenReadCompletedEventHandler(m_WebClient_OpenReadCompleted);
    }
    else
    {
        m_WebClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(m_WebClient_DownloadStringCompleted);
    }
}

다운로드 구분에 따라서 이벤트를 다르게 생성해 줍니다.

다운로드 시작 함수

public void Start()
{
    if (m_DownloadKind == DownloadKind.Binary)
    {
        m_WebClient.OpenReadAsync(m_DownloadUri);
    }
    else
    {
        m_WebClient.DownloadStringAsync(m_DownloadUri);
    }
}

역시 다운로드 구분에 따라서 Async 함수를 구분해서 실행 합니다.

바이너리 파일 다운로드 완료 이벤트

void m_WebClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
    if ((e.Error == null) && (e.Cancelled == false))
    {
        OnDownloadComplete(new DownloadCompleteEventArgs(e.Result));
    }
    else
    {
        OnDownloadError(new DownloadErrorEventArgs(e.Error.Message));
    }
}

텍스트 파일 다운로드 완료 이벤트

void m_WebClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    if(e.Error == null)
    {
        OnDownloadComplete(new DownloadCompleteEventArgs(e.Result));
    }
    else
    {
        OnDownloadError(new DownloadErrorEventArgs(e.Error.Message));
    }
}

이렇게 해서 Downloader 클래스 작성을 완료 했습니다. 이 클래스에 자신이 원하는 기능을 추가해서 확장 클래스를 작성해서 사용해도 좋을 것입니다.

Downloader 클래스 다운로드



2. Downloader 클래스 사용방법

다운로드 하기

Uri uri = new Uri(http://localhost/DownloaderTest.aspx);
Downloader down = new Downloader(uri, DownloadKind.Text);
down.DownloadComplete += new DownloadCompleteEventHandler(down_DownloadComplete);
down.DownloadError += new DownloadErrorEventHandler(down_DownloadError);
down.Start();

이벤트

void down_DownloadError(DownloadErrorEventArgs e)
{
    txtResult.Text = "에러 : " + e.Error;
}
void down_DownloadComplete(DownloadCompleteEventArgs e)
{
    // 텍스트일 경우
    txtResult.Text = e.ResultString;

    // 바이너리일 경우
    MediaElement media = new MediaElement();
    media.SetSource = e.ResultStream;
    ...
}

도메인 접근시 크로스 도메인이 허용이 안된 사이트는 에러 이벤트를 발생 시킵니다.
이 경우 에러는 Download Failure 라는 에러가 나오네요!!

이것으로 다운로더 클래스 사용 방법도 마치도록 하겠습니다.
아무쪼록 이 글을 보시는 분들께 많은 도움이 되었으면 좋겠습니다.

작성자 : 상현넘™ [http://www.shblitz.net]
Posted by 굿데이
2008. 12. 20. 02:21
//POST 방식으로 데이터를 업로드 하는 예제 입니다.

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Web;

class OpenWrite
{
        static void Main()
        {               
                string uriString = "http://***.***.***.**/write_ok.php";

                string name = "tester";
                string email = "oracler@oraclejava.co.kr";
                string subject = "방가와요...";
                string password = "1111";
                string memo = "사이트 오픈을 축하 합니다.";
                string id = "guest";

                string postData = "id=" + HttpUtility.UrlEncode(id) + "&" +
                                  "name=" + HttpUtility.UrlEncode(name) + "&" +
                                  "email=" + HttpUtility.UrlEncode(email) + "&" +
                                  "subject" + HttpUtility.UrlEncode(subject) + "&" +
                                  "password=" + HttpUtility.UrlEncode(password) + "&" +
                                  "memo" + HttpUtility.UrlEncode(memo);
                                       
                byte[] postArray = Encoding.Default.GetBytes(postData);

                // Create a new WebClient instance.
                WebClient myWebClient = new WebClient();
                myWebClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

                // postStream implicitly sets HTTP POST as the request method.
                Console.WriteLine("Uploading to {0} ...",  uriString);                                                        
                Stream postStream = myWebClient.OpenWrite(uriString);

                postStream.Write(postArray,0,postArray.Length);

                // Close the stream and release resources.
                postStream.Close();

                Console.WriteLine("\n 방명록에 성공적으로 글을 올렸습니다...");
        }
}
Posted by 굿데이