.NET Documents List Uploader का सैंपल

जेफ़ फ़िशर, Google Data APIs टीम
जनवरी 2008

एक नज़र: सैंपल का स्कोप

प्रोग्राम इंटरफ़ेस का स्क्रीनशॉट

सैंपल एक्ज़ीक्यूटेबल डाउनलोड करें.

Documents List Data API की एक अच्छी बात यह है कि यह डेवलपर को ऐसे उपयोगकर्ताओं के लिए माइग्रेशन टूल बनाने की अनुमति देता है जो अब भी Google Docs का इस्तेमाल कर रहे हैं. इस एपीआई का इस्तेमाल करने के लिए, मैंने .NET क्लाइंट लाइब्रेरी का इस्तेमाल करके .NET 2.0 अपलोडर ऐप्लिकेशन बनाया है. इसे "DocList Uploader" नाम दिया गया है. अपलोडर का सोर्स सबवर्ज़न से पाया जा सकता है.

इस सैंपल का मकसद, उपयोगकर्ता को अपने दस्तावेज़ों को कंप्यूटर से Google Docs में आसानी से माइग्रेट करने में मदद करना है. इसकी मदद से, उपयोगकर्ता अपने Google खाते में लॉग इन कर सकते हैं. इसके बाद, वे उन फ़ाइलों को खींचकर छोड़ सकते हैं जिन्हें अपलोड किया जा सकता है. ये फ़ाइलें अपने-आप अपलोड हो जाती हैं. इस सैंपल में, फ़ाइलें अपलोड करने के लिए, Windows Explorer शेल में राइट क्लिक मेन्यू का विकल्प जोड़ने का विकल्प भी मिलता है. यह सैंपल, Apache 2.0 लाइसेंस के तहत उपलब्ध कराया गया है. इसलिए, इसका इस्तेमाल अपने प्रोग्राम के लिए शुरुआती बिंदु के तौर पर किया जा सकता है.

इस लेख में, .NET फ़्रेमवर्क का इस्तेमाल करके, सैंपल के कुछ व्यवहारों को पूरा करने का तरीका बताया गया है. इसमें ज़्यादातर, काम के सेक्शन से एनोटेट किए गए कोड स्निपेट शामिल होते हैं. इस लेख में, ऐप्लिकेशन के फ़ॉर्म और अन्य यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट बनाने के तरीके के बारे में नहीं बताया गया है. इसके बारे में ज़्यादा जानकारी देने वाले कई Visual Studio लेख उपलब्ध हैं. अगर आपको यह जानना है कि यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट कैसे कॉन्फ़िगर किए गए थे, तो क्लाइंट लाइब्रेरी डाउनलोड करके प्रोजेक्ट फ़ाइल को खुद लोड किया जा सकता है. इसके लिए, "clients\cs\samples\DocListUploader" सबडायरेक्ट्री में जाएं.

सिस्टम ट्रे ऐप्लिकेशन बनाना

ट्रे ऐप्लिकेशन का उदाहरण

माइग्रेशन टूल आम तौर पर ऑपरेटिंग सिस्टम में बिना किसी रुकावट के काम करते हैं. इससे ऑपरेटिंग सिस्टम को ज़्यादा फ़ंक्शन मिलते हैं और उपयोगकर्ता को भी कोई परेशानी नहीं होती. 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 के साथ NotifyIcon में ContextMenu जोड़ने का विकल्प चुना है. क्लिक हैंडलर लिखना आसान है. इसके लिए, HiddenForm के Close तरीके को कॉल करें.

बैलून टिप

सिस्टम ट्रे के कई ऐप्लिकेशन, उपयोगकर्ता को गुब्बारे के आकार की सूचना दिखाकर कम्यूनिकेट करते हैं. यह सूचना, NotifyIcon से निकलने वाले गोल बब्बल की तरह दिखती है. बबल को इस तरह दिखाया जा सकता है:

DocListNotifyIcon.ShowBalloonTip(10000, "Title", "Example Text", ToolTipIcon.Info);

पहले आर्ग्युमेंट में, बबल को दिखाने के लिए मिलीसेकंड में समय दिया जाता है. ध्यान दें कि ओएस, इस फ़ील्ड के लिए कम से कम और ज़्यादा से ज़्यादा समय की अनुमति देगा. यह समय, क्रमशः 10 और 30 सेकंड है. दूसरे और तीसरे आर्ग्युमेंट में, बबल के लिए टाइटल और कुछ कॉन्टेंट के बारे में बताया गया है. आखिरी आर्ग्युमेंट की मदद से, बबल के मकसद को दिखाने के लिए आइकॉन चुना जा सकता है.

दस्तावेज़ अपलोड करना

दस्तावेज़ अपलोड करना आसान है. ज़्यादातर काम, DocumentsService ऑब्जेक्ट के UploadDocument तरीके से किया जाता है. इस प्रोसेस के बारे में ज़्यादा जानकारी, Documents List API के लिए डेवलपर की गाइड में दी गई है.

service = new DocumentsService("DocListUploader");
((GDataRequestFactory) service.RequestFactory).KeepAlive = false;
service.setUserCredentials(username, password);

सबसे पहले, DocumentsService ऑब्जेक्ट को शुरू किया जाना चाहिए और उपयोगकर्ता के क्रेडेंशियल दिए जाने चाहिए. एक साथ कई फ़ाइलें अपलोड करते समय कुछ समस्याओं को रोकने के लिए, "keep-alive" एचटीटीपी हेडर को बंद कर दिया गया है. ऐसा इसलिए किया गया है, क्योंकि इससे .NET फ़्रेमवर्क में कुछ समस्याएं आती हैं.

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 फ़्रेमवर्क करता है.

शेल कॉन्टेक्स्ट मेन्यू जोड़ना

शेल के कॉन्टेक्स्ट मेन्यू में कोई आइटम जोड़ने का सबसे आसान तरीका, रजिस्ट्री में बदलाव करना है. हमें 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 "Local" नेमस्पेस में है. इसलिए, मशीन पर अलग सेशन में हमारे ऐप्लिकेशन को अलग से चलाया जा सकता है. हालांकि, हमें कुछ बातों का ध्यान रखना होगा, क्योंकि हम ग्लोबल रजिस्ट्री में बदलाव कर रहे हैं.

इंटर-प्रोसेस कम्यूनिकेशन

जब कोई उपयोगकर्ता, हमारी जोड़ी गई किसी फ़ाइल के लिए शेल कॉन्टेक्स्ट मेन्यू आइटम पर क्लिक करता है, तो हमारे ऐप्लिकेशन का नया इंस्टेंस लॉन्च किया जाता है. साथ ही, उसे डिस्क पर फ़ाइल की पूरी जगह दी जाती है. अब यह जानकारी, ऐप्लिकेशन के पहले से चल रहे इंस्टेंस को देनी होगी. इसके लिए, .NET फ़्रेमवर्क के आईपीसी मैकेनिज़्म का इस्तेमाल किया जा सकता है. ये मैकेनिज़्म, वर्शन 2.0 में पेश किए गए थे.

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");
    
}

ऊपर दिए गए उदाहरण में ध्यान दें कि यह नाम वाला आईपीसी चैनल रजिस्टर करता है. साथ ही, यह RemoteMessage ऑब्जेक्ट की एक कॉपी उपलब्ध कराता है, जिसे हमने खुद तय किया है. यह इसे खुद के रेफ़रंस से शुरू करता है.

प्रोग्राम के बाद के इंस्टेंस के लिए, args पैरामीटर के ज़रिए Main को दी गई स्ट्रिंग को ओरिजनल इंस्टेंस में पास करना होगा. लिसनिंग आईपीसी चैनल से कनेक्ट करने और ओरिजनल इंस्टेंस से 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);
    }
}

रिमोट मैसेजिंग सिस्टम बहुत काम का है. इसकी मदद से, हम अपने प्रोग्राम के एक इंस्टेंस से जुड़े ऑब्जेक्ट को, लोकल आईपीसी चैनलों पर दूसरे इंस्टेंस के लिए उपलब्ध करा सकते हैं.

नतीजा

इस लेख में, DocList Uploader के सैंपल में इस्तेमाल किए गए कुछ तरीकों और तरकीबों के बारे में बताया गया है. इनका इस्तेमाल, Google Docs के लिए माइग्रेशन की आसान सुविधा देने के लिए किया जाता है. आपके पास अब भी अपने ऐप्लिकेशन में कई फ़ंक्शन जोड़ने का विकल्प है. साथ ही, अपने हिसाब से सैंपल को बढ़ाने का विकल्प भी है.

यहां उन डेवलपर के लिए कुछ काम के संसाधन दिए गए हैं जो Documents List Data API का इस्तेमाल करना चाहते हैं. साथ ही, उन डेवलपर के लिए भी संसाधन दिए गए हैं जो .NET का इस्तेमाल, Google Data API के साथ करना चाहते हैं: