Jeff Fisher, Google Data API팀
2008년 1월
소개: 샘플의 범위
Documents List Data API의 장점 중 하나는 개발자가 아직 Google Docs에 익숙하지 않은 사용자를 위한 이전 도구를 만들 수 있다는 것입니다. 이 API를 실행하기 위해 .NET 클라이언트 라이브러리를 사용하여 .NET 2.0 업로더 애플리케이션을 만들었으며, 이 애플리케이션의 제목은 'DocList Uploader'로 적절하게 지정했습니다. 서브버전에서 업로더의 소스를 가져올 수 있습니다.
이 샘플은 사용자가 컴퓨터에서 Google Docs로 문서를 쉽게 이전할 수 있도록 설계되었습니다. 사용자가 Google 계정에 로그인한 다음 지원되는 파일을 드래그 앤 드롭하면 자동으로 업로드됩니다. 이 샘플에서는 Windows 탐색기 셸에 마우스 오른쪽 버튼 클릭 메뉴 옵션을 추가하여 파일을 업로드하는 옵션도 제공합니다. 이 샘플은 Apache 2.0 라이선스에 따라 제공되므로 자체 프로그램의 시작점으로 자유롭게 사용할 수 있습니다.
이 도움말에서는 .NET 프레임워크를 사용하여 샘플의 일부 동작을 구현하는 방법을 보여줍니다. 주로 관련 섹션의 주석이 달린 코드 스니펫으로 구성됩니다. 이 도움말에서는 애플리케이션 자체의 양식 및 기타 UI 구성요소를 구성하는 방법을 다루지 않습니다. 이와 관련된 자세한 내용은 Visual Studio 도움말을 참고하세요. UI 구성요소가 어떻게 구성되었는지 궁금하다면 클라이언트 라이브러리를 다운로드하고 'clients\cs\samples\DocListUploader' 하위 디렉터리를 살펴보고 프로젝트 파일을 직접 로드하면 됩니다.
작업 표시줄 앱 만들기
마이그레이션 도구는 일반적으로 운영체제에서 눈에 띄지 않게 실행되어 사용자에게 너무 많은 방해를 주지 않고 OS가 할 수 있는 작업을 확장할 수 있습니다. Windows에서 이러한 도구를 구성하는 한 가지 방법은 작업 표시줄을 어지럽히지 않고 시스템 트레이에서 실행되도록 하는 것입니다. 따라서 사용자가 특정 작업을 수행해야 할 때만 프로그램을 여는 대신 프로그램을 계속 실행할 가능성이 훨씬 높아집니다. 이 샘플에서는 인증 사용자 인증 정보를 디스크에 저장할 필요가 없으므로 특히 유용한 아이디어입니다.
작업 표시줄의 시계 근처 영역인 시스템 트레이에서 NotifyIcon만으로 주로 실행되는 애플리케이션이 시스템 트레이 애플리케이션입니다. 이러한 애플리케이션을 설계할 때는 사용자가 상호작용하는 기본 양식이 프로젝트의 기본 양식이 아니어야 합니다. 대신 애플리케이션이 실행될 때 표시할 별도의 양식을 만듭니다. 그 이유는 잠시 후에 설명하겠습니다.
이 예에서는 HiddenForm(대부분의 로직이 포함된 애플리케이션의 기본 양식)과 OptionsForm(사용자가 일부 옵션을 맞춤설정하고 Google 계정에 로그인할 수 있는 양식)이라는 두 개의 양식을 만들었습니다. 또한 HiddenForm에 DocListNotifyIcon이라는 NotifyIcon을 추가하고 내 아이콘으로 맞춤설정했습니다. HiddenForm이 사용자에게 표시되지 않도록 불투명도를 0%, WindowState를 Minimized, ShowInTaskbar 속성을 False로 설정했습니다.
일반적으로 프로그램이 시스템 트레이에서 실행 중일 때 애플리케이션을 닫으면 프로그램이 중지되지 않고 활성 양식이 숨겨지고 NotifyIcon만 표시됩니다. 이렇게 하려면 다음과 같이 폼의 'FormClosing' 이벤트를 재정의해야 합니다.
private void OptionsForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(e.CloseReason == CloseReason.UserClosing) {
this.Hide();
e.Cancel = true;
}
}알림 아이콘이 이미 있으므로 사용자가 최소화할 때는 양식을 숨기는 것이 좋습니다. 작업 표시줄에 공간을 차지할 이유가 없기 때문입니다. 다음 코드를 사용하면 됩니다.
private void OptionsForm_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide();
}
}이제 사용자가 OptionsForm을 닫을 수 없으므로 HiddenForm에 연결된 인스턴스 하나만 유지하면 됩니다. OptionsForm을 다시 표시하려면 Show 메서드를 호출하면 됩니다.
이 애플리케이션의 기본 양식인 HiddenForm은 사용자에게 표시되지 않으므로 사용자에게 애플리케이션을 실제로 종료할 방법을 제공해야 합니다. 애플리케이션을 닫는 ToolStripMenuItem이 있는 ContextMenu를 NotifyIcon에 추가하기로 했습니다. 클릭 핸들러를 작성하는 것은 간단합니다. HiddenForm의 Close 메서드를 호출하면 됩니다.
풍선 도움말
많은 시스템 트레이 애플리케이션은 NotifyIcon에서 파생된 둥근 풍선 모양의 풍선 도움말을 표시하여 사용자와 통신합니다. 풍선은 다음과 같이 표시될 수 있습니다.
DocListNotifyIcon.ShowBalloonTip(10000, "Title", "Example Text", ToolTipIcon.Info);
첫 번째 인수는 풍선을 표시할 시간(밀리초)입니다. OS에서 이 필드에 허용하는 최소 시간과 최대 시간은 각각 10초와 30초입니다. 두 번째 및 세 번째 인수는 풍선의 제목과 일부 콘텐츠를 지정합니다. 마지막 인수를 사용하면 풍선의 목적을 설명하는 아이콘을 선택할 수 있습니다.
문서 업로드
문서를 업로드하는 것은 간단합니다. 대부분의 작업은 DocumentsService 객체의 UploadDocument 메서드에 의해 실행됩니다. 이 프로세스는 Documents List API 개발자 가이드에 더 명확하게 설명되어 있습니다.
service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password);먼저 DocumentsService 객체를 초기화하고 사용자의 사용자 인증 정보를 제공해야 합니다. 여러 파일을 업로드할 때 발생하는 일부 문제를 방지하기 위해 .NET Framework에 일부 문제를 일으키는 것으로 알려진 'keep-alive' HTTP 헤더가 사용 중지되었습니다.
lastUploadEntry = service.UploadDocument(fileName, null);
이 스니펫은 문자열 fileName에 포함된 경로에 있는 파일을 업로드합니다. 두 번째 인수가 null이면 Google Docs 파일 이름이 원본 파일 이름과 동일해야 함을 나타냅니다.
드래그 앤 드롭 처리
업로드를 더 쉽게 하려면 사용자가 폴더에서 애플리케이션으로 파일을 드래그 앤 드롭하여 업로드할 수 있도록 하는 것이 좋습니다. 첫 번째 단계는 파일에서 드롭 작업을 허용하는 것입니다. 아래 코드는 드롭이 허용됨을 나타내도록 커서를 변경합니다.
private void OptionsForm_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
e.Effect = DragDropEffects.Copy;
}
}파일 또는 파일 그룹이 드롭되면 드롭된 각 파일을 살펴보고 업로드하여 이 이벤트를 처리해야 합니다.
private void OptionsForm_DragDrop(object sender, DragEventArgs e)
{
string[ fileList = (string[) e.Data.GetData(DataFormats.FileDrop);
foreach (string file in fileList)
{
mainForm.UploadFile(file);
}
}문서 나열
서버에서 문서 목록을 가져오는 것은 사용자에게 이미 업로드한 항목을 상기시켜 주는 좋은 방법입니다. 아래 스니펫은 앞에서 초기화한 DocumentsService 객체를 사용하여 서버에서 모든 문서를 가져옵니다.
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
}이 데이터를 시각화하는 편리한 방법은 ListView를 사용하는 것입니다. OptionsForm에 DocList라는 이름의 ListView를 추가했습니다. 더 보기 좋게 하기 위해 다양한 문서 유형을 보여주는 아이콘의 맞춤 ImageList도 만들었습니다. 다음 코드는 위의 피드에서 가져온 정보로 ListView를 채웁니다.
public void UpdateDocList()
{
DocList.Items.Clear();
DocumentsFeed feed = mainForm.GetDocs();
foreach (DocumentEntry entry in feed.Entries)
{
string imageKey = "";
if (entry.IsDocument)
{
imageKey = "Document.gif";
}
else if (entry.IsSpreadsheet)
{
imageKey = "Spreadsheet.gif";
}
else
{
imageKey = "Presentation.gif";
}
ListViewItem item = new ListViewItem(entry.Title.Text, imageKey);
item.SubItems.Add(entry.Updated.ToString());
item.Tag = entry;
DocList.Items.Add(item);
}
foreach (ColumnHeader column in DocList.Columns)
{
column.AutoResize(ColumnHeaderAutoResizeStyle.ColumnContent);
}
}imageKey 변수는 연결된 ImageList에서 각 행에 사용할 이미지를 선택합니다. 여기서는 Tag 속성을 사용하여 원래 항목을 저장합니다. 이는 나중에 문서에 대한 작업을 실행하는 데 유용할 수 있습니다. 마지막으로 AutoResize 메서드는 ListView에서 열 너비를 자동 형식 지정하는 데 사용됩니다.
브라우저에서 문서 열기
이러한 문서는 Google Docs에 저장되므로 사용자가 브라우저에서 문서를 볼 수 있도록 하는 것이 좋습니다. Windows에는 이를 수행하는 내장 기능이 있습니다.
using System.Diagnostics;
private void OpenSelectedDocument()
{
if (DocList.SelectedItems.Count > 0)
{
DocumentEntry entry = (DocumentEntry) DocList.SelectedItems[0].Tag;
Process.Start(entry.AlternateUri.ToString());
}
}
여기서는 Tag 속성에서 원래 항목을 다시 가져온 다음 선택한 문서의 AlternateUri를 사용하여 Process.Start를 호출합니다. 나머지는 .NET Framework의 마법으로 처리됩니다.
셸 컨텍스트 메뉴 추가
셸의 컨텍스트 메뉴에 항목을 추가하는 가장 간단한 방법은 레지스트리를 수정하는 것입니다. HKEY_CLASSES_ROOT 아래에 애플리케이션을 가리키는 항목을 만들어야 합니다. 사용자가 메뉴 항목을 클릭하면 애플리케이션의 새 인스턴스가 열립니다. 이 문제는 다음 섹션에서 다루겠습니다.
using Microsoft.Win32;
public void Register()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME+"\\command");
if (key == null)
{
key = Registry.ClassesRoot.CreateSubKey("*\\shell\\" + KEY_NAME + "\\command");
}
key.SetValue("", Application.ExecutablePath + " \"%1\"");
}이 코드는 현재 실행 중인 애플리케이션이 있는 위치에 레지스트리 키를 만듭니다. '%1' 표기법은 셸에서 선택한 파일이 이 매개변수 내부에 전달되어야 함을 나타내는 데 사용됩니다. KEY_NAME는 컨텍스트 메뉴의 항목 텍스트를 결정하는 정의된 문자열 상수입니다.
public void UnRegister()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME);
if (key != null)
{
Registry.ClassesRoot.DeleteSubKeyTree("*\\shell\\"+KEY_NAME);
}
}이 메서드는 추가한 맞춤 키가 있는 경우 이를 삭제합니다.
여러 인스턴스 방지
애플리케이션이 시스템 트레이에 있으므로 프로그램의 여러 인스턴스가 한 번에 실행되지 않도록 해야 합니다. Mutex를 사용하여 하나의 인스턴스만 실행되도록 할 수 있습니다.
using System.Threading;
bool firstInstance;
Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance);
if (!firstInstance)
{
return;
}위 코드는 프로그램이 이미 실행 중인 경우 조기에 종료되도록 애플리케이션의 Main 메서드에 배치할 수 있습니다. Mutex가 '로컬' 네임스페이스 내에 있으므로 머신에서 다른 세션이 애플리케이션을 별도로 실행할 수 있습니다. 하지만 전역 레지스트리를 수정하므로 몇 가지 추가 주의사항이 있습니다.
프로세스 간 통신
사용자가 이전에 추가한 파일의 셸 컨텍스트 메뉴 항목을 클릭하면 애플리케이션의 새 인스턴스가 실행되고 파일이 디스크에 있는 전체 경로가 제공됩니다. 이제 이 정보를 이미 실행 중인 애플리케이션 인스턴스에 전달해야 합니다. 이는 버전 2 .0에서 도입된.NET Framework의 IPC 메커니즘을 사용하여 수행할 수 있습니다.
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc;
전달하는 메시지는 맞춤 객체 형태를 취합니다. 여기서는 이 애플리케이션의 로직이 포함된 HiddenForm에 대한 참조가 포함된 객체를 만들었습니다. 이 객체는 원래 인스턴스에서 호스팅되므로 후속 인스턴스가 원래 인스턴스의 기본 양식과 통신할 수 있는 방법을 제공합니다.
class RemoteMessage : MarshalByRefObject
{
private HiddenForm mainForm;
public RemoteMessage(HiddenForm mainForm)
{
this.mainForm = mainForm;
}
public void SendMessage(string file)
{
mainForm.HandleUpload(file);
}
}
애플리케이션의 첫 번째 인스턴스가 초기화되면 다음 코드를 통해 연속 인스턴스를 수신할 수 있습니다.
public void ListenForSuccessor()
{
IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader");
ChannelServices.RegisterChannel(serverChannel, false);
RemoteMessage remoteMessage = new RemoteMessage(this);
RemotingServices.Marshal(remoteMessage,"FirstInstance");
}위에서 이름이 지정된 IPC 채널을 등록하고 정의한 RemoteMessage 객체의 사본을 제공하여 자체 참조로 초기화합니다.
프로그램의 연속 인스턴스의 경우 args 매개변수를 통해 Main에 제공된 문자열이 원래 인스턴스에 전달되어야 합니다. 다음 코드를 호출하여 수신 대기 IPC 채널에 연결하고 원래 인스턴스에서 RemoteMessage 객체를 가져올 수 있습니다. 그런 다음 SendMessage 메서드를 사용하여 파일 이름을 원래 인스턴스에 전달합니다.
public static void NotifyPredecessor(string file)
{
IpcClientChannel clientChannel = new IpcClientChannel();
ChannelServices.RegisterChannel(clientChannel, false);
RemoteMessage message = (RemoteMessage) Activator.GetObject(typeof(RemoteMessage),
"ipc://DocListUploader/FirstInstance");
if (!message.Equals(null))
{
message.SendMessage(file);
}
}원격 메시지 시스템은 프로그램의 한 인스턴스에 속하는 객체를 로컬 IPC 채널을 통해 다른 인스턴스에 표시할 수 있으므로 매우 강력합니다.
결론
이 도움말에서는 DocList Uploader 샘플에서 Google 문서에 친숙한 이전 유틸리티를 제공하는 데 사용되는 다양한 방법과 트릭을 개략적으로 설명합니다. 자체 애플리케이션에 추가할 수 있는 기능이 아직 많이 있으며, 자체 목적에 맞게 샘플을 자유롭게 확장할 수 있습니다.
다음은 Documents List Data API를 사용하려는 개발자와 .NET을 다른 Google Data API와 함께 사용하려는 개발자에게 유용한 리소스입니다.