Archive for November, 2010

iTunes에서 Podcast 구독 목록 백업 및 복구하기

iTunes의 podcast 구독목록 백업은 생각보다 간단하다. 하지만 문제는 자세히 보지 않으면 찾기가 힘들다는 것이 문제다. DVD Writer가 있다면 그냥 모두 백업하고 복구하면 간단한 이야기 일 수 있지만, 그렇게까지 하고 싶지는 않다. 그냥 구독 목록만 다른데서 복구되고, 업데이트된 것만 받으면 되니깐..

구독목록 백업을 하려면 왼쪽 패널에서 Podcast를 선택하고 컨택스트 메뉴에서 내보내기를 선택한다.

여기까지는 뭐 다 아는 이야기일 것고, 나도 구글링을 통해서 찾아보기 전까지는 이렇게 했었다. 메뉴를 선택하고 그냥 백업해보면 좀 당황스럽다. 그냥 Podcast 목록이 백업된다. 내가 원한건 이게 아니었는데, 구독 목록이었는데... 그렇치 여기까지 나도 그랬었다.

자 다시 백업파일 입력하는 대화상자에서 파일 타입을 OPML로 선택하고 저장해보자. 그리고 저장한 파일을 텍스트 편집기로 열어 확인해보자. 내가 원한건 이거다!

그런데 다시 이것을 복구하는 방법은?.. 열심히 메뉴나 기타를 뒤져보지만 역시 나오지 않는다. Apple이 자주 사용하는 기능들은 편하지만, 좀 특별하거나 복잡한 기능들은 꼭꼭 숨겨놓는다.

그냥 그 백업 파일을 iTunes로 드래그하면 podcast가 아주 간단하게 추가된다. 아주 허무할 정도로...

PS. iTunes U는 적용이 되지 않는다. 백업은 되는데 백업된 파일을 추가할 수가 없다. 이건 수동으로 해야하는 모양이다. ㅡㅡ;

PS1. 스마트 재생목록도 목록이 아니라 설정들이 저장되면 좋겠는데, 그게 안된다. ㅡㅡ

PS2. 스마트재생목록은 Podcast 처럼 내보낼때 XML 형식으로 하면 되는 것 같다. 가져오기는 파일 -> 보관함 -> 재생목록 가져오기를 하면 된다.


iOS SDK 설치중

여기를 참고하여 VMWare에 Snow Leopard를 설치하고, iOS SDK 설치하는 중.... 아공.. 정말로 오래걸린다...

엇.. 글을 쓰는 도중에 설치가 완료되었다!!! ㅋㅋ


Snow Leopard에서 Safari가 죽어버릴 때..

하여간 우여곡절 끝에 Snow Leopard로 업데이트 했습니다. 기분 좋게 여기저기 Safari로 웹 서핑을 즐기는데, 사파리가 죽어버리는 현상이 자주 발생합니다.

대략 보니 자바스크립트가 많이 돌아가는 곳에서 그럽니다. 이거 참..~ gmail, sf.net... 등등 자주가는 곳에서 이러니 정말 답답합니다. 역시나 구글링 했더니 답을 알려주시는군요.

http://forums.macrumors.com/showthread.php?t=790540 를 보면 사파리를 32비트 모드로 설정하면 없어진다고 하네요.

32비트 모드로 설정하니 아주 나이스하게 문제없이 돌아갑니다. 그리고 Snow Leopard Cache Cleaner를 사용하면 이 현상이 없어진다고 하네요. 이건 한번 시도해봐야겠습니다.

ps. 32비트 모드가 훨씬 동작이 부드럽군요.


Firefox 4.0 Beta 7으로 업데이트 했더니..

상태바가 사라졌다. 뭐냐.~

메뉴를 뒤져봐도 상태바를 찾을 수 없다. 상태바에서 돌아가는 몇가지 Add-On들은 어떻게 할겨...

추가:

구글에 물어보니 간단하게 알려주십니다. "부가 기능 모음"이라는 다소 난해한 이름으로 바뀜...


TDBXMetaDatabaseColumnNames.SupportsParameterMetadata

