Jeff Fisher, zespół interfejsów API danych Google
Styczeń 2008 r.
Wprowadzenie: zakres próbki
Pobierz przykładowy plik wykonywalny.
Jedną z zalet interfejsu Documents List Data API jest to, że umożliwia on programistom tworzenie narzędzi do migracji dla użytkowników, którzy dopiero zaczynają korzystać z Dokumentów Google. Na potrzeby korzystania z tego interfejsu API użyłem biblioteki klienta.NET do utworzenia aplikacji do przesyłania w języku .NET 2.0, odpowiednio nazwanej „DocList Uploader”. Źródło przesyłającego możesz uzyskać z systemu Subversion.
Ten przykład ma ułatwić użytkownikowi przenoszenie dokumentów z komputera do Dokumentów Google. Umożliwia użytkownikom zalogowanie się na konto Google, a następnie przeciągnięcie i upuszczenie obsługiwanych plików, które zostaną automatycznie przesłane. Przykładowy kod zawiera też opcję dodania do powłoki Eksploratora Windows opcji menu, która umożliwia przesyłanie plików po kliknięciu prawym przyciskiem myszy. Ten przykład jest udostępniany na licencji Apache 2.0, więc możesz go użyć jako punktu wyjścia do tworzenia własnych programów.
W tym artykule pokazujemy, jak niektóre działania przykładowego kodu zostały zrealizowane przy użyciu platformy .NET. Składa się on głównie z fragmentów kodu z odpowiednich sekcji z adnotacjami. Ten artykuł nie zawiera informacji o tym, jak tworzyć formularze i inne komponenty interfejsu aplikacji, ponieważ istnieje wiele artykułów o Visual Studio, które szczegółowo opisują ten proces. Jeśli chcesz się dowiedzieć, jak skonfigurowano komponenty interfejsu, możesz samodzielnie wczytać plik projektu, pobierając bibliotekę klienta i przeglądając podkatalog „clients\cs\samples\DocListUploader”.
Tworzenie aplikacji w obszarze powiadomień
Narzędzia do migracji zwykle działają w systemie operacyjnym w sposób nienachalny, rozszerzając jego możliwości bez zbytniego rozpraszania użytkownika. Jednym ze sposobów na uporządkowanie takiego narzędzia w systemie Windows jest uruchamianie go z zasobnika systemowego, a nie z paska zadań. Dzięki temu użytkownicy częściej pozostawiają program uruchomiony w sposób ciągły, zamiast otwierać go tylko wtedy, gdy muszą wykonać określone zadanie. Jest to szczególnie przydatne w tym przykładzie, ponieważ nie wymaga on przechowywania na dysku danych uwierzytelniających.
Aplikacja w obszarze powiadomień to aplikacja, która działa głównie z ikoną NotifyIcon w obszarze powiadomień (obszar obok zegara na pasku zadań). Podczas projektowania takiej aplikacji pamiętaj, że główna forma projektu nie powinna być tą, z którą użytkownik wchodzi w interakcję. Zamiast tego utwórz osobny formularz, który będzie się wyświetlać po uruchomieniu aplikacji. Za chwilę wyjaśnimy, dlaczego tak jest.
W moim przykładzie utworzyłem 2 formularze: HiddenForm, czyli główny formularz aplikacji z większością logiki, oraz OptionsForm, czyli formularz, który umożliwia użytkownikowi dostosowanie niektórych opcji i zalogowanie się na konto Google. Dodałem też do HiddenForm element NotifyIcon o nazwie DocListNotifyIcon i dostosowałem go za pomocą własnej ikony. Aby użytkownik nie widział elementu HiddenForm, ustawiłem jego krycie na 0%, stan okna na zminimalizowany, a właściwość ShowInTaskbar na False.
Zwykle, gdy program działa na pasku systemowym, zamknięcie aplikacji nie powinno go zatrzymywać, ale ukrywać aktywne formularze i pozostawiać widoczną tylko ikonę powiadomień. Aby to zrobić, musimy zastąpić zdarzenie „FormClosing” w naszym formularzu w ten sposób:
private void OptionsForm_FormClosing(object sender, FormClosingEventArgs e)
{
if(e.CloseReason == CloseReason.UserClosing) {
this.Hide();
e.Cancel = true;
}
}Prawdopodobnie warto też ukryć formularz, gdy użytkownik go zminimalizuje, ponieważ nie ma powodu, aby zajmował miejsce na pasku zadań, skoro mamy już ikonę powiadomień. Można to zrobić za pomocą tego fragmentu kodu:
private void OptionsForm_Resize(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Hide();
}
}Ponieważ nie zezwalamy użytkownikowi na zamknięcie elementu OptionsForm, możemy zachować jedną instancję powiązaną z elementem HiddenForm. Gdy chcemy ponownie wyświetlić OptionsForm, możemy po prostu wywołać jego metodę Show.
Główny formularz tej aplikacji, HiddenForm, nie jest widoczny dla użytkownika, więc musimy zapewnić mu możliwość wyjścia z aplikacji. Postanowiłem dodać do ikony powiadomień menu kontekstowe z elementem ToolStripMenuItem, który umożliwia zamknięcie aplikacji. Napisanie modułu obsługi kliknięć jest proste – wystarczy wywołać metodę Close klasy HiddenForm.
Wskazówki dotyczące balonów
Wiele aplikacji na pasku zadań komunikuje się z użytkownikiem, wyświetlając dymek, który wygląda jak zaokrąglony balonik wychodzący z ikony NotifyIcon. Dymek może być wyświetlany w ten sposób:
DocListNotifyIcon.ShowBalloonTip(10000, "Title", "Example Text", ToolTipIcon.Info);
Pierwszy argument to czas wyświetlania dymku w milisekundach. Pamiętaj, że system operacyjny zezwala na minimalny i maksymalny czas w tym polu, który wynosi odpowiednio 10 i 30 sekund. Drugi i trzeci argument określają tytuł i treść dymka. Ostatni argument pozwala wybrać ikonę, która zilustruje cel dymku.
Przesyłanie dokumentów
Przesłanie dokumentu jest proste. Większość pracy wykonuje metoda UploadDocument obiektu DocumentsService. Ten proces jest dokładniej opisany w przewodniku dla deweloperów korzystających z interfejsu API listy dokumentów.
service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password);Najpierw należy zainicjować obiekt DocumentsService i podać dane logowania użytkownika. Aby zapobiec niektórym problemom podczas przesyłania wielu plików, wyłączyliśmy nagłówek HTTP „keep-alive”, ponieważ powoduje on problemy z platformą .NET Framework.
lastUploadEntry = service.UploadDocument(fileName, null);
Ten fragment kodu przesyła plik ze ścieżki zawartej w ciągu fileName. Drugi argument o wartości null oznacza, że nazwa pliku Dokumentów Google ma być taka sama jak nazwa oryginalnego pliku.
Obsługa przeciągania i upuszczania
Aby ułatwić przesyłanie, warto umożliwić użytkownikowi przeciąganie plików z folderów do aplikacji w celu ich przesłania. Pierwszym krokiem jest zezwolenie na operację przeciągania z pliku. Poniższy kod zmieni kursor, aby wskazać, że przeciąganie jest dozwolone:
private void OptionsForm_DragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
{
e.Effect = DragDropEffects.Copy;
}
}Po upuszczeniu pliku lub grupy plików musimy obsłużyć to zdarzenie, przechodząc przez każdy upuszczony plik i przesyłając go:
private void OptionsForm_DragDrop(object sender, DragEventArgs e)
{
string[ fileList = (string[) e.Data.GetData(DataFormats.FileDrop);
foreach (string file in fileList)
{
mainForm.UploadFile(file);
}
}Wyświetlanie listy dokumentów
Pobranie listy dokumentów z serwera to dobry sposób na przypomnienie użytkownikowi, co już przesłał. Poniższy fragment kodu używa zainicjowanego wcześniej obiektu DocumentsService do pobrania wszystkich dokumentów z serwera.
public DocumentsFeed GetDocs()
{
DocumentsListQuery query = new DocumentsListQuery();
DocumentsFeed feed = service.Query(query);
return feed;
}Wygodnym sposobem wizualizacji tych danych jest użycie widoku listy. Do elementu OptionsForm dodano element ListView o nazwie DocList. Aby uatrakcyjnić ten element, utworzyłem też niestandardową listę obrazów ikon ilustrujących różne typy dokumentów. Poniższy kod wypełnia widok listy informacjami pobranymi z powyższego pliku danych:
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);
}
}Zmienna imageKey wybiera obraz z powiązanej listy ImageList, który ma być używany w każdym wierszu. Właściwość Tag służy tutaj do przechowywania oryginalnego wpisu, co może być przydatne do późniejszego wykonywania operacji na dokumencie. Na koniec metoda AutoResize służy do automatycznego formatowania szerokości kolumny w widoku listy.
Otwieranie dokumentów w przeglądarce
Ponieważ te dokumenty są przechowywane w Dokumentach Google, warto umożliwić użytkownikowi wyświetlenie dokumentu w przeglądarce. W systemie Windows jest wbudowana funkcja, która to umożliwia:
using System.Diagnostics;
private void OpenSelectedDocument()
{
if (DocList.SelectedItems.Count > 0)
{
DocumentEntry entry = (DocumentEntry) DocList.SelectedItems[0].Tag;
Process.Start(entry.AlternateUri.ToString());
}
}
W tym przypadku pobieramy oryginalny wpis z właściwości Tag, a następnie używamy AlternateUri wybranego dokumentu, aby wywołać Process.Start. Reszta jest obsługiwana przez .NET Framework.
Dodawanie menu kontekstowego powłoki
Najprostszym sposobem dodania elementu do menu kontekstowego powłoki jest zmodyfikowanie rejestru. Musimy utworzyć wpis w sekcji HKEY_CLASSES_ROOT, który będzie wskazywać naszą aplikację. Pamiętaj, że gdy użytkownik kliknie element menu, otworzy się nowa instancja naszej aplikacji. Zajmiemy się tym w kolejnych sekcjach.
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\"");
}Ten kod tworzy klucz rejestru, w którym znajduje się aktualnie działająca aplikacja. Notacja „%1” oznacza, że wybrany plik w powłoce powinien być przekazywany w ramach tego parametru. KEY_NAME to zdefiniowana stała tekstowa, która określa tekst wpisu w menu kontekstowym.
public void UnRegister()
{
RegistryKey key = Registry.ClassesRoot.OpenSubKey("*\\shell\\"+KEY_NAME);
if (key != null)
{
Registry.ClassesRoot.DeleteSubKeyTree("*\\shell\\"+KEY_NAME);
}
}Ta metoda po prostu usuwa dodany przez nas klucz niestandardowy, jeśli istnieje.
Zapobieganie tworzeniu wielu instancji
Ponieważ nasza aplikacja znajduje się w obszarze powiadomień, nie chcemy, aby jednocześnie działało wiele instancji programu. Możemy użyć Mutex, aby zapewnić, że tylko jedna instancja pozostanie uruchomiona.
using System.Threading;
bool firstInstance;
Mutex mutex = new Mutex(true, "Local\\DocListUploader", out firstInstance);
if (!firstInstance)
{
return;
}Powyższy kod można umieścić w metodzie Main naszej aplikacji, aby wcześnie zakończyć działanie programu, jeśli jest on już uruchomiony. Ponieważ znak Mutex znajduje się w przestrzeni nazw „Local”, umożliwia to uruchomienie naszej aplikacji w innej sesji na tym samym komputerze. Należy jednak zachować szczególną ostrożność, ponieważ modyfikujemy globalny rejestr.
Komunikacja między procesami
Gdy użytkownik kliknie element menu kontekstowego powłoki dla pliku, który dodaliśmy wcześniej, zostanie uruchomiona nowa instancja naszej aplikacji, która otrzyma pełną ścieżkę do miejsca, w którym plik znajduje się na dysku. Te informacje muszą zostać przekazane do działającej już instancji aplikacji. Można to zrobić za pomocą mechanizmów IPC platformy .NET Framework, które zostały wprowadzone w wersji 2.0.
using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Ipc;
Przekazywana wiadomość ma postać obiektu niestandardowego. Utworzyłem tu obiekt, który zawiera odwołanie do HiddenForm zawierającego logikę tej aplikacji. Ponieważ ten obiekt będzie hostowany w pierwotnej instancji, późniejsza instancja będzie mogła się komunikować z główną formą pierwotnej instancji.
class RemoteMessage : MarshalByRefObject
{
private HiddenForm mainForm;
public RemoteMessage(HiddenForm mainForm)
{
this.mainForm = mainForm;
}
public void SendMessage(string file)
{
mainForm.HandleUpload(file);
}
}
Gdy pierwsza instancja aplikacji zostanie zainicjowana, poniższy kod umożliwi jej nasłuchiwanie kolejnych instancji:
public void ListenForSuccessor()
{
IpcServerChannel serverChannel = new IpcServerChannel("DocListUploader");
ChannelServices.RegisterChannel(serverChannel, false);
RemoteMessage remoteMessage = new RemoteMessage(this);
RemotingServices.Marshal(remoteMessage,"FirstInstance");
}Zwróć uwagę, że rejestruje on nazwany kanał IPC i udostępnia kopię zdefiniowanego przez nas obiektu RemoteMessage, inicjując go odwołaniem do samego siebie.
W przypadku kolejnych instancji programu ciąg znaków przekazany do funkcji Main za pomocą parametru args musi zostać przekazany do pierwotnej instancji. Poniższy kod można wywołać, aby połączyć się z nasłuchującym kanałem IPC i pobrać obiekt RemoteMessage z pierwotnej instancji. Następnie metoda SendMessage służy do przekazywania nazwy pliku do pierwotnej instancji.
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);
}
}System zdalnego przesyłania wiadomości jest bardzo wydajny, ponieważ umożliwia nam udostępnianie obiektów należących do jednej instancji programu innym instancjom za pomocą lokalnych kanałów IPC.
Podsumowanie
W tym artykule opisujemy niektóre metody i sztuczki użyte w przykładowym narzędziu do przesyłania DocList, które ułatwiają migrację do Dokumentów Google. W swoich aplikacjach możesz dodać wiele innych funkcji. Możesz też rozszerzyć przykład, aby dostosować go do własnych potrzeb.
Oto kilka przydatnych materiałów dla deweloperów, którzy chcą korzystać z interfejsu Documents List Data API, a także dla tych, którzy chcą używać .NET z innymi interfejsami Google Data API: