۱۳۸۹/۰۶/۱۴

اجرای فقط یک نمونه از برنامه در WPF

در بعضی برنامه های بنا به دلایلی برای توسعه دهنده هیچ اهمیتی ندارد که کاربر چند نمونه از برنامه را اجرا کند ولی اگر زمانی اجرای برنامه محدود بشود (به یک فرم )،برای این جور برنامه ها 3 نوع سناریو وجود دارد که به شرح آنها می پردازیم.
اولین سناریو را می توان نرم افزاری در نظر گرفت که فقط یک نمونه از آن اجرا می شود (فقط یک پنجره اصلی) و می تواند پارامتر های را در هنگام اجرا بپذیرد به طور مثال برنامه ی مثل ویندوز مدیا پلیر و سناریو دیگر این است که در واقع از نرم افزار فقط یک نمونه اجرا می شود ولی دارای تعدادی پنجره مختلف است (Document Base) از این برنامه ها می توان MS Word رو مثال زد که کاربر شاید فکر بکند چند نمونه از برنامه را اجرا کرده در صورتی که فقط یک نمونه از برنامه اجرا شده و پنجره های مختلف را مدیریت می کند  که از افزوده شدن سربار اضافی به سیستم جلوگیری می کند و سناریو آخری که می توان در نظر داشت برنامه ای که فقط یک نمونه از ان اجرا می شود (فقط یک پنجره اصلی) و هیچ پارامتر ورودی نمی پذیرد.البته سناریوهای دیگری را نیز می توان نام برد ولی ما به همین سه سناریو اکتفا می کنیم

برای پیاده سازی این قبیل نرم افزار ها راه حل های زیادی وجود دارد که هر کدام کم و بیش این قابلیت را به ما می دهند ولی ما قصد بررسی روشی رو داریم که در آن بتوان هر سه سناریو را پیاده سازی کرد

مثلا برای این منظور از Mutex استفاده می کنند که در آن نمی توان سناریویی اول و دوم را پیاده سازی کرد و یا پردازش ها بررسی می کنند که باز هم شامل سناریویی 1و2 نمی شود

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // Get Reference to the current Process
        Process thisProc = Process.GetCurrentProcess();
        // Check how many total processes have the same name as the current one
        if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
        {
            // If ther is more than one, than it is already running.
            MessageBox.Show("Application is already running.");
            Application.Current.Shutdown();
            return;
        }

        base.OnStartup(e);
    }
} 

با ارائه شدن WPF توسط مایکرسافت برای این منظور در #C هیچ تمهیداتی پیش بینی نشده بود ولی تیم توسعه  VB.NET این امکان را فراهم کرده بودند که ما نیز قصد استفاده از آن را در #C داریم

در VB.NET در فضای نام کلاسی وجود دارد به نام Microsoft.VisualBasic.ApplicationServices که این امکان را برای ما فراهم می کندو ما با مشتق کردن کلاسی از این کلاس می توانیم موفق به پیاده سازی هر سه سناریو بشویم.
این کلاس سه عضو را پیاده سازی می کند که از آنها برای این منظور  استفاده می کنیم

  • IsSingleInstance : یک خصیصه است و  مشخص می کند که آیا از این برنامه فقط یک نمونه اجرا بشود یا نه؟ که ما آن را در سازنده کلاس با true مقدار دهی می کنیم.
  • OnStartup : این رویداد زمانی رخ می دهد که اولین نمونه از برنامه در حال ساخته شدن است و می توانیم به آرگومان های ورودی دسترسی داشته باشیم( StartupEventArgs ).
  • OnStartupNextInstance : همانطور که از نامش پیداست این زمانی رخ می دهد که نمونه دیگری از برنامه در حال ساخته شدن باشد.در این رویداد نیز می توان به آرگومان های ورودی دسترسی داشته باشیم ( StartupNextInstanceEventArgs )
خوب برای این که بتوانید به این کلاس دسترسی پیدا کنید با ید  یک ارجاع به اسمبلی Microsoft.VisuaBasic.dllدر پروژه داشته باشید.

در اینجا ما سناریوی دوم را بررسی می کنیم که مشکلترین آنهاست و توسعه دهندگان اکثرا با پیاده سازی آن مشکل دارند
ابتدا یک کلاس را از WindowsFormsApplicationBase مشتق می کنیم و پیاده سازی زیر را انجام می دهیم

using Microsoft.VisualBasic.ApplicationServices;

    public class SingleInstanceManager : WindowsFormsApplicationBase
    {
        public App app { get; private set; }

        public SingleInstanceManager()
        {
            this.IsSingleInstance = true;
        }

        protected override bool OnStartup(StartupEventArgs e)
        {
            // First time app is launched
            app = new App();
            app.Run();

            return false;
        }

        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
        {
            app.MyWindow.Activate();

            app.ProcessArgs(eventArgs.CommandLine.ToArray(), false);
            
        }
    }

این کلاس یک نمونه از شی Application مورد نظر ما را نگهداری می کند (App) و در رویداد OnStartup یک نمونه از شی Application ساخته می شود و در رویداد OnStartNextInstance پنجره اصلی برنامه با نام MyWindow فعال می شود و سپس آرگومان های ورودی پردازش می شوند .توجه کنید که این قاعده کلی است و اگر این قسمت را متوجه شوید 70% راه را طی نموده اید.
حال ما قسمت کد نویسی شی Application را بررسی می کنیم (نه کد XAML)

/// 
    /// Interaction logic for App.xaml
    /// 
    public partial class App : Application
    {
        public MainWindow MyWindow { get; private set; }

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            // Create and show the application's main window
            MyWindow = new MainWindow();
            MyWindow.Show();
            
            //cache Arguments
            ProcessArgs (e.Args, true);
        }

        //cache Arguments
        public void ProcessArgs(string[] args, bool firstInstance)
        {
            if (firstInstance )
            {
             MyWindow.ProcessArgs(args, true );   
            }else 
                 //cache Arguments
                 MyWindow.ProcessArgs(args, false);
        }


    }
در این کلاس تابع ProcessArgs آرگومان های ورودی را گرفته و تجزیه می کند سپس اطلاعات را به تابعی با همین نام در شئ MyWindow تحویل می دهد (ورودی ها در ادامه بررسی خواهند شد)

حال نگاهی به  پیاده سازی MyWindow می اندازیم

/// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        //Parse Arguments
        public void ProcessArgs(string[] args, bool firstInstance)
        {
              if (!firstInstance)
              {
                  Block.Text += "Other try" + Environment.NewLine; ;
                  if (args.Length >= 1)
                  {
                      string temp = "With Argument" + Environment.NewLine;

                      for (int i = 0; i = 1)
                  {
                      string temp = "With Arguments" + Environment.NewLine;

                      for (int i = 0; i < args.Length; i++)
                      {
                          temp += "args  " + i + " = " + args[i] + Environment.NewLine;
                      }
                      this.Block.Text += temp;
                  }
                
              }
        }
    }
در این پنجره که به عنوان پنجره اصلی برنامه است شما می بایست آرگومان های ورودی را بررسی (Parse) کنید و نمایش صحیحی رو بسته به برنامه در اختیار کاربر قرار دهید که ما در اینجا وقت به نمایش آرگومان های ورودی بسنده می کنیم.
ورودی اول آرگومان های ورودی است و ورودی دوم مشخص می کند که آیا نمونه اول و اصلی است یا نمونه دیگر (اجرای اول با دیگر)

توجه کنید که تا این جا این راه حل برای یک Build از برنامه کار می کند واگر بخواهید که برای انواع Build های نرم افزار در یک سیستم جواب بدهد باید در فایل Assembly.cs پروژه خود یک اتریبیوت به نام  GuidAttribute را بیفزایید چون اگر Guidرو مقداردهی نکنید دات نت در هر بار Build یک Guid به آن اختصاص می دهدو در Build های مختلف برنامه نمی توانید یک فرم واحد داشته باشید.

[assembly: GuidAttribute("67ab3432-7388-4f47-af08-edabe2306cfd")]


حاصل  برنامه بالا با توجه به ورودی های مختلف در تصویر زیر مشخص است


منابع : من اول این نکته رو در کتاب Pro WPF in C# 2008" by Matthew MacDonald خوندم بعد از MSDNتکمیلش کردم.

فایل پروژه را می توانید از  { اینجا } دریافت کنید.
Vote on iDevCenter

هیچ نظری موجود نیست:

ارسال یک نظر