내 블로그에 한RSS 추가 버튼 넣기

원문 : http://kkamagui.springnote.com/pages/600799


들어가기 전에...


0.시작하면서...

 블로그를 돌아다니다 보면 심심치 않게 한RSS의 이미지와 함께 "이 블로그를 한RSS에 추가하세요"와 같은 문구를 볼 수 있다. 클릭해 보면 한RSS의 사이트로 이동하고 RSS 추가화면으로 이동하는 것을 볼 수 있는데, 상당히 편리하고 구독자를 늘리는데 효율적인 방법이라 생각된다.

 지금부터 블로그에 한RSS 구독버튼을 추가하는 방법을 알아보자.


1.한RSS 등록 페이지

 http://www.hanrss.com/help/bloggers.qst로 이동하면 아래와 같은 화면이 표시된다.

한RSS1.PNG 

<사이트 등록 페이지>


 RSS 주소를 붉은 색 부분에 붙여 넣고 "HTML 생성" 버튼을 누르면 추가할 수 있는 HTML 코드가 생성된다. 이제 RSS 주소를 찾아보자.


2.내 블로그 RSS 주소 찾기

 RSS 주소를 어디서 얻을 수 있을까? 블로그에 보면 아래와 같은 아이콘을 찾을 수 있다.

한RSS2.PNG 

<RSS 주소 아이콘>


 위의 아이콘을 클릭하면 아래와 같이 RSS 주소가 표시된다. 이 주소를 한RSS에 붙여넣으면 된다.

한RSS4.PNG 

<RSS 주소>


3.HTML 코드 생성

 한RSS 페이지로 다시 이동해서 블로그 구독 주소를 입력하고 "HTML 생성" 버튼을 클릭하면 아래와 같이 HTML 코드가 생성된다.

한RSS3.PNG 

<생성된 한RSS 추가 HTML 코드>


 위의 코드를 블로그 스킨에 삽입하면 된다.


5.블로그 스킨 변경

 내가 티스토리를 쓰고 있는 관계로 티스토리를 위주로 설명하겠다. 티스토리의 관리 메뉴로 이동하여  "스킨" 탭으로 이동하자. "HTML/CSS 수정" 을 눌러서 skin.html에 생성된 HTML 코드를 추가하자.

스프링노트12.PNG 

<skin.html 코드 수정>


 skin.html 코드를 뒤져서 아래의 파란색 코드(생성된 HTML 코드)를 추가하자.

  1.       <!-- 본문 -->
          <div class="article">##_article_rep_desc_##</div>

  2. <!-- kkamagui 추가 Han Rss에 추가 시작 -->
    <center><strong>
    이 블로그를 한 RSS에 추가하세요 ^^ => <a href="
    http://www.hanrss.com/add_sub.qst?url=http%3A%2F%2Fkkamagui.tistory.com%2Frss" title="한RSS에 이 블로그를 추가"><img src="http://static.hanrss.com/images/add_to_hanrss3.gif" border="0" /></a>
    </strong></center>
    <!-- Han Rss에 추가 끝 -->

  3.       <div class="author">
           Posted by ##_article_rep_author_##
          </div>      

 코드를 추가한 뒤 "미리보기" 버튼을 누르면 아래와 같이 한RSS에 추가버튼이 표시된다. 정상적으로 표시되면 저장하면 끝이다.

한RSS5.PNG 

<추가된 구독 버튼> 


7.마치면서...

 이것으로 블로그에 한RSS 구독 버튼을 추가하는 방법에 대해서 모두 알아보았다. 구독 버튼을 설치하지 않아도 괜찮지만 구독자의 편의를 위해서라면 시간을 조금 투자하는 것도 나쁘지 않을 것 같다.



이 글은 스프링노트에서 작성되었습니다.

스프링노트(Springnote)와 블로그(티스토리, 이글루스) 동시에 활용하기

원문 : http://kkamagui.springnote.com/pages/599820


들어가기 전에...

0.시작하면서...

 "블로그(Blog)와 위키(Wiki) 중 어느 것이 더 좋은가?" 이 물음에 대해서 누가 더 좋다고 한마디로 결정하기 어렵다. 블로그는 매일매일 일어나는 일상에 대한 기록(log)를 남기는 목적에서 출발한 것이고, 위키는 여러사람이 지식을 공유하고 관리하기위한 목적에서 출발한 것이다. 블로그의 글이 "완성된 글""올리는데" 목적이 있다면 위키의 글은 "계속해서 자라나는 글""관리하는데" 목적이 있다고 할 수 있다.


 서로 목적이 다르므로 목적에 맞게 골라쓰는 것이 제일 좋은 것이고 나도 티스토리(Tistory) 블로그와 스프링노트(Springnote)를 같이 사용하고 있다. 스프링노트는 위키기반의 시스템으로 개발이나 스터디를 정리하는 목적으로 사용하고 있고 티스토리는 일상의 신변잡기나 간단한 이슈들, 그리고 스프링노트의 글을 외부로 "공개"하는 용도로 사용하고 있다. 다행히도 스프링노트에는 작성된 글을 블로그로 보내주는 기능이 있으니 쉽게 블로그(티스토리, 이글루스 등등)로 보낼 수 있다.


 지금부터 스프링노트와 블로그를 동시에 활용하는 방법을 알아보자. 블로그는 내가 티스토리를 쓰고 있는 관계로, 티스토리를 위주로 설명할 것이다. 이글루스나 네이버 블로그도 조금만 수정하면 되므로 끝까지 읽어본 뒤 적용해 보자.


1.티스토리에 Blog API 활성화하기

 스프링노트에서 티스토리로 그을 보내기위해서는 Blog API를 활성화해야 한다. Blog API 관련 설정은 관리 모드에서 "환경설정" 탭의 "기타설정" 에서 찾을 수 있다. 페이지의 가장 아래로 내려가면 Blog API 관련 설정을 볼 수 있다.

스프링노트3.PNG 

<환경설정 - 기타설정 화면>


스프링노트5.PNG

<Blog API 관련 설정> 


  아래 화면에서 "BlogAPI를 사용합니다" 를 체크한 후에 아래의 정보를 추후 스프링노트에 설정하면 된다.


2.스프링노트에 블로그 등록하기

 스프링노트에 블로그를 등록하기위해서는 관리화면으로 이동해야 한다. 관리화면은 우측 하단의 "설정" 버튼을 클릭하면 이동할 수 있다.

스프링노트1(1).PNG

<관리 화면으로 이동>


 관리화면에서 "블로그 설정" 탭으로 이동하면 "새로 추가하기" 버튼이 있다. 버튼을 눌러 아래의 설정을 다 체운뒤 "연결테스트" 버튼을 눌러 테스트가 정상적으로 끝나면 "확인" 버튼을 눌러 설정을 저장하자.

스프링노트2.PNG

<블로그 설정> 


 블로그의 종류는 현재 태터툴즈, 티스토리, 이글루스, 워드프레스를 지원하고 위 항목에 없는 기타 블로그를 위해 직접설정을 제공한다. 아래의 XML-RPC 주소나 Bloag API Blog ID와 같은 부분은 티스토리의 관리화면에서 봤던 정보를 입력하자. 입력이 끝난 후 "연결테스트" 버튼을 클릭해서 정상적으로 수행되었다는 메시지가 표시되면 성공한 것이다.


3.스프링노트의 글을 티스토리로 내보내기

 스프링노트에서 작성된 글을 티스토리로 내보내기위해서는 스프링노트의 우측 상단에 있는 "메뉴" 버튼을 클릭해서 "블로그로 발행하기" 를 클릭하면 된다.

스프링노트6.PNG 

<블로그로 발행하기> 


 스프링노트7.PNG

<블로그 선택 및 카테고리 선택> 


  블로그 이름과 카테고리를 선택한 후 "블로그로 내보내기" 버튼을 누르면 해당 블로그로 글을 내보낼 수 있다.


4.스프링노트에서 보내진 글 예쁘게 보기

4.1 기본 설정으로 보기

 스프링노트에서 보내진 글을 별다른 설정없이 블로그에서 보면 약간 이상하게 보인다. 일단 아래를 한번 보자.

스프링노트9.PNG 

<스프링노트의 글>


스프링노트8.PNG 

<블로그의 글 - 숫자가 표시됨, 박스 표시 없음>


스프링노트10.PNG 

<스프링노트의 글>


스프링노트11.PNG 

<블로그의 글 - 박스 표시 없음>


 위와 같이 많은 차이가 나게된다. 따라서 스프링노트의 글을 스프링 노트와 비슷하게 보려면 몇가지 설정을 해야 한다. 어떤 것을 수정해야 할까?


4.2 웹페이지 소스 분석 및 태그 찾기

 블로그에서 오른쪽 버튼을 클릭하여 "소스보기" 로 페이지 소스를 보면 아래와 같은 소스를 볼 수 있다.

  1. ... 생략 ... 
  2. <H2>들어가기 전에...</H2>
    <DIV>
    <UL>
    <LI><STRONG>이 글은 kkamagui에 의해 작성된 글입니다.</STRONG>
    <LI><STRONG>마음껏 인용하시거나 사용하셔도 됩니다. 단 출처(<A href="http://kkamagui.springnote.com/"><SPAN style="COLOR: #800080">http://kkamagui.springnote.com</SPAN></A>)는 밝혀 주십시오.</STRONG>
    <LI><STRONG>기타 사항은 kkakkunghehe at daum.net 이나</STRONG> <A href="http://kkamagui.tistory.com/"><STRONG><SPAN style="COLOR: #800080">http://kkamagui.tistory.com</SPAN></STRONG></A>, <A href="http://kkamagui.egloos.com/"><STRONG><SPAN style="COLOR: #800080">http://kkamagui.egloos.com</SPAN></STRONG></A><STRONG>으로 보내주시면 반영하겠습니다.</STRONG> </LI></UL></DIV>
  3. ... 생략 ...
  4. <OL class=code>
    <LI>&nbsp; &nbsp; // 압력계산 코드<br />&nbsp; &nbsp; <STRONG><SPAN style="COLOR: #0000ff">sPressure = (((( touch.px + 1 ) * touch.z2) &gt;&gt; 6) / touch.z1) - (touch.px &gt;&gt; 6);<br /></SPAN></STRONG>&nbsp;&nbsp; &nbsp;<STRONG><SPAN style="COLOR: #0000ff">sPressure1 = ( touch.z2/( touch.z1 - 1 ) );<br /></SPAN></STRONG>&nbsp; &nbsp; if( sPressure1 &lt; sPressure )<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; sPressure2 = sPressure1;<br />&nbsp; &nbsp; }<br />&nbsp; &nbsp; else<br />&nbsp; &nbsp; {<br />&nbsp; &nbsp; &nbsp; &nbsp; sPressure2 = sPressure;<br />&nbsp; &nbsp; }
    <LI><br />&nbsp; &nbsp; // 아래의 Touch와 XY버튼은 특별한 케이스이다.<br />&nbsp; &nbsp; // Touch Down<br />&nbsp;&nbsp; &nbsp;// Down 메시지는 계속 보낸다.<br />&nbsp; &nbsp; if( ( usSpecial &amp; ( 0x01 &lt;&lt; 6 ) ) &amp;&amp; ( touch.x != 0 ) &amp;&amp; ( touch.y != 0 ) &amp;&amp; <STRONG><SPAN style="COLOR: #0000ff">( sPressure2 &lt; 5 )</SPAN></STRONG> ) </LI></OL>
  5.  ... 생략 ...

 위에서 보는 것과 같이 문단으로는 <DIV> 태그를 사용하고 코드 영역으로는 <OL class=code> 태그를 사용함을 알 수 있다. 다시 말하면 이 태그의 속성을 잘 조절해 주면 지금보다 더 나은 화면을 볼 수 있다는 말이다. 이제 블로그의 스타일 코드를 수정하자.


4.3 블로그의 CSS 코드 수정

 티스토리의 관리화면으로 이동해서 "스킨" 탭으로 이동하자. "HTML/CSS 수정" 을 눌러서 style.css를 적당히 수정해서 어느정도 괜찮은 화면을 만드는 것이 목표다.

 스프링노트12.PNG

<HTML/CSS 편집 화면> 


 아래의 style.css 수정으로 가서 아래의 파란색 코드를 붙여넣자.

  1. /* content 기본스타일 */ <<== 기존 코드
    .content {font:90%/1.5 dotum, Verdana;}
    .content h1{ font-size:200%;}
    .content h2{ font-size:180%;}
    .content h3{ font-size:140%; margin:0 5px; padding:0 0;}
    .content h4{ font-size:120%; margin:0 5px; padding:0 0;}
    .content input{ border:1px solid #6e6e6e; color:#666666;}
    .content textarea{ border:1px solid #6e6e6e; color:#666666; overflow:visible; height:150px;}
    .content .name{font-size:95%;}
    .content .date{font:80% Tahoma;  color:#999; letter-spacing:0;}
    .content .cnt {font:75% Tahoma; letter-spacing:0; color:#999;}
  2. /* kkamagui 추가  시작 */
    .content .article a{color:#0011BB;}
    .content .article h1{ font-size:200%;}
    .content .article h2{ font-size:180%;}
    .content .article h3{ font-size:140%; margin:0 5px; padding:0 0;}
    .content .article h4{ font-size:120%; margin:0 5px; padding:0 0;}
    /* 스프링 노트의 글을 붙였을 때 예쁘게 보이기위해 박스와 코드 부분 추가 */
    .article div { border: dashed; border-width: thin; color: BLACK; background: #FFFF8C; }
    .article .code { border: dashed; border-width: thin; color: BLACK; background: #CCFFCC; margin: 0px; padding: 0px;}
    /* 코드 출력시 숫자 제외 */
    .article .code li { list-style-type: none; margin: 0; padding: 0;}

  미리보기를 통해서 페이지를 열어보면 아래와 같이 변한 화면을 볼 수 있다.

스프링노트13.PNG 

<수정된 후 화면> 


 박스가 생겼으며 숫자도 사라졌다. 색깔이 마음에 안들면 background : #XXXXXX 부분을 수정해주면 된다. 색깔에 대한 내용은 http://kkamagui.tistory.com/97 페이지를 참고하면 적절한 색깔을 고를 수 있으니 참고하자. ^^


5.마치면서...

 스프링노트와 블로그를 동시에 활용하는 방법에 대해서 간단히 알아보았다. 스프링노트가 블로그보다 상대적으로 검색이 약하고 블로그가 계속해서 글을 키워나가기에 부족한 부분이 있으니 위키와 블로그 어느 하나를 선택하기가 쉽지 않다. 둘중 하나를 선택할 수 없다면 간단한 팁으로 편리하게 관리하는 것도 좋은 방법이라 생각한다. ^ㅡ^


이 글은 스프링노트에서 작성되었습니다.

 리눅스 운영체제를 사용하고 계신다면 그놈(GNOME) 매니져를 사용해서 애플 Mac OS 레퍼드(Leopard) 스타일로 바꾸실 수 있습니다. ^^
 Mac4Lin 프로젝트가 바로 그것인데요, 간단히 그놈(GNOME) 매니져로 변경하고 테마(Theme)를 설정하는 것만으로 레퍼드와 거의 흡사하게 만들어 줍니다. 아래는 실제로 테마(Theme)를 변경해서 찍은 스크린 샷입니다. ^^
사용자 삽입 이미지

Mac 레퍼드(Leopard) 스타일


 설치방법 및 프로젝트 링크는 아래와 같습니다. 영어라는 것이 약간 아쉽네요 ^^;;;;


 리눅스를 사용하시는 분이라면 한번쯤 시도해보시는 것도 좋을 듯 합니다. ^^
 와~ 정말 파이썬으로 안되는 것이 없군요. 처음에 파이썬을 접했을 때 많은 모듈이 있는 걸 알았지만 주로 웹이나 텍스트 처리, DB쪽 관련이었습니다. 특히 파이썬으로 GUI 프로그래밍을 하려면 다른 언어와 섞어 사용하는 것으로 알고 있어서 GUI 관련 쪽은 약하다고 생각했는데... 파이썬으로 GPU를 컨트롤 할 수 있네요. ^^;;;
 원문은 http://www.hackszine.com/blog/archive/2007/10/pygpu_write_software_for_the_g.html 에서 볼 수 있습니다.

 이미지 프로세싱 관련 처리를 GPU에게 넘겨서 빨리 처리할 수 있도록 해준다는 것 같은데 GIMP에서 사용하는 모듈인 듯하네요. ^^

 파이썬~ 정말 멋진 언어인 것 같습니다. ^^
 친구가 류광님의 블로그에서 재미있는 것을 발견해서 보내줬는데, 빈칸의 위력이라는 글입니다(원문은 http://occamsrazr.net/tt/171 에서 보실 수 있습니다 ^^). 아래가 그 빈칸의 위력을 보여주는 코드인데 한번 보시죠 ^^;;;

 정상적으로 쓴다면 아래와 같이 쓰겠지요 ^^;;;

 빈칸 하나로 저렇게 코드가 달라보이다니... 마치 --> 연산자가 생긴 것 같기도 하고... ㅡ_ㅡa... 류광님께서는 상당히 마음에 드신다고 했는데, 제가 만약 유지보수 프로그래머였다면 이 코드를 만든 사람에게 전화를 했을 겁니다. ㅋㅋ
 
 코딩할 때 상당히 조심해야겠군요. 죄악(??)을 저지르지 않도록 ^^;;;;

 제 티스토리에 구글 애드센스를 단 기념으로 간단하게 정리해서 올립니다. ^^ 수입은 얼마 안되겠지만 그래도 왠지 뿌듯한 기분이라서 ^^;;;; 그럼 지금부터 간단히 알아보겠습니다.

1. 구글 애드센스 가입 및 승인 받기
 구글 애드센스를 달기 위해서는 먼저 구글 애드센스 사이트에 등록을 해서 승인을 받아야 합니다. 주소는 http://www.google.com/adsense 입니다. 링크로 가시면 아래와 같은 화면이 표시됩니다.

사용자 삽입 이미지

구글 애드센스 사이트


 구글 계정이 없으신 분은 "지금 가입" 버튼을 눌러서 먼저 구글에 가입하시고 계정이 있으신분(gmail 등등의 계정)은 아래의 이메일과 비밀번호를 입력하셔서 로그인 하시면 됩니다.

 로그인이 되고 나면 애드센스를 표시할 블로그 주소 또는 사이트 주소를 입력하고 사용자의 기타 정보를 입력하게 됩니다. 물론 다 영어로 입력해야되고 입력이 끝난 뒤에 "제출" 또는 "확인" 버튼을 누르면 구글에서 자료를 읽어보고 승인하게 됩니다.

 적절한 절차를 거쳐서 승인되고 나면 구글 메일로 아래와 같은 환영 메일이 오게됩니다. ^^ 그럼 이제 반은 끝난겁니다. ^^)/~
사용자 삽입 이미지

애스센스 승인 메일



2. 애드센스 만들기
 가입이 승인되었으니 이제 게시할 광고를 만들 차례입니다. 애드센스 사이트에 로그인을 해서 "애드센스 설정"의 "광고관리"에 가시면 아래와 같이 "콘텐츠용 애드센스"를 발견할 수 있습니다.
사용자 삽입 이미지

콘텐츠용 애드센스


 클릭하면 세부설정으로 가게되는데 광고 단위를 설정하시고 표시할 광고의 종류를 설정합니다. 일반적으로 텍스트 광고를 많이 하므로 "텍스트 광고만"을 선택한 후 "계속하기"를 클릭합니다.
사용자 삽입 이미지

광고 단위 설정


 이제 광고의 크기 및 스타일을 설정해야하는데 광고의 크기는 "광고형식"을 클릭하시면 볼 수 있습니다. 블로그나 사이트의 레이아웃에 맞는 적당한 광고를 선택하시고 아래에 색상을 선택하셔서 원하시는 모양으로 조합한 후 "계속하기" 버튼을 클릭합니다.

 채널 설정화면이 나오게 되는데 그냥 "계속하기" 버튼을 클릭해서 다음으로 넘어갑니다.
사용자 삽입 이미지

채널 설정


 이제 최종 완료만 남았습니다. "제출 및 코드 가져오기" 버튼을 클릭하면 지금까지의 내용이 저장되고 광고를 붙일 수 있는 자바 스크립트 코드가 나오게 됩니다.
사용자 삽입 이미지

제출 및 코드 가져오기


 이제 아래의 애드센스 단위 코드를 블로그 스킨 코드 또는 사이트의 html 코드에 삽입하면 끝입니다 ^^.
사용자 삽입 이미지

애드센스 단위 코드



3. 티스토리에 삽입
 이제 광고 코드를 티스토리에 삽입해 보겠습니다. 티스토리의 "관리자" 메뉴로 가서 "스킨" 항목으로 갑니다. 그후 "html/css 편집"으로 가시면 skin.html 코드 및 style.css 코드를 편집할 수 있는 아래와 같은 화면이 나오게됩니다.
사용자 삽입 이미지

skin.html 수정


 skin.html 부분에서 스크롤 해보시면 중간쯤에 "<!-- 본문 -->" 을 찾을 수 있고 그 아래에 보면 "##_article_rep_author_##"을 볼 수 있습니다. 그 아래에 광고 코드를 붙이고 제일 아래의 "저장하기" 버튼을 누르시면 모든 작업은 끝납니다. ^^

 블로그로 다시 이동하면 글 아래에 아래와 같이 구글 광고가 표시되는 것을 볼 수 있습니다. 지금까지 고생하셨습니다. ^^
사용자 삽입 이미지

티스토리에 표시된 구글 광고


 아래는 본문 우측에 광고를 삽입하는 코드와 본문 상단에 광고를 삽입하는 코드입니다.


 블로그의 스킨을 손보다 색깔을 넣기가 귀찮아서 무조건 #000000을 넣었습니다. 그러나 그것도 어느덧 한계에 다다르고... 결국 다른 색깔을 넣어봐야겠다는 생각에 무작위로 색깔을 넣고 미리보기로 테스트하고 다시 바꾸기를 반복했습니다.

 머리가 나쁘면 손발이 고생한다고 했던가요? ^^;;; 문득 색상표를 보면 되겠다는 생각이 들어서 검색해보니 역시나 있더군요(이런... ㅜ_ㅜ 시간 낭비가... ㅜ_ㅜ...)

 색상표 아래에 나갑니다. ^^

출처 : http://www.homejjang.com/
000000 000033 000066
000099 0000CC 0000FF
003300 003333 003366
003399 0033CC 0033FF
006600 006633 006666
006699 0066CC 0066FF
009900 009933 009966
009999 0099CC 0099FF
00CC00 00CC33 00CC66
00CC99 00CCCC 00CCFF
00FF00 00FF33 00FF66
00FF99 00FFCC 00FFFF
330000 330033 330066
330099 3300CC 3300FF
333300 333333 333366
333399 3333CC 3333FF
336600 336633 336666
336699 3366CC 3366FF
339900 339933 339966
339999 3399CC 3399FF
33CC00 33CC33 33CC66
33CC99 33CCCC 33CCFF
33FF00 33FF33 33FF66
33FF99 33FFCC 33FFFF
660000 660033 660066
660099 6600CC 6600FF
663300 663333 663366
663399 6633CC 6633FF
666600 666633 666666
666699 6666CC 6666FF
669900 669933 669966
669999 6699CC 6699FF
66CC00 66CC33 66CC66
66CC99 66CCCC 66CCFF
66FF00 66FF33 66FF66
66FF99 66FFCC 66FFFF
990000 990033 990066
990099 9900CC 9900FF
993300 993333 993366
993399 9933CC 9933FF
996600 996633 996666
996699 9966CC 9966FF
999900 999933 999966
999999 9999CC 9999FF
99CC00 99CC33 99CC66
99CC99 99CCCC 99CCFF
99FF00 99FF33 99FF66
99FF99 99FFCC 99FFFF
CC0000 CC0033 CC0066
CC0099 CC00CC CC00FF
CC3300 CC3333 CC3366
CC3399 CC33CC CC33FF
CC6600 CC6633 CC6666
CC6699 CC66CC CC66FF
CC9900 CC9933 CC9966
CC9999 CC99CC CC99FF
CCCC00 CCCC33 CCCC66
CCCC99 CCCCCC CCCCFF
CCFF00 CCFF33 CCFF66
CCFF99 CCFFCC CCFFFF
FF0000 FF0033 FF0066
FF0099 FF00CC FF00FF
FF3300 FF3333 FF3366
FF3399 FF33CC FF33FF
FF6600 FF6633 FF6666
FF6699 FF66CC FF66FF
FF9900 FF9933 FF9966
FF9999 FF99CC FF99FF
FFCC00 FFCC33 FFCC66
FFCC99 FFCCCC FFCCFF
FFFF00 FFFF33 FFFF66
FFFF99 FFFFCC FFFFFF
 원래 기본 스킨이 검은색 스킨이었는데, 왠지 조금 무거워 보여서 스킨을 조금 고치려했습니다.... 만... ㅡ_ㅡa...어쩌다보니 색깔 부분은 다 손대게 되었습니다. ㅜ_ㅜ... 할 것도 많은데, 왜 이 짓을 했는지는 모르지만... 스킨은 백업 차원에서 올려둡니다.

DIV 박스 추가한 부분과 색깔 부분을 수정한게 전부입니다.

ps) 나중에 시간나면 스킨 부분 고치는 요령 같은 것도 하나 써야겠군요. 저처럼 HTML에 거의 무지한 사람도 필요한 부분은 쉽게 손볼 수 있도록요... ^^


00 오른쪽 마우스 메뉴에 도스창 열기 추가하기

참고 : http://zextor.tistory.com/2669790

들어가기 전에...

  • 이 글은 kkamagui에 의해 작성된 글입니다.

1.추가 방법

DOS 시절부터 컴퓨터를 이용하였거나 프로그래머 개발자의 경우 아직도 Dos이용하고 있습니다.

하지만 DOS를 실행하고 원하는 폴더에 접근하기 위해서는 원도우키 + R  또는 [시작]→[실행] 후 “ CMD “ 를 입력 후 도스창이 실행되면 CD 명령어를 이용하여 원하는 폴더에 이동할 수 있습니다.

레지스트리를 수정하여 탐색기를 이용하여 먼저 가고자 하는 폴더에 접근 후 DOS를 실행 하여 바로 해당 폴더의 경로로 연결되어 사용하기 편리합니다.

1. [시작]→[실행]에서 “regedit “를 입력하고 레지스트리 편집기를 실행한 후, 다음 키 값을 찾는다. HKEY_CLASSES_ROOT\Directory\shell

2. Shell 키 위에서 마우스 오른쪽 마우스 클릭(또는 shell 키 선택 후 오른쪽 공백에서 오른쪽 마우스 클릭) 후 [새로 만들기(N)] → [키]를 선택합니다.

3. 새로운 키의 이름을 DOS(이름은 원하시는 이름으로 하셔도 됩니다.)로 수정합니다.

4. 만들어진 DOS 키를 클릭 후 오른쪽의 기본값을 더블 클릭하여 오른쪽 마우스에 표시될 이름을 입력하여 주십시오. ( 예를 들어 도스창이라 입력합니다. )

5. 다음 새로 만들어진 DOS에서 Shell 과 마찬가지로 새로운 키를 만들어 Command 이름으로 수정합니다. ( DOS 와는 달리 반드시 command 이름으로 하여야 합니다. )

6. 만들어진 Command 키를 클릭 후 오른쪽의 기본값을 더블 클릭하여 cmd.exe /k cd "%1" 이라는 문자열을 입력하여 주십시오.

7. 컴퓨터를 재 시작하여 탐색기 실행 후 가고자 하는 폴더를 선택 후 오른쪽 마우스 클릭 후 도스창을 클릭하시면 해당 폴더의 경로로 도스창이 열리는 것을 확인 할 수 있습니다.

본 자료는 (주)웰비아닷컴 의 커뮤니티 - 활용팁 에서 스크랩한 것입니다.

이 자료 외에도 많은 정보가 있으니 필요하신 분은 직접 방문해 보시길 바랍니다.

본 페이지에서는 재부팅 후에 사용할 수 있다고 기술하였지만 바로 사용하고 싶으시면 아래와 같이 해주시면 됩니다.

1. '시작' 이 있는 작업표시줄에 마우스 오른쪽 버튼을 눌러 작업관리자를 띄움니다.

2. 프로세스 탭에서 explorer.exe 를 선택하여 프로세스 끝내기를 합니다. 경고가 뜰 경우 그냥 "예" 를 선택하십시오.

3. 그럼 밑에 작업표시줄이 없어질 것 입니다.

4. 그럼 아까 작업관리자의 응용 프로그램 탭으로 이동한 후 새 작업을 클릭합니다.

5. 열기 옆에 있는 입력창에 "explorer" 를 입력합니다.

6. 이제 탐색기의 오른쪽 메뉴에 DOS를 사용할 수 있습니다.

이 글은 스프링노트에서 작성되었습니다.

09 서브버전(Subversion) 윈도우 서버 설치

 

들어가기 전에...

  • 이 글은 kkamagui에 의해 작성된 글입니다.
  • 마음껏 인용하시거나 사용하셔도 됩니다. 단 출처(https://kkamagui.tistory.com)는 밝혀 주십시오.
  • OS 제작에 대한 상세한 내용은 책 "64비트 멀티코어 OS 구조와 원리"를 참고하시기 바랍니다.
64비트 멀티코어 OS 원리와 구조

0.시작하면서...

 개발자라면 한번쯤 소스 관리에 대해서 고민해 봤을 것이다. 특히나 수정이 잦은 프로그램이라면 더욱 더 버전 관리가 중요한데, 이걸 일일이 폴더나 날짜별로 관리하다가는 낭패를 보기 십상이다. 그래서 버전관리 프로그램을 사용하는데, 대표적인 것으로 소스세이프(Source Safe), CVS, Subversion 있다.

 윈도우 프로그래머라면 소스세이프가 굉장히 유용한데(Visual Studio를 설치하면 자동으로 깔리므로... ㅡ_ㅡa..), 사용해보니 속도가 무지 느리고 VC 또한 한참 뒤에 뜨게하는 무시무시한 단점이 있어서 다른 것을 찾다가 서브버전을 선택하게 되었다.

 서브버전의 장점은 인터넷에 잘 나오므로 굳이 이야기하지 않겠고, 실제 윈도우 버전 설치 및 설정에 대해서 알아보자.

 

1.Tortoise 서브버전(Subversion) 클라이언트 설치

1.1 Tortoise 서브버전(Subversion) 클라이언트 다운로드

 서브버전 서버를 설치한다면서 왜 클라이언트를 설치하는 것일까? 그것은 서버 설치후 나머지 작업을 편리하게 할 수 있기 때문이다.

 서브버전 클라이언트는 http://tortoisesvn.net/downloads 에서 다운 받을 수 있다.

Tortoise 서브버전 클라이언트

1.2 Tortoise 서브버전(Subversion) 클라이언트 설치

 클라이언트 설치는 간단하다. 무조건 "Next" 버튼을 눌러서 완료를 하면 알아서 다 해준다.

Tortoise 서브버전(Subversion) 클라이언트 설치

 설치 후 탐색기에서 오른쪽 버튼을 클릭했을 때 아래와 같은 메뉴가 뜨면 정상적으로 설치된 것이다. 

설치 완료

 

2.서브버전(Subversion) 서버 설치

2.1 서브버전(Subversion) 서버 다운로드

 서브버전의 윈도우용 설치 파일은 http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=91 에서 찾을 수 있다. 위 사이트로 이동하면 아래와 같은 화면을 볼 수 있는데, 최신버전의 설치 파일을 다운받아서 설치하도록 하자.

서브버전 서버 파일 다운로드

 

2.2 서브버전(Subversion) 설치

 설치 과정은 아주 간단하다. 윈도우 인스톨 파일을 더블클릭해서 기본 옵션으로 설치하면 된다. "Next" 를 계속해서 클릭하여 완료하자.

인스톨 화면

2.3 서브버전(Subversion) 설정

2.3.1 서브버전(Subversion) 데이터 폴더 생성(Repository)

 서버 설치가 끝났으니 데이터를 저장할 폴더를 생성해야한다. 저장소는 하드디스크의 특정 폴더로 하면 되고, 임의의 이름으로 선택 가능하다. 일단 D:\Repository로 해서 생성하자.

저장소 생성

2.3.2 서비스(Service) 등록

 서브버전 서버를 서비스로 등록하여 윈도우 부팅시에 자동으로 실행되도록 하자. 윈도우 서비스 등록은 sc.exe 프로세스로 등록가능하다. cmd.exe를 실행해서 아래와 같이 입력하도록 하자. 

서비스 등록

 아래는 서브버전 서비스를 등록하고 서비스를 해제하는 명령이다.

서비스 등록 : sc create svn binpath= "C:\Program Files\Subversion\bin\svnserve.exe --service -r D:\repository" displayname= "Subversion Server" depend= Tcpip start= auto
서비스 해제 : sc delete svn displayname= "Subversion Server"

 별다른 문제가 없다면 성공적으로 등록했다는 메시지가 출력될 것이다.

2.3.3 방화벽(Firewall) 해제

 서브버전은 TCP 3690 포트와 UDP 3690 포트를 사용한다. 윈도우 방화벽 및 Anti-Virus의 방화벽을 해제하도록 하자.

 아래는 윈도우 방화벽에서 포트를 추가하는 방법이다. TCP와 UDP 각각 등록해서 모두 가능하도록 하자.

방화벽 해제

2.4 저장소(Repository) 생성

 이제 서버 설정이 끝났으니 실제로 소스 또는 데이터를 관리할 저장소(Repository)를 생성해야 한다. 서브버전 관련 데이터는 D:\Repository 에 저장하기로 했으므로 하위 폴더에 저장소를 생성하자.

 

2.4.1 커맨드 라인(Command Line) 방식

 cmd.exe 를 실행한 뒤 D:\Repository 폴더로 이동하여 아래와 같이 입력한다.

"C:\Program Files\Subversion\bin\svnadmin" create --fs-type fsfs test

 위의 파란색으로 표시된 test를 유의해서 보자. test 대신에 생성을 원하는 폴더명으로 바꿔서 입력하면 된다.

 아래는 위의 명령을 실행한 후 결과 화면이다.

저장소 생성

2.4.2 Tortois Subversion 클라이언트를 사용한 방식

 Tortois Subversion 클라이언트를 설치했다면 좀더 편한 방법으로 생성할 수 있다. 아래는 Tortoise Subversion 클라이언트를 통해 생성하는 방법이다. 

저장소 생성

D:\Repository 폴더에 생성할 저장소 이름(Test)의 폴더를 미리 생성한 후 Tortoise Subversion 클라이언트에서 "Create repository here"를 클릭하면 된다.

 파일 시스템을 선택하는 다이얼로그가 뜨면 "Native filesystem(fsfs)"를 선택한 후 OK를 눌러서 생성하면 된다.

 

2.5 저장소 접근 설정

 저장소를 생성하고 나면 아래와 같은 폴더와 파일들이 생긴다.

저장소 폴더 상태

 이 중에서 접근 권한을 제어하기위해서는 2개의 파일을 손봐야 하는데 다음 항목을 보자

 

2.5.1 svnserve.conf

### This file controls the configuration of the svnserve daemon, if you
### use it to allow access to this repository.  (If you only allow
### access through http: and/or file: URLs, then this file is
### irrelevant.)
### Visit http://subversion.tigris.org/ for more information.\

[general]
### These options control access to the repository for unauthenticated
### and authenticated users.  Valid values are "write", "read",
### and "none".  The sample settings below are the defaults.

anon-access = none     <== 로그인 하지 않은 사용자는 아무것도 못하도록 한다.
auth-access = write
### The password-db option controls the location of the password
### database file.  Unless you specify a path starting with a /,
### the file's location is relative to the conf directory.
### Uncomment the line below to use the default password file.

password-db = passwd   <== ID와 Password를 저장하는 파일 이름

### The authz-db option controls the location of the authorization
### rules for path-based access control.  Unless you specify a path
### starting with a /, the file's location is relative to the conf
### directory.  If you don't specify an authz-db, no path-based access
### control is done.
### Uncomment the line below to use the default authorization file.
#authz-db = authz
### This option specifies the authentication realm of the repository.
### If two repositories have the same authentication realm, they should
### have the same password database, and vice versa.  The default realm
### is repository's uuid.

realm = KKAMAGUI Repository   <== 접근했을 때 클라이언트에게 보여줄 저장소 메시지

 위와 같이 파일을 수정한 다음 저장한다.

 

2.5.2 passwd

### This file is an example password file for svnserve.
### Its format is similar to that of svnserve.conf. As shown in the
### example below it contains one section labelled [users].
### The name and password for each user follow, one account per line.
[users]
# harry = harryssecret
# sally = sallyssecret
kkamagui = 사용할 패스워드

 위와 같이 ID = Password의 형태로 입력한 뒤 저장하면 된다.

 

3.간단한 서브버전(Subversion) 서버 테스트

 테스트 방법은 간단하다. 탐색기에서 오른쪽 버튼을 눌러서 표시되는 메뉴에서 "Repo-Browser" 를 클릭하여 아래와 같은 창이 뜨면 서브버전 서버가 설치된 주소와 저장소 이름을 같이 입력해주면 된다.

Repo-browser 메뉴

 이제 주소와 저장소의 이름을 입력하자. 주소를 kkamagui.egloos.com, 그리고 저장소를 test라고 가정하고 입력하면 아래와 같이 될 것이다.

서브버전 주소 입력

"OK" 버튼을 누르면 실제 서버에 접속해서 저장소 정보를 얻어오는데, 아래와 같은 화면이 표시될 것이다. 실제로 정상적으로 접속이 된다면 "test" 항목 아래에 아무것도 표시되지 않을 것이지만 문제가 발생한다면 아래와 같이 에러메시지가 표시될 것이다.

Repo-Browser 실행-오류 발생

 위와 같은 에러 메시지가 표시되면 처음 단계부터 설정을 다시 한번 확인하자. 아무런 에러 메시지가 없다면 정상적으로 설치된 경우이므로 열심히 Check-out, commit, update를 반복하면 된다.

 

4.서브버전(Subversion) 서버를 설치하지 않고 로컬(Local)에서 소스 관리하기

 서브버전 서버가 설치되어있어야 꼭 소스 버전 관리가 가능한 것일까? "답은 그렇지 않다" 이다.

 Tortoise Subversion 클라이언트를 설치했다면 로컬에 저장소를 만들고 file:/// 키워드로 접근하여 소스 버전을 관리하는 것이 가능하다.

4.1 저장소 생성

 위의 "2.4 저장소(Repository) 생성" 부분을 참고해서 로컬에 Tortoise를 이용하여 저장소를 생성하자. 그리고 파일들을 수정해서 특정 유저만 접근가능하도록 수정하자.

4.2 저장소 접근 테스트

 위의 "3.간단한 서브버전(Subversion) 서버 테스트" 부분을 참고하여 "Repo-browser"를 실행하고 주소에 아래와 같이 입력한 후 OK를 누르자.

file:///d:\repository\test

 정상적으로 실행되면 아래와 같은 화면이 표시될 것이다.

Repo-browser 실행
 아무런 에러가 없으므로 정상적으로 실행되었음을 알 수 있다. 이렇게 함으로써 서버를 설치하지 않고도 로컬에서 소스 버전관리를 할 수 있다.

5.기타 팁

5.1 버전 관리시 무시할 파일 확장자 설정

 소스를 컴파일해서 나오는 object 파일이나 기타 필요없는 부산물들은 버전관리를 할 필요가 없다. 그런 파일들을 일일이 수작업으로 제외하기는 상당히 귀찮은 작업인데, 다행이 Tortoise에서 이것을 편리하게 할 수 있는 옵션이 있다.

 "Settings" 메뉴에 가면 아래와 같은 화면이 표시된다. 여기에 "Global Ignore Pattern" 항목에 무시할 파일의 확장자나 파일명을 입력하면 된다.

*.scc *.sbr *.pch *.pcb *.ilk *.idb *.res *.o *.obj *.ncb *.opt *.plg
확장자 및 파일명 입력

 

6.마치면서...

 이로서 그 지긋지긋하게 느린 소스세이프(SourceSafe)에서 벗어날 수 있게 되었다(이렇게 좋을 수가... ㅜ_ㅜ). 소스 버전 관리를 통해 프로젝트를 보다 효율적으로 관리하고 협업의 능률을 최대한 활용하자. @0@)/~~!!

 

7.참고 사이트

설치에 대해서 아주 자세하게 잘 나와있다. 부족한 부분은 여기서 참고하도록 하자.

08 BSD 소켓 프로그래밍(Socket Programming) 예제

원문 : http://kkamagui.springnote.com/pages/551093

 

들어가기 전에...

 

0.시작하면서...

 BSD 소켓 프로그래밍은 MFC 소켓에 비하면 쓰기가 까다롭고 알아야할 부분이 많다. 특히나 윈도우 프로그래밍을 하면 MFC 소켓에 익숙해지기때문에 까먹기가 십상이다. 

 이번에 NDS 소켓 프로그래밍을 하면서 우연히 다시 볼 기회가 생겨 정리한다.

 

1.참고 함수들 

1.1 select 함수 

 Single Thread로 Multi-Socket을 컨트롤 하는 방법은 여러가지가 있겠지만, 가장 대표적인 것이 select이다. select는 아래와 같은 원형을 가지고 있다.

  1. int select(
      int nfds,
      fd_set FAR* readfds,
      fd_set FAR* writefds,
      fd_set FAR* exceptfds,
      const struct timeval FAR* timeout
    ); 

 select 함수는 nfds에 설정된 소켓의 수만큼 소켓을 체크하므로 반드시 가장 큰 소켓 번호 + 1의 크기만큼을 nfds로 넘겨줘야 함을 잊지 말자( ex: fd + 1 )

 return 값은 아래와 같은 의미를 가진다. 

  • 양수 : readfds or writefds or exceptfds 중에 양수 개의 fd가 이벤트가 발생했다.

    • fds에 이벤트가 발생한 fd만 플래그가 설정되므로 FD_ISSET 매크로를 이용해서 해당 socket을 찾을 수 있다.
    • timeout에 남은 시간이 저장되므로 이를 활용하면 추가적인 처리가 가능하다
  • 0 : timeout이 되었다. timeout 값의 경우 0으로 설정되면 무한대로 대기한다. 
  • 음수 : readfds에 닫힌 소켓이 있거나 기타 에러가 발생했다.

 

 fd_set 및 timeval은 구조체로 아래와 같은 형태를 가진다. 

  1. typedef struct fd_set {
      u_int fd_count;
      SOCKET fd_array[FD_SETSIZE];
    } fd_set; 
  2. struct timeval {
      long tv_sec;  // second 단위
      long tv_usec; // millisecond 단위
    }; 

 timeval은 위에서 보는 것 그대로 second/millisecond 단위로 설정해서 사용하면 된다.

 

 하지만 fd_set과 같은 경우 어떻게 사용해야할지 좀 막막하다. 다행이 이를 처리해주는 매크로가 있으니 아래와 같다.

  • FD_ZERO( fd_set* fdset ) :  fdset을 초기화. 처음에는 반드시 한번 호출해야 함
  • FD_SET( int fd, fd_set* fdset ) : fdset에 fd 소켓을 등록한다. 
  • FD_CLR( int fd, fd_set* fdset ) : fdset에 fd 소켓을 삭제한다.
  • FD_ISSET( int fd, fd_set* fdset ) : fdset에 fd 소켓이 있는가 확인한다. 

 자주 사용하는 매크로이니 한번쯤은 읽어두자. 자세한 사용 방법은 아래의 Linux 예제를 보면 된다.

 

2.윈도우(Window) 환경

2.1 서버(Server)

  1. #include <winsock2.h>
    #include <stdio.h>
    #include <string.h>
  2. #define DEFAULT_PORT 2924
    #define DEFAULT_BUFFER_SIZE 4096
  3. int main()
    {
        char Buffer[DEFAULT_BUFFER_SIZE + 1];
        WSAData wsd;
  4.     if(WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
            printf("Winsock 초기화 에러!\n");
            return -1;
        }
  5.     SOCKET ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  6.     if (ls == INVALID_SOCKET) {
            printf("소켓 생성 실패!\n");
            return -1;
        }
  7.     sockaddr_in service;
        memset(&service, 0, sizeof(service));
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = INADDR_ANY;
        service.sin_port = htons(DEFAULT_PORT);
       
        if (bind(ls, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
            printf("bind 실패!\n");
            return -1;
        }
       
        if (listen(ls, 1) == SOCKET_ERROR) {
            printf("listen() 실패!\n");
            return -1;
        }
  8.     SOCKET as;
  9.     printf("클라이언트 연결 대기.\n");
  10.     while (1) {
            as = accept(ls, NULL, NULL);
            if (as == SOCKET_ERROR) continue;
            printf("클라이언트 연결됨.\n");
  11.         int nbyte = recv(as, Buffer, DEFAULT_BUFFER_SIZE, 0);
  12.         if (nbyte <= 0) {
                printf("recv 에러!\n");
                break;
            }
           
            Buffer[nbyte] = '\0';
            printf("에코 : %s\n", Buffer);
  13.         send(as, Buffer, nbyte, 0);
  14.         if (strncmp(Buffer, "quit", 4) == 0) {
                printf("클라이언트의 요청에 의해 서버 종료\n");
                break;
            }
           
            closesocket(as);
            printf("클라이언트 연결 해제.\n새로운 클라이언트 연결 대기.\n");
        }
  15.     closesocket(ls);
        WSACleanup();
       
        return 0;
    }

 

2.2 클라이언트(Client)

  1. #include <winsock2.h>
    #include <stdio.h>
    #include <string.h>
  2. #define DEFAULT_PORT 2924
    #define DEFAULT_BUFFER_SIZE 4096
  3. int main(int argc, char** argv)
    {
        char Buffer[DEFAULT_BUFFER_SIZE + 1];
        WSAData wsd;
  4.     if (argc != 2) {
            printf ("사용법 : %s [IP 주소]\n", argv[0]);
            return -1;
        }
       
        if(WSAStartup(MAKEWORD(2, 2), &wsd) != 0) {
            printf("Winsock 초기화 에러!\n");
            return -1;
        }
       
        SOCKET cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
       
        if (cs == INVALID_SOCKET) {
            printf("소켓 생성 실패!\n");
            return -1;
        }
       
        sockaddr_in client;
        memset(&client, 0, sizeof(client));
     
        client.sin_family = AF_INET;
        client.sin_addr.s_addr = inet_addr(argv[1]);
        client.sin_port = htons(DEFAULT_PORT);
       
        if (connect(cs, (SOCKADDR *)&client, sizeof(client)) == SOCKET_ERROR) {
                printf("connect 에러!\n");
                return -1;
        }
  5.     printf("입력 : ");
        gets(Buffer);
  6.     send(cs, Buffer, strlen(Buffer), 0);
        int nbyte = recv(cs, Buffer, DEFAULT_BUFFER_SIZE, 0);
  7.     Buffer[nbyte] = '\0';
        printf("에코 : %s", Buffer);
  8.     closesocket(cs);
        WSACleanup();
       
        return 0;
    }

 

3.Linux or Unix or NDS

3.1 서버(Server) 

 윈도우쪽 소스를 조금 수정했다. 

  1. #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
  2. int main()
    {
        char Buffer[256 + 1];
  3.     int ls = socket(AF_INET, SOCK_STREAM, 0);
        if (ls == INVALID_SOCKET) {
            printf("소켓 생성 실패!\n");
            return -1;
        }
  4.     sockaddr_in service;
        memset(&service, 0, sizeof(service));
        service.sin_family = AF_INET;
        service.sin_addr.s_addr = INADDR_ANY;
        service.sin_port = htons(DEFAULT_PORT);
       
        if (bind(ls, (SOCKADDR*) &service, sizeof(service)) == SOCKET_ERROR) {
            printf("bind 실패!\n");
            return -1;
        }
       
        if (listen(ls, 1) == SOCKET_ERROR) {
            printf("listen() 실패!\n");
            return -1;
        }
        int as;
        printf("클라이언트 연결 대기.\n");
        while (1) {
            as = accept(ls, NULL, NULL);
            if (as == SOCKET_ERROR) continue;
            printf("클라이언트 연결됨.\n");
            int nbyte = recv(as, Buffer, DEFAULT_BUFFER_SIZE, 0);
            if (nbyte <= 0) {
                printf("recv 에러!\n");
                break;
            }
           
            Buffer[nbyte] = '\0';
            printf("에코 : %s\n", Buffer);
            send(as, Buffer, nbyte, 0);
            if (strncmp(Buffer, "quit", 4) == 0) {
                printf("클라이언트의 요청에 의해 서버 종료\n");
                break;
            }
           
            close(as);
            printf("클라이언트 연결 해제.\n새로운 클라이언트 연결 대기.\n");
        }
        close(ls);
       
        return 0;
    }

 

3.2 클라이언트(Client)

 NDS에서 사용하는 예제를 조금 수정했다.

  1. #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
  2. int main(void)
    {
  3.     //////////////////////////////////////////////////////////////////////////
        // Let's send a simple HTTP request to a server and print the results!
  4.     // store the HTTP request for later
        const char * request_text =
            "GET / HTTP/1.1\r\n\r\n";
    //        "Host: www.akkit.org\r\n"
    //        "User-Agent: Nintendo DS\r\n\r\n";
  5.     // Find the IP address of the server, with gethostbyname
        // DNS를 이용해서 Name으로 IP를 얻는다.
        // 2007/10/24 현재, 아직 잘 안되는 것 같다.
        iprintf( "DNS Resolve Start\n" );
        //struct hostent * myhost = gethostbyname( "www.google.org" );
        //iprintf("Found IP Address![www.google.org] [%08X]\n",
        //        myhost->h_addr_list[0] );
     
        // Tell the socket to connect to the IP address we found, on port 80 (HTTP)
        struct sockaddr_in sain;
        sain.sin_family = AF_INET;
        sain.sin_port = htons(80);
        // Host Resolve가 끝났으면 아래와 같이 사용한다.
        //sain.sin_addr.s_addr= *( (unsigned long *)(myhost->h_addr_list[0]) );
        // 아래는 google의 IP 주소이다.
        sain.sin_addr.s_addr = inet_addr( "72.14.235.99" );   
       
        // Create a TCP socket
        int my_socket;
        fd_set readfd;
        fd_set tempfd;
        struct timeval stTime;
        struct timeval stTempTime;
        int iRet;
       
        stTime.tv_sec = 5;
        stTime.tv_usec = 0;
    Retry:
       
        my_socket = socket( AF_INET, SOCK_STREAM, 0 );
        iprintf("Created Socket!\n");
       
        iprintf( "Try To Connect\n" );
        connect( my_socket,(struct sockaddr *)&sain, sizeof(sain) );
        iprintf("Connected to server!\n");
  6.     // send our request
        send( my_socket, request_text, strlen(request_text), 0 );
        iprintf("Sent our request!\n");
  7.     // Print incoming data
        iprintf("Printing incoming data:\n");
  8.     int recvd_len;
        char incoming_buffer[256];
  9.     iprintf("Recv Start\n");
        FD_ZERO( &readfd );
        FD_SET( my_socket, &readfd );
        while( 1 )
        {
            tempfd = readfd;
            stTempTime = stTime;
            iRet = select( my_socket + 1, &tempfd, NULL, NULL, &stTempTime );
            if( iRet > 0 )
            {
                recvd_len = recv( my_socket, incoming_buffer, 255, 0 );
                iprintf("Recv End Size[%d]\n", recvd_len );
                if( recvd_len > 0 )
                { // data was received!
                    incoming_buffer[ recvd_len ] = 0; // null-terminate
                    iprintf( incoming_buffer );
                }
            }
            // Time Expired
            else if( iRet == 0 )
            {
                iprintf( "Time Expired If You Press B, Exit Receiving Process\n" );
                if( ~REG_KEYINPUT & KEY_B )
                {
                    break;
                }
            }
            else
            {
                iprintf( "Error~~!!\n" );
                break;
            }
        }
       
        iprintf("Other side closed connection!\n");
  10.     shutdown(my_socket,0); // good practice to shutdown the socket.
  11.     close(my_socket); // remove the socket.
       
        iprintf( "Press Any A Key To Retry\n" );
        while( REG_KEYINPUT & KEY_A ) ;
        
        goto Retry;
  12.     while(1);
  13.     return 0;
    }

 

4.마치면서... 

 간단하게나마 코드 조각을 정리해 보았다. 이제 구글링할 필요없이 바로 붙여넣고 쓰면된다. 다시한번 네트워크의 세계로 빠져보자. 

 

 

이 글은 스프링노트에서 작성되었습니다.

07 이클립스(Eclipse) 단축키 및 환경설정

원문 : http://kkamagui.springnote.com/pages/432649

 

들어가기 전에...

 

0.시작하면서...

 이클립스 단축키 및 환경설정은 Window -> Preferences 메뉴를 통해 설정가능하다. 여기에 설정된 내용은 프로젝트와 관계없이 Global 하게 설정되는 내용이며 해당 프로젝트에만 특수하게 적용되는 설정은 Project -> Properties 메뉴를 통해서 설정가능하다.

 그럼 이제부터 이클립스 환경을 Visual Studio 환경과 비슷하게 설정하는 방법을 알아보자.

 

 

1.윈도우 폰트 설정

 설정1.PNG

<폰트 설정 화면>

  우측의 Basic 항목에서 Text Font와 C/C++에서 Editor Font를 수정하면 된다.

 

 

2.단축키 설정

2.1 설정 윈도우

 단축키 설정은 아래의 화면에서 설정가능하다.

 설정2.PNG

<단축키 설정화면>

우측 상단에 에디트 박스에는 단축키 설정 항목에 대한 검색을 수행할 수 있는 에디트 박스가 있다. 위 항목에 설정하고 싶은 검색어를 넣으면 해당 항목이 포함된 부분만 필터링하여 표시해 준다. 주의할 점은 아래의 Include unbound commands 체크 박스를 설정하여서 모든 기능에 대해서 검색이 수행되게 해야 한다는 것이다. 위 체크박스를 클릭하지 않으면 이미 단축키가 할당된 항목에서만 검색을 하므로 단축키가 설정되지 않은 기능에 대해서는 검색할 수 없다.

 검색의 결과로 나온 항목 중에 필요한 항목을 클릭한 후 제일 아래의 Binding: 에디트 박스를 클릭하여 포커스를 이동한 후 입력하고 싶은 키를 누르면 단축키로 설정된다.

 

2.2 단축키 참고

 Visual Studio와 비슷하게 동작하기 위해서는 아래의 각 항목을 검색하여 단축키를 수정하면 된다(개인적인 취향이 포함된 설정이라 참고만 하도록하자.)

 간혹 설정한 단축키가 먹지 않는 경우가 있는데, 중복 설정된 경우에 흔히 발생하므로 해당 키로 다시 검색하여 다른 기능에 맵핑된 키를 삭제하면 된다.

  • Build Project : F5
  • Clean Project : F6
  • Toggle Source/Header : ALT + O 
    • 소스파일과 헤더를 번갈아 가면서 보여주는 옵션
  • Next Editor : Control + Tab
    • 에디터 탭간의 이동
  • Content Assist : Control + Enter
    • Visual Studio의 자동완성과 같은 기능, 아래의 Content Assist 설정 참조
  • Bookmark Plugin 관련 : Bookmark 플러그인 설치에 대해서는 아래의 북마크 플러그인 참조
    • Go to next bookmark : F2
    • Go to previous bookmark : Shift + F2
    • Toggle bookmark : Control + F2
  • Show Outline : Control + O
    • 해당 파일에 있는 모든 함수 및 변수 표시
  • Open Declaration : F3 
    • 정의로 이동

 

 

3.Content Assist 설정

3.1 Indexer 설정

 Content Assist 기능을 사용하려면 Indexer를 이용하여 인덱스를 생성해야 한다. 아래는 Indexer를 설정하는 예제이다.

설정6.PNG

<Indexer 설정>

 메모리 여유가 좀 있다면 위와 같이 Full로 설정하는 것이 좋다. 파일이 변경되는 즉시 업데이트되어 아주 편리하게 사용할 수 있다.

 

3.2 Content Assist 설정

 ., ->, ::와 같은 확장 가능한 문자열 다음에 얼마의 Delay 뒤에 Content Assist를 표시할 것인지를 설정한다.

설정7.PNG

<Content Assist 설정>

 

3.3 단축키 설정

Zelon 님의 제보로 키보드 타입을 USB 3으로 할 시에 Control + Space 가 한자 키로 맵핑되어 안되었다는 사실을 알았다.

Zelon 님께 감사드린다.

 Control + Enter로 수정하면 잘 동작한다.

Eclipse.PNG

<Content Assist 단축키 설정>

 

 

 

4.플러그인(Plugin) 설치

4.1 북마크 플러그인(Bookmark Plugin) 설치

 이클립스의 북마크는 Visual Studio의 북마크보다 약간 불편하다. 북마크를 생성할 때마다 이름을 입력해야 하며 Quick하게 이동하기도 불편하게 되어있다. 이러한 기능을 보강하고자 나온 것이 북마크 플러그인이다. http://wimy.com/tt/106에서 내용을 찾아볼 수 있다.


http://etc.to/eclipse_bookmarks_plugin에 보면 아래와 같은 기능 설명과 업데이트 경로가 나온다.

I just created my second Eclipse plugin which adds several bookmark methods to the editor. The "default" bookmark methods of Eclipse ask for a name and this is often not very useful. The code here adds bookmarks a la "Borland" and bookmarks a la "JCreator". The code has the following options which can all be bound/rebound with the keys dialog under the "Edit" category:

  • Add a numbered bookmark at the cursor: Alt+[digit]. This creates a quick bookmark with the specified number using a "single" keypress. Only one bookmark of the given number can be present in the workspace. Setting the same number bookmark again will clear the earlier instance.
  • Goto a numbered bookmark: Alt+Shift+[digit]. Moves to the bookmark as it was set using Alt+[digit].
  • Toggle Bookmark: Ctrl+B. This drops an unnamed bookmark at the current location. Pressing Ctrl+B again releases the bookmark at that location again. This is often used with "Goto next bookmark" and "Goto previous bookmark".
  • Goto next bookmark in file: Ctrl+N. This locates the next bookmark in the current file and moves there.
  • Goto previous bookmark in file: Ctrl+P. This locates the previous bookmark in the current file and moves there. warning: the Ctrl+P binding is a suggestion only; since Ctrl+P is currently assigned you must add your own key binding using Window -> Preferences -> General -> Keys.

I'll be posting the source of the plugin later on; the plugin can be installed using the update site

http://eclipse.etc.to/updates/

 설치 방법은 아래와 같이 메뉴에서 Install/Update를 이용하면 된다.

설정3.PNG

<Plugin 설치메뉴>

 

 위 메뉴를 클릭하면 Install/Update 다얼로그가 표시되는데 Search for new features to install 항목을 선택하여 Next 버튼을 누르면 아래와 같은 화면이 표시된다.

설정4.PNG

<새로운 사이트 추가>

 우측 상단의 New Remote Site를 클릭하고 다이얼로그를 위와 같이 체운 후 OK 버튼을 누르면 etc.to 사이트가 추가된다. 북마크 플러그인을 설치하기위해서는 Java Development Tools(JDT)을 설치해야 하므로 Europa Discovery Site 항목을 체크한 뒤에 Finish 버튼을 클릭한다.

 잠시 시간이 지나면 아래와 같은 검색 결과가 표시되는데 Java Development Tools를 체크하여 Next 버튼을 누르면 JDT가 설치된다.

설정5.PNG

<JDT 설치화면>

 JDT의 설치가 끝나면 동일한 방법으로 etc.to 항목을 클릭하고 검색결과로 나온 Eclipse Editor Extentions를 설치하면 정상적으로 완료된다. 북마크 키에 대한 설정은 위의 단축키 설정을 참고하자.

 

5.기타 기능

5.1 빌드 시에 수정된 파일 자동으로 저장하기

 Eclipse1.PNG

 

5.2 필요없는 자동완성 막기

설정8.PNG

 

5.3 소스 폴딩(Folding) 기능 설정

설정9.PNG

 

 

 

5.4 탭(Tab)을 공백(Space)로 변경 및 80 컬럼(Column) 가이드 라인 설정

 

설정10.PNG

 

 

6.마치면서...

 이상으로 이클립스의 단축키 설명 및 환경 설정방법에 대해 알아보았다. 윈도우 프로그래밍에는 Visual Studio를 사용하고 기타(임베디드 or 자바 or 기타 등등) 프로그래밍에는 이클립스를 사용하여 프로그래밍을 편리하게 하자. @0@)/~

이 글은 스프링노트에서 작성되었습니다.

06 이클립스(Eclipse) CDT 설치

원문 : http://kkamagui.springnote.com/pages/446531

 

들어가기 전에...

 

0.시작하면서..

 이클립스는 Java기반의 개발환경으로써 편리한 Java 프로그램 개발을 위한 IDE였다. 그러나 다양한 플러그인이 개발되면서 점점 범위를 넓혀가고 있으며 다양한 언어(C/C++, Java, PHP 등등)을 지원하고 있다. 윈도우 프로그래머에게 대중적으로 사용되고 있는 Visual Studio 만큼의 강력한 Editor와 자동완성 기능을 가지고 있으므로 편리하게 프로그래밍 할 수 있으며, 다양한 툴 체인을 설정할 수 있으므로 윈도우 프로그래밍 외의 다른 용도로도 사용 가능하다.

 그럼 이제부터 설치 방법에서 설정까지 전 과정을 알아보자.

 

1.툴체인(Tool Chain) 설치

 이클립스 자체는 컴파일러와 링커를 포함하고 있지 않으므로 따로 설치해야 한다. 많이 사용하는 툴 체인인 Cygwin이나 MingW을 설치하거나 djgpp와 같은 DOS용 GCC같은 컴파일러를 설치하도록 한다. 나는 도스용 GCC인 DJGPP를 설치하였으므로 DJGPP를 기준으로 설명하겠다.

 

2.이클립스 설치

2.1 이클립스 다운로드 및 설치

 이클립스는 http://www.eclipse.org/downloads/ 에서 다운 받을 수 있고, 각 언어에 맞는 개발 suit를 받으면 된다.

이클립스1.PNG

<이클립스의 다양한 언어 지원>

 C/C++ 언어를 주로 사용하므로 C/C++ 버전인 CDT를 다운받아서 설치하도록 하자. 이클립스 사이트에서 Eclipse IDE for C/C++ Developers 버전을 다운받은 후 간단히 압축을 풀어서 특정 폴더로 이동하면 설치가 끝난다. 별도의 설치 프로그램은 없고 Java Runtime Environment(JRE)가 필요한데, Sun 사이트에서 받을 수 있다.

 

 

2.2 Java Runtime Environment(JRE) 설치

 이클립스는 Java 환경의 개발툴이므로 JRE가 필요하다. JRE는 http://java.sun.com/javase/downloads/index.jsp에서 최신버전을 다운 받을 수 있으니 받아서 설치하면 된다.

이클립스2.PNG

 <Java Download>

 

 

3.이클립스 실행

 이클립스 폴더에 있는 eclipse.exe를 실행시키면 이클립스 IDE를 실행할 수 있다.

이클립스3.PNG

<이클립스 실행화면>

 이클립스를 처음 실행하는 경우는 아래와 같은 화면이 보이는데, 워크벤치(Workbench)를 생성하고 나면 별로 볼일이 없다.

 이클립스4.PNG

<처음 실행화면>

 제일 오른쪽에 있는 아이콘을 클릭하여 워크벤치(Workbench)로 이동하자. 워크벤치(Workbench)로 이동하면 아래와 같은 화면이 보인다.

 이클립스5.PNG

<이클립스 프로젝트화면>

 왼쪽에 보이는 것이 프로젝트를 표시하는 뷰이고 가운데 부분이 코드를 표시해 주는 코드 에디터 부분이다. 오른쪽 부분은 함수를 보여주거나 하는 기능을 하며 아래 부분은 콘솔의 실행 결과 및  콘솔에서 발생한 에러를 표시해준다. 상당 부분이 Visual Studio와 비슷하니 Visual Studio에 익숙한 사람이라면 그리 낯설지 않을 것이다.

 

4.makefile 프로젝트 생성

 많이 사용하는 makefile 프로젝트를 생성해보자. File->New->Project를 이용해서 생성할 수 있다.

이클립스6.PNG

<새 프로젝트 생성>

 

이클립스7.PNG이클립스8.PNG

<프로젝트 설정> 

 위와 같이 프로젝트 이름을 적당히 입력해주고 아래의 Hellow World C++ Project 로 설정한 뒤 Finish를 누르면 프로젝트가 생성된다. 프로젝트의 경로를 수정하려면 Use default location 체크 박스를 해제하고 경로를 입력해 주면 된다.

이클립스9.PNG

<결과화면>

 프로젝트를 생성하였으니 프로그램을 빌드하여 실행할 차례이다. 좌측에 Project Explorer에 있는 makefile을 더블클릭하여 열어보면 아래와 같이 되어있는데, 컴파일을 위해 CXX를 djgpp로 설정한 후 Project 메뉴에 Build All 이나 Build Project를 누르면 build가 되어 Test1.exe 파일이 생성된다.

  1. CXXFLAGS = -O2 -g -Wall -fmessage-length=0
    CXX = djgpp <== 추가된 부분
    OBJS =  Test1.o
  2. LIBS =
  3. TARGET = Test1.exe
  4. $(TARGET): $(OBJS)
     $(CXX) -o $(TARGET) $(OBJS) $(LIBS)
  5. all: $(TARGET)
  6. clean:
     rm -f $(OBJS) $(TARGET)

 아래는 Build 후 결과 화면이다.

이클립스10.PNG

 <Build 실행 결과>

좌측의 Project Explorer를 통해 Test1.exe 파일을 실행해 볼 수 있다.

 

5.단축키 설정 및 환경설정

 이클립스 환경은 Visual Studio와 상당히 다르기 때문에 자신의 스타일에 맞게 단축키를 설정하는 것이 필수이다. 단축키 설정에 대한 자세한 내용은 07 이클립스(Eclipse) 단축키 및 환경설정문서를 참고하도록 하자.

 

6.마치며...

 간단하게 이클립스를 설치하고 이를 활용하는 방법을 알아보았다. 단축키와 환경 설정만 자신에게 맞도록 설정한다면 이클립스는 메모장(??)을 대체하는 아주 좋은 툴이 될 수 있다. 100% 활용해서 코드 노가다를 줄이도록 하자. @0@)/~

 

7.첨부

 

 

이 글은 스프링노트에서 작성되었습니다.

05 VC 6.0으로 유니코드(UNICODE) 프로젝트 만들기

원문 : http://kkamagui.springnote.com/pages/421193

참고 : http://www.npteam.net/297

 

들어가기 전에...

 

1.생성 방법

  • 프로젝트를 생성한 다음 메뉴의 Build->Configurations로 이동해서 Add 버튼을 누른다.
  • Debug로 생성하려면 Debug_Unicode로, Release로 생성하려면 Release_Unicode로 프로젝트를 추가한다.
  • Projtect->Settings에 가서 아래와 같이 _MBCS 매크로를 _UNICODE로 수정한다.

Setting1.PNG

  •  빌드를 실행했을때 msvcrtd.lib(crtexew.obj): error LNK2001:unresolvec external symbol _WinMain@16 라는 에러가 나오면 Project->Settings에서  /entry:"wWinMainCRTStartup" 를 추가함

 Setting2.PNG

  • 다시 Build 하면 이상없이 완료 @0@)/~

 

이 글은 스프링노트에서 작성되었습니다.

04 윈도우 콘솔(console) 입출력 리다이렉션(Input/Out Redirection)

원문 : https://kkamagui.tistory.com/85

들어가기 전에...

첫번째 방법

** 원문 :** http://cafe.naver.com/winmain.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=62

HOWTO: Spawn Console Processes with Redirected Standard Handles

Q190351

SUMMARY

This article describes how to redirect the input and output of a child process that receives input from the standard input handle or sends output to the standard output handle. The Win32 API enables applications to spawn a child console process with redirected standard handles. This feature allows a parent process to send and receive the input and output of the child process.

이 글은 표준 입력 핸들에서 입력을 가져오거나 표준 출력 핸들로 출력을 보내는 자식 프로세스의 입출력을 리다이렉트(redirect)하는 방법을 기술한다. Win32 API는 애플리케이션이 자식 콘솔 프로세스를 리다이렉트된 표준 핸들과 함께 생성할 수 있도록 해준다. 이 기능은 부모 프로세스가 자식 프로세스의 입출력을 보내거나 가져오는 일을 가능하게 해준다.

NOTE: Some console based applications do not use the standard handles for their input/output (IO) operations. The Win32 API does not support redirection of these processes.

NOTE: 몇몇 콘솔 기반 애플리케이션은 입출력 처리에 있어 표준 핸들을 사용하지 않는다. Win32 API는 이러한 프로세스들의 리다이렉트를 지원하지 않는다.

MORE INFORMATION

The CreateProcess() API through the STARTUPINFO structure enables you to redirect the standard handles of a child console based process. If the dwFlags member is set to STARTF_USESTDHANDLES, then the following STARTUPINFO members specify the standard handles of the child console based process:

CreateProcess() API를 통해 넘겨지는 STARTUPINFO 구조체는 여러분이 자식 콘솔 기반 프로세스의 표준 핸들을 리다이렉트할 수 있도록 해준다. dwFlags의 숫자가 STARTF_USESTDHANDLES로 설정되면, 나머지 STARTUPINFO 멤버들은 자식 콘솔 기반 프로세스의 표준 핸들을 지정한다.

HANDLE hStdInput - Standard input handle of the child process.
HANDLE hStdOutput - Standard output handle of the child process.
HANDLE hStdError - Standard error handle of the child process.

You can set these handles to either a pipe handle, file handle, or any handle that can do synchronous reads and writes through the ReadFile() and WriteFile() API. The handles must be inheritable and the CreateProcess() API must specify that inheritable handles are to be inherited by the child process by specifying TRUE in the bInheritHandles parameter. If the parent process only wishes to redirect one or two standard handles, specifying GetStdHandle() for the specific handles causes the child to create the standard handle as it normally would without redirection. For example, if the parent process only needs to redirect the standard output and error of the child process, then the hStdInput member of the STARTUPINFO structure is filled as follows:

여러분은 이 핸들들을 파이프 핸들, 파일 핸들 또는 ReadFile()과 WriteFile() API를 통해 동기화된 읽기와 쓰기를 할 수 있는 어떠한 핸들로도 설정할 수 있다. 이 핸들들은 상속 가능해야 하며, CreateProcess API는 bInheritHandles 파라미터에 TRUE를 설정함으로써 이 상속 가능한 핸들들이 상속되도록 설정해야 한다. 부모 프로세스가 한 개나 두 개의 표준 핸들만을 리다이렉트하길 원한다면, GetStdHandle()을 그 핸들에 지정하여 자식 프로세스가 리다이렉트가 없는 것처럼 정상적으로 표준 핸들을 생성하도록 한다. 예를 들어 부모 프로세스가 자식 프로세스의 표준 출력과 에러 핸들만을 리다이렉트할 필요가 있다면, STARTUPINFO 구조체의 hStdInput 멤버는 다음과 같이 채워져야 한다:

hStdInput = GetStdHandle(STD\_INPUT\_HANDLE);

NOTE: Child processes that use such C run-time functions as printf() and fprintf() can behave poorly when redirected. The C run-time functions maintain separate IO buffers. When redirected, these buffers might not be flushed immediately after each IO call. As a result, the output to the redirection pipe of a printf() call or the input from a getch() call is not flushed immediately and delays, sometimes-infinite delays occur. This problem is avoided if the child process flushes the IO buffers after each call to a C run-time IO function. Only the child process can flush its C run-time IO buffers. A process can flush its C run-time IO buffers by calling the fflush() function.

NOTE: printf()와 fprintf()와 같은 C 런타임 함수들을 사용하는 자식 프로세스들은 리다이렉트되었을 때 이상하게 작동할 수 있다. C 런타임 함수들은 별도의 IO 버퍼들을 유지한다. 리다이렉트되었을 때, 이 버퍼들은 IO 호출이 끝날 때마다 즉시 버퍼를 비우지(flush) 않을 수도 있다. 결과적으로, printf() 호출의 리다이렉트 파이프로의 출력이나 getch() 호출로부터의 입력은 즉시 버퍼를 비우지 않고 지연되고, 얼마간에서-무한한 지연이 발생한다. 이 문제는 자식 프로세스가 C 런타임 IO 함수에 대한 호출이 끝날때마다 IO 버퍼를 비운다면 피할 수 있다. 오직 자식 프로세스만이 자신의 C 런타임 IO 버퍼를 비울 수 있다. 프로세스는 fflush() 함수를 호출함으로써 C 런타임 IO 버퍼를 비울 수 있다.

NOTE: Windows 95 and Windows 98 require an extra step when you redirect the standard handles of certain child processes. For additional information, please see the following article in the Microsoft Knowledge Base:

NOTE: 윈도우 95와 윈도우 98은 어떤 자식 프로세스의 표준 핸들을 리다이렉트할 때 추가적인 과정이 필요하다. 추가적인 정보에 대해서는 마이크로소프트 Knowledge Base에 있는 다음 글을 보기 바란다.

Q150956 INFO: Redirection Issues on Windows 95 MS-DOS Applications

The following sample redirects the standard input, output, and error of the child process specified in the CreateProcess call. This sample redirects the provided console process (Child.c).

다음 예제는 CreateProcess 호출에서 지정된 자식 프로세스의 표준 입력 출력, 에러를 리다이렉트한다. 이 예제는 제공된 콘솔 프로세스(Child.c)를 리다이렉트한다.

Sample Code

/*++
    Copyright (c) 1998  Microsoft Corporation
      Module Name:
         Redirect.c
      Description:
          This sample illustrates how to spawn a child console based
          application with redirected standard handles.
          The following import libraries are required:
          user32.lib
    Dave McPherson (davemm)   11-March-98
--*/

#include<windows.h>

void DisplayError(char \*pszAPI);
void ReadAndHandleOutput(HANDLE hPipeRead);
void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                  HANDLE hChildStdIn,
                                  HANDLE hChildStdErr);
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam);
HANDLE hChildProcess = NULL;
HANDLE hStdIn = NULL; // Handle to parents std input.
BOOL bRunThread = TRUE;

void main ()
{
    HANDLE hOutputReadTmp,hOutputRead,hOutputWrite;
    HANDLE hInputWriteTmp,hInputRead,hInputWrite;
    HANDLE hErrorWrite;
    HANDLE hThread;
    DWORD ThreadId;
    SECURITY\_ATTRIBUTES sa;

    // Set up the security attributes struct.
    sa.nLength= sizeof(SECURITY\_ATTRIBUTES);
    sa.lpSecurityDescriptor = NULL;
    sa.bInheritHandle = TRUE;

    // Create the child output pipe.
    if (!CreatePipe(&hOutputReadTmp,&hOutputWrite,&sa,0))
        DisplayError("CreatePipe");
    // Create a duplicate of the output write handle for the std error
    // write handle. This is necessary in case the child application
    // closes one of its std output handles.
    if (!DuplicateHandle(GetCurrentProcess(),hOutputWrite,
                         GetCurrentProcess(),&hErrorWrite,0,
                         TRUE,DUPLICATE\_SAME\_ACCESS))
        DisplayError("DuplicateHandle");

    // Create the child input pipe.
    if (!CreatePipe(&hInputRead,&hInputWriteTmp,&sa,0))
        DisplayError("CreatePipe");

    // Create new output read handle and the input write handles. Set
    // the Properties to FALSE. Otherwise, the child inherits the
    // properties and, as a result, non-closeable handles to the pipes
    // are created.
    if (!DuplicateHandle(GetCurrentProcess(),hOutputReadTmp,
                         GetCurrentProcess(),
                         &hOutputRead, // Address of new handle.
                         0,FALSE, // Make it uninheritable.
                         DUPLICATE\_SAME\_ACCESS))
        DisplayError("DupliateHandle");

    if (!DuplicateHandle(GetCurrentProcess(),hInputWriteTmp,
                         GetCurrentProcess(),
                         &hInputWrite, // Address of new handle.
                         0,FALSE, // Make it uninheritable.
                         DUPLICATE\_SAME\_ACCESS))
        DisplayError("DupliateHandle");

    // Close inheritable copies of the handles you do not want to be
    // inherited.
    if (!CloseHandle(hOutputReadTmp)) DisplayError("CloseHandle");
    if (!CloseHandle(hInputWriteTmp)) DisplayError("CloseHandle");

    // Get std input handle so you can close it and force the ReadFile to
    // fail when you want the input thread to exit.
    if ( (hStdIn = GetStdHandle(STD\_INPUT\_HANDLE)) == INVALID\_HANDLE\_VALUE )
        DisplayError("GetStdHandle");

    PrepAndLaunchRedirectedChild(hOutputWrite,hInputRead,hErrorWrite);
    // Close pipe handles (do not continue to modify the parent).
    // You need to make sure that no handles to the write end of the
    // output pipe are maintained in this process or else the pipe will
    // not close when the child process exits and the ReadFile will hang.
    if (!CloseHandle(hOutputWrite)) DisplayError("CloseHandle");
    if (!CloseHandle(hInputRead )) DisplayError("CloseHandle");
    if (!CloseHandle(hErrorWrite)) DisplayError("CloseHandle");

    // Launch the thread that gets the input and sends it to the child.
    hThread = CreateThread(NULL,0,GetAndSendInputThread,
                           (LPVOID)hInputWrite,0,&ThreadId);
    if (hThread == NULL) DisplayError("CreateThread");

    // Read the child's output.
    ReadAndHandleOutput(hOutputRead);
    // Redirection is complete
   // Force the read on the input to return by closing the stdin handle.
   if (!CloseHandle(hStdIn)) DisplayError("CloseHandle");
   // Tell the thread to exit and wait for thread to die.
   bRunThread = FALSE;
   if (WaitForSingleObject(hThread,INFINITE) == WAIT\_FAILED)
      DisplayError("WaitForSingleObject");
   if (!CloseHandle(hOutputRead)) DisplayError("CloseHandle");
   if (!CloseHandle(hInputWrite)) DisplayError("CloseHandle");
}

///////////////////////////////////////////////////////////////////////
// PrepAndLaunchRedirectedChild
// Sets up STARTUPINFO structure, and launches redirected child.
///////////////////////////////////////////////////////////////////////
 void PrepAndLaunchRedirectedChild(HANDLE hChildStdOut,
                                   HANDLE hChildStdIn,
                                   HANDLE hChildStdErr)
 {
    PROCESS\_INFORMATION pi;
    STARTUPINFO si;
    // Set up the start up info struct.
    ZeroMemory(&si,sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF\_USESTDHANDLES;
    si.hStdOutput = hChildStdOut;
    si.hStdInput  = hChildStdIn;
    si.hStdError  = hChildStdErr;
    // Use this if you want to hide the child:
    //     si.wShowWindow = SW\_HIDE;
    // Note that dwFlags must include STARTF\_USESHOWWINDOW if you want to
    // use the wShowWindow flags.
    // Launch the process that you want to redirect (in this case,
    // Child.exe). Make sure Child.exe is in the same directory as
    // redirect.c launch redirect from a command line to prevent location
    // confusion.
    if (!CreateProcess(NULL,"Child.EXE",NULL,NULL,TRUE,
                       CREATE\_NEW\_CONSOLE,NULL,NULL,&si,&pi))
       DisplayError("CreateProcess");
    // Set global child process handle to cause threads to exit.
    hChildProcess = pi.hProcess;
    // Close any unnecessary handles.
    if (!CloseHandle(pi.hThread)) DisplayError("CloseHandle");
 }

 ///////////////////////////////////////////////////////////////////////
 // ReadAndHandleOutput
 // Monitors handle for input. Exits when child exits or pipe breaks.
 ///////////////////////////////////////////////////////////////////////
 void ReadAndHandleOutput(HANDLE hPipeRead)
 {
    CHAR lpBuffer\[256\];
    DWORD nBytesRead;
    DWORD nCharsWritten;
    while(TRUE)
    {
       if (!ReadFile(hPipeRead,lpBuffer,sizeof(lpBuffer),
                                        &nBytesRead,NULL) || !nBytesRead)
       {
          if (GetLastError() == ERROR\_BROKEN\_PIPE)
             break; // pipe done - normal exit path.
          else
             DisplayError("ReadFile"); // Something bad happened.
       }
       // Display the character read on the screen.
       if (!WriteConsole(GetStdHandle(STD\_OUTPUT\_HANDLE),lpBuffer,
                         nBytesRead,&nCharsWritten,NULL))
          DisplayError("WriteConsole");
    }
 }

 ///////////////////////////////////////////////////////////////////////
 // GetAndSendInputThread
 // Thread procedure that monitors the console for input and sends input
 // to the child process through the input pipe.
 // This thread ends when the child application exits.
 ///////////////////////////////////////////////////////////////////////
 DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
 {
    CHAR read\_buff\[256\];
    DWORD nBytesRead,nBytesWrote;
    HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
    // Get input from our console and send it to child through the pipe.
    while (bRunThread)
    {
       if(!ReadConsole(hStdIn,read\_buff,1,&nBytesRead,NULL))
          DisplayError("ReadConsole");
       read\_buff\[nBytesRead\] = '\\0'; // Follow input with a NULL.
       if (!WriteFile(hPipeWrite,read\_buff,nBytesRead,&nBytesWrote,NULL))
       {
          if (GetLastError() == ERROR\_NO\_DATA)
             break; // Pipe was closed (normal exit path).
          else
          DisplayError("WriteFile");
       }
    }
    return 1;
 }

 ///////////////////////////////////////////////////////////////////////
 // DisplayError
 // Displays the error number and corresponding message.
 ///////////////////////////////////////////////////////////////////////
 void DisplayError(char \*pszAPI)
 {
     LPVOID lpvMessageBuffer;
     CHAR szPrintBuffer\[512\];
     DWORD nCharsWritten;
     FormatMessage(
              FORMAT\_MESSAGE\_ALLOCATE\_BUFFER|FORMAT\_MESSAGE\_FROM\_SYSTEM,
              NULL, GetLastError(),
              MAKELANGID(LANG\_NEUTRAL, SUBLANG\_DEFAULT),
              (LPTSTR)&lpvMessageBuffer, 0, NULL);
     wsprintf(szPrintBuffer,
       "ERROR: API    = %s.\\n   error code = %d.\\n   message    = %s.\\n",
              pszAPI, GetLastError(), (char \*)lpvMessageBuffer);
     WriteConsole(GetStdHandle(STD\_OUTPUT\_HANDLE),szPrintBuffer,
                   lstrlen(szPrintBuffer),&nCharsWritten,NULL);
     LocalFree(lpvMessageBuffer);
     ExitProcess(GetLastError());
 }

 //////////////////////////////////////////////////////////////////////
 // child.c
 // Echoes all input to stdout. This will be redirected by the redirect
 // sample. Compile and build child.c as a Win32 Console application and
 // put it in the same directory as the redirect sample.
 //
 #include<windows.h>
 #include<stdio.h>
 #include<string.h>
 void main ()
 {
    FILE\*    fp;
    CHAR     szInput\[1024\];
    // Open the console. By doing this, you can send output directly to
    // the console that will not be redirected.
    fp = fopen("CON", "w");
    if (!fp) {
       printf("Error opening child console - perhaps there is none.\\n");
       fflush(NULL);
    }
    else
    {
    // Write a message direct to the console (will not be redirected).
       fprintf(fp,"This data is being printed directly to the\\n");
       fprintf(fp,"console and will not be redirected.\\n\\n");
       fprintf(fp,"Since the standard input and output have been\\n");
       fprintf(fp,"redirected data sent to and from those handles\\n");
       fprintf(fp,"will be redirected.\\n\\n");
       fprintf(fp,"To send data to the std input of this process.\\n");
       fprintf(fp,"Click on the console window of the parent process\\n");
       fprintf(fp,"(redirect), and enter data from it's console\\n\\n");
       fprintf(fp,"To exit this process send the string 'exit' to\\n");
       fprintf(fp,"it's standard input\\n");
       fflush(fp);
    }
    ZeroMemory(szInput,1024);
    while (TRUE)
    {
       gets(szInput);
       printf("Child echoing \[%s\]\\n",szInput);
       fflush(NULL);  // Must flush output buffers or else redirection
                      // will be problematic.
       if (!\_stricmp(szInput,"Exit") )
          break;
       ZeroMemory(szInput,strlen(szInput) );
    }
 }

REFERENCES

MSDN Library SDK documentation: CreateProcess(); STARTUPINFO structure
Inherit sample in the Win32 Platform SDK under:
\MSSDK\samples\winbase\ipc\inherit

Additional query words: Inheritance redirection redirected stdhandles

Keywords : kbAPI kbConsole kbIPC kbKernBase kbOSWinNT400 kbOSWin2000 kbSDKPlatform kbOSWin95 kbOSWin98 kbFAQ kbDSupport kbGrpDSKernBase
Issue type : kbhowto
Technology : kbAudDeveloper kbWin32sSearch kbWin32API

두번째 방법

원문 http://www.codeproject.com/threads/redir.asp

첨부 : **[redir_demo.zip](http://kkamagui.springnote.com/pages/410158/attachments/171448 "redir_demo.zip"), [redir_src.zip**](http://kkamagui.springnote.com/pages/410158/attachments/171449 "redir_src.zip")

Introduction

To redirect the input/output of a console application is interesting and useful. You can display the child's output in a window (just like Visual Studio's output window), or search some keywords in the output string to determine if the child process has completed its work successfully. An old, 'ugly' DOS program could become an useful component of your fancy Win32 GUI program.

My idea is to develop a simple, easy to use redirector class which can redirect an arbitrary console, and won't be affected by the behavior of the child process.

Background

The technique of redirecting the input/output of a console process is very sample: The CreateProcess() API through the STARTUPINFO structure enables us to redirect the standard handles of a child console based process. So we can set these handles to either a pipe handle, file handle, or any handle that we can read and write. The detail of this technique has been described clearly in MSDN: HOWTO: Spawn Console Processes with Redirected Standard Handles.

However, MSDN's sample code has two big problem. First, it assumes the child process will send output at first, then wait for input, then flush the output buffer and exit. If the child process doesn't behave like that, the parent process will be hung up. The reason of this is the ReadFile() function remains blocked untill the child process sends some output, or exits.

Second, It has problem to redirect a 16-bit console (including console based MS-DOS applications.) On Windows 9x, ReadFile remains blocked even after the child process has terminated; On Windows NT/XP, ReadFile always returns FALSE with error code set to ERROR_BROKEN_PIPE if the child process is a DOS application.

Solving the block problem of ReadFile

To prevent the parent process from being blocked by ReadFile, we can simply pass a file handle as stdout to the child process, then monitor this file. A more simple way is to call PeekNamedPipe() function before calling ReadFile(). The PeekNamedPipe function checks information about data in the pipe, then returns immediately. If there's no data available in the pipe, don't call ReadFile.

By calling PeekNamedPipe before ReadFile, we also solve the block problem of redirecting a 16-bit console on Windows 9x.

The class CRedirector creates pipes and launchs the child process at first. then creates a listener thread to monitor the output of the child process. This is the main loop of the listener thread:

for (;;)
    {
        // redirect stdout till there's no more data.
        nRet = pRedir->RedirectStdout();
        if (nRet <= 0)
            break;

        // check if the child process has terminated.
        DWORD dwRc = ::WaitForMultipleObjects(
            2, aHandles, FALSE, pRedir->m_dwWaitTime);
        if (WAIT_OBJECT_0 == dwRc)      // the child process ended
        {
            ...
            break;
        }
        if (WAIT_OBJECT_0+1 == dwRc)    // m_hEvtStop was signalled, exit
        {
            ...
            break;
        }
    }

This is the main loop of the RedirectStdout() function:

for (;;)
    {
        DWORD dwAvail = 0;
        if (!::PeekNamedPipe(m_hStdoutRead, NULL, 0, NULL,
            &dwAvail, NULL))    // error, the child process might ended
            break;

        if (!dwAvail)           // no data available, return
            return 1;

        char szOutput[256];
        DWORD dwRead = 0;
        if (!::ReadFile(m_hStdoutRead, szOutput, min(255, dwAvail),
            &dwRead, NULL) || !dwRead)  
                 // error, the child process might ended
            break;

        szOutput[dwRead] = 0;
        WriteStdOut(szOutput);          // display the output
    }

WriteStdOut is a virtual member function. It does nothing in CRedirector class. However it can be overrided to achieve our specific target, like I did in the demo project:

int nSize = m_pWnd->GetWindowTextLength();  
             // m_pWnd points to a multiline Edit control
    m_pWnd->SetSel(nSize, nSize);
    m_pWnd->ReplaceSel(pszOutput);      
           // add the message to the end of Edit control

To redirect DOS console based applications on NT/2000/XP

MSDN's solution is to launch an intermediate Win32 Console application as a stub process between the Win32 parent and the 16-bit console based child. In fact the DOS prompt program (on NT/XP it's cmd.exe, on 9x it's command.com) is a natural stub process we just need. We can test this in RedirDemo.exe:

  1. Input 'cmd.exe' in Command Editbox, then press Run button.
  2. Input the name of the 16-bit console based application (dosapp.exe for example) in the Input Editbox, then press Input button. Now we can see the output of the 16-bit consol.
  3. Input 'exit' in the Input Editbox, then press Input button to terminate cmd.exe

Apparently this is not a good solution because it's too complicated. A more effective way is to use a batch file as the stub. Edit stub.bat file like this:

%1 %2 %3 %4 %5 %6 %7 %8 %9

Then run a command like 'stub.bat dosapp.exe', then the 16-bit DOS console application runs OK.

About nickadams

  Nick Adams is one of my favorite figures in Hemingway's stories. I use it because Jeff Lee has been occupied on Codeproject.

Click here to view nickadams's online profile.

Other popular Threads, Processes & IPC articles:

02 간단한 Make 사용법

원문 : https://kkamagui.tistory.com/84

들어가기 전에...

0.시작하면서...

통합 개발툴(IDE)를 이용해서 개발하는 경우에 굳이 Makefile을 생성할 필요는 없지만 IDE를 사용하지 않고 개발할 경우, 많은 소스파일을 관리하기위해서 makefile이 필수이다. make는 실제로 굉장히 많은 기능을 가지고 있지만, 그중 몇가지 유용한 기능만 알아도 훨씬 코드 관리가 편리해 진다.

이제 그 몇가지에 대해서 알아보자. @0@)/~

1.makefile의 기본 형식

makefile은 기본적으로 아래와 같은 형식으로 이루어진다.

Target File : Source File(Dependency) 
    <tab> command

좌측의 Target File은 make command의 실행 결과로 생성할 파일이고 Source File은 make command에 사용될 파일이다. 아래의 command는 탭으로 한칸 띄우는데 이 탭이 command의 구분자의 역할을 한다. 만약 Abc.c 파일을 gcc -c로 컴파일 해서 Abc.o 파일을 생성한다면 아래와 같이 된다(주의-대소문자 구분함).

Abc.o : Abc.c 
    <tab> gcc -c Abc.o Abc.c

실제로 여러 파일을 생성한다고 가정하면 위와 같은 코드들이 아래와 같이 나열된다.

# 이건 주석입니다.
Abc.o : Abc.c 
    <tab>gcc -c Abc.o \\   <- 멀티라인 사용법
        Abc.c
Abb.o : Abb.c 
    <tab> gcc -c Abb.o Abb.c

A.exe : Abc.o Abb.o 
    <tab> gcc -o A.exe Abb.o Abc.o
...... 반복.....

주석은 #로 시작하고 멀티라인은 \를 마지막 라인에 추가하면 된다. 아주 간단하지 않는가? 위와 같이 파일을 생성하여 makefile을 만든 후, make.exe를 실행하면 자동으로 makefile을 읽어서 위의 조건에 따라 파일을 생성해 준다.

만약 어떤 Target File이 생성되기 위해 다른 파일이 Target File이 필요한 경우 makefile이 알아서 필요한 파일 순서대로 결과 파일을 만든 후 최종 결과물을 만들어 준다. 위의 makefile을 수행하면 A.exe와 각종 .o 파일들이 생긴다.

2.매크로(Macro)의 사용

만약 소스파일이 손으로 칠 수 없을 만큼 많다면? 또는 어떤 Target File을 생성하는데 규칙이 일정하다면?(.c 파일을 .o 파일로 바꾸는 것과 같은 경우) 과연 이런 경우에도 일일이 다 손으로 써줘야 할까?

매크로를 이용하면 이것을 편리하게 할 수 있다. GNU-Make에서 매크로는 아래와 같이 정의하고 사용한다.

OBJECT = A.o B.o C.o
CC = gcc
A.exe : $(OBJECT)
    <tab> $(CC) -o A.exe $(OBJECT)

OBJECT 매크로가 없다면 일일이 손으로 저 파일들을 다 쳐줘야 할 것을 OBJECT 매크로를 정의함으로써 편리하게 해결했다.

그럼 이제 좀더 이것을 확장해 보자. 일반적으로 .c 파일을 .o 파일로 만드는 규칙은 거의 동일하다. 컴파일 옵션에 -c 옵션을 줄 것이며 A.o A.c와 같은 같은 파일명을 사용하고 확장자만 다르다. 이것을 어떻게 간단하게 할 수 있을까? 답은 패턴 규칙을 이용하는 것이다. %를 사용하는 아래를 보자.

%.o : %.c 
    <tab> gcc -c $@ $<

위와 같이 하면, .c가 들어간 파일은 .o로 바꾸어 주는데 gcc -c 옵션을 적용하여 .o 파일을 생성한다. 즉 %는 확장자를 제외한 파일명을 의미한다. $@와 같은 생소한 문자를 볼 수 있는데, 이것은 make에서 미리 정의해둔 매크로로 아래와 같은 것들이 있다.

  • $@ : Target File. 파일명 및 확장자 포함. 왼쪽 전체 패턴을 치환
  • $< : Source File. 파일명 및 확장자 포함. 오른쪽 첫번째 패턴을 치환
  • $* : 파일명만 포함
  • $^ : Source File. 오른쪽 전체 리스트를 치환

그럼 패턴 규칙을 이용할때 파일명 그대로만 사용 가능할까? 답은 "그렇지 않다" 이다. 아래와 같은 사용도 가능하다.

%\_debug.o : %.c
    <tab> gcc -c $@ $<

위처럼하면 파일이름에 _debug가 붙은 파일을 생성할 수 있다. 저 규칙을 잘 이용하면 여러가지 종류의 파일을 생성할 수 있다.
자 그러면 매크로로 정의한 내용 중에 필요한 내용만 따로 치환할 수 는 없을까? 물론 가능하다. 아래는 .o를 .c로 치환하는 문장이다.

OBJECT = a.o b.o c.o d.o
SOURCE = $(OBJECT:.o=.c)

아주 간단하지 않은가? @0@)/~ 정말 놀랍다. @0@)/~~!!!!

그럼 만약 target에서 사용될 이름과 동일한 파일명이 존재한다면? 즉 make clean과 같이 clean을 사용해야 하는데 마침 이름이 clean인 항목이 있다면 어떻게 할까? make clean을 하면 해당 항목이 컴파일 될 것인데... 우리가 원하는 것은 그것이 아니다...

이런 경우 아래와 같이 사용하면 된다.

.PHONY : clean

.PHONY가 의미하는 것은 clean이 build의 대상이 아님을 말한다.

이번에는 make 실행 시 입력값에 의해 Active하게 플래그를 변경해서 실행해 보자. makefile안에 여러 모드로 컴파일하는 기능도 넣어보자. 아무옵션이 없이 그냥 make만 실행되었을 때, 디폴트로 가는 레이블은 all 이다. 이 기능을 이용해서 디폴트 Target 파일을 지정해보고 clean 기능을 넣어서 .o 파일을 모두 지우게 하자.

all : A.exe

.... 어쩌구 저쩌구 ...

A.exe : $(OBJ) 
    <tab> $(CC) $(FLAG) $(OBJ)

.... 어쩌구 저쩌구 ...

clean :
    <tab> del \*.o

위 처럼 Source File 즉 Dependency에 아무것도 넣지 않거나 만들 Target File 명을 넣으면 된다. A.exe를 만들고 싶으면 "make"라고 입력하던지 "make all"이라고 입력하면 되고 clean 기능을 동작 시키려면 "make clean"이라고 입력하면 된다.

조금 더 응용해서 make 시에 컴파일 플래그를 조절하도록 해보자

ifdef FULL
FLAG = -f -c
endif

위와 같이 한 뒤에 "make FULL=yes" 라고 실행하면 FULL 플래그를 설정하여 FLAG를 활성화 시킬 수 있다.

3.디렉토리의 변경

makefile을 생성하여 사용할때 여러개의 프로젝트를 동시에 빌드하여 하나의 output이 나와야 할 경우가 있다. 이런 경우 하위 폴더에 프로젝트들을 넣어두고 상위 폴더에 하위 폴더를 다 make 하는 형식을 사용하는데, 이때 각 프로젝트 폴더로 이동해서 해당 폴더의 makefile을 실행해야한다.

이때 사용하는 옵션이 -C 이고 make를 실행하기 전에 폴더를 변경하라는 옵션이다.

$> make -C "Boot" -f makefile

위의 명령은 Boot 폴더로 폴더를 이용해서 make를 수행하라는 명령이다.

4.출력 메시지

make 도중에 메시지를 화면에 출력하고 싶으면 @echo "메시지" 의 형태로 사용하면 된다.

@echo "빌드중입니다..."

5.마무리-makefile 적용

5.1 Build 파일을 직접 선택

자 이제 이것을 실제로 적용해 보자. 아래의 makefile은 make에 대해서 잘 몰랐을 때, 노가다로 작성한 파일이다. 상당히 불현하고 특히 자주 파일이 추가되거나 변경되는 Custom 폴더의 경우 파일이 추가될때 마다 일일이 명령을 추가해줘야하는 단점이 있었다.

#   Kernel Make File  
#  
#   Written KKAMAGUI, [http://kkamagui.egloos.com](http://kkamagui.egloos.com/)
all: Kkernel

# Compile 옵션  
GCC = gcc -ffreestanding -c  
NASM = nasm -f coff  
FWDIR = FW/  
CUSTOMDIR = Custom/

# Frame Work 파일  
# Compile할 파일 이름 다 적기  
A.o: $(FWDIR)Asm.asm  
    $(NASM) -o A.o $(FWDIR)Asm.asm  
Is.o: $(FWDIR)Isr.asm  
    $(NASM) -o Is.o $(FWDIR)Isr.asm  
K.o: $(FWDIR)Kernel.c  
    $(GCC) -o K.o $(FWDIR)Kernel.c  
D.o: $(FWDIR)Descriptor.c  
    $(GCC) -o D.o $(FWDIR)Descriptor.c  
Int.o: $(FWDIR)Interrupt.c  
    $(GCC) -o Int.o $(FWDIR)Interrupt.c  
Key.o: $(FWDIR)Keyboard.c  
    $(GCC) -o Key.o $(FWDIR)Keyboard.c  
Stdlib.o : $(FWDIR)stdlib.c  
    $(GCC) -o Stdlib.o $(FWDIR)Stdlib.c  
Task.o : $(FWDIR)Task.c  
    $(GCC) -o Task.o $(FWDIR)Task.c

# 응용 프로그램 파일  
FW.o : $(CUSTOMDIR)Framework.c  
    $(GCC) -o FW.o $(CUSTOMDIR)FrameWork.c  
KShell.o : $(CUSTOMDIR)KShell.c  
    $(GCC) -o KShell.o $(CUSTOMDIR)KShell.c  
Sched.o : $(CUSTOMDIR)Scheduler.c  
    $(GCC) -o Sched.o $(CUSTOMDIR)Scheduler.c

#Object 파일 이름 다 적기  
#아래의 순서대로 링크된다.  
OBJ = A.o K.o Is.o D.o Int.o Key.o Stdlib.o Task.o FW.o KShell.o Sched.o
Kkernel: $(OBJ)  
    ld $(OBJ) -o kkernel.bin --oformat binary -Ttext 0x100000
clean:  
    del \*.o

위의 makefile을 배운 것을 이용하여 간단하게 정리하면 아래와 같이 쓸 수 있다.

#   Kernel Make File  
#  
#   Written KKAMAGUI, [http://kkamagui.egloos.com](http://kkamagui.egloos.com/)
all: kernel

# Compile 옵션 및 폴더 설정  
GCC = gcc -ffreestanding -c  
NASM = nasm -f coff  
FWDIR = FW/  
CUSTOMDIR = Custom/

#Object 파일 이름 다 적기  
#아래의 순서대로 링크된다. 새로운 파일이 생기면 뒤에다 추가하자  
OBJ = Asm.o Kernel.o Isr.o Descriptor.o Interrupt.o Keyboard.o StdLib.o Task.o \\  
      FrameWork.o KShell.o Scheduler.o

#컴파일할 파일의 확장자에 따른 규칙
# FW 폴더 밑의 파일들 컴파일
%.o: $(FWDIR)%.asm  
    $(NASM) -o $@ $<  
%.o: $(FWDIR)%.c  
    $(GCC) -o $@ $<

# Custom 폴더 밑의 파일들 컴파일  
%.o : $(CUSTOMDIR)%.c  
    $(GCC) -o $@ $<

# 최종 링크  
kernel: $(OBJ)  
    ld $(OBJ) -o kkernel.bin --oformat binary -Ttext 0x100000
clean:  
    del \*.o

우와~ 정말 깔끔해졌다. 확장 또한 한부분만 추가하면 되니까 훨씬 간단해 졌다. makefile 사용해서 효율적인 프로젝트 관리를 해보자.

5.2 특정 폴더의 특정 확장자의 파일을 모두 빌드

이클립스 버전으로 옮겨가면서 makefile 또한 정리하였다. 자세한 내용은 21 OS 프레임워크 소스 릴리즈의 릴리즈 파일을 참고하도록하고 아래는 00Kernel 폴더의 makefile이다.

#   Kernel Make File  
#  
#   Written KKAMAGUI, [http://kkamagui.egloos.com](http://kkamagui.egloos.com/)
all: kernel

# Compile 옵션 및 폴더 설정  
GCC = djgcc -ffreestanding -c  
LD = djld  
NASM = nasm -f coff  
FWDIR = FW  
CUSTOMDIR = Custom  
SOURCEDIR = $(FWDIR) $(CUSTOMDIR)

#Object 파일 이름 다 적기  
#아래의 순서대로 링크된다. 새로운 파일이 생기면 뒤에 다 추가하자  
#커널에 꼭 필요한 Object 파일들. ASM.o 파일은 항상 제일 앞에 와야한다. 그 이유는  
#커널의 엔트리포인트가 있는 함수이기 때문이다.  
ESSENTIALOBJ = Asm.o Isr.o

# 디렉토리에 있는 C 파일들을 다 찾아서 넣도록 한다.  
CFILE = $(foreach dir,$(SOURCEDIR),$(notdir $(wildcard $(dir)/\*.c )))  
    CFILEOBJ = $(CFILE:.c=.o)  

#확장자 규칙을 이용해서 컴파일 한다.  
%.o: $(FWDIR)\\%.asm  
    $(NASM) -o $@ $<  
%.o: $(FWDIR)\\%.c  
    $(GCC) -o $@ $<  
%.o: $(CUSTOMDIR)\\%.asm  
    $(NASM) -o $@ $<  
%.o: $(CUSTOMDIR)\\%.c  
    $(GCC) -o $@ $<  

# 최종 링크  
kernel: $(ESSENTIALOBJ) $(CFILEOBJ)  
    @echo "==> Making Kernel..."  
    $(LD) $(ESSENTIALOBJ) $(CFILEOBJ) -o kkernel.bin --oformat binary -Ttext 0x100000  
    @echo "==> Complete"

clean:  
    del \*.o  
    del kkernel.bin

6.make 문법 quick reference

GNU make의 quick refernce는 http://www.viper.pe.kr/docs/make-ko/make-ko_15.html 에서 찾을 수 있다.

01 웹 프로그래밍

원문 :  http://kkamagui.springnote.com/pages/347953

 

들어가기 전에...

 

스프링노트로 쓴 글 이글루스에 보냈을 때 코드 상자 표시

 스프링노트로 글을 쓴다음 이 글을 이글루스로 보내면 그룹 상자나 코드 상자가 제대로 보이지 않는다. 이것을 해결하기 위해 소스를 열어보니 ol.code 라는 스타일과 li 라는 스타일이 보였다.

 

 이것을 이글루스 스킨에 css 영역에 아래와 같이 추가해 줬다.

div.BOX { border: dashed; border-width: thin; width: 100%; color: white; background: #404040; PADDING: 5PX;  line-height:2.0em}
ol.code { border: dashed; border-width: thin; width: 100%; color: white; background: #151515; PADDING: 5PX;  line-height:2.0em}
ol { border: dashed; border-width: thin; width: 100%; color: white; background: #404040; PADDING: 5PX; line-height:2.0em}
div { background: #202020 }

 

 이제 코드 상자도 잘 보이고 글 상자도 잘 보인다. 단 맨 처음이 항상 개행되는 문제만 제외하면.. ㅡ,.ㅡ;;;;

이 글은 스프링노트에서 작성되었습니다.

00 윈도우 프로그래밍 팁

원문 :  http://kkamagui.springnote.com/pages/347952

 

들어가기 전에...

 

각종 윈도우 팁들 - Debug Lab

 http://www.debuglab.com/knowledge/

 

WinDBG의 심볼 경로 설정

SRV*c:\websymbol*http://msdl.microsoft.com/download/symbols  -->

 

VC에서 메모리 관련 디버그 명령

INIT_CRTDEBUG(); <== 메모리 누수 모니터링 시작
BREAK_ALLOC(X); <== 메모리 X 블럭 할당시 Break

 

VC에서 Alt-F4 막기

Alt+F4 키와 같은 메세지를 처리해주기 위해서 CWinApp 함수의 PreTranslateMessage() 를 override 해서 아래의 코드를 넣어 주면 된다.

  1. if(pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_F4)
    {
        return TRUE;
    }

 

 

익스플로러가 죽어도 트레이아이콘에 계속 상주시키기

  1. // 메시지 등록 과정
    UINT g_uShellRestart;
    g_uShellRestart = RegisterWindowsMessage( "TaskbarCreated" );

    // Message Map 에서
    ON_REGISTERED_MESSAGE( g_uShellRestart, OnTrayShow )

    // 메시지를 처리합니다.
    LRESULT CMyDlg::OnTrayShow( WPARAM wParam, LPARAM lParam )

       // TrayIcon을 다시 보여줍니다. ShowTray는 Tray를 보여주는 함수입니다.
       m_Tray.ShowTray();
    }  -->

 

 

 Variable Argument(가변 인자)

  1. // crt_vsprintf.c
    // This program uses vsprintf to write to a buffer.
    // The size of the buffer is determined by _vscprintf.

    #include
    #include

    void test( char * format, ... )
    {
       va_list args;
       int len;
       char * buffer;

       va_start( args, format );
       len = _vscprintf( format, args ) // _vscprintf doesn't count
       + 1; // terminating '\0'
       buffer = malloc( len * sizeof(char) );
       vsprintf( buffer, format, args );
       printf( buffer );
       free( buffer );
    }

    int main( void )
    {
       test( "%d %c %d\n", 123, '<', 456 );
       test( "%s\n", "This is a string" );
    }

 

 

윈도우에서 Command Line Argument 얻기

 argc = __argc, argv = __argv 로 얻으면 된다.

 

 

윈도우 공유 폴더 로그인 시 이상한 계정으로 로그인 되는 경우

 관리도구->사용자계정->좌측 상단의 네트워크 연결관리 에서 서버와 계정을 추가한다.

 

 

툴팁 생성하기

 툴팁 생성 코드

  1.     m_clToolTip.Create( this, WS_VISIBLE | WS_BORDER );
        m_clToolTip.AddTool( this, "" );
        m_clToolTip.SetDelayTime( 100 );
        m_clToolTip.Activate( TRUE );

 

 툴팁 표시를 위한 메시지 릴레이 코드

  1. /**
        Enter와 Esc로 인한 종료를 막는다.
    */
    BOOL CSubPartition::PreTranslateMessage(MSG* pMsg)
    {
        if( pMsg->message == WM_KEYDOWN )
        {
            if( ( pMsg->wParam == VK_ESCAPE ) ||
                ( pMsg->wParam == VK_RETURN ) )
            {
                return TRUE;
            }
        }
  2.     // ToolTip 표시를 위해 메시지 릴레이 설정
        m_clToolTip.RelayEvent(pMsg);
        return CDialog::PreTranslateMessage(pMsg);
    }

 

 

Shell Execute로 프로그램 실행하기

 

  1. BOOL ExecuteProgram( String FileName, String Params, INT Flag )
    {
      SHELLEXECUTEINFO execinfo;
     
      // 실행을 위해 구조체 세트
      ZeroMemory( &execinfo, sizeof(execinfo) );
      execinfo.cbSize = sizeof(execinfo);
      execinfo.lpVerb = "open";
      execinfo.lpFile = FileName.c_str();
      execinfo.lpParameters = Params.c_str();
      execinfo.fMask = SEE_MASK_FLAG_NO_UI SEE_MASK_NOCLOSEPROCESS;
      execinfo.nShow = SW_SHOWDEFAULT;
     
      // 프로그램을 실행한다.
      int r = (int)ShellExecuteEx( &execinfo );
      if ( r == 0 ) return ( false );
     
      // 만약 Sync 플랙이 세트되었으면,
      // 실행이 종료될 때까지 기다린다.
      if ( Flag == 1 )
      {
        DWORD ec;
        do
        {
          GetExitCodeProcess( execinfo.hProcess, &ec );
          Application->ProcessMessages();
        }
        while ( ec == STILL_ACTIVE );
      }
     
      return ( true );
    }

 

 Open URL로 웹 연결하기

CInternetFile* pFile = NULL;
CInternetSession InetSession;
 
try
{
  pFile = (CInternetFile *)InetSession.OpenURL( "URL 주소 : http://???" )
}
catch(CInternetException *pEx)
{
  pFile = NULL;
  pEx = NULL;
  AfxMessageBox( "OpenURL Excption Error!" );
}
 
CString strData;
 
if( pFile )
{
  CString strTemp;
  pFile->SetReadBufferSize( 4096 );
 
  while( true )
  {
    if( pFile->ReadString( strTemp ) )
    {
      strTemp += "\r\n";
      strData += strTemp;
    }
    else
    {
      break;
    }
  }
}
else
{
  AfxMessageBox( "OpenURL pFile is NULL!!" );
}
 
MessageBox( strData );

 

윈도우에서 F1 키 막기 또는 기능 변경하기

 APP의 아래부분을 막거나 변경한다.

  1.  ON_COMMAND(ID_HELP, CApp::OnHelp)

 

 

DLL의 함수 Export시 정해진 이름으로 Export 하기

 DLL의 함수를 그냥 Export 하면 함수 이름 그대로 Export 되지 않고 기타 정보가 더 붙는다. extern을 사용하면 어느정도 해결되지만 확실한 해결책은 def 파일을 이용하는 것이다. 아래와 같이 쓰면된다.

  1. LIBRARY MyDll
    EXPORTS
       Function1=Function1
       Function2=Funciton2
  2.    Data1                 DATA <== 데이터를 외부로 Export 할때
  3.    Function3             @1   <== 서수를 같이 Export 할때
  4.    Function4             @2   NONAME  <== 서수로만 Export 할때, 함수 이름 포함 X
  5.    Function5                  PRIVATE <== lib 파일에 Function5에 대한 내용 제외, DLL에는 있음

 

파일(File) 및 라인(Line) 관련 매크로(Macro)

  • __FILE__ : 해당 파일 이름으로 대체
  • __LINE__ : 해당 라인 번호로 대체

 

  1. printf( "%d %d", __FILE__, __LINE__ );

 

printf 직접 구현하기

 C/C++의 Calling Convention을 보면 Stack에 파라메터를 넘긴다. 이것을 이용하여 Variable Argument List를 찾아서 넣는 방식이다.

  1. /**
     임의로 만든 printf 함수
      printf 함수의 간략버전
    */
    void kPrintf( char* pcFormat, ... )
    {
        DWORD* pdwArg;
        char vcBuffer[ 1024 ];
        int iBufferIndex;
        int iFormatIndex;
        char* pcString;
        int iLength;
  2.     // 2번째 Argument를 pdwArg가 가리키고 있다.
        pdwArg = ( DWORD* ) &pcFormat + 1;
        iBufferIndex = 0;
        iFormatIndex = 0;
  3.     // 문자열 끝까지 한다.
        while( pcFormat[ iFormatIndex ] != '\0' )
        {
            if( pcFormat[ iFormatIndex ] != '%' )
            {
                vcBuffer[ iBufferIndex ] = pcFormat[ iFormatIndex ];
                iFormatIndex++;
                iBufferIndex++;
                continue;
            }
  4.         iFormatIndex++;
            switch( pcFormat[ iFormatIndex ] )
            {
                // 16진수 출력
            case 'X':
            case 'x':
                // 10진수 출력
            case 'd':
                kDToA( vcBuffer + iBufferIndex, *pdwArg );
                iBufferIndex += 8;
                iFormatIndex++;
                pdwArg++;
                break;
  5.             // 문자열 출력
            case 's':
                pcString = ( char* )( *pdwArg );
                iLength = strlen( pcString );
  6.             kMemCpy( vcBuffer + iBufferIndex, pcString, iLength );
                iBufferIndex += iLength;
                iFormatIndex++;
                pdwArg++;
                break;
     
                // 문자 출력
            case 'c':
             vcBuffer[ iBufferIndex ] = *pdwArg & 0xFF;
             iFormatIndex++;
             iBufferIndex++;
             pdwArg++;
             break;
             
                // % 출력
            case '%':
                vcBuffer[ iBufferIndex ] = '%';
                iFormatIndex++;
                iBufferIndex++;
                break;
           
                // 그외 기타
            default:
                vcBuffer[ iBufferIndex ] = pcFormat[ iFormatIndex ];
                iFormatIndex++;
                iBufferIndex++;
                break;
            }
        }
        vcBuffer[ iBufferIndex ] = '\0'; 
       
        // 내부 출력함수 이용
        kPrintfInternal( vcBuffer );
    }

 

 

조사식 창(Watch)에 에러(Error) 내용 보기

 아래와 같이 입력하면 에러코드와 내용을 볼 수 있다.

  1. @err,hr 

 

VC 6.0에서 XP 스타일 적용하기 

원문 :  http://blog.naver.com/kisatsg?Redirect=Log&logNo=20004074897

① 다음 내용을 편집하신후에 프로젝트 아래의 res 디렉토리에 ApplicationManifestXMLFile 파일로 저장합니다.

 

./res/ApplicationManifestXMLFile

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
                 manifestVersion="1.0">
<assemblyIdentity
    version="1.0.0.0"
    processorArchitecture="X86"
    name="Microsoft.Windows.YourApplication"
    type="win32"
/>
<description>YourApplication</description>
<dependency>
    <dependentAssembly>
        <assemblyIdentity
            type="win32"
            name="Microsoft.Windows.Common-Controls"
            version="6.0.0.0"
            processorArchitecture="X86"
            publicKeyToken="6595b64144ccf1df"
            language="*"
        />
    </dependentAssembly>
</dependency>
</assembly>
② resource.h 에 아래 두줄 추가
#define IDR_MANIFEST  1
 #define RT_MANIFEST  24
③ .rc2 파일은 거의 손 안대시죠? 그 파일안에 다음 내용을 쳐 넣습니다.
// Add manually edited resources here...
IDR_MANIFEST RT_MANIFEST MOVEABLE PURE
             "res\\ApplicationManifestXMLFile"
④ Instance 시작 파일에 다음 내용을 쳐 넣습니다.
BOOL MoneyApp::InitInstance()
{
  InitCommonControls();    // initialize common control library
  CWinApp::InitInstance(); // call parent class method

#ifdef _AFXDLL
  Enable3dControls();      // Call this when using MFC in a
                           // shared DLL
#else
  Enable3dControlsStatic(); // Call this when linking to MFC
                            // statically
#endif

  // the rest of the code
}
이상입니다. 모 사실 ④번은 안해도 되는데, 원저자가 하라길래 했지요. ^^;

 

 

Tray Icon 관련 

1.Add Tray 

  1.  void CMainFrame::AddSysTray()
    {
     NOTIFYICONDATA data;
     data.cbSize = sizeof(NOTIFYICONDATA);
     data.hWnd = m_hWnd;
     data.uID = IDR_MAINFRAME;
     data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
     data.uCallbackMessage = WM_SYSTRAYCLICKED;
     data.hIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
     strcpy(data.szTip , VERSION);
     Shell_NotifyIcon( NIM_ADD, &data );
    }

 

2.Delete Tray

  1. void CMainFrame::DeleteSysTray()
    {
     NOTIFYICONDATA data;
     data.cbSize = sizeof( NOTIFYICONDATA );
     data.hWnd = m_hWnd;
     data.uID = IDR_MAINFRAME;
     Shell_NotifyIcon( NIM_DELETE , &data );
    }

 

3.메시지 처리 

  1. LONG CMainFrame::OnSystrayClicked( WPARAM wParam , LPARAM lParam )
    {
     CYesNoDlg dlg;
  2.     switch( lParam )
     {
     case WM_LBUTTONDBLCLK:
      //::ShowWindow( m_pVisibleWnd->m_hWnd , SW_RESTORE );
      m_pVisibleWnd->ShowVisibleFrame();
      TRACE("에루 버튼\n");
      break;
     case WM_RBUTTONDBLCLK:
      TRACE("아루 버튼\n");
      if( dlg.DoModal() == IDOK )
      {
       PostMessage( WM_CLOSE , 0 , 0 );
      }
      break;
     }
     return TRUE;
    }

 

4.태스크바가 다시 생성되었을 때 재등록

  1. LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
    {
     // 만약 테스크바가 제시작 되었으면
     // 익스플로어가 한번 죽었던 것이므로..
     // 새로 Tray에 더한다.
     if( message == RegisterWindowMessage("TaskbarCreated") )
     {
      AddSysTray();
     }  
     return CFrameWnd::DefWindowProc(message, wParam, lParam);
    }

 

다이얼로그 기반 프로그램에서 처음 시작시 다이얼로그 안띄우기 

 WM_WINDOWPOSCHANGING 메시지를 이용한다. 이 메시지는 Dialog 메시지에는 없으므로 클래스 위져드에서 메시지 쪽에 메시지 필터를 "Window"로 바꿔줘야 나온다.

  1. void CHidDlgDlg::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
    {
        CDialog::OnWindowPosChanging(lpwndpos);
      
        // TODO: Add your message handler code here
        if(m_bShowFlag)
            lpwndpos->flags |= SWP_SHOWWINDOW;
        else
            lpwndpos->flags &= ~SWP_SHOWWINDOW;
  2. }
  3. BOOL CHidDlgDlg::ShowWindowEx(int nCmdShow)
    {
        m_bShowFlag = (nCmdShow == SW_SHOW);
        return (GetSafeHwnd()) ? ShowWindow(nCmdShow) : TRUE;
    }
  4. 출처 : Tong - luster님의 Tip통

 

 

윈도우 핸들(Window Handle, hwnd)로 클래스 이름(Class Name) 얻기

  1. int GetClassName( HWND hWnd, LPTSTR lpClassName, int nMaxCount );

 이걸 찾을려고 온 MSDN을 다 뒤졌다는.. ㅡ_ㅡa...

 

이 글은 스프링노트에서 작성되었습니다.

04 진보된 DLL 디스어셈블러 만들기

원문 :  http://kkamagui.springnote.com/pages/389711

 

들어가기 전에...

 

 요전에 DLL 디스어셈블리어 프로그램을 하나 만들었다. 그런데 단순한 디스어셈블러였을 뿐, 실제 분석에 필요한 뭔가가 없었다. 보통 분석할 때(내가.. ㅡ_ㅡ;;; 다른 사람은 모르겠음...) 함수 단위로 call, jmp 하는 부분 위주로 따라가면서 분석하는데, 그러한 기능이 없었다.

 그래서 이러한 함수 분석 기능을 추가하여 JMP, JL, JE 등등과 같은 JMP와 call을 분석해서 따라갈 수 있는 것은 따라가 주도록 만들었다. 아직은 파이썬에 익숙하지 않아서 시간이 좀 걸렸지만... 하면서도 나름 재미있었다는.. ㅠ_ㅠ...

 아래는 코드이다.

  1. # -*- coding: cp949 -*-
    from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits
    # c관련 함수를 사용하기위해 처리
    from ctypes import *
    import ctypes
  2. g_assemblyBuffer = [];
    g_gotoBufferArray = [];
  3. # Disassembly를 수행하는 함수
    def Disassembly( intAddress, curLevel, maxLevel ):
        buffer = ctypes.c_buffer( 4096 );
        libc.memcpy( buffer, intAddress, 4096 );
     
        l = Decode(intAddress, buffer, Decode32Bits)
        for i in l:
            # 아래와 같은 형식으로 출력된다.
            # 0x7c8309e1 8bff                 MOV EDI, EDI
            buffer = "0x%08x %-20s %s" % (i[0], i[3].upper(),  i[2])
            g_assemblyBuffer.append( buffer );
           
            # 만약 Jmp나 call 같은 명령이면 그 뒤에 오는 주소를 넣는다.
            if ( "J" in i[ 2 ] or "CALL" in i[ 2 ] ) and \
               ( "0x" in i[ 2 ] and "+" not in i[ 2 ] and "-" not in i[ 2 ] and \
                '*' not in i[ 2 ] and '/' not in i[ 2 ] ) and \
                curLevel < maxLevel:
                index = i[ 2 ].find( " " );
                stringAddr = i[ 2 ][ index + 1 : ];
                g_gotoBufferArray.append( [ curLevel + 1, stringAddr ] );
                #print "gotoBufferAdd", buffer;
               
            # 만약 ret거나 JMP 이면 그만 한다.
            if ( "RET" in i[ 2 ] ) or "NOP" in i[ 2 ]:
                g_assemblyBuffer.append( " " );
                return ;
  4. # Hex값을 Int로 바꾼다.
    def HexAddrToIntAddr( address ) :
        intAddress = 0;
        address = address[ 2 : ];
        for i in address :
            intAddress = intAddress << 4;
            if ord( i ) >= ord( '0' ) and ord( i ) <= ord( '9' ) :
                intAddress += int( i );
            else :
                intAddress += ( ord( i ) - ord( 'a' ) + 10 );
        return intAddress;
  5. # Indirect Addressing을 처리 함수
    def GetIndirectValue( address ):
        intAddress = 0;
        intAddress = HexAddrToIntAddr( address );
       
        #print "Indirect Address[%x]" % intAddress;
        buffer = ctypes.c_buffer( 4 );
       
        libc.memcpy( buffer, intAddress, 4 );
        #little Endian 이므로 역순으로 들어가있다. 이것을 바꿔준다.
        value = 0;
        mulValue = 0x00000001;
        for i in buffer :
            value += ( ord( i ) * mulValue );
            mulValue = mulValue << 8;
            #print "%x" %ord( i );
  6.     #print "%x" %value;
        return value;

  7. # 이미 Disassemby한 Address 인가 확인
    def IsAlreadyDisassembly( address ):
        for i in g_assemblyBuffer:
            if address in i[ : 10 ] :
                return 1;
        return 0;
  8. # c 런타임 라이브러리를 가지고 있음
    libc = cdll.msvcrt;
  9. dllName = raw_input( "DLL Name : " );
    functionName = raw_input( "Procedure Name : " );
    maxLevel = input( "Analysis Level( 0 ~ 20 ) : " );
  10. # 함수를 찾는다.
    dll = windll.LoadLibrary( dllName );
    functionAddress = dll.GetProcAddress( dll._handle, functionName );
  11. # 시작 Address를 넣는다.
    g_gotoBufferArray.append( [ 0, "0x%x" %functionAddress ] );
  12. for i in g_gotoBufferArray :
        [ level, stringAddress ] = i;
  13.     if '[' in stringAddress :
            intAddress = GetIndirectValue( stringAddress[ 1 : -1 ] );
            g_assemblyBuffer.append( "==============Level[%d] %s 0x%x============\n" \
                %( level, stringAddress, intAddress ) );
        else :
            intAddress = HexAddrToIntAddr( stringAddress );
            g_assemblyBuffer.append( "==============Level[%d] %s============\n" \
                %( level, stringAddress ) );
           
        # 점프하는 주소가 이미 Disassembly 되어있으면 안한다.
        if IsAlreadyDisassembly( "0x%x" %intAddress ) == 1 :
            g_assemblyBuffer.append( "0x%x Is Already Disassembly\n" %intAddress );
            continue;
       
        Disassembly( intAddress, level, maxLevel );
  14. # 출력하는 부분
    file = open( dllName + functionName + ".txt", "w" );
    print "Now Writing....."
    for i in g_assemblyBuffer:
        file.writelines( i + "\n" );
        #print i;
    file.close();
    print "Write Complete....."

 

 이것을 실행하면 DLL 이름과 함수 이름, 그리고 얼마나 깊이 call을 추적할 것인가가 나오는데, call 추적은 3~4 정도가 충분하다.

 아래는 kernel32.dll, OpenProcess, 3으로 함수를 분석한 결과이다. 아주 깔끔하게 잘 분석되었다 @0@)/~!!

==============Level[0] 0x7c8309e1============

0x7c8309e1 8BFF                 MOV EDI, EDI
0x7c8309e3 55                   PUSH EBP
0x7c8309e4 8BEC                 MOV EBP, ESP
0x7c8309e6 83EC 20              SUB ESP, 0x20
0x7c8309e9 8B45 10              MOV EAX, [EBP+0x10]
0x7c8309ec 8945 F8              MOV [EBP-0x8], EAX
0x7c8309ef 8B45 0C              MOV EAX, [EBP+0xc]
0x7c8309f2 56                   PUSH ESI
0x7c8309f3 33F6                 XOR ESI, ESI
0x7c8309f5 F7D8                 NEG EAX
0x7c8309f7 1BC0                 SBB EAX, EAX
0x7c8309f9 83E0 02              AND EAX, 0x2
0x7c8309fc 8945 EC              MOV [EBP-0x14], EAX
0x7c8309ff 8D45 F8              LEA EAX, [EBP-0x8]
0x7c830a02 50                   PUSH EAX
0x7c830a03 8D45 E0              LEA EAX, [EBP-0x20]
0x7c830a06 50                   PUSH EAX
0x7c830a07 FF75 08              PUSH DWORD [EBP+0x8]
0x7c830a0a 8D45 10              LEA EAX, [EBP+0x10]
0x7c830a0d 50                   PUSH EAX
0x7c830a0e 8975 FC              MOV [EBP-0x4], ESI
0x7c830a11 C745 E0 18000000     MOV DWORD [EBP-0x20], 0x18
0x7c830a18 8975 E4              MOV [EBP-0x1c], ESI
0x7c830a1b 8975 E8              MOV [EBP-0x18], ESI
0x7c830a1e 8975 F0              MOV [EBP-0x10], ESI
0x7c830a21 8975 F4              MOV [EBP-0xc], ESI
0x7c830a24 FF15 0C11807C        CALL [0x7c80110c]
0x7c830a2a 3BC6                 CMP EAX, ESI
0x7c830a2c 5E                   POP ESI
0x7c830a2d 0F8C B7710000        JL 0x7c837bea
0x7c830a33 8B45 10              MOV EAX, [EBP+0x10]
0x7c830a36 C9                   LEAVE
0x7c830a37 C2 0C00              RET 0xc
 
==============Level[1] [0x7c80110c] 0x7c93dd7b============

0x7c93dd7b B8 7A000000          MOV EAX, 0x7a
0x7c93dd80 BA 0003FE7F          MOV EDX, 0x7ffe0300
0x7c93dd85 FF12                 CALL [EDX]
0x7c93dd87 C2 1000              RET 0x10
 
==============Level[1] 0x7c837bea============

0x7c837bea 50                   PUSH EAX
0x7c837beb E8 7B17FDFF          CALL 0x7c80936b
0x7c837bf0 33C0                 XOR EAX, EAX
0x7c837bf2 E9 3F8EFFFF          JMP 0x7c830a36
0x7c837bf7 90                   NOP
 
==============Level[2] 0x7c80936b============

0x7c80936b 8BFF                 MOV EDI, EDI
0x7c80936d 55                   PUSH EBP
0x7c80936e 8BEC                 MOV EBP, ESP
0x7c809370 56                   PUSH ESI
0x7c809371 FF75 08              PUSH DWORD [EBP+0x8]
0x7c809374 FF15 6C10807C        CALL [0x7c80106c]
0x7c80937a 8BF0                 MOV ESI, EAX
0x7c80937c 56                   PUSH ESI
0x7c80937d E8 2EFFFFFF          CALL 0x7c8092b0
0x7c809382 8BC6                 MOV EAX, ESI
0x7c809384 5E                   POP ESI
0x7c809385 5D                   POP EBP
0x7c809386 C2 0400              RET 0x4
 
==============Level[2] 0x7c830a36============

0x7c830a36 Is Already Disassembly

==============Level[3] [0x7c80106c] 0x7c93fb3d============

0x7c93fb3d 6A 08                PUSH 0x8
0x7c93fb3f 68 78FB937C          PUSH 0x7c93fb78
0x7c93fb44 E8 79F2FFFF          CALL 0x7c93edc2
0x7c93fb49 64 A1 18000000       MOV EAX, FS:[0x18]
0x7c93fb4f 85C0                 TEST EAX, EAX
0x7c93fb51 74 11                JZ 0x7c93fb64
0x7c93fb53 8365 FC 00           AND DWORD [EBP-0x4], 0x0
0x7c93fb57 8B4D 08              MOV ECX, [EBP+0x8]
0x7c93fb5a 8988 F40B0000        MOV [EAX+0xbf4], ECX
0x7c93fb60 834D FC FF           OR DWORD [EBP-0x4], -0x1
0x7c93fb64 FF75 08              PUSH DWORD [EBP+0x8]
0x7c93fb67 E8 1D000000          CALL 0x7c93fb89
0x7c93fb6c E8 91F2FFFF          CALL 0x7c93ee02
0x7c93fb71 C2 0400              RET 0x4
 
==============Level[3] 0x7c8092b0============

0x7c8092b0 8BFF                 MOV EDI, EDI
0x7c8092b2 55                   PUSH EBP
0x7c8092b3 8BEC                 MOV EBP, ESP
0x7c8092b5 56                   PUSH ESI
0x7c8092b6 57                   PUSH EDI
0x7c8092b7 64 A1 18000000       MOV EAX, FS:[0x18]
0x7c8092bd 8B75 08              MOV ESI, [EBP+0x8]
0x7c8092c0 8BF8                 MOV EDI, EAX
0x7c8092c2 A1 C446887C          MOV EAX, [0x7c8846c4]
0x7c8092c7 85C0                 TEST EAX, EAX
0x7c8092c9 0F85 20090300        JNZ 0x7c839bef
0x7c8092cf 3977 34              CMP [EDI+0x34], ESI
0x7c8092d2 0F85 43040000        JNZ 0x7c80971b
0x7c8092d8 5F                   POP EDI
0x7c8092d9 5E                   POP ESI
0x7c8092da 5D                   POP EBP
0x7c8092db C2 0400              RET 0x4

 

 역시나 오늘도 파이썬 만쉐 @0@)/~!!

이 글은 스프링노트에서 작성되었습니다.

03 DLL 디스어셈블러 만들기

원문 : http://kkamagui.springnote.com/pages/388049

 

들어가기 전에...

 

 항상 분석을 하면 노가다가 많고 일일이 손으로 클릭을 해가면서 또는 눈으로 찾아가면서 분석을 해야 했다. 내가 원체 UI에 별로 관심이 없다보니 UI의 수준은 당연 극악의 콘솔형태... 물론 생긴건 GUI다.(그만큼 UI가 엉망이라는.... ㅡ_ㅡ;;;). 언젠가 한번 DLL 분석 툴을 파이썬으로 만들면 재미있겠다고 생각했는데... 파이썬용 디스어셈블러 자료는 찾아놓고 실제로 DLL을 파이썬으로 당겨야 하는데 엄두가 나지 않았다.

 오늘 잠시 짬을 내서 일하다 잠도 안자고 해봤는데, 의외로 C 함수를 쓰는게 간단하다.

 

DLL 및 C 타입의 변수 등등의 사용

 DLL을 로드해서 사용하는 코드를 C로 만들면 LoadLibrary() 함수를 호출하고 다시 GetProcAddress()를 호출하면 된다. 파이썬에서는 이것을 어떻게 할까? 블로그를 찾아보고 파이썬 마을을 둘러봤더니 ctypes라는 것이 보였다. Python 2.5 버전에서 표준 라이브러리에 포함되어있는 라이브러리라는데, 상당히 C 스타일 적이다.(아래는 Python 문서에 포함된 예제이다.)

  1. >>> from ctypes import *
    >>> print windll.kernel32
  2. <WinDLL 'kernel32', handle ... at ...>
    >>> print cdll.msvcrt
  3. <CDLL 'msvcrt', handle ... at ...>
    >>> libc = cdll.msvcrt

 windll에 보면 kernel32.dll, gdi32.dll 등등의 DLL의 정보를 포함하는 모듈이 들어있음을 알 수 있다. C 런타임 라이브러리도 사용할 수 있는데, cdll.msvcrt를 모듈을 이용하면 C 라이브러리를 사용할 수 있다.

  1. >>> from ctypes import *
    >>> libc.printf
    <_FuncPtr object at 0x...>
    >>> print windll.kernel32.GetModuleHandleA
  2. <_FuncPtr object at 0x...>

 재미있는 부분은 포인터도 사용할 수 있다는 것이다.(크아... 이제 완전 게임 끝났다.. @0@)/~ 이렇게 좋을 수가.. ㅜ_ㅜ)

  1. >>> from ctypes import *
    >>> i = c_int(42)
    >>> pi = pointer(i)
    >>> pi.contents
    c_long(42)

 더 자세한 사항은 파이썬 도움말을 참고하도록 하자.

 

디스어셈블러(Disassember) 모듈

 디스어셈블러 모듈은 파이썬 용으로 따로 개발된게 있다.(어찌나 다행인지.. ㅡ_ㅡ;;; 내가 가지고 있는걸로 파이썬에 붙일려고 했는데, 이렇게 되면 기절한다... ㅡ_ㅡ;;;)

 http://www.ragestorm.net/distorm/ 사이트에 가면 파이썬용 모듈을 받을 수 있고 간단히 pyd파일을 받아서 파이썬이 설치된 Lib 폴더에 넣기만 하면 편하게 사용할 수 있다. 아래는 위 홈페이지에서 설명해 놓은 사용 예제이다.

  1. Note: Save the file in %PYTHONDIR%\Lib\site-packages\
    from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits
    l = Decode(0x100, open("file.com", "rb").read(), Decode16Bits)
    for i in l:
     print "0x%08x (%02x) %-20s %s" % (i[0],  i[1],  i[3],  i[2])

 

 

디스어셈블러 작성

 이제 디스어셈블러를 작성할 준비가 모두 끝났다. 이제 이 모듈들을 이용하여 kernel32.dll에 있는 OpenProcess 함수를 디스어셈블리해보자.

  1. # -*- coding: cp949 -*-
    from distorm import Decode, Decode16Bits, Decode32Bits, Decode64Bits
    # c관련 함수를 사용하기위해 처리
    from ctypes import *
    import ctypes
  2. # c 런타임 라이브러리를 가지고 있음
    libc = cdll.msvcrt;
    # kernel32.dll을 여는 여러 방법
    #kernel32 = windll.kernel32;
    kernel32 = windll.LoadLibrary( 'kernel32.dll' );
  3. # OpenProcess를 얻는다.
    openProcessAddress = kernel32.GetProcAddress( kernel32._handle, "OpenProcess" );
  4. # 버퍼를 할당해서 디스어셈블리를 하기위해 버퍼에 넣는다.
    buf = ctypes.c_buffer( 4096 );
    libc.memcpy( buf, openProcessAddress, 4096 );
    #libc.memcpy( buf, 0x7c809b96, 4096 );
  5. l = Decode(openProcessAddress, buf, Decode32Bits)
    for i in l:
        # 아래와 같은 형식으로 출력된다.
        # 0x7c8309e1 (02) 8bff                 MOV EDI, EDI
        print "0x%08X (%02X) %-20s %s" % (i[0],  i[1],  i[3].upper(),  i[2])

 

 소스 코드는 위와 같이 아주 간단하다. 출력 결과는 아래와 같이 깔끔하게 나온다.

0x7C8309E1 (02) 8BFF                 MOV EDI, EDI
0x7C8309E3 (01) 55                   PUSH EBP
0x7C8309E4 (02) 8BEC                 MOV EBP, ESP
0x7C8309E6 (03) 83EC 20              SUB ESP, 0x20
0x7C8309E9 (03) 8B45 10              MOV EAX, [EBP+0x10]
0x7C8309EC (03) 8945 F8              MOV [EBP-0x8], EAX
0x7C8309EF (03) 8B45 0C              MOV EAX, [EBP+0xc]
0x7C8309F2 (01) 56                   PUSH ESI

 

 정말 멋지다.. ㅜ_ㅜ... 이렇게 몇줄 작성하지 않았는데 이런 결과가 나오다니... ㅡ_ㅜ... 정말 환상적이지 않을 수 없다. ㅜ_ㅜ...

 파이썬 만쉐이.. ㅜ_ㅜ...

 

첨부

 

 

TODO

  • 코드 내부에 있는 Jump 및 Call 관련 루틴을 따라가서 분석하는 기능 추가하기

이 글은 스프링노트에서 작성되었습니다.

 01 이글루스에서 덧글 뽑기

원문 :  http://kkamagui.springnote.com/pages/377691

 

들어가기 전에...


 

 오늘로써 파이썬을 배운지 약 하루(24시간??)이 지났다. 파이썬이 아주 편리한 언어임을 뼈저리게 깨닫기에는 충분한 시간이었다. 그래서 내친김에 프로그램을 하나 만들었는데, 그것은 바로 @0@)/~!!! 이글루스에서 덧글 뽑기!!!

 

 간혹 덧글을 확인하러 이글루스에 들어가는데... 이것을 파이썬으로 대체해서 덧글만 뽑는 것이다. @0@)/~!!! 전에 비슷한 것을 VC++로 한적이 있는데... 상당히 긴 코드였다. 하지만.. 파이썬으로 했을때는... ㅠ_ㅠ... 아래 코드가 전부이다.

  1. # -*- coding: cp949 -*-
    import urllib
  2. # euc-kr Text를 utf-8의 형태로 바꾼다.
    def ConvUtf8( buffer ):
        uniText = unicode( buffer, "euc-kr" );
        return uniText.encode( "utf-8" );
  3. # utf-8 Text를 euc-kr의 형태로 바꾼다.
    def ConvEucKr( buffer ):
        uniText = unicode( buffer, "utf-8" );
        return uniText.encode( "euc-kr" );
  4. # 버퍼에서 각 부분을 자른다.
    def ExtractItem( buffer, startTag, endTag ):
        replyStart = ConvUtf8( startTag );
        replyEnd = ConvUtf8( endTag );
       
        #print "개별 추출 함";
  5.     try:
            startIndex = buffer.index( replyStart );
            endIndex = buffer[ startIndex : ].index( replyEnd );
        except:
            return [ buffer, "" ];
       
        if startIndex != -1 and endIndex != -1:
            endIndex = startIndex + endIndex;
            #print "찾음", startIndex, endIndex;
           
            reply = buffer[ startIndex : endIndex + len( replyEnd ) ];
            return [ buffer[ endIndex + len( replyEnd ) : ], reply ];
           
        else:
            #print "찾을 수 없습니다";
            return [ buffer, "" ];
  6.        
    # 메인 코드
    urlHandle = urllib.urlopen( "http://kkamagui.egloos.com" );
    buffer = urlHandle.read();
    # 덧글 블럭만 뽑는다.
    [ buffer, extractBuffer ]= ExtractItem( buffer,"<DIV CLASS=MNTTL>최근 등록된 덧글</DIV>",
                                "<CENTER>");
    #print extractBuffer;
  7. # 개별 덧글을 다 찾는다.
    i = 1;
    while 1:
        print "";
        print i;
  8.     reply = "";
        [ extractBuffer, reply ] = ExtractItem( extractBuffer, "<a href=", "</div>" );
        if( reply == "" ):
            break;
        #print reply;
       
        # 덧글은 아래와 같이 되어있다.
        #<a href="http://kkamagui.egloos.com/3287761#9763213">
        #네 감사합니다 ^^</a><br/><span class="SMALL">by kkamagui at 07/20</span>
        #<br/><div style="margin:12px;"></div>
        item = "";
        [ reply, item ] = ExtractItem( reply, "http", '">' );
        item = item[ : -2 ];
        item = ConvEucKr( item );
        print "PageNumber####", item;
  9.     [ reply, item ] = ExtractItem( reply, "", "<" );
        item = item[ : -1 ];
        item = ConvEucKr( item );
        print "Contents###", item;
  10.     [ reply, item ] = ExtractItem( reply, 'SMALL">', "</span" );
        item = item[ 7 : -6 ];
        item = ConvEucKr( item );
        print "Writer###", item;
        i = i + 1;
  11. i = input( "끝입니다. 아무키나 눌러주세요" );

 뭐 간단히 보면 HTML 특정 부분 파싱이 전부이다. 약간 주의할 점은 utf-8 형태로 HTML 코드가 전송되는데, 이것을 콘솔 화면에 적당히 출력하기위해서 euc-kr로 바꿔주는 부분이 조금 특이하다.

 

 이로써 덧글을 확인하러 블로그에 들어가지 않아도 되게 되었다. ㅠ_ㅠ

 

파이썬 만쉐이 ㅠ0ㅠ)/~!!!

 

이 글은 스프링노트에서 작성되었습니다.

01 파이썬(Python) 팁

원문 : http://kkamagui.springnote.com/pages/391699

 

들어가기 전에...

 

리스트(List) 팁

  • list.append( x ) : 리스트의 마지막에 x 추가
  • list.sort() : 리스트 정렬
  • list.reverse() : 리스트 뒤집기
  • list.index( x ) : x가 있는 인덱스를 표시
  • list.remove( x ) : 리스트에서 제일 처음 나온는 x를 제거
  • list.pop() : 리스트의 맨 마지막 요소를 반환하고 제거 
    • list.pop( i ) : 리스트의  i 번째를 반환하고 제거
  • list.count() :  리스트의 아이템 개수를 반환
    • list.count( x ) : 리스트에 존재하는 x의 개수를 반환
  • list.extend( x ) : 리스트의 끝에 x를 확장

 

C 타입(type)의 변수 할당 및 DLL 사용

 ctypes라는 기본 라이브러리를 사용하면 C Style의 변수와 포인터를 사용할 수 있다. 크아~ 멋지다 @0@)/~!!

  1. from ctypes import *
    import ctypes
  2. # c 런타임 라이브러리를 가지고 있음
    libc = cdll.msvcrt;
  3. buffer = ctypes.c_buffer( 4096 );
    libc.memcpy( buffer, intAddress, 4096 );
  4. # 아래와 같이 DLL을 찾고 DLL에서 함수를 얻어오는 것도 가능
  5. dll = windll.LoadLibrary( dllName );
    functionAddress = dll.GetProcAddress( dll._handle, functionName );

 

if __name__ == "__main__"의 의미

 __name__은 현재 모듈의 이름을 가지고 있는 전역 변수이다. "python a.py" 와 같이 실행되는 경우 __name__의 값은 __main__ 이 된다. 하지만 import a.py 와 같이 사용될 경우 __name__ 의 값은 a가 된다.

 따라서 쉘로 실행되는 경우 실행해야 할 루틴이 있으면 아래와 같이 사용하면 된다.

  1. if __name__ == "__main__" :
    1. print "이것은 쉘로 실행되었을때 호출되는 루틴입니다"; 

 

모듈(Module)을 사용하는 여러가지 방법

  • import a : a.py에 있는 모듈들을 사용한다. a.xxx() 와 같은 방식으로 접근 가능하다.
  • from a imprt XXX, YYY :  a.py에 있는 모듈들 중에 XXX, YYY 함수를 쓴다. XXX(), YYY() 로 접근 가능하다.
  • from a import * : a.py 에 있는 모든 모듈을 쓴다.

 

하드디스크에 있는 오브젝트(Object) 파일 지우기

 참고 : http://docs.python.org/lib/os-file-dir.html

  1. # -*- coding: cp949 -*-
    import os;
    ExtList = [ '.obj', '.idb', '.o', '.pdb', '.scc', '.ilk', '.pch' ];

    buf = '';
    ext = '';
    for p, ds, fs in os.walk('d:\\'):
        #for d in ds:
        #    print os.path.join(p, d);
           
        for f in fs:
            buf = os.path.join(p, f);
            for i in ExtList :
                if len( f ) > len( i ) :
                    ext = buf[-len( i ) : ];
                    if ext.upper() == i.upper() :
                        print buf;
                        os.remove( buf );

 

 

이 글은 스프링노트에서 작성되었습니다.

00 파이썬(Python) 프로그래밍 연습

원문 :  http://kkamagui.springnote.com/pages/391698

 

들어가기 전에...

 

첫번째 프로그램 : input, if, while

  1. # 이건 내 첫번째 파이썬 플그램이다 @0@)/~!!
    # ;는 안붙여도 상관없다. ()도 없어도 상관없다.
    # 문자열은 ''로 감싸던지 ""로 감싸자.
    i = input("loop Count");
  2. # 들여쓰기된 부분은 다 if나 else의 영향을 받는단다.
    if i >= 10 :
        print "Too big"
        exit();
    else :
        print "OK"
  3. while i < 10:
        i = i + 1;
        print i,
       
    password = raw_input("Password");
    while password != 'User':
        print 'Password is not match'
        password = raw_input("Password Retype");
       
    print 'End'

 

두번째 프로그램 : 함수

  1. # 두번째 프로그램
    # 함수 생성 예제, 우와 간단하다 @0@)/~
    def abs( num ):
        if( num < 0 ):
            num = -num;
        return num;
  2. a = 3;
    a = abs( a );
    print a;
  3. a = -1;
    a = abs( a );
    print a;

 

세번째 프로그램 : list, range

  1. #세번째 프로그램 리스트 프로그램
    nameList = ["KKAMAGUI", "NANMAGUI", "NUMAGUI" ];
  2. index = input( "Input Index" );
    print nameList[ index ];
  3. nameList.append( "GAMAGUI" );
    print nameList;
  4. nameList.sort();
    print nameList;
  5. if( "NAMAGUI" in nameList ):
        print "NAMAGUI is in list"
    elif( "NANMAGUI" in nameList ):
        print "NANMAGUI is in list"
    else:
        print "NAMAGUI is not in list"
       
    print "List Length", len(nameList);
    print "NANMAGUI Index is", nameList.index( "NANMAGUI" );
    del nameList[ 2 ];
    nameList.remove( "KKAMAGUI" );
  6. print nameList;
  7. print range( 1, 10 );

 

네번째 프로그램 : module

  1. #네번째 프로그램
    #달력 출력 예제
    import calendar;
    from calendar import prcal;
  2. print "Calendar Print###############";
    calendar.prcal(2007);
    prcal(2007);

  3. #OS 관련...
    import os;
    #import os.path;
  4. print "OS Print###############";
    a = "";
    a = os.path.abspath( a );
    print a;
    a = os.path.dirname( a );
    print a;
  5. a = "";
    os.chdir( "d:/" );
    a = os.path.abspath( a );
    print a;
  6. #시간 관련
    import time;
  7. print "Time Print###############";
    timeString = time.ctime( time.time() );
    print timeString;

 

다섯번째 프로그램 : for, buffer, split

  1. #다섯번째 프로그램
  2. a = "abcdefghijklmn";
    for ch in a:
        print ch;
        print a.index( ch );
       
    print a[ 2 : 20 ];
  3. # 끝에서 하나를 뺀다.
    a = a[ : -1 ];
    print a;
    [ b, c ] = a.split( "f" );
    print b, "+", c;
  4. # 위의 String의 출력결과
    #cdefghijklmn
    #abcdefghijklm
    #abcde + ghijklm

 

여섯번째 프로그램 : urllib로 http 코드 얻기

 내 블로그에 접근해서 최근 덧글부터 뒷부분만 추려서 화면에 출력하는 예제(pyscriptor라는 에디터를 썼다가 한글이 안나와서 삽질.. ㅠ_ㅠ)/~!! ) 가만히 생각해 보니 뒤에서 찾으면 금방 될꺼 같기도 한데.. ㅋㅋ

  1. # -*- coding: cp949 -*-
    #여섯번째 프로그램
    import urllib
  2. urlHandle = urllib.urlopen( "http://kkamagui.egloos.com" );
    buffer = urlHandle.read();
    #print buffer;
  3. text = "최근 등록된 덧글";
    uniText = unicode( text, "euc-kr" );
    text = uniText.encode( "utf-8" );
    #print text;
  4. if text in buffer:
        index = buffer.index( text ) + len( text );
        print "First Index", index;
       
        while text in buffer[ index : ]:
            newIndex = buffer[ index : ].index( text );
            if( newIndex == -1 ):
                break;
            index = newIndex + index + len( text );
            print "New Index", index;
               
        print buffer[ index : ];
  5. else:
        print "FAIL";

 

일곱번째 프로그램 : 파일 입출력

 seek와 readline(), readlines() 테스트

  1. # -*- coding: cp949 -*-
    #일곱번째 프로그램
    sourceFile = open( "c:/test.txt", "r" );
    targetFile = open( "c:/testOutput.txt", "w" );
  2. buffer = sourceFile.read();
    print "읽기의 결과#######";
    print buffer;
  3. #seek
    sourceFile.seek(0, 0 );
  4. # 각 라인별로 라인 번호를 출력해 준다.
    i = 0;
    for buffer in sourceFile.readlines():
        # printf의 식처럼 쓸수 있다. %와 ,를 찍는 위치를 잘 보자
        newBuffer = "%02d %s" %( i, buffer );
       
        # 이 부분은 buffer안의 단어별로 루프를 도는 부분이다.
        for word in buffer.split():
            print word;
           
        targetFile.write( newBuffer );
        i++;
           
    sourceFile.close();
    targetFile.close();
  5. for i in range( 0, 10 ):
        print i;

 

이 글은 스프링노트에서 작성되었습니다.

 티스토리로 옮겨오고나서 제일 좋은 점이 자바 스크립트가 먹는다는 것입니다. ^^ 아는 후배가 티스토리로 옮겨오자마자 문법 하일라이트(Syntax Highlight)를 해주는 자바 스크립트를 올렸더군요(어찌나 기특하던지 ㅎㅎ). 자바 스크립트 소스코드는 http://code.google.com/p/syntaxhighlighter/ 에서 받을 수 있습니다.

 소스코드를 올리면 아래와 같이 이쁘게 나옵니다. ^^;;;


 방법은 html 모드로 전환한 뒤 아래와 같이 쓰거나


 아래와 같이 사용하면 됩니다. ^^


 자세한 설치 방법은 아래의 사이트를 참고하시면 됩니다. ^^