pgdbx4를 Delphi XE로 돌려봤습니다. 그런데 "Invalid Ordinal" 에러가 튀어나오는 군요. 이런 전혀 유추할 수 없는 에러메시지 싫습니다.

하여간 찾아보니 TDBXMetaDatabaseColumnNames.SupportsParameterMetadata 가 추가되었군요. 간단하게 넣어서 이 작업은 완료!!

그러나 테스트 케이스가 하나 오류나는 것이 있어서 그것 확인하고 다시 릴리스 하렵니다.


pkg_version: Comparison failed

ports에 업데이트할 것이 있는지 확인하려고 pkg_version -vIL= 을 실행했다. 그런데 갑자기 Comparison failed라는 메시지가 나오면서 아무런 것도 안한다.

수동으로 portsnap fetch update 하면 모든 포트가 없데이트 되었다고 나온다. 뭔가 잘못된거다. 여기저기 뭔가하고 뒤저보다가... 무심결에 ls -l /usr/ports 하는 순간..

허걱 /usr/ports/INDEX 파일의 사이즈가 0이다. 뭐 이건 인덱스 다시 빌드하면 되는 것이고.. 또 뒤져보니 /var/db/portsnap 파일의 일부가 사라졌다. 이건 뭣인가? 그래 언젠가부터 ports의 nfs 서버가 이상하긴 했지, 뭐 그녀석 이제 생을 마감할 때도 되었고, 그냥 ports나 서비스 해라 하고 놔두고 있었는데, 그녀석이 하드웨어적인 이유로 재부팅 되면서 몇몇 파일을 날려버렸던 거다.

원인은 파악됐고... 해결은 간단하다 /var/db/portsnap 지워버리고 portsnap fetch extract 끄읏...

근데 그 에러난 머신은 어떻하냐구? 말했잖아! 조용히 생을 마감하도록 호스피스 병동에 격리시킬 생각이지요. 이렇게 가끔 ports nfs 서비스 하다가... 그게 잦아지면 그냥 생을 마감시키려해... 그래 그녀석 서버로서 지난 8년간의 새월은 참으로 오랜 세월이었지???

너도 젊었을 때는 스펙으론 꽤나 날리던 녀석이었는데, 물론 대형 형님들은 제외하고 말이다... 그리고 너와 참 많은 일을 했었는데, 아쉽다.... 그래 그래... 나도 그 정때문에 널 아직까지 버리지 못하고 있는걸거야...


Windows Media Player on Windows 7

네이버에 축구영상이 올라왔길레 보려고 했더니.. Windows Media Player가 필요하답니다. 그래서 설치하라는 링크 클릭하고 다운로드 하려는데, 아무리 뒤져봐도 Windows 7용이 없습니다. 거기서 찾은 말은 Windows 7 대부분의 에디션에 포함되어 있습니다(다운로드 제공되지 않음). 이라는 말 뿐..

근데 내껀 왜? Windows Media Player가 설치되어있지 않은겨? 하고 봤더니.. "Windows 기능 사용/ 사용 안 함"에서 제거가 되어있더군요. ㅡㅡ


VirtualTree updated to 4.8.7 … 그리고..

오늘 보니 VirtualTree가 4.8.7로 10월 말에 업데이트 되었네요. 그래서 가차없이 업데이트 했는데  TVirtualStringTree를 를 그대로 쓰지않고 몇 가지 수정을 가해서 사용하고 있던 부분에서 오류가 발생합니다.

DoHeaderClick의 파라미터가 달라지고 더이상 virtual이 아니다.

