michgan software studio

.net compact framework에는 (3.5까지도) System.Threading.Semaphore 클래스가 빠져 있습니다. 그렇다고 Windows Mobile에서 Semaphore를 쓸 수 없는 것은 아닙니다. CE용의 Win32 API로는 CreateSemaphore 와 ReleaseSemaphore가 존재하기 때문이죠.

따라서 Semaphore 클래스가 필요하다면 Win32 API를 P/Invoke로 호출하는 wrapper 클래스를 만들면 되겠습니다. 

이 소스에서는 System.Threading namespace에 직접 Semaphore를 추가하고 있습니다. 원래는 일개 평민은 System namespace를 건드리면 안 되겠지만, .net CF와 일반 프레임워크 간의 호환성을 위해서 침범했다고 합시다.

주의: 내부적으로 호출하는 Win32 함수에 대한 에러 처리가 되어 있지 않습니다.

코드 보기


저작자 표시 비영리 동일 조건 변경 허락


Windows Mobile 6.0 SDK 를 설치하셨다면, 아래 위치에 샘플 소스코드가 같이 설치됩니다.

64 비트 Windows의 경우:
C:\Program Files (x86)\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS

32 비트 Windows의 경우:
C:\Program Files\Windows Mobile 6 SDK\Samples\PocketPC\CS\GPS

위도, 경도, 고도 정도만 측정하는 경우라면 이 소스를 사용해도 괜찮을거 같네요.

저작자 표시 비영리 동일 조건 변경 허락


이번 글에서는 Windows Mobile 휴대폰에서 기지국 정보를 구하는 방법을 소개합니다.

3G 네트워크에서는 기지국 하나가 커버할 수 있는 반경이 2-3km 정도 되기 때문에 기지국 정보만으로도 제한적인 위치 정보 기반 서비스를 할 수 있습니다. 특히 지하에서는 전파 전달이 잘 되지 않기 때문에 곳곳에 기지국을 설치해야 하죠. 때문에 기지국 ID를 알아낼 수 있다면 사용자가 현재 어느 지하철 역에 있는지, 어느 쇼핑몰 안에 있는지, 어느 대학교 안에 있는지를 구분해서 서비스를 제공할 수 있을 것입니다. 지하철에서는 배차 정보나 행선지 찾기 서비스에 응용할 수도 있고, 쇼핑몰에서는 약도나 상품 검색 서비스를 제공할 수 있을 것입니다.

C#으로 Cell ID를 가져오는 방법은 생각보다 복잡합니다. P/Invoke 라는 기술을 사용해야 하는데요. C 언어와 32비트 CPU에 대한 이해가 없으면 헤맬 가능성이 높습니다.

스크린 샷은 다음과 같습니다. 심플하게 MCC/MNC, LAC, CellId 를 찍어 주고 있습니다.

MCC는 국가 코드입니다. 450번이 우리나라인가봐요.
MNC는 통신망 코드입니다. 5번은 SKT 입니다. 전체 코드는 http://en.wikipedia.org/wiki/Mobile_Network_Code 에 나열되어 있습니다.
LAC하고 CellId가 지역/기지국 정보입니다.


코드에 대한 설명을 시작하겠습니다. 

너무 길어서 가려둡니다


저작자 표시 비영리 동일 조건 변경 허락


요즘 모바일용으로 새로운 프로그램을 하나 구상하고 있는데, 데이타베이스 기능이 필수적으로 필요합니다. 물론 MS에서는 SQL Server Compact 3.5 for Windows Mobile을 공개하고 있지만, 핸드폰에 저장된 데이타베이스 파일을 PC로 옮겨서 사용하는 상황을 생각해보면, 핸드폰의 데이타와 동기화하기 위한 MS SQL Server 까지 필요하게 되더군요. 그럴 바엔 공개 라이브러리를 택하는 편이 낫겠다 싶어 조금 찾아보았습니다.

그러다 SQLite 라는 라이브러리를 발견하였습니다. 속도가 얼마나 빠른지는 모르겠지만, 완전히 public domain으로 공개된 소스라 제 마음에 쏙 들더군요. 다운 받을 수 있는 경로는 http://www.sqlite.org 입니다. 원 제작자들은 C 함수로 API를 만들어 놨습니다만, 훌륭한 사람들이 C# 용으로도 개발해 놨더군요. 이것 역시 public domain으로 공개되어 있습니다. http://sqlite.phxsoftware.com/ 에서 다운 받을 수 있습니다.

제가 테스트해본 쪽은 후자쪽입니다. 인스톨 파일을 다운 받아 설치하고, C# 프로젝트를 하나 만든 후에 Reference에 System.Data.SQLite 를 추가해줍니다.

아래는 테스트해볼 겸해서 직접 짜본 코드입니다.

데이타 베이스 파일을 하나 만들고, table을 생성한 뒤에, C:\windows 밑에 있는 파일 목록을 집어 넣습니다. 이후에 1024 ~ 1048576 의 크기를 갖는 파일을 SELECT 하는데, ORDER BY Size로 정렬합니다.

일단 이 정도만 되더라도 모바일에서 쓰기엔 괜찮을거 같다는 생각이 듭니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SQLite;
using System.IO;

namespace sqlitecs
{
    class Program
    {
        static void Main(string[] args)
        {
            //  Creating a database.
            SQLiteConnection.CreateFile("test.db");

            //  Open the database.
            using (var connection = new SQLiteConnection("Data Source=test.db"))
            {
                connection.Open();                 try
                {
                    //  Create table.
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "CREATE TABLE File (Name text, Size bigint, Modified datetime);";
                        command.ExecuteNonQuery();
                    }

                    //  Insert datas.
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "INSERT INTO File VALUES (?,?,?);";
                        SQLiteParameter p1 = new SQLiteParameter();
                        SQLiteParameter p2 = new SQLiteParameter();
                        SQLiteParameter p3 = new SQLiteParameter();
                        command.Parameters.Add(p1);
                        command.Parameters.Add(p2);
                        command.Parameters.Add(p3);

                        foreach (var fi in (new DirectoryInfo("c:\\Windows")).GetFiles())
                        {
                            p1.Value = fi.Name;
                            p2.Value = fi.Length;
                            p3.Value = fi.LastWriteTimeUtc;
                            command.ExecuteNonQuery();
                        }
                    }

                    //  Select files by its size between 1k and 1M
                    using (SQLiteCommand command = connection.CreateCommand())
                    {
                        command.CommandText = "SELECT * from File where Size >= 1024 and Size <= 1048576 order by Size;";
                        using (SQLiteDataReader reader = command.ExecuteReader())
                        {
                            while(reader.HasRows && reader.Read())
                            {
                                Console.WriteLine("{0}, {1}, {2}", reader.GetString(0), reader.GetInt64(1), reader.GetDateTime(2));
                            }
                        }
                    }
                }
                finally
                {
                    connection.Close();
                }
            }
        }
    }
}



저작자 표시 비영리 동일 조건 변경 허락
  1. 김상진  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 이번에 윈도우 모바일을 처음으로 개발하게 됬는데요

    c#에서 sqlite를 사용하려고 위와같이 따라 했는데 아래와 같은 오류가 나오더라구요.

    무슨 에러일 까요? wm쪽은 처음이다 보니 경험도 없어 잘 모르겠네요.ㅠㅠ

    --------------------------------------------------------------------
    'System.Data.Common.DbConnection' 형식이 참조되지 않은 어셈블리에 정의되었습니다.
    'System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'
    어셈블리에 참조를 추가해야 합니다.

    2010/02/22 10:48
  2. 세상살기  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사합니다..

    다만, 위의 Insert 문장은 트랜잭션 처리를 건별로 자동적으로 수행하기 때문에 속도가 무척 느립니다. c 버젼이나 php 버젼등에서는 begin ; loop {insert } ; commit ; 이런 순으로 바꾸어서 처리해서 속도를 올리죠.

    C#은 없는 줄 알았는데 아래처럼 하면 insert 속도가 대략 200배 정도 차이가 날 정도로 빨라지네요..;;;

    using (SQLiteTransaction dbTrans = connection.BeginTransaction())
    {
    using (SQLiteCommand command = connection.CreateCommand())
    {
    command.CommandText = "INSERT INTO File VALUES (?,?,?);";
    SQLiteParameter p1 = new SQLiteParameter();
    SQLiteParameter p2 = new SQLiteParameter();
    SQLiteParameter p3 = new SQLiteParameter();
    command.Parameters.Add(p1);
    command.Parameters.Add(p2);
    command.Parameters.Add(p3);
    foreach (var fi in (new DirectoryInfo("c:\\Windows")).GetFiles())
    {
    p1.Value = fi.Name;
    p2.Value = fi.Length;
    p3.Value = fi.LastWriteTimeUtc;
    command.ExecuteNonQuery();
    label1.Text = "B-INSERT TABLE";
    }
    }
    dbTrans.Commit();
    }

    2010/03/25 15:18

직접 만든 인코딩 변환 라이브러리의 속도를 테스트해보았습니다. MBCS에서 UNICODE로 혹은 그 반대로 매핑하는 2차원 배열을 만들어 놓고 참조하는 방식입니다. 테이블 데이타를 static 형식으로 코드에 삽입시켰더니, exe 파일이 400-500 kb 정도가 되는군요. (코드 페이지 = 932, 936, 949, 950)

꽁수를 안 쓰고 2바이트 매핑 테이블을 그대로 탑재시키면, 1M 정도였을 겁니다. 지금은 바이트 범위를 조사해서 필요 없는 부분은 저장하지 않는 방식을 사용합니다. gz 스트림으로 압축시키면 실행 파일의 크기를 더 줄일 수 있기(333k에서 256k로)는 한데, 용량 감소폭 대비 번거로움을 따져보면 그냥 쓰는게 나을거 같습니다.

아무튼 직접 만든 인코딩 변환 라이브러리의 속도를 비교해봅시다. 비교 대상은 System.Text.Encoding 객체입니다. libiconv도 하고 싶으나 솔직히 귀찮습니다.

400kb 정도 되는 한글 텍스트 파일(243943 유니코드 글자)을 가지고 MBCS로 인코딩하고 디코딩하는 속도를 재었습니다. 5번 재어서 평균 냈고 사용한 기종은 소니 엑스페리아 X1, Windows 6.1 한글판입니다. 컴파일러는 VisualStudio 2008 sp1 + Windows Mobile 6 SDK 입니다.

단위는 msec.
  내거 Enc  내거 Dec  C# Enc  C# Dec 
 Unicode   19 19   25    25 
 UnicodeBE   49 51  132    72 
 UTF-8   60  86    88  134 
 CP949 160  77  214    52 

Encoding: Unicode -> MBCS. 즉, WideCharToMultiByte
Decoding: MBCS -> Unicode, 즉, MultiByteToWideChar

의외로 MS가 제공하는 라이브러리가 느렸군요.

생각보다 C#이 빠르다는 사실을 알 수 있습니다. 물론 빠른 속도를 내기 위해서 코드를 좀 간결하게 바꾸는 작업을 진행하긴 했지만 말입니다. 이것이 바로 JIT의 힘입니다. 좋군요.
저작자 표시 비영리 동일 조건 변경 허락


프로젝트를 진행하다 보면 텍스트 인코딩을 변환해야하는 경우가 있습니다. 일반적으로 win32 환경이라면 MultiByteToWideChar() 이라는 함수를 사용해서 유니코드 문자열로 변환하게 됩니다. 리눅스나 스크립트 언어 환경이라면 iconv() 함수를 사용하게 되겠죠.

문제는 win32 환경인데 MultiByteToWideChar() 함수가 통하지 않는 환경입니다. 예를 들어 PocketPC 에서는 OS 언어와 UTF-8 인코딩에 대한 변환만 지원할 뿐입니다. 이럴 경우에는 어떻게 하느냐? 가 이번 포스트의 주제입니다.


첫번째 방법은 제가 작년에 사용한 "libiconv 라이브러리 포팅하기"입니다. 말 그대로 win32 환경으로 포팅하는 방법이죠. 원래 libiconv는 win32 네이티브 컴파일을 지원했기 때문에 포팅하기가 생각보다 쉽습니다. 관심 있으신 분들은 해당 포스트로 가셔서 보시기 바랍니다.

두번째 방법은 지금 제가 적용하고 있는 방법입니다. "내 프로젝트에는 GPL처럼 독이 묻은 라이센스를 포함할 수 없다!"라고 생각하신다면 직접 만들어 쓰시는 수 밖에 없겠죠. libiconv의 경우에는 다행히 LGPL 라이센스이긴 합니다만, 그래도 왠지 고기 먹고 이를 닦지 않은 듯 개운하지는 않습니다.

직접 만든다고 했을 때 가장 문제가 되는 점은 MBCS 문자에서 유니코드 문자로의 코드 매핑을 알아내는 점입니다. 이 테이블만 구할 수 있다면 텍스트 변환 라이브러리의 작성은 거의 70% 정도가 끝났다고 볼 수 있습니다.

그리고 다행히도 코드 변환 테이블이 인터넷에 공개되어 있습니다. 무려 마이크로소프트 사가 제공한 정보입니다. 아래의 링크는 cp949(=한국어)에 대한 매핑 테이블입니다. cp932(=일본어)나 중국어도 쉽게 찾을 수 있습니다.
http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT

테이블은 일관성 있는 규칙으로 작성되어 있기 때문에 이것을 읽어들이는 유틸은 손쉽게 작성할 수 있습니다.

다음은 위 자료를 읽기 위한 간단한 perl 스크립트입니다. 이 세상에서 텍스트 파일을 이렇게 간단하게 파싱할 수 있는 언어는 perl 밖에 없을거 같네요.

#!perl -w

open(X, "<cp949.txt" );
my @x = grep {!/^\#/} <X>;
close( X );
chomp( @x );

foreach my $y ( @x )
{
    my @a = split( /\t/, $y );
    if ( $#a == 2 )
    {
        printf( "%s -> %s\n", $a[0], $a[1] );
    }
}

cp949 에 대해 적용해보면 17304 개의 매핑을 얻을 수 있습니다.

이 데이타를 토대로 1:1 매핑하는 코드를 작성하는 것은 별로 어렵지 않을 것입니다.

C# 으로 cp932, 936, 949, 950 에 대해 MBCS to UNICODE 매핑 테이블을 작성한 후에 컴파일 하면 대충 160kb 정도의 바이너리가 만들어집니다.

이렇게 만든 변환 코드와 win32 API와의 속도 차이에 대해서는 다음에 기회가 있으면 포스팅하도록 하지요.

저작자 표시 비영리 동일 조건 변경 허락


이번 포스트에서는 Google의 Data API의 동작 구조에 대해서 설명을 하겠다.

제일 첫 글에서 소개했던 Calendar 이벤트를 받아오는 코드를 다시 살펴보자.
this.calendarService = new CalendarService("GoogleCalendarSample");
this.googleAuthToken = loginDialog.AuthenticationToken;  // 위에서 받은 토큰
this.calendarService.SetAuthenticationToken(this.googleAuthToken);

EventQuery query = new EventQuery("http://www.google.com/calendar/feeds/default/private/full/");
EventFeed feed = this.calendarService.Query(query);
foreach (EventEntry entry in feed.Entries)
{
    // list 업데이트
}

현재 3개의 객체가 눈에 띄인다. CalendarService, EventQuery, EventFeed 가 바로 그것인데, 이 3종의 클래스가 구글의 Data API에서 가장 핵심적인 역할을 담당한다.

구글의 Data API는 우리가 만들 클라이언트와 구글 서버 간의 Request-Reply 프로토콜을 정의한 것이다. 이 프로토콜은 XML 형식으로 정의되어 있는데, 보통 XML은 생성하거나 파싱하기가 까다로운게 아니라 귀찮기 때문에 클래스 형식으로 구현한 것이 바로 EventQuery 클래스와 EventFeed 클래스가 되겠다. Query 류에서는 Request를 담당하고, Feed 류에서는 Reply를 담당하게 된다.

그렇다면, Service는? Query에서 만들어진 XML을 소켓을 통해 보내고, 응답을 받은 후에 Feed 객체를 만들어서 반환하는 역할을 해준다.

이 3종 클래스 세트가 있기 때문에 .net 에서 편안하게 Google 서버에 저장된 데이타를 꺼내올 수 있는 것이다.
저작자 표시 비영리 동일 조건 변경 허락


목차:
1. Google Data API를 사용해보자
2. Google Data API의 구조
3. Calendar
   3-1. Event Entry 살펴보기
   3-2. Calendar List 가져오기
4. Contacts
   4-1. Contact Entry 살펴보기
   4-2. Contact Group 가져오기
저작자 표시 비영리 동일 조건 변경 허락


gmail.com 에 접속하면 볼 수 있는 Contacts(연락처) 역시 Google data api 로 가져올 수 있다. 이번에는 Google 프로젝트 중에서 Contacts 를 선택해서 생성하면 된다. 로그인하고 Contact entry를 가져오는 코드는 Calendar event를 가져오는 코드와 클래스 이름만 바뀌었을 뿐, 거의 동일하다. 그래서 자세한 내용은 생략하기로 하고, contacts 에 있는 필드를 가져오는 방법만 살펴보기로 하자.


구글에서 입력 가능한 contacts의 필드는 아래의 스크린샷과 같다. 여기에 Group 이 빠져 있을 뿐이다. 김영진은 본인의 친구다.

위에 보이는 필드를 가져오는 방법은 아래와 같다.
 이름 ContactEntry.Title.Text 
 직위 및 회사 ContactEntry.Organizations 컬렉션, Organization 객체를 받아서 Title과 Name 필드를 보면 됨.
 Email ContactEntry.Emails 컬렉션, Email 객체를 받아서 Email.Address 를 보면 됨.
 전화번호 ContactEntry.Phonenumbers 컬렉션, PhoneNumber 객체를 받아서 Value 필드를 보면 됨. 참고로, Home, Other, Work와 같은 필드가 있어서 어떤 전화 번호인지 알 수 있다.
 집 주소 ContactEntry.PostalAddresses 컬렉션, PostalAddress 객체를 받아서 Value 필드를 보면 되는 듯.
 소속 그룹 ContactEntry.GroupMembership 컬렉션, GroupMembership 객체를 받아서 보면 안 됨. 

소속 그룹이 좀 복잡하다. GroupMembership 객체에는 그룹의 명칭이 담겨 있지 않다. 대신 GroupsQuery를 보낼 주소를 담고 있는데, 아래와 같은 코드로 그룹의 명칭을 알아 올 수 있다.

// GroupMembership group;
GroupsQuery q = new GroupsQuery(group.HRef);
GroupsFeed f = contactsService.Query(q);

foreach( GroupEntry e in f.Entries )
{
    hash[group.HRef] = e.Title.Text;

위의 코드에서는 Query 회수를 줄이기 위해서 Hashtable을 생성해서 결과를 캐시하고 있다.

사용자가 직접 생성한 Group 외의 SystemGroup 즉, Contacts, Family, Co-workesr, Friends 그룹으로 분류하면 어디에 소속한 것인지 알아낼 길이 없는 듯?
저작자 표시 비영리 동일 조건 변경 허락

 

지난 번 글을 통해서 Google Calendar 항목들을 가져오는 방법을 살펴 보았다. 이번에는 Calendar 항목에 어떤 필드가 있는지 살펴볼 차례다. 일목 요연하게 설명하고 있는 페이지가 없어서 조금 고생했다.

구글에서 칼렌다 이벤트를 살펴보면 아래와 같은 필드를 갖는다.


여기 있는 필드는 다음과 같이 조회할 수 있다.

 내용 EventEntry.Title.Text 
 시간 EventEntry.Times 컬렉션
When 이라는 객체에서 종일은 AllDay, 시작 시간은 StartTime 멤버를 보면 됨.
 반복 빈도 아직 분석 안함
 장소 EventEntry.Locations 컬렉션
Where 라는 객체에서 ValueString을 보면 됨. Google map과 연동되는 멤버도 있음.
 캘린더 캘린더는 Query의 주소를 만들 때 이미 지정했음. 
 설명 EventEntry.Content.Content

어디에 데이타가 들어 있는지만 알면 조회하는 코드를 짜는 것은 금방이다.
저작자 표시 비영리 동일 조건 변경 허락

1 2 
BLOG main image
michgan software studio
Copyright (c) 1992-2008 michgan
by michgan

카테고리

분류 전체보기 (60)
release (34)
document (23)
review (1)