원문 : http://gyuha.tistory.com/?_best_tistory=best_blogger3


ps) javascript 파일을 모두 하나로 뭉쳐서 shTotal.js 파일로 만든다음 </body> 바로 위에 아래와 같이 넣어도 된다.


으으.. 오늘 또 실컷 코딩하고 Commit을 날렸는데, 얼래.. up-to-date에러가 난다. 당체 먼지.. ㅡ_ㅡ;;; 분명 내 Sandbox에 있는 놈은 최신판이고 소스는 나밖에 안건드리는데.. ㅡ_ㅡ;;

물론 소스를 Commit하기전에 백업했기땜시롱 크게 문제는 없는데, 자꾸 에라가 나는걸 보면 먼가 문제가 확실히 있는거 같다.

쩝쩝... 이를 어쩌징...아무래도 CVS 클라이언트가 버그가 있는거 같기도 하고, 내 사용법이 문제가 있는거 같기도 한데, 조심해서 쓰도록 해야겠다.

낼 가서 또 삽질을 해야겠군. 제길... ㅡ0ㅠ/~


ps) 흐흐 나중에 알고 봤더니 Sandbox의 파일들을 일정시간 이상 Update하지 않으면 나타나는 경고(?)같은 현상이었음.. ㅋㅋ

요즘 회사일때문에 한참 폭발코딩중이다. 그중에 하나가 스크립트 파일을 읽어서 조건에 맞는 어떤일을 해주는 스크립트 프로그램인데, 이게 아주 예술(??)이다.

학교를 얼마 다니지 못하고 일하러 끌려온(??) 나로서 오토마타니 컴파일러니.. 이런 것들에 대한 지식이 있을리 만무하다. ㅡ0ㅡ/~~ 그나마 예전에 x86 Assembler를 만든다고 책을 좀 봐놓은게 있어서 구현하는데 무리를 느끼는건 아니지만, 그래도 체
계적인 지식이 부족하다고 생각한다. (학교로 돌아가면 공부를 열심히 해야 겠군..)

그동안 회사에서 한 일들이 그렇게 복잡도를 요구하는것이 아니어서 시간에 쪼달릴 뿐 머리를 그렇게 많이 쓰게 만드는건 없었건만, 이건.. 아까 언급했듯.. 아주 예술이다.

내가 원체 프로그램 기능 추가하고 머하고 하는걸 싫어하기 때문에, 이번 프로젝트는 추가적인 기능을 하는 놈을 아예 스크립트로 빼버려서 텍스트만 밀어넣고 데이터만 몇개 주면 조건에 맞추어 추가적인 기능을 하는 놈을 실행해 버리는.. 그런 대찬 프로젝트를 계획했다.

한 3일해서 아래의 if/else if/else문을 약간의 문제를 남기로 처리하는데까지 구현을 했는데, 역시 머리가 윽시로 아프다. ㅡ0ㅠ/~~


 

if( ( ( NODEID == 1001 ) || ( NODEID == 1002 ) ) &amp;&amp; ( GROUPID == 1000 ) )
{
 if( ( NODEID == 1000 ) || ( GROUPID != 1000 ) )
 {
  if( NODEID == 1001 )
  {
   exec 1.exe;
  }
  else
  {
   exec 2.exe;
  }
  exec a.exe;
 }
 else if( GROUPID == 1000 )
 {
  exec b.exe;
 }
}
else if( ( GROUPID != 1000 ) &amp;&amp; ( ( NODEID &gt; 2000 ) &amp;&amp; ( NODEID &lt; 2002 ) )
{
 exec c.exe;
}
else if( GROUPID != 1000 )
{
 exec d.exe;
}
else
{
 exec e.exe;
}

exec f.exe;
kill PROCESSID;



음.. 적고나니 실제로 이것보다는 약간 더 복잡했던듯.. ㅡ_ㅡ;;;
사실 예전부터 이런걸 한번 해보고 싶었는데, 이번에 새로 프로젝트를 진행하게되어 한번 해볼 수 있었다.

역시나 복잡하군.. ㅡ_ㅡ;;;
조 30줄도 안되는걸 처리할라고 내가 아는 모든 지식은 총동원되고, 아아 빡심...
나의 내공은 아직도 멀었나 보다.

쩝쩝.. 좀 한가해지면 x86Assembly나 빨리 완성해야 겠다.
그람 먼가 내공이 좀 올라가겠지. ㅋㅋㅋ

얼마전부터 사용하기 시작한 CVS인데, 처음에는 약간 회의적인 생각도 들었지만, 쪼금더 생각해 보니, CVS가 있으므로해서 얻는 이득이 귀찮은거 보다 더 큰거 같아서 한참 익숙해 질려고 노력하고 있다.

머니머니해도 젤 좋은점은 그 골치아픈 버전관리에 크게 신경을 쓸일이 없다는 것인데, 이건 정말 큰 매력인거 같다.

안그래도 요즘 여기저기 사이트가 늘어서 소스관리에 정신이 없는데, 정말 적절한시기가 아닐까 싶다. 무엇보다 서버가 외부망에 있으니, 어디서든 인터넷만 되면 바로 다운받거나 업하거나 할 수 있다.

음.. 대단 대단 @0@/~~

+ Recent posts