procedure DoHeaderClick(HitInfo: TVTHeaderHitInfo); virtual;
procedure DoHeaderClick(Column: TColumnIndex; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

파라미터 달라진 것은 이해하겠습니다. 그런데 virtual 이었던 것이 virtual이 아닌것으로 돌아가다니.. 다른 DoXXX 함수들은 virtual인것을 보니 아마도 실수를 한 듯 합니다.

결국은 DoHeaderMouseUp을 override 하는 것으로 처리했지요

procedure TVirtualStringTreeEx.DoHeaderMouseUp(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  Column: Integer;
begin
  inherited;

  // Convert vertical position to local coordinates.
  Inc(Y, Header.Height);
  Column := Header.Columns.ColumnFromPosition(Point(X, Y));

  DoSometing;
end;

TimeSync를 업데이트 하다가…. Windows Service/ NamedPipe

TimeSync가 Windows 7에서 동작하지 않았던 문제를 최근에서야 알고, 그 문제를 수정하면서 했던 일 몇가지 기록해봅니다.

SetLocalTime과 권한

윈도우즈 시간 설정은 SetLocalTime API를 사용합니다. 이게 Windows Vista/7 에 오면서 보호된 시스템 리소스로 변경되면서 SE_SYSTEMTIME_NAME 권한을 필요로합니다. 관리자 권한으로 실행하도록 하면 되겠지만, TimeSync 원래 목적이 자동으로 시간을 맞추는 것였기 때문에 사용자에게 관리자 권한 물어보는 다이얼로그가 나오는 상황을 원치 않았습니다.

그래서 결국은 시간 동기화 서비스를 만들고, 클라이언트 프로그램으로 서비스를 호출하는 것으로 변경했습니다. 그렇습니다. 배보다 배꼽이 더 커졌습니다.

Windows Service

서비스 등록 및 해지

서비스의 등록과 삭제는 관리자 권한이 필요합니다. 관리자 권한으로 서비스 프로그램에 옵션을 주어서 실행합니다.

ServiceApplication.exe /INSTALL [/SILENT]
ServiceApplication.exe /UNINSTALL [/SILENT]

서비스 시작 및 종료

net.exe start ServiceName
net.exe stop ServiceName

Delphi로 Windows Service 작성하기

서비스 작성

Delphi로 서비스를 작성하는 것은 아래 참조 링크에 아주 자세히 설명이 되어있습니다. 서비스지만 서버라고 보시면 됩니다. 그래서 일반적인 서버를 작성하는 개념으로 작성해야합니다.

서비스는 서비스 Thread를 하나 만들고, 서비스의 각각의 이벤트에 해당 Thread를 컨트럴 해줍니다.

  1. OnServiceStart에 서비스 Thread 시작
  2. OnServiceStop에 서비스 Thread 종료
  3. OnServiceContinue, OnServicePause에 서비스 Thread Start/ Suspend
  4. OnServiceExecute에서 해당 요청을 처리하면 됩니다.

이렇게 해서 서비스의 대략적인 소스는 다음과 같습니다.

procedure TTimeSyncService.ServiceContinue(Sender: TService;
  var Continued: Boolean);
begin
  ServiceThread.Start;
  Continued := True;
end;

procedure TTimeSyncService.ServiceExecute(Sender: TService);
begin
  while not Terminated do
    ServiceThread.ProcessRequests(False);
end;

procedure TTimeSyncService.ServicePause(Sender: TService; var Paused: Boolean);
begin
  ServiceThread.Suspended := True;
  Paused := True;
end;

procedure TTimeSyncService.ServiceStart(Sender: TService; var Started: Boolean);
begin
  TimeSyncServiceThread := TTimeSyncServiceThread.Create(False);
  Started := True;
end;

procedure TTimeSyncService.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  TimeSyncServiceThread.Terminate;
  Stopped := True;
end;

이 서비스 Thread에서 클라이언트와 통신을 대기하면 됩니다. 처음 서비스를 작성하려고 생각했을때 서비스니깐 뭔가 클라이언트와 통신하는 방법을 기본으로 제공해줄까? 하고 생각을 했었지만 아무것도 없습니다. 그 방법은 작성하는 사람이 TCP/IP, PIPE, MemoryMap 등등 제공하는 IPC 중에서 편한것 선택해서 하면 됩니다.

여기서는 그냥 가장 간단할 것 같은(하지만 또 다른 복병때문에 삽질 했지만요)  NamedPipe를 이용했습니다.

Service Thread

TimeSync는 여러 크라이언트의 동시 연결 또는 여러 복잡 다단한 명령들이 필요하지 않기에 요청이 있으면 바로 수행하고 연결을 끊어버리는 구조로 구성했습니다. HTTP와 비슷하게요.

대락적인 코드는 다음과 같습니다.

procedure TTimeSyncServiceThread.Execute;
begin
  // 통신할 파이프 생성
  PipeHandle := CreateNamedPipe(PIPE_NAME,
      PIPE_ACCESS_DUPLEX,
      PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
      PIPE_UNLIMITED_INSTANCES,
      BUFSIZE,
      BUFSIZE,
      NMPWAIT_USE_DEFAULT_WAIT,
      @sa);

  try
    while not Terminated do
    begin
      // 서비스 클라이언트의 연결 대기
      ConnectNamedPipe(PipeHandle, nil);

      // 클라이언트 패킷 수신
      FileRead(PipeHandle, Buffer, ....);

      // 클라이언트의 요청에 따라서 뭔가 수행
      DoSometing(Buffer);

      // 처리 결과를 클라이언트에 보내기
      FileWrite(PipeHandle, Buffer, ....);

      // 연결 해제하고 다른 클라이언트의 연결을 기다림...
      DisconnectNamedPipe(PipeHandle);
    end;
  finally
    CloseHandle(PipeHandle)
  end;
end;

클라이언트에서 마찬가지로 파이프에 연결해서 처리합니다.

procedure Sync;
var
  PipeHandle: THandle;
  PipeStream: THandleStream;
  Command, Result: DWORD;
begin
  PipeHandle := CreateFile(PIPE_NAME,
        GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);

  // 연결할 파이프가 부족하면 잠시 대기합니다. 하지만 TimeSync에서는 가능성이 극히 적음
  if GetLastError = ERROR_PIPE_BUSY then
  begin
    if not WaitNamedPipe(PIPE_NAME, 2000) then
      raise Exception.Create(StrServiceConnectFailed);
  end;
  try
    FileWrite(PipeHandle, Buffer, ....);
    FileRead(PipeHandle, Buffer, ....);
  finally
    DisconnectNamedPipe(PipeHandle);
    CloseHandle(PipeHandle);
  end;
end;

아주 아주 간단한 구조로 구성이 되었고, 잘 될거라고 믿고 있었습니다.

Named Pipe로 서비스와 통신하기

하지만 항상 복병이 있지요. 우성 서비스로 작성하기 전에 일반 어플리케이션으로 파이프 서버 작성하고 했더니 잘 되어서 서비스로 적용하는데, 파이프로 연결하는 부분부터 안됩니다. 연결 자체가 안됩니다.

원인은 파이브를 기본으로 만들면 그 프로세스를 생성한 사용자만 그 파이브에 접근할 수 있습니다. Service는 관리자 권한으로 실행되는 것이니, 당연히 일반 사용자로 실행되는 클라이언트의 접근을 거부해버립니다.

그래서 서버의 파이프를 생성할때 CreateNamedPipe의 마지막 인자로 SecurityAttribute를 설정해야합니다. 아래는 누구나 다 그 만들어진 파이프에 연결이 가능하게 하는 SecurityAttribite를 만드는 코드입니다.

var
  PipeHandle: THandle;
  sa: TSecurityAttributes;
  sd: TSecurityDescriptor;
begin
  InitializeSecurityDescriptor(@sd, SECURITY_DESCRIPTOR_REVISION);
  SetSecurityDescriptorDacl(@sd, True, nil, False);

  FillChar(sa, SizeOf(sa), 0);
  sa.nLength := SizeOf(sa);
  sa.lpSecurityDescriptor := @sd;
  sa.bInheritHandle := False;

  PipeHandle := CreateNamedPipe(PIPE_NAME,
      PIPE_ACCESS_DUPLEX,
      PIPE_TYPE_MESSAGE or PIPE_READMODE_MESSAGE or PIPE_WAIT,
      PIPE_UNLIMITED_INSTANCES,
      BUFSIZE,
      BUFSIZE,
      NMPWAIT_USE_DEFAULT_WAIT,
      @sa);

이렇게 했더니만 연결이 잘 됩니다. 이제 프로그램은 완성했습니다. ^^;

InnoSetup으로 설치 스크립트 만들기

이렇게 만들고 보니 프로그램을 실행하기 위해서 먼저 해결해야할 것이 있네요.

  1. 서비스 등록
  2. 서비스 실행
  3. 클라이언트 시작프로그램에 등록

이걸 개인이 하라고 하는건 무린것 같고(특히 서비스 등록이나 실행은 관리자 권한이 필요하죠). 그래서 InnoSetup으로 설치 스크립트를 만들었습니다.

[Run]
Filename: net.exe; Parameters: stop TimeSyncService
Filename: {app}\TimeSyncSvc.exe; Parameters: /INSTALL /SILENT
Filename: net.exe; Parameters: start TimeSyncService

[UninstallRun]
Filename: net.exe; Parameters: stop TimeSyncService
Filename: {app}\TimeSyncSvc.exe; Parameters: /UNINSTALL /SILENT

[Icons]
Name: {commonstartup}\시간 동기화; Filename: {app}\TimeSync.exe; IconFilename: {app}\TimeSync.exe; Languages:

포인트는 설치중에 서비스 설치/ 실행하고, 설치 제거하면 서비스 정지/ 해제하는 것, 그리고 시작프로그램에 등록하는 것입니다.

기타

오류: "지정된 서비스가 지워진 것으로 표시되었습니다"

서비스를 잘못 시작/ 종료/ 삭제하다보면 위의 메시지가 나올때가 있습니다(지금 떠올려보면 서비스가 실행된 상태에서 서비스를 uninstall 했던 것 같습니다). 이것은 서비스가 말그대로 <a href="http://www.pyrasis.com/blog/entry/ServiceDeleteFlag">삭제 플레그가 설정된 것으로 시간이 지나면 자동으로 삭제된다</a>고 합니다.

하염없이 기다리시거나 아니면 재부팅하면 됩니다. 한없이 기다리기 뭐해서 재부팅하고 커피한잔 마셨습니다.

서비스를 실행했는데, 파이프가 만들어 졌을까?

이런 고민이 있을 수 있습니다. 서비스 디버깅 하면 되긴하지만, 간단한 것에 귀찮아서 안했고, 그냥 파이프가 만들어 졌는지만 확인하려고 했습니다.

파이프가 만들어진 것을 확인하는 툴은 SysInternalsSuite에 있는 pipelist.exe 툴을 이용하면 확인 가능합니다.

TimeSync_Pipe가 여기서 사용하는 파이프입니다.

참고 자료


TimeSync: Windows7에서 안되는 문제

SyncTime 이라는 컴퓨터의 시간을 타입서버와 맞춰주는 개인적으로 만든 프로그램이 있었지요.

그런데 Windows 7으로 업그레이드한 후 작동을 하지 않습니다. 아주아주 간단한 프로그램인데 말입니다.

뒤져보니 SetLocalTime API가 SE_SYSTEMTIME_NAME 이라는 권한을 필요하다고 하네요. Windows Vista 이후 부터는 시간이라는 것도 시스템 자원이라는 개념으로 들어갔다는군요.

그래서 이 권한을 획득하고 처리했는데... 안됩니다. 뭐냐고.. Indy에서도 확인해보니 SE_SYSTEMTIME_NAME 권한을 획득하고 처리하고 있지요. 하라는데로 다 했는데 SetLocalTime 호출하면 오류가 납니다.

뒤져보니 사용자가 SetLocalTime을 호출할 이유가 없다라는 답도 있었지만, 필요에 따라서는 시간을 맞춰줄 필요가 있는데 말입니다. 위 링크에 이야기 언급한 서비스로 만드는건 배보다 배꼽이 큽니다.

생각하다가 그냥 서비스까지 동원해서 작업해버리기로 했습니다. 덕분에 설치하는 것이 조금 복잡해 지긴 했습니다. 이렇게 복잡해지니 단순 실행파일만 가지고는 안되서 셋업본으로 만들었습니다.

설치 및 다운로드는 TimeSync 페이지를 보세요.


  • Copyright © 1996-2010 Your wish is my command. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress