একক-উদাহরণ ডাব্লুপিএফ অ্যাপ্লিকেশন তৈরি করার সঠিক উপায় কী?


655

.NET ( উইন্ডোজ ফর্ম বা কনসোলের চেয়ে ) এর অধীনে সি # এবং ডাব্লুপিএফ ব্যবহার করে , একটি অ্যাপ্লিকেশন তৈরির সঠিক উপায় কী যা কেবলমাত্র একক উদাহরণ হিসাবে চালানো যেতে পারে?

আমি জানি যে এটি মিউটেক্স নামে পরিচিত কিছু পৌরাণিক জিনিসের সাথে কিছু করার আছে, খুব কমই আমি এমন কাউকে খুঁজে পেতে পারি যে থামাতে এবং এগুলির মধ্যে একটি কী তা ব্যাখ্যা করতে বিরক্ত করে।

কোডটি ইতিমধ্যে চলমান উদাহরণটিও জানাতে হবে যে ব্যবহারকারী দ্বিতীয়টি শুরু করার চেষ্টা করেছিল এবং যদি উপস্থিত থাকে তবে কোনও কমান্ড-লাইন যুক্তিও পাস করতে পারে।


14
অ্যাপ্লিকেশনটি যেকোনভাবে বন্ধ হয়ে গেলে কী সিএলআর স্বয়ংক্রিয়ভাবে কোনও অপ্রকাশিত মুউটেক্সগুলি প্রকাশ করে না?
কোকোওয়ালা

1
@ কোকোয়াল্লা: চূড়ান্তকরণকারীর নিয়ন্ত্রণহীন মুউটেক্সগুলি নিষ্পত্তি করা উচিত যদি না এটি জানা যায় না যে মুটেক্স পরিচালিত অ্যাপ্লিকেশন দ্বারা তৈরি করা হয়েছিল বা কোনও বিদ্যমানটির সাথে সংযুক্ত ছিল কিনা।
Ignacio সৌর গার্সিয়া

আপনার অ্যাপ্লিকেশনটির কেবল একটি উদাহরণ থাকা যুক্তিসঙ্গত। তবে ইতিমধ্যে বিদ্যমান অ্যাপ্লিকেশনটিতে তর্কগুলি পাস করার বিষয়টি আমার কাছে কিছুটা নির্বাক appears আমি এটি করার কোনও কারণ দেখতে পাচ্ছি না। আপনি যদি কোনও অ্যাপ্লিকেশনকে ফাইলের সম্প্রসারণের সাথে সংযুক্ত করেন, ব্যবহারকারী যতদূর নথি খুলতে চান আপনার তত বেশি অ্যাপ খুলতে হবে open এটি প্রতিটি ব্যবহারকারীর প্রত্যাশা এমন স্ট্যান্ডার্ড আচরণ।
এরিক অয়েললেট

9
@ কোকোয়াল্লা সিএলআর স্থানীয় উত্সগুলি পরিচালনা করে না। তবে, যদি কোনও প্রক্রিয়া সমাপ্ত হয়, সমস্ত হ্যান্ডলগুলি সিস্টেমের মাধ্যমে মুক্ত হয় (ওএস, সিএলআর নয়)।
IInspectable

1
আমি উত্তর @ হুসিইয়েন্ট দ্বারা পছন্দ করি। এটি মাইক্রোসফ্টের নিজস্ব 'সিঙ্গলআইনস্ট্যানস সি' ক্লাস ব্যবহার করে, তাই আপনাকে মুটেক্সেস এবং ইন্টিপ্টার্স সম্পর্কে চিন্তা করতে হবে না। এছাড়াও, ভিজুয়ালবাসিক (ইউক) এর উপর কোনও নির্ভরতা নেই। দেখুন codereview.stackexchange.com/questions/20871/... আরো জন্য ...
Heliac

উত্তর:


537

মিটেক্স সমাধান সম্পর্কিত একটি খুব ভাল নিবন্ধ এখানে । নিবন্ধটি দ্বারা বর্ণিত পদ্ধতির দুটি কারণে সুবিধাজনক।

প্রথমত, এটি মাইক্রোসফ্টের উপর নির্ভরতা প্রয়োজন না। ভিজুয়ালবাসিক সমাবেশ assembly যদি আমার প্রকল্পটির ইতিমধ্যে সেই সমাবেশের উপর নির্ভরতা থাকে তবে আমি সম্ভবত অন্য উত্তরে দেখানো পদ্ধতির সাহায্যে অ্যাডভোকেট করব । তবে যেমনটি হয়, আমি মাইক্রোসফ্টটি ব্যবহার করি না is ভিজুয়ালবাসিক সমাবেশটি, এবং আমি বরং আমার প্রকল্পে একটি অপ্রয়োজনীয় নির্ভরতা যুক্ত করব না।

দ্বিতীয়ত, ব্যবহারকারী যখন অন্য কোনও উদাহরণ শুরু করার চেষ্টা করে তখন কীভাবে অ্যাপ্লিকেশনটির বিদ্যমান উদাহরণটিকে অগ্রভাগে আনা যায় তা নিবন্ধটি দেখায়। এটি একটি খুব সুন্দর স্পর্শ যা এখানে বর্ণিত অন্যান্য মিটেক্স সমাধানগুলি সম্বোধন করে না।


হালনাগাদ

8/1/2014 পর্যন্ত, উপরে আমি যে নিবন্ধটি লিঙ্ক করেছি তা এখনও সক্রিয়, তবে কিছুক্ষণের মধ্যে ব্লগটি আপডেট হয়নি। এটি আমাকে উদ্বিগ্ন করে তোলে যে শেষ পর্যন্ত এটি অদৃশ্য হয়ে যেতে পারে এবং এটির সাথে, পরামর্শক সমাধান। আমি এখানে প্রবন্ধের বিষয়বস্তু উত্তরোত্তর জন্য পুনরুত্পাদন করছি। কথায় ব্লগ মালিকের কাছে একমাত্র অন্তর্গত বৈধতা ফ্রি কোডিং

আজ আমি এমন কিছু কোড রিফ্যাক্টর করতে চেয়েছিলাম যা আমার অ্যাপ্লিকেশনটিকে একাধিক বার চালানো থেকে নিষেধ করেছিল।

পূর্বে আমার সিস্টেমে ডায়াগনস্টিক্স ব্যবহার ছিল the প্রক্রিয়া তালিকায় আমার মাইএপ.এক্স.এক্স.এর উদাহরণ অনুসন্ধান করার জন্য প্রসেস। এটি কাজ করার সময় এটি প্রচুর ওভারহেড নিয়ে আসে এবং আমি আরও পরিষ্কার কিছু চেয়েছিলাম।

আমি এটির জন্য একটি মুটেক্স ব্যবহার করতে পারি তা জেনে (তবে এটি আগে কখনও হয়নি) আমি আমার কোডটি কেটে ফেলতে এবং আমার জীবনকে সহজ করার উদ্দেশ্যে যাত্রা শুরু করেছিলাম।

আমার অ্যাপ্লিকেশন প্রধানের ক্লাসে আমি মেটেক্স নামে একটি স্ট্যাটিক তৈরি করেছি :

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

একটি নামকরণ করা মিটেক্স থাকা আমাদের একাধিক থ্রেড এবং প্রক্রিয়াগুলি জুড়ে সিঙ্ক্রোনাইজেশনকে সজ্জিত করতে দেয় যা আমি খুঁজছি কেবল সেই যাদু।

Mutex.WaitOne এর একটি ওভারলোড রয়েছে যা আমাদের অপেক্ষা করার জন্য সময়ের পরিমাণ নির্দিষ্ট করে। যেহেতু আমরা আসলে আমাদের কোডটি সিঙ্ক্রোনাইজ করতে চাইছি না ( এটি কেবল বর্তমানে ব্যবহার হচ্ছে কিনা তা পরীক্ষা করে দেখুন) আমরা দুটি পরামিতি সহ ওভারলোডটি ব্যবহার করি: মুটেক্স.ওয়েটঅন (টাইমস্প্যানের সময়সীমা, বুল প্রস্থানকন্টেক্সট) । এটি প্রবেশ করতে সক্ষম হলে সত্যের প্রত্যাশার অপেক্ষা রাখে না এবং এটি না থাকলে মিথ্যা। এই ক্ষেত্রে, আমরা মোটেও অপেক্ষা করতে চাই না; যদি আমাদের মিউটেক্স ব্যবহার করা হচ্ছে, এটিকে এড়িয়ে যান এবং এগিয়ে যান, সুতরাং আমরা টাইমস্প্যান.জিরোতে (0 মিলিসেকেন্ড অপেক্ষা করুন) পাস করে প্রস্থানকন্টেক্সটকে সত্যে সেট করি যাতে এটিতে কোনও লক অর্জনের চেষ্টা করার আগে আমরা সিঙ্ক্রোনাইজেশন প্রসঙ্গে প্রস্থান করতে পারি। এটি ব্যবহার করে আমরা আমাদের অ্যাপ্লিকেশনটি গুটিয়ে ফেলি like

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

সুতরাং, যদি আমাদের অ্যাপটি চলমান থাকে তবে ওয়েটওন মিথ্যা প্রত্যাবর্তন করবে এবং আমরা একটি বার্তা বাক্স পাব।

কোনও বার্তা বাক্সটি দেখানোর পরিবর্তে, আমি আমার চলমান উদাহরণটি জানানোর জন্য কিছুটা উইন 32 ব্যবহার করে বেছে নিয়েছি যে কেউ ভুলে গিয়েছিল যে এটি ইতিমধ্যে চলছে (অন্য উইন্ডোর শীর্ষে নিজেকে নিয়ে এসে)। এটি অর্জনের জন্য আমি প্রতিটি উইন্ডোতে একটি কাস্টম বার্তা সম্প্রচার করতে পোস্টম্যাসেজ ব্যবহার করেছি (কাস্টম বার্তাটি আমার চলমান অ্যাপ্লিকেশন দ্বারা RegisterWindowMessage এর সাথে নিবন্ধিত হয়েছিল, যার অর্থ কেবলমাত্র আমার অ্যাপ্লিকেশনটি এটি কী তা বোঝায়) তবে আমার দ্বিতীয় উদাহরণটি উপস্থিত হয়। চলমান অ্যাপ্লিকেশন উদাহরণটি বিজ্ঞপ্তিটি গ্রহণ করবে এবং এটি প্রক্রিয়া করবে। এটি করার জন্য, আমি আমার মূল ফর্মটিতে ডাব্ল্যান্ডড্রোককে ওভাররড করেছি এবং আমার কাস্টম বিজ্ঞপ্তিটি শুনেছি। যখন আমি এই বিজ্ঞপ্তিটি পেয়েছি তখন আমি ফর্মের টপমস্ট সম্পত্তিটি শীর্ষে আনতে সত্যে সেট করেছিলাম।

আমি এখানে যা শেষ করেছি তা এখানে:

  • Program.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs (সামনের দিকের আংশিক)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}

5
এই উত্তরটি কম কোড এবং কম লাইব্রেরি ব্যবহার করে এবং শীর্ষ কার্যকারিতা বাড়িয়ে তোলে তার ভিত্তিতে, আমি এটি নতুন গ্রহণযোগ্য উত্তর করতে যাচ্ছি। যদি কেউ এপিআই এর সাহায্যে ফর্মটি শীর্ষে আনার আরও সঠিক উপায় জানেন তবে এটিকে যুক্ত করে নির্দ্বিধায়।
নিডোনোকু

11
@ ব্লুরাজ, আপনি প্রথম অ্যাপ্লিকেশন উদাহরণ শুরু করলেন। আপনি যখন দ্বিতীয় অ্যাপ্লিকেশনটি শুরু করেন, এটি সনাক্ত করে যে অন্য একটি দৃষ্টান্ত ইতিমধ্যে চলছে এবং শাটডাউন করার জন্য প্রস্তুত রয়েছে। এটি করার আগে এটি প্রথম দেখায় একটি "শো" দেশীয় বার্তা প্রেরণ করে, যা প্রথম উদাহরণটি শীর্ষে নিয়ে আসে। .NET এর ইভেন্টগুলি ক্রস-প্রক্রিয়া যোগাযোগের অনুমতি দেয় না, এজন্যই স্থানীয় বার্তা ব্যবহৃত হয়।
ম্যাট ডেভিস

7
অন্য উদাহরণ থেকে কমান্ড লাইনগুলি পাস করার কোনও উপায় আছে, সম্ভবত?
গিউরিস্ক

22
@ নম, Mutexকনস্ট্রাক্টরকে কেবল স্ট্রিংয়ের প্রয়োজন হয়, সুতরাং আপনি যে কোনও স্ট্রিংয়ের নাম সরবরাহ করতে পারেন, যেমন, "এটি আমার মাইটেক্স"। যেহেতু একটি 'মুটেক্স' একটি সিস্টেম অবজেক্ট যা অন্যান্য প্রক্রিয়াগুলিতে উপলভ্য, আপনি সাধারণত নামটি অনন্য হতে চান তাই এটি একই সিস্টেমে থাকা অন্য 'মুটেক্স' নামের সাথে সংঘাত না করে। নিবন্ধে, ক্রিপ্টিক-চেহারাযুক্ত স্ট্রিংটি একটি 'গাইড'। কল করে আপনি এই প্রোগ্রামটিমেটিক্যালি জেনারেট করতে পারবেন System.Guid.NewGuid()। নিবন্ধের ক্ষেত্রে, ব্যবহারকারী সম্ভবত এটি এখানে দেখানো হিসাবে ভিজ্যুয়াল স্টুডিওর মাধ্যমে তৈরি করেছে: এমএসডিএন.মাইক্রোসফট /en-us/library/ms241442(VS.80).aspx
ম্যাট ডেভিস

6
মুটেক্স পদ্ধতির ধারণা কি একই ব্যবহারকারী আবার অ্যাপ্লিকেশন শুরু করার চেষ্টা করছেন? অবশ্যই একটি 'ব্যবহারকারী সুইচ' পর অর্থে দেখা যায় না "পুরোভাগে আবেদন বিদ্যমান উদাহরণস্বরূপ" আনয়ন
dumbledad

107

আপনি মুটেক্স ক্লাসটি ব্যবহার করতে পারেন, তবে শীঘ্রই আপনি খুঁজে পাবেন যে তর্কগুলি এবং এই জাতীয় নিজেকে পাস করার জন্য আপনার কোডটি প্রয়োগ করতে হবে। ওয়েল, আমি ক্রিস সেল এর বই যখন পড়ি তখন উইনফোর্ডসে প্রোগ্রাম করার সময় আমি একটি কৌশল শিখেছি । এই কৌশলটি যুক্তি ব্যবহার করে যা ইতিমধ্যে ফ্রেমওয়ার্কে আমাদের কাছে উপলব্ধ। আমি আপনার সম্পর্কে জানিনা, তবে আমি যখন জিনিসগুলি শিখি তখন আমি কাঠামোর মধ্যে পুনরায় ব্যবহার করতে পারি, এটি হ'ল চাকাটি পুনরায় উদ্ভাবনের পরিবর্তে আমি যে পথে নিয়ে যাই। অবশ্যই এটি আমার যা করতে চায় তা করে না।

আমি যখন ডাব্লুপিএফ এ উঠলাম, আমি একই কোডটি ব্যবহার করার একটি উপায় নিয়ে এসেছি, তবে ডাব্লুপিএফ অ্যাপ্লিকেশনটিতে। এই সমাধানটি আপনার প্রশ্নের ভিত্তিতে আপনার প্রয়োজনগুলি পূরণ করবে।

প্রথমত, আমাদের অ্যাপ্লিকেশন ক্লাস তৈরি করা দরকার। এই শ্রেণিতে আমরা অন স্টার্টআপ ইভেন্টটিকে ওভাররাইড করতে যাচ্ছি এবং অ্যাক্টিভেট নামক একটি পদ্ধতি তৈরি করব যা পরে ব্যবহৃত হবে।

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

দ্বিতীয়ত, আমাদের এমন একটি শ্রেণি তৈরি করা দরকার যা আমাদের দৃষ্টান্তগুলি পরিচালনা করতে পারে। আমরা এটির আগে যাওয়ার আগে আমরা আসলে মাইক্রোসফ্টে থাকা কিছু কোড পুনরায় ব্যবহার করতে যাচ্ছি is ভিজুয়ালবাসিক সমাবেশে। যেহেতু, আমি এই উদাহরণে সি # ব্যবহার করছি, তাই আমাকে সমাবেশে একটি রেফারেন্স তৈরি করতে হয়েছিল। আপনি যদি VB.NET ব্যবহার করে থাকেন তবে আপনাকে কিছু করতে হবে না। আমরা যে ক্লাসটি ব্যবহার করতে যাচ্ছি সেটি হ'ল উইন্ডোজফর্মস অ্যাপ্লিকেশনবেজ এবং এটি থেকে আমাদের ইনস্ট্যান্স ম্যানেজারের উত্তরাধিকারী হয় এবং তারপরে একক ইনস্ট্যান্সিং পরিচালনা করার জন্য বৈশিষ্ট্য এবং ইভেন্টগুলি উপার্জন করতে পারে।

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

মূলত, আমরা ভিবি বিটগুলি একক উদাহরণ সনাক্ত করতে এবং সেই অনুযায়ী প্রক্রিয়াটি ব্যবহার করছি। প্রথম দৃষ্টান্তটি লোড হলে অনস্টার্টআপ বরখাস্ত করা হবে। অ্যাপ্লিকেশনটি আবার চালু করার সময় অনস্টার্টআপনেক্সটেন্সটি বরখাস্ত করা হয়। আপনি দেখতে পাচ্ছেন, ইভেন্ট আর্গুমেন্টের মাধ্যমে আমি কমান্ড লাইনে যা পেরেছিলাম তা পেতে পারি। আমি একটি উদাহরণ ক্ষেত্রে মান সেট করে। আপনি এখানে কমান্ড লাইনটি পার্স করতে পারেন, বা আপনি এটি কনস্ট্রাক্টর এবং অ্যাক্টিভেট পদ্ধতিতে কলটির মাধ্যমে আপনার অ্যাপ্লিকেশনটিতে পাঠাতে পারেন।

তৃতীয়ত, আমাদের এন্ট্রিপয়েন্টটি তৈরি করার সময় এসেছে। আপনার মতো সাধারণত অ্যাপ্লিকেশনটি নতুন করে ফেলার পরিবর্তে আমরা আমাদের সিঙ্গলআইন্সট্যান্স ম্যানেজারের সুবিধা নিতে যাচ্ছি।

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

ঠিক আছে, আমি আশা করি আপনি সবকিছু অনুসরণ করতে সক্ষম হবেন এবং এই বাস্তবায়নটি ব্যবহার করতে সক্ষম হবেন এবং এটিকে আপনার নিজের করে তুলতে পারবেন।


9
আমি মুটেক্স সমাধানটি আটকে থাকি কারণ ফর্মগুলির সাথে এটির কোনও সম্পর্ক নেই।
স্টিভেন সুদিত

1
আমি এটি ব্যবহার করেছি কারণ আমার অন্যান্য পদ্ধতির সাথে আমার সমস্যা ছিল তবে আমি নিশ্চিত যে এটি হুডের নীচে রিমোটিং ব্যবহার করে। আমার অ্যাপ্লিকেশনটিতে দুটি সম্পর্কিত সমস্যা রয়েছে - কিছু গ্রাহকরা বলেছেন যে এটি ঘরে বসে ফোন করার চেষ্টা করেছে যদিও তারা তা না করার কথা বলেছে। যখন তারা আরও সাবধানে তাকান, সংযোগটি লোকালহোস্টের সাথে হয়। তবুও, তারা প্রাথমিকভাবে তা জানে না। এছাড়াও, আমি অন্য উদ্দেশ্যে (আমি মনে করি?) রিমোটিং ব্যবহার করতে পারি না কারণ এটি ইতিমধ্যে এর জন্য ব্যবহৃত হচ্ছে। আমি যখন মুটেক্স পদ্ধতির চেষ্টা করেছি তখন আমি আবার রিমোটিং ব্যবহার করতে পারি।
রিচার্ড ওয়াটসন

4
আমাকে ক্ষমা করুন, তবে আমি যদি কিছু মিস না করি তবে আপনি 3 লাইন কোড লেখা এড়াতে এবং পরিবর্তে আপনি এটি করার জন্য ভারী কোডটি লিখতে ফ্রেমওয়ার্কটি পুনরায় ব্যবহার করেছেন। তাহলে সঞ্চয় কোথায়?
গ্রিনোল্ডম্যান

2
উইনফর্মগুলিতে এটি করা সম্ভব?
জ্যাক

1
আপনি যদি অ্যাপ্লিকেশনের উদাহরণে আরিনালাইজকম্পোনটেন্ট () না কল করেন তবে আপনি সংস্থানগুলি সমাধান করতে পারবেন না ... _ অ্যাপ্লিকেশন = নতুন সিঙ্গলইনস্ট্যান্স অ্যাপ্লিকেশন (); _application.InitializeComponent (); _application.Run ();
নিক

84

থেকে এখানে

ক্রস-প্রক্রিয়া মিটেক্সের একটি সাধারণ ব্যবহার হ'ল এটি নিশ্চিত করা যে কোনও প্রোগ্রামের কেবলমাত্র উদাহরণ একবারে চলতে পারে। এটি কিভাবে সম্পন্ন হয়েছে তা এখানে:

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

মুটেক্সের একটি ভাল বৈশিষ্ট্য হ'ল যদি অ্যাপ্লিকেশনটি রিলিজমিউটেক্সকে প্রথমে কল না করেই শেষ করা হয়, তবে সিএলআর স্বয়ংক্রিয়ভাবে মিটেক্সটি প্রকাশ করবে।


5
আমি বলতে পেরেছি, এটি উইনফোর্ডের উপর নির্ভর করে না বলেই এই উত্তরটি গ্রহণযোগ্য উত্তরটির চেয়ে অনেক বেশি পছন্দ করি। ব্যক্তিগতভাবে আমার বেশিরভাগ বিকাশ ডাব্লুপিএফ-তে চলেছে এবং আমি এর মতো কোনও কিছুর জন্য উইনফর্ম লাইব্রেরিগুলিতে টানতে চাই না।
সুইটটার

5
অবশ্যই, একটি সম্পূর্ণ উত্তর হতে গেলে, আপনাকে অন্য উদাহরণগুলিতেও যুক্তিগুলি পাস করার বর্ণনা দিতে হবে :)
সাইমন বুখান

@ জেসন, ভাল, ধন্যবাদ! তবে আমি কোনও টাইমআউট পাস না করা পছন্দ করি। এটি অনেকটা সাবজেক্টিভ এবং অনেকগুলি ভেরিয়েবলের উপর নির্ভর করে। আপনি যদি আর কোনও অ্যাপ্লিকেশন শুরু করতে সক্ষম করতে চান তবে কেবল আপনার মিটেক্সকে দ্রুত মুক্তি দিন .. উদাহরণস্বরূপ ব্যবহারকারী যত তাড়াতাড়ি নিশ্চিত হন
এরিক ওয়েললেট

@ এরিক অয়েললেট: ট্যাব রয়েছে এমন প্রতিটি প্রোগ্রামের প্রায়শই এটি করে - ফটোশপ, সাব্লাইম টেক্সট, ক্রোম .... আপনার যদি কোনও "মাস্টার" প্রক্রিয়া করার উপযুক্ত কারণ থাকে (বলুন যে আপনার কাছে সেটিংসের জন্য ইন-প্রোক্ট ডিবি রয়েছে) আপনি সম্ভবত এটি ইউআই প্রদর্শন করতে চাই যেন এটিও একটি নতুন প্রক্রিয়া।
সাইমন বুচান

@ সিমন, আপনি ঠিক বলেছেন। আমি কেবল নিজেকে খুব পুরানো জিনিস সম্পর্কে প্রশ্ন করি ... এমডিআই বনাম এসডিআই (মাল্টি ডকুমেন্টইনফেরেস বনাম সিঙ্গল ডকুমেন্ট ইন্টারফেস)। আপনি যখন ট্যাবগুলির বিষয়ে কথা বলেন, আপনি এমডিআই উল্লেখ করেন। 1998 সালে, একটি মাইক্রোসফ্ট বই প্রতিটি এমডিআই অ্যাপ্লিকেশন বাদ দেওয়ার পরামর্শ দেয়। মাইক্রোসফ্ট ওয়ার্ড, এক্সেল ... এসডিআই-তে পরিবর্তন করেছে যা আমি মনে করি এটি সহজ এবং ভাল better আমি বুঝতে পারি যে ক্রোম এবং অন্যান্য (এখন আইই) এমডিআইতে ফিরে যেতে চায়। আমি ব্যক্তিলি (কোনও কিছুই / ব্যক্তিগত অনুভূতির ভিত্তিতে) ফাইল এসোসিয়েশন নির্বাচন করা হলে একটি নতুন অ্যাপ্লিকেশন খোলার চেয়ে আরও ভাল। তবে আমি এখন জিজ্ঞাসা করা প্রশ্নটি আরও ভাল করে বুঝতে পারি। ধন্যবাদ!
এরিক ওয়েললেট

58

এমএসডিএন এর ঠিক এটি করতে সি # এবং ভিবি উভয়ের জন্য একটি নমুনা অ্যাপ্লিকেশন রয়েছে: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

একক-উদাহরণ সনাক্তকরণের বিকাশের জন্য সর্বাধিক সাধারণ এবং নির্ভরযোগ্য কৌশল হ'ল মাইক্রোসফ্ট। নেট ফ্রেমওয়ার্ক রিমোটিং অবকাঠামো (System.Remoting) ব্যবহার করা। মাইক্রোসফ্ট। নেট ফ্রেমওয়ার্ক (সংস্করণ ২.০) এর মধ্যে উইন্ডোজফর্মস অ্যাপ্লিকেশনবেস একটি প্রকার অন্তর্ভুক্ত রয়েছে যা প্রয়োজনীয় রিমোটিং কার্যকারিতা encapsulates। এই প্রকারটিকে ডাব্লুপিএফ অ্যাপ্লিকেশনটিতে অন্তর্ভুক্ত করার জন্য একটি প্রকারের থেকে এটি নেওয়া উচিত এবং অ্যাপ্লিকেশন স্ট্যাটিক এন্ট্রি পয়েন্ট পদ্ধতি, মেইন এবং ডাব্লুপিএফ অ্যাপ্লিকেশনটির অ্যাপ্লিকেশন ধরণের মধ্যে শিম হিসাবে ব্যবহার করা উচিত। শিমটি সনাক্ত করে যখন কোনও অ্যাপ্লিকেশন প্রথম চালু হয় এবং পরবর্তী লঞ্চগুলি চেষ্টা করা হয় এবং লঞ্চগুলি কীভাবে প্রক্রিয়াকরণ করা যায় তা নির্ধারণের জন্য ডাব্লুপিএফ অ্যাপ্লিকেশন টাইপ নিয়ন্ত্রণ করে।

  • সি # জনগণের জন্য কেবল একটি গভীর শ্বাস নেয় এবং পুরো 'আমি ভিজ্যুয়াল বেসিক ডিএলএল অন্তর্ভুক্ত করতে চাই না' ভুলে যান। এই কারণে এবং স্কট হ্যানসেলম্যান কী বলেছে এবং এটি যে সত্যই এটি সমস্যার সবচেয়ে পরিষ্কার সমাধান এবং এমন লোকেরা ডিজাইন করেছেন যা আপনার চেয়ে কাঠামো সম্পর্কে আরও অনেক কিছু জানেন।
  • ব্যবহারের দিক থেকে সত্য যদি আপনার ব্যবহারকারী কোনও অ্যাপ্লিকেশন লোড করে থাকে এবং এটি ইতিমধ্যে উন্মুক্ত এবং আপনি তাদের একটি ত্রুটি বার্তা দিচ্ছেন 'Another instance of the app is running. Bye'তবে তারা খুব খুশি ব্যবহারকারী হতে পারে না then আপনার কেবলমাত্র (একটি জিইউআই অ্যাপ্লিকেশনটিতে) সেই অ্যাপ্লিকেশনটিতে স্যুইচ করতে হবে এবং প্রদত্ত যুক্তিগুলি পাস করতে হবে - অথবা যদি কমান্ড লাইনের প্যারামিটারগুলির কোনও অর্থ না থাকে তবে আপনাকে অবশ্যই অ্যাপ্লিকেশনটি পপ আপ করতে হবে যা হ্রাস করা হতে পারে।

কাঠামোর ইতিমধ্যে এটির জন্য সমর্থন রয়েছে - এটি ঠিক যে কিছু বোকা ডিএলএল নাম দিয়েছে Microsoft.VisualBasicএবং এটি তেমন Microsoft.ApplicationUtilsকিছুতে লাগেনি। এটি পেতে - বা প্রতিচ্ছবি খুলুন।

টিপ: আপনি যদি এই পদ্ধতির ঠিক মতো ব্যবহার করেন এবং ইতিমধ্যে আপনার কাছে সম্পদ ইত্যাদির সাথে একটি App.xaml রয়েছে তবে আপনি এটিও একবার দেখতে চান


'এটিও একবার দেখুন' লিঙ্কটি অন্তর্ভুক্ত করার জন্য আপনাকে ধন্যবাদ। আমার যা প্রয়োজন তা হ'ল। যাইহোক, আপনার লিঙ্কে সমাধান # 3 হ'ল সেরা।
শাশ্বত 21

আমি ফ্রেমওয়ার্ক এবং বিশেষভাবে ডিজাইন করা লাইব্রেরিগুলিতে যেখানে সম্ভব সেখানে ডেলিগেটির একজন আইনজীবী ocate
এনিওলা

23

এই কোডটি মূল পদ্ধতিতে যাওয়া উচিত। তাকান এখানে WPF প্রধান পদ্ধতি সম্পর্কে আরও তথ্যের জন্য।

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;

static void Main() 
{
    Process currentProcess = Process.GetCurrentProcess();
    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();
    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
       return; 
    }
}

পদ্ধতি 2

static void Main()
{
    string procName = Process.GetCurrentProcess().ProcessName;
    // get the list of all processes by that name

    Process[] processes=Process.GetProcessesByName(procName);

    if (processes.Length > 1)
    {
        MessageBox.Show(procName + " already running");  
        return;
    } 
    else
    {
        // Application.Run(...);
    }
}

দ্রষ্টব্য: উপরের পদ্ধতিগুলি আপনার প্রক্রিয়া / অ্যাপ্লিকেশনটির একটি অনন্য নাম ধরেছে। কারণ এটি কোনও বিদ্যমান প্রসেসর কিনা তা অনুসন্ধানের জন্য প্রক্রিয়া নাম ব্যবহার করে। সুতরাং, যদি আপনার আবেদনের খুব সাধারণ নাম থাকে (যেমন: নোটপ্যাড), উপরের পদ্ধতির কাজ হবে না।


1
এছাড়াও, একই নামে আপনার কম্পিউটারে অন্য কোনও প্রোগ্রাম চলমান থাকলে এটি কাজ করবে না। ProcessNameএক্সিকিউটেবল ফাইলের নাম বিয়োগ করে দেয় exe। আপনি যদি "নোটপ্যাড" নামে একটি অ্যাপ্লিকেশন তৈরি করেন এবং উইন্ডোজ নোটপ্যাড চলমান থাকে তবে এটি আপনার অ্যাপ্লিকেশনটি চলমান হিসাবে এটি সনাক্ত করবে।
জেএলসি

1
এই উত্তরের জন্য ধন্যবাদ। আমি অনেকগুলি অনুরূপ প্রশ্ন পেয়েছি এবং উত্তরগুলি সর্বদা এতটা বিস্তৃত এবং / অথবা বিভ্রান্তিকর হয়েছিল যে আমি তাদের অকেজো পেয়েছি। এটির একটি (পদ্ধতি # 1) সোজা, পরিষ্কার এবং এগুলির বেশিরভাগই আমাকে আমার কোড চালাতে সাহায্য করেছিল।
ElDoRado1239

20

ঠিক আছে, এর জন্য আমার একটি ডিসপোজেবল ক্লাস রয়েছে যা বেশিরভাগ ব্যবহারের ক্ষেত্রে সহজেই কাজ করে:

এটি এর মতো ব্যবহার করুন:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

এটা এখানে:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}

1
এই এক কাজ করা খুব সহজ ছিল। আমি অ্যাপ্লিকেশন পরিবর্তন না হওয়া পর্যন্ত এটি দ্বিতীয় অ্যাপ্লিকেশনটি বন্ধ করবে না xএক্সিট (); সাধারণ রিটার্নে; কিন্তু এটি মহান ছাড়া অন্য। যদিও আমি স্বীকার করি আমি পূর্ববর্তী সমাধানটি আরও কাছাকাছি দেখতে যাচ্ছি কারণ এটি একটি ইন্টারফেস ব্যবহার করে। ব্লগস.মাইক্রোসফট.এইল
ব্লগস /

15

একটি নতুন যা মুটেক্স এবং আইপিসি স্টাফ ব্যবহার করে এবং যে কোনও কমান্ড লাইন আর্গুমেন্টকে চলমান দৃষ্টান্তগুলিতেও পাস করে, হ'ল ডাব্লুপিএফ সিঙ্গল ইনস্ট্যান্স অ্যাপ্লিকেশন


আমি এটি মহান সাফল্যের সাথে ব্যবহার করি। আপনি যদি এর সাথে নামযুক্ত পাইপগুলি অন্তর্ভুক্ত করেন তবে আপনি মূল অ্যাপ্লিকেশনটিতে কমান্ড-লাইন আর্গুমেন্টও পাস করতে পারেন। মাইক্রোসফ্ট লিখেছিল 'সিঙ্গেলআইন্সট্যানস। সি' ক্লাসটি। আমি কোডপ্রজেক্টে আরিক পোজনানস্কির ব্লগের আরও পাঠযোগ্য সংস্করণে আরও একটি লিঙ্ক যুক্ত করেছি।
হিলিয়াক

11

কোড সি # .NET সিঙ্গেল ইনস্ট্যান্স অ্যাপ্লিকেশন যা চিহ্নিত উত্তরের জন্য রেফারেন্স a

যাইহোক, আমি দেখতে পেয়েছি যে এটি ইতিমধ্যে উপস্থিত পরিস্থিতিগুলির কোনও মডেল ডায়ালগ খোলা আছে, সেই ডায়ালগটি কোনও পরিচালিত (যেমন অন্য কোনও ফর্মের মতো যেমন বাক্সের মতো), বা একটি নিয়ন্ত্রণহীন (যেমনটির মতো) পরিচালনা করে না স্ট্যান্ডার্ড .NET ক্লাসটি ব্যবহার করার সময়ও ওপেনফায়ালডায়ালগ)। আসল কোড সহ, প্রধান ফর্মটি সক্রিয় করা হয়, তবে মডেলটি অ্যাক্টিভ থাকে, যা দেখতে অদ্ভুত লাগে, এছাড়াও অ্যাপটিকে ব্যবহার করে চলতে ব্যবহারকারীকে অবশ্যই এটিতে ক্লিক করতে হবে।

সুতরাং, আমি উইনফর্মস এবং ডাব্লুপিএফ অ্যাপ্লিকেশনগুলির জন্য বেশ কয়েকটি স্বয়ংক্রিয়ভাবে এটি পরিচালনা করার জন্য একটি সিঙ্গলআইনস্ট্যান্স ইউটিলিটি ক্লাস তৈরি করেছি।

উইনফর্মস :

1) প্রোগ্রাম ক্লাসটি এর মতো পরিবর্তন করুন:

static class Program
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);

    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

2) মূল উইন্ডো শ্রেণিটি এর মতো পরিবর্তন করুন:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        // if needed, the singleton will restore this window
        Program.Singleton.OnWndProc(this, m, true);

        // TODO: handle specific messages here if needed
        base.WndProc(ref m);
    }
}

WPF:

1) অ্যাপ পৃষ্ঠাটি এর মতো পরিবর্তন করুন (এবং নিশ্চিত করুন যে আপনি মূল পদ্ধতিটিকে নতুন করে সংজ্ঞায়িত করতে সক্ষম করার জন্য পৃষ্ঠায় এর বিল্ড অ্যাকশনটি সেট করেছেন):

public partial class App : Application
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);

    [STAThread]
    public static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }
}

2) মূল উইন্ডো শ্রেণিটি এর মতো পরিবর্তন করুন:

public partial class MainWindow : Window
{
    private HwndSource _source;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        _source = (HwndSource)PresentationSource.FromVisual(this);
        _source.AddHook(HwndSourceHook);
    }

    protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // if needed, the singleton will restore this window
        App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);

        // TODO: handle other specific message
        return IntPtr.Zero;
    }

এবং এখানে ইউটিলিটি শ্রেণি রয়েছে:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceUtilities
{
    public sealed class SingleInstance
    {
        private const int HWND_BROADCAST = 0xFFFF;

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int RegisterWindowMessage(string message);

        [DllImport("user32.dll")]
        private static extern bool SetForegroundWindow(IntPtr hWnd);

        public SingleInstance(string uniqueName)
        {
            if (uniqueName == null)
                throw new ArgumentNullException("uniqueName");

            Mutex = new Mutex(true, uniqueName);
            Message = RegisterWindowMessage("WM_" + uniqueName);
        }

        public Mutex Mutex { get; private set; }
        public int Message { get; private set; }

        public void RunFirstInstance(Action action)
        {
            RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
        }

        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (WaitForMutext(wParam, lParam))
            {
                try
                {
                    action();
                }
                finally
                {
                    ReleaseMutex();
                }
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return;

            FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
        }

        public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
        {
            if (m == Message)
            {
                if (restorePlacement)
                {
                    WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
                    if (placement.IsValid && placement.IsMinimized)
                    {
                        const int SW_SHOWNORMAL = 1;
                        placement.ShowCmd = SW_SHOWNORMAL;
                        placement.SetPlacement(hwnd);
                    }
                }

                if (activate)
                {
                    SetForegroundWindow(hwnd);
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
                }
            }
        }

#if WINFORMS // define this for Winforms apps
        public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            if (m == Message)
            {
                if (activate)
                {
                    if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                    {
                        form.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    }

                    form.Activate();
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
                }
            }
        }

        public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
        }
#endif

        public void ReleaseMutex()
        {
            Mutex.ReleaseMutex();
        }

        public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
        {
            bool b = PrivateWaitForMutext(force);
            if (!b)
            {
                PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
            }
            return b;
        }

        public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
        {
            return WaitForMutext(false, wParam, lParam);
        }

        private bool PrivateWaitForMutext(bool force)
        {
            if (force)
                return true;

            try
            {
                return Mutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }

    // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowPlacement
    {
        public int Length { get; set; }
        public int Flags { get; set; }
        public int ShowCmd { get; set; }
        public int MinPositionX { get; set; }
        public int MinPositionY { get; set; }
        public int MaxPositionX { get; set; }
        public int MaxPositionY { get; set; }
        public int NormalPositionLeft { get; set; }
        public int NormalPositionTop { get; set; }
        public int NormalPositionRight { get; set; }
        public int NormalPositionBottom { get; set; }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        private const int SW_SHOWMINIMIZED = 2;

        public bool IsMinimized
        {
            get
            {
                return ShowCmd == SW_SHOWMINIMIZED;
            }
        }

        public bool IsValid
        {
            get
            {
                return Length == Marshal.SizeOf(typeof(WindowPlacement));
            }
        }

        public void SetPlacement(IntPtr windowHandle)
        {
            SetWindowPlacement(windowHandle, ref this);
        }

        public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
        {
            WindowPlacement placement = new WindowPlacement();
            if (windowHandle == IntPtr.Zero)
                return placement;

            placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
            if (!GetWindowPlacement(windowHandle, ref placement))
            {
                if (throwOnError)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return new WindowPlacement();
            }
            return placement;
        }
    }

    public static class FormUtilities
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);

        private class ModalWindowUtil
        {
            private const int GW_OWNER = 4;
            private int _maxOwnershipLevel;
            private IntPtr _maxOwnershipHandle;

            private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
            {
                int level = 1;
                if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
                {
                    if (level > _maxOwnershipLevel)
                    {
                        _maxOwnershipHandle = hwnd;
                        _maxOwnershipLevel = level;
                    }
                }
                return true;
            }

            private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
            {
                IntPtr o = GetWindow(hwnd, GW_OWNER);
                if (o == IntPtr.Zero)
                    return false;

                if (o == owner)
                    return true;

                level++;
                return IsOwned(owner, o, ref level);
            }

            public static void ActivateWindow(IntPtr hwnd)
            {
                if (hwnd != IntPtr.Zero)
                {
                    SetActiveWindow(hwnd);
                }
            }

            public static IntPtr GetModalWindow(IntPtr owner)
            {
                ModalWindowUtil util = new ModalWindowUtil();
                EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
                return util._maxOwnershipHandle; // may be IntPtr.Zero
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            ModalWindowUtil.ActivateWindow(hwnd);
        }

        public static IntPtr GetModalWindow(IntPtr owner)
        {
            return ModalWindowUtil.GetModalWindow(owner);
        }
    }
}

10

এখানে এমন একটি উদাহরণ রয়েছে যা আপনাকে কোনও অ্যাপ্লিকেশনটির একক উদাহরণ থাকতে দেয়। যখন কোনও নতুন দৃষ্টান্ত লোড হয়, তখন তারা তাদের যুক্তিগুলি মূল দৃষ্টান্তের মধ্যে চলে যা যা চলছে।

public partial class App : Application
{
    private static Mutex SingleMutex;
    public static uint MessageId;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        IntPtr Result;
        IntPtr SendOk;
        Win32.COPYDATASTRUCT CopyData;
        string[] Args;
        IntPtr CopyDataMem;
        bool AllowMultipleInstances = false;

        Args = Environment.GetCommandLineArgs();

        // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
        MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
        SingleMutex = new Mutex(false, "AppName");

        if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
        {
            new Main();
        }
        else if (Args.Length > 1)
        {
            foreach (Process Proc in Process.GetProcesses())
            {
                SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    2000, out Result);

                if (SendOk == IntPtr.Zero)
                    continue;
                if ((uint)Result != MessageId)
                    continue;

                CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));

                CopyData.dwData = IntPtr.Zero;
                CopyData.cbData = Args[1].Length*2;
                CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);

                Marshal.StructureToPtr(CopyData, CopyDataMem, false);

                Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    5000, out Result);

                Marshal.FreeHGlobal(CopyData.lpData);
                Marshal.FreeHGlobal(CopyDataMem);
            }

            Shutdown(0);
        }
    }
}

public partial class Main : Window
{
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        HwndSource Source;

        Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        Source.AddHook(new HwndSourceHook(Window_Proc));
    }

    private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
    {
        Win32.COPYDATASTRUCT CopyData;
        string Path;

        if (Msg == Win32.WM_COPYDATA)
        {
            CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
            Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);

            if (WindowState == WindowState.Minimized)
            {
                // Restore window from tray
            }

            // Do whatever we want with information

            Activate();
            Focus();
        }

        if (Msg == App.MessageId)
        {
            Handled = true;
            return new IntPtr(App.MessageId);
        }

        return IntPtr.Zero;
    }
}

public class Win32
{
    public const uint WM_COPYDATA = 0x004A;

    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int    cbData;
        public IntPtr lpData;
    }

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL             = 0x0000,
        SMTO_BLOCK              = 0x0001,
        SMTO_ABORTIFHUNG        = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
        SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}

আমি কী করব তার এটি একটি দুর্দান্ত উদাহরণ। নাথান, সমস্ত পদ্ধতি কি এই পদ্ধতিটি ব্যবহার করে প্রেরণ করা হয়? আমার অ্যাপটিতে আমার 7 বা তত বেশি রয়েছে এবং আমি মনে করি যে এই কোডটি কার্যকর হবে।
কেভিপি

1
আমার উদাহরণে, কেবল প্রথম যুক্তিটি প্রেরণ করা হয়েছে, তবে এটি পরিবর্তন করা যেতে পারে যাতে সেগুলি সমস্ত প্রেরণ করা হয়।
নাথন মoinনভাজিরী

8

কিছু কিছু ধারণা: এমন কিছু ক্ষেত্রে রয়েছে যখন প্রয়োজন হয় যে কোনও অ্যাপ্লিকেশনটির কেবলমাত্র একটি উদাহরণ "খোঁড়া" না হয় কারণ কিছু আপনার বিশ্বাস করতে পারে। ডাটাবেস অ্যাপ্লিকেশন ইত্যাদির পরিমাণ আরও জটিলতার অর্ডার হয় যদি কোনও একক ব্যবহারকারীর জন্য কোনও ডাটাবেস অ্যাক্সেস করার জন্য অ্যাপটির একাধিক উদাহরণের অনুমতি দেয় (আপনি জানেন যে অ্যাপ্লিকেশনগুলিতে অ্যাপ্লিকেশনের একাধিক ক্ষেত্রে খালি সমস্ত রেকর্ড আপডেট করে থাকে মেশিন, ইত্যাদি)। প্রথমে, "নাম সংঘর্ষের জিনিসটির জন্য, কোনও মানব পাঠযোগ্য নাম ব্যবহার করবেন না - পরিবর্তে একটি জিইউইডি ব্যবহার করুন বা আরও ভাল একটি জিইউইডি + মানব পাঠযোগ্য নাম ব্যবহার করুন name নাম সংঘর্ষের সম্ভাবনা সবেমাত্র রাডার থেকে নামিয়ে দিয়েছে এবং মন্টেক্সের কোনও যত্ন নেই । যেহেতু কেউ উল্লেখ করেছে যে কোনও ডস আক্রমণটি চুষতে পারে, তবে দূষিত ব্যক্তি যদি মিটেক্স নামটি পেয়ে এবং তাদের অ্যাপ্লিকেশনটিতে এটি সংযোজন করার সমস্যায় পড়ে থাকে, আপনি যে কোনও উপায়েই বেশ লক্ষ্য এবং নিজেকে একটি সুরকারের নাম বাদ দেওয়ার চেয়ে নিজেকে রক্ষা করতে আরও অনেক কিছু করতে হবে। এছাড়াও, যদি কেউ এর রূপটি ব্যবহার করে: নতুন মুটেক্স (সত্য, "কিছু জিইউইডি প্লাস নাম", এআইআইফারস্টাইন্সট্যান্স আউট), আপনার ইতিমধ্যে আপনার সূচকটি আছে যে মুটেক্স প্রথম উদাহরণ কিনা।


6

এমন একটি আপাতদৃষ্টিতে সরল প্রশ্নের এত উত্তর। এখানে কিছুটা সামান্য ঝাঁকুনি দেওয়া আমার এই সমস্যার সমাধান।

একটি মিটেক্স তৈরি করা ঝামেলা হতে পারে কারণ JIT-er কেবল আপনাকে এটি আপনার কোডের একটি ছোট অংশের জন্য ব্যবহার করতে দেখে এবং এটি আবর্জনা সংগ্রহের জন্য প্রস্তুত হিসাবে চিহ্নিত করতে চায়। এটি আপনি অনেকক্ষণ যে স্মৃতিচারণ করতে চান তা ভেবে স্মার্ট করতে চাইছে। বাস্তবে আপনি যতক্ষণ আপনার অ্যাপ্লিকেশনটি চালাচ্ছেন ততক্ষণ এই মুটেক্সটিতে ঝুলতে চান। আবর্জনা সংগ্রাহককে আপনাকে মুতেেক্সকে একা রেখে যাওয়ার সেরা উপায় হ'ল গ্যারেজ সংগ্রহের বিভিন্ন প্রজন্মের বাইরে থাকলেও এটিকে বাঁচিয়ে রাখতে বলুন। উদাহরণ:

var m = new Mutex(...);
...
GC.KeepAlive(m);

আমি এই পৃষ্ঠা থেকে ধারণাটি তুলেছি: http://www.ai.uga.edu/~mc/SingleInstance.html


3
অ্যাপ্লিকেশন ক্লাসে এর একটি ভাগ করা অনুলিপি সংরক্ষণ করা কি সহজ হবে না?
রসাইসিয়েড

6

দেখে মনে হচ্ছে এটি হ্যান্ডেল করার সত্যিই ভাল উপায় আছে:

ডাব্লুপিএফ একক ইনস্ট্যান্স অ্যাপ্লিকেশন

এটি এমন একটি শ্রেণি সরবরাহ করে যা আপনি যুক্ত করতে পারেন যা আপনার রূপায়নটি সহজতর করার জন্য যেখানে সরলভাবে তুচ্ছ all


এটি ব্যবহার করার সময় বিদ্যমান উইন্ডোটিকে অগ্রভূমিতে আনার কথা মনে হয় না।
র্যান্ডম এঙ্গি

6

নিম্নলিখিত কোডটি আমার ডাব্লুসিএফ নামক পাইপগুলির সমাধান যা একক-উদাহরণ অ্যাপ্লিকেশনটি নিবন্ধ করতে পারে। এটি দুর্দান্ত কারণ এটি যখন একটি ইভেন্ট উত্থাপন করে যখন অন্য উদাহরণ শুরু করার চেষ্টা করে এবং অন্য উদাহরণটির কমান্ড লাইন গ্রহণ করে।

এটি ডাব্লুপিএফের দিকে প্রস্তুত কারণ এটি System.Windows.StartupEventHandlerক্লাস ব্যবহার করে , তবে এটি সহজেই সংশোধন করা যেতে পারে।

এই কোডটির জন্য একটি রেফারেন্স প্রয়োজন PresentationFramework, এবং System.ServiceModel

ব্যবহার:

class Program
{
    static void Main()
    {
        var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");

        if (SingleInstanceManager.VerifySingleInstance(applicationId))
        {
            SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;

            // Start the application
        }
    }

    static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
    {
        // Do something in response to another instance starting up.
    }
}

সোর্স কোড:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
  /// <summary>
  /// Raised when another instance attempts to start up.
  /// </summary>
  public static event StartupEventHandler OtherInstanceStarted;

  /// <summary>
  /// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
  /// send the main instance this instance's startup information.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if this instance is the main instance.</returns>
  public static bool VerifySingleInstace(Guid guid)
  {
    if (!AttemptPublishService(guid))
    {
      NotifyMainInstance(guid);

      return false;
    }

    return true;
  }

  /// <summary>
  /// Attempts to publish the service.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if the service was published successfully.</returns>
  private static bool AttemptPublishService(Guid guid)
  {
    try
    {
      ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
      NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
      serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
      serviceHost.Open();

      return true;
    }
    catch
    {
      return false;
    }
  }

  /// <summary>
  /// Notifies the main instance that this instance is attempting to start up.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  private static void NotifyMainInstance(Guid guid)
  {
    NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
    using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
    {
      ISingleInstance singleInstance = factory.CreateChannel();
      singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
    }
  }

  /// <summary>
  /// Creates an address to publish/contact the service at based on a globally unique identifier.
  /// </summary>
  /// <param name="guid">The identifier for the application.</param>
  /// <returns>The address to publish/contact the service.</returns>
  private static string CreateAddress(Guid guid)
  {
    return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
  }

  /// <summary>
  /// The interface that describes the single instance service.
  /// </summary>
  [ServiceContract]
  private interface ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    [OperationContract]
    void NotifyMainInstance(string[] args);
  }

  /// <summary>
  /// The implementation of the single instance service interface.
  /// </summary>
  private class SingleInstance : ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    public void NotifyMainInstance(string[] args)
    {
      if (OtherInstanceStarted != null)
      {
        Type type = typeof(StartupEventArgs);
        ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
        StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
        FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
        Debug.Assert(argsField != null);
        argsField.SetValue(e, args);

        OtherInstanceStarted(null, e);
      }
    }
  }
}

5

একক-দৃষ্টান্ত অ্যাপ্লিকেশন প্রয়োগ করার জন্য আপনার কখনই নামযুক্ত মুটেক্স ব্যবহার করা উচিত নয় (বা কমপক্ষে উত্পাদন কোডের জন্য নয়)। ক্ষতিকারক কোডগুলি সহজেই আপনার পাছার ডিওএস ( পরিষেবা প্রত্যাখ্যান ) করতে পারে ...


8
"আপনার কোনও নামযুক্ত মুটেক্স ব্যবহার করা উচিত নয়" - কখনও কখনও বলবেন না। যদি আমার মেশিনে দূষিত কোডটি চলমান থাকে তবে আমি সম্ভবত ইতিমধ্যে হোজেড।
জো

আসলে এটি এমনকি দূষিত কোড হতে হবে না। এটি কেবল একটি দুর্ঘটনা নামের সংঘর্ষ হতে পারে।
ম্যাট ডেভিসন

তাহলে আপনার কি করা উচিত?
কেভিন বাররিজ

আরও ভাল প্রশ্ন হ'ল আপনি কী আচরণের সম্ভাব্য কারণটি চান। আপনার অ্যাপ্লিকেশনটিকে একক উদাহরণ অ্যাপ্লিকেশন হিসাবে ডিজাইন করবেন না =)। আমি জানি এটি একটি খোঁড়া উত্তর তবে ডিজাইনের দৃষ্টিকোণ থেকে এটি প্রায়শই সঠিক উত্তর। অ্যাপটি সম্পর্কে আরও কিছু না জেনে আরও বেশি বলা শক্ত।
ম্যাট ডেভিসন

2
কমপক্ষে উইন্ডোজের অধীনে, মুটেক্সেসের অ্যাক্সেস নিয়ন্ত্রণ রয়েছে, সুতরাং একজন আপনার সামগ্রীর সাথে খেলনা করতে পারে। নিজেদের সংঘর্ষের নাম হিসাবে, সেই জন্যই ইউইউডি / জিইউডি যেখানে আবিষ্কার হয়েছিল।
নিউস্কুলার

5

ফলোলিং কোডটি দেখুন। ডাব্লুপিএফ অ্যাপ্লিকেশনটির একাধিক ঘটনা প্রতিরোধের জন্য এটি দুর্দান্ত এবং সাধারণ সমাধান।

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process thisProc = Process.GetCurrentProcess();
    if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
    {
        MessageBox.Show("Application running");
        Application.Current.Shutdown();
        return;
    }

    var wLogin = new LoginWindow();

    if (wLogin.ShowDialog() == true)
    {
        var wMain = new Main();
        wMain.WindowState = WindowState.Maximized;
        wMain.Show();
    }
    else
    {
        Application.Current.Shutdown();
    }
}

4

আমি যা ব্যবহার করি তা এখানে। এটি "সক্রিয় ক্লিককারীদের" থেকে সুরক্ষার জন্য সুইচিং এবং মিটেক্স সম্পাদন করার জন্য প্রক্রিয়া গণনার সংযুক্ত করে:

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }

4

আমি সহজ সমাধানটি পেয়েছি, ডেল রাগানের মতো, তবে কিছুটা সংশোধন করেছি। এটি ব্যবহারিকভাবে আপনার প্রয়োজন এবং স্ট্যান্ডার্ড মাইক্রোসফ্ট উইন্ডোজফর্মস অ্যাপ্লিকেশনবেস ক্লাসের উপর ভিত্তি করে সমস্ত কিছু করে।

প্রথমত, আপনি সিঙ্গেলআইন্সটেন্স কন্ট্রোলার ক্লাস তৈরি করেন, যা আপনি উইন্ডোজ ফর্ম ব্যবহার করে এমন সমস্ত অন্যান্য একক-উদাহরণ অ্যাপ্লিকেশনগুলিতে ব্যবহার করতে পারেন:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace SingleInstanceController_NET
{
    public class SingleInstanceController
    : WindowsFormsApplicationBase
    {
        public delegate Form CreateMainForm();
        public delegate void StartNextInstanceDelegate(Form mainWindow);
        CreateMainForm formCreation;
        StartNextInstanceDelegate onStartNextInstance;
        public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
        {
            // Set whether the application is single instance
            this.formCreation = formCreation;
            this.onStartNextInstance = onStartNextInstance;
            this.IsSingleInstance = true;

            this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);                      
        }

        void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
        {
            if (onStartNextInstance != null)
            {
                onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
                                                    // for example, by clicking on the exe file.
            }                                       // This code can determine how to re-activate the existing main window of the running application.
        }

        protected override void OnCreateMainForm()
        {
            // Instantiate your main application form
            this.MainForm = formCreation();
        }

        public void Run()
        {
            string[] commandLine = new string[0];
            base.Run(commandLine);
        }
    }
}

তারপরে আপনি এটিকে আপনার প্রোগ্রামে ব্যবহার করতে পারেন:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;

namespace SingleInstance
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static Form CreateForm()
        {
            return new Form1(); // Form1 is used for the main window.
        }

        static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
                                                         // the main window is activated again.
        {
            mainWindow.WindowState = FormWindowState.Maximized;
        }
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);            
            SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
            controller.Run();         
        }
    }
}

প্রোগ্রাম এবং সিঙ্গেলআইন্সটেন্স কন্ট্রোলার_ নেট সমাধান উভয়েরই মাইক্রোসফ্ট.ভিজুয়ালবাসিক উল্লেখ করা উচিত। ব্যবহারকারী যখন চলমান প্রোগ্রামটি পুনরায় চালু করার চেষ্টা করে আপনি কেবল চলমান অ্যাপ্লিকেশনটিকে একটি সাধারণ উইন্ডো হিসাবে পুনরায় সক্রিয় করতে চান তবে সিঙ্গেলআইন্সট্যান্স কনট্রোলারের দ্বিতীয় প্যারামিটারটি বাতিল হতে পারে। প্রদত্ত উদাহরণে উইন্ডোটি সর্বাধিক করা হয়েছে।


4

আপডেট 2017-01-25। কয়েকটি জিনিস চেষ্টা করার পরে, আমি ভিজুয়ালবাসিকের সাথে যাবার সিদ্ধান্ত নিয়েছি d এটি আরও সহজ এবং আরও ভাল কাজ করে (কমপক্ষে আমার জন্য)। আমি আমার আগের উত্তরটি ঠিক রেফারেন্স হিসাবে দিয়েছি ...

ঠিক যেমন রেফারেন্স হিসাবে, আমি এভাবেই যুক্তিগুলি পাস না করেই করেছি (যা আমি এরকম করার কোনও কারণ খুঁজে পাচ্ছি না ... আমি যুক্তিযুক্ত একটি একক অ্যাপ্লিকেশনটিকে বোঝাতে চাইছি যা একটি উদাহরণ থেকে অন্য একটি ক্ষেত্রে চলে যেতে পারে)। যদি ফাইল অ্যাসোসিয়েশন প্রয়োজন হয় তবে প্রতিটি অ্যাপ্লিকেশনকে (প্রতিটি ব্যবহারকারীর স্ট্যান্ডার্ড প্রত্যাশা) প্রতিটি নথির জন্য ইনস্ট্যান্সিয়েশন করা উচিত। আপনার যদি বিদ্যমান অ্যাপ্লিকেশনটিতে আরোগুলি পাস করতে হয় তবে আমার মনে হয় আমি vb dll ব্যবহার করব।

আরোগুলি পাস না করে (কেবলমাত্র একক উদাহরণ অ্যাপ্লিকেশন), আমি কোনও নতুন উইন্ডো বার্তা নিবন্ধন না করা এবং ম্যাট ডেভিস সলিউশন হিসাবে সংজ্ঞায়িত বার্তা লুপকে ওভাররাইড না করা পছন্দ করি। যদিও ভিজ্যুয়াল বেসিক ডেল যুক্ত করা কোনও বড় বিষয় নয়, তবে আমি কেবল একক উদাহরণ অ্যাপ্লিকেশন করতে কোনও নতুন রেফারেন্স যুক্ত করতে পছন্দ করি না। এছাড়াও, আমি যত তাড়াতাড়ি সম্ভব প্রস্থান করার বিষয়টি নিশ্চিত করতে অ্যাপ.স্টার্টআপ ওভাররাইড থেকে শাটডাউন কল করার পরিবর্তে মেইন সহ একটি নতুন ক্লাস ইনস্ট্যান্সেট করতে পছন্দ করি না।

আশা করি যে কেউ এটি পছন্দ করবে ... বা কিছুটা অনুপ্রাণিত করবে :-)

প্রজেক্টের স্টার্টআপ ক্লাসটি 'সিঙ্গলইনস্ট্যান্স অ্যাপ' হিসাবে সেট করা উচিত।

public class SingleInstanceApp
{
    [STAThread]
    public static void Main(string[] args)
    {
        Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");

        if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

            }
            finally
            {
                _mutexSingleInstance.ReleaseMutex();
                _mutexSingleInstance.Close();
            }
        }
        else
        {
            MessageBox.Show("One instance is already running.");

            var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
            {
                if (processes.Length > 1)
                {
                    foreach (var process in processes)
                    {
                        if (process.Id != Process.GetCurrentProcess().Id)
                        {
                            WindowHelper.SetForegroundWindow(process.MainWindowHandle);
                        }
                    }
                }
            }
        }
    }
}

WindowHelper:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace HQ.Util.Unmanaged
{
    public class WindowHelper
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

3

যদিও মুটেেক্স ব্যবহার করছেন না, সহজ উত্তর:

System.Diagnostics;    
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;

if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

এটি ভিতরে রাখুন Program.Main()
উদাহরণ :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sample
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //simple add Diagnostics namespace, and these 3 lines below 
            string thisprocessname = Process.GetCurrentProcess().ProcessName;
            if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Sample());
        }
    }
}

আপনি- স্টেটমেন্টে যুক্ত MessageBox.Showকরতে পারেন ifএবং "অ্যাপ্লিকেশন ইতিমধ্যে চলছে" রাখতে পারেন।
এটি কারও পক্ষে সহায়ক হতে পারে।


4
যদি একই সাথে দুটি প্রক্রিয়া শুরু হয় তবে তারা উভয়ই দুটি সক্রিয় প্রক্রিয়া এবং স্ব-সমাপ্ত হতে পারে।
এটি

@AT হ্যাঁ ঠিক, অ্যাডমিনিস্ট্রেটর হিসাবে চলমান অ্যাপ্লিকেশনগুলিতেও এটি সহায়ক হতে পারে বা অন্যথায়
newbieguy

আপনি যদি আপনার অ্যাপ্লিকেশনটির একটি অনুলিপি তৈরি করেন এবং এটির পুনরায় নামকরণ করেন, আপনি একই সাথে মূল এবং অনুলিপিটি চালাতে পারেন।
ডোমিনিক বিজনেস

2

নামযুক্ত-মুটেক্স-ভিত্তিক পন্থাগুলি ক্রস প্ল্যাটফর্ম নয় কারণ নামযুক্ত মুটেক্সগুলি মনোতে বিশ্বব্যাপী নয়। প্রক্রিয়া-গণনা-ভিত্তিক পদ্ধতির কোনও সিঙ্ক্রোনাইজেশন নেই এবং এর ফলে ভুল আচরণ হতে পারে (যেমন: একই সময়ে শুরু হওয়া একাধিক প্রক্রিয়া সময় অনুসারে সমস্ত স্ব-সমাপ্ত হতে পারে)। উইন্ডোভিং-সিস্টেম-ভিত্তিক পদ্ধতিগুলি কোনও কনসোল অ্যাপ্লিকেশনে পছন্দসই নয়। ডিভিনের উত্তরের উপরে নির্মিত এই সমাধানটি এই সমস্ত সমস্যার সমাধান করে:

using System;
using System.IO;

namespace TestCs
{
    public class Program
    {
        // The app id must be unique. Generate a new guid for your application. 
        public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";

        // The stream is stored globally to ensure that it won't be disposed before the application terminates.
        public static FileStream UniqueInstanceStream;

        public static int Main(string[] args)
        {
            EnsureUniqueInstance();

            // Your code here.

            return 0;
        }

        private static void EnsureUniqueInstance()
        {
            // Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
            string lockDir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
                "UniqueInstanceApps");
            string lockPath = Path.Combine(lockDir, $"{AppId}.unique");

            Directory.CreateDirectory(lockDir);

            try
            {
                // Create the file with exclusive write access. If this fails, then another process is executing.
                UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);

                // Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
                // (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
                UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
                UniqueInstanceStream.Flush();
            }
            catch
            {
                throw new Exception("Another instance of the application is already running.");
            }
        }
    }
}

2

আমি একাধিক ঘটনা প্রতিরোধের জন্য আমার সমাধানটিতে মুটেক্স ব্যবহার করি।

static Mutex mutex = null;
//A string that is the name of the mutex
string mutexName = @"Global\test";
//Prevent Multiple Instances of Application
bool onlyInstance = false;
mutex = new Mutex(true, mutexName, out onlyInstance);

if (!onlyInstance)
{
  MessageBox.Show("You are already running this application in your system.", "Already Running..", MessageBoxButton.OK);
  Application.Current.Shutdown();
}

1

মিউটেক্স দ্রবণটি ব্যবহার করুন:

using System;
using System.Windows.Forms;
using System.Threading;

namespace OneAndOnlyOne
{
static class Program
{
    static String _mutexID = " // generate guid"
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Boolean _isNotRunning;
        using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
        {
            if (_isNotRunning)
            {
                Application.Run(new Form1());
            }
            else
            {
                MessageBox.Show("An instance is already running.");
                return;
            }
        }
    }
}
}

1

এখানে আমি ব্যবহার করি এমন একটি হালকা ওজনের সমাধান যা কাস্টম উইন্ডোজ বার্তাগুলি বা অন্ধভাবে প্রক্রিয়া নাম অনুসন্ধান না করে অ্যাপ্লিকেশনটিকে ইতিমধ্যে বিদ্যমান উইন্ডোটিকে অগ্রভাগে আনতে দেয়।

[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);

static readonly string guid = "<Application Guid>";

static void Main()
{
    Mutex mutex = null;
    if (!CreateMutex(out mutex))
        return;

    // Application startup code.

    Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}

static bool CreateMutex(out Mutex mutex)
{
    bool createdNew = false;
    mutex = new Mutex(false, guid, out createdNew);

    if (createdNew)
    {
        Process process = Process.GetCurrentProcess();
        string value = process.Id.ToString();

        Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
    }
    else
    {
        string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
        Process process = null;
        int processId = -1;

        if (int.TryParse(value, out processId))
            process = Process.GetProcessById(processId);

        if (process == null || !SetForegroundWindow(process.MainWindowHandle))
            MessageBox.Show("Unable to start application. An instance of this application is already running.");
    }

    return createdNew;
}

সম্পাদনা: আপনি মুটেক্স এবং স্টাটিক্যালি নতুন তৈরি এবং সংরক্ষণ করতে পারেন, তবে আপনি একবার মুটিেক্সটি সম্পূর্ণ করার পরে তা স্পষ্টভাবে নিষ্পত্তি / প্রকাশ করতে হবে। ব্যক্তিগতভাবে, আমি মুটেক্স স্থানীয় রাখতে পছন্দ করি কারণ এটি মাইনের শেষ না পৌঁছানো ছাড়াই অ্যাপ্লিকেশনটি বন্ধ হয়ে গেলেও এটি স্বয়ংক্রিয়ভাবে নিষ্পত্তি হবে।



1

আমি নেটিভমেথডস ক্লাসে একটি সেন্ডম্যাসেজ পদ্ধতি যুক্ত করেছি।

স্পষ্টতই পোস্টম্যাসেজ পদ্ধতিটি ডোজেন্ট কাজ করে, যদি অ্যাপ্লিকেশনটি টাস্কবারে না দেখানো হয় তবে সেন্ডমেজ পদ্ধতি ব্যবহার করে এটি সমাধান করে।

class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}

1

ইভেন্টের মাধ্যমে বাস্তবায়িত একই জিনিস এখানে।

public enum ApplicationSingleInstanceMode
{
    CurrentUserSession,
    AllSessionsOfCurrentUser,
    Pc
}

public class ApplicationSingleInstancePerUser: IDisposable
{
    private readonly EventWaitHandle _event;

    /// <summary>
    /// Shows if the current instance of ghost is the first
    /// </summary>
    public bool FirstInstance { get; private set; }

    /// <summary>
    /// Initializes 
    /// </summary>
    /// <param name="applicationName">The application name</param>
    /// <param name="mode">The single mode</param>
    public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
    {
        string name;
        if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
            name = $"Local\\{applicationName}";
        else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
            name = $"Global\\{applicationName}{Environment.UserDomainName}";
        else
            name = $"Global\\{applicationName}";

        try
        {
            bool created;
            _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
            FirstInstance = created;
        }
        catch
        {
        }
    }

    public void Dispose()
    {
        _event.Dispose();
    }
}

1

[আমি নীচে কনসোল এবং ডাব্লুপিএফ অ্যাপ্লিকেশনগুলির জন্য নমুনা কোড সরবরাহ করেছি]]

আপনাকে কেবলমাত্র এর মান পরীক্ষা করতে হবে createdNew পরিবর্তনকারী উদাহরণটি তৈরি করার পরে আপনাকে ভেরিয়েবলের (নীচে উদাহরণ!)।

বুলিয়ান মিথ্যাcreatedNew ফিরে আসবে :

যদি "আপনার অ্যাপ্লিকেশননাম এখানে" নামক মুটেক্স ইনস্ট্যান্সটি ইতিমধ্যে কোথাও কোথাও সিস্টেমে তৈরি করা হয়েছিল

বুলিয়ান সত্যcreatedNew ফিরে আসবে :

যদি এটি সিস্টেমে "আপনার অ্যাপ্লিকেশননামহিয়ার" নামক প্রথম মুটেক্স হয়।


কনসোল অ্যাপ্লিকেশন - উদাহরণ:

static Mutex m = null;

static void Main(string[] args)
{
    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        using (m = new Mutex(true, mutexName, out createdNew))
        {
            if (!createdNew)
            {
                Console.WriteLine("instance is alreday running... shutting down !!!");
                Console.Read();
                return; // Exit the application
            }

            // Run your windows forms app here
            Console.WriteLine("Single instance app is running!");
            Console.ReadLine();
        }


    }
    catch (Exception ex)
    {

        Console.WriteLine(ex.Message);
        Console.ReadLine();
    }
}

WPF-উদাহরণ:

public partial class App : Application
{
static Mutex m = null;

protected override void OnStartup(StartupEventArgs e)
{

    const string mutexName = "YourApplicationNameHere";
    bool createdNew = false;

    try
    {
        // Initializes a new instance of the Mutex class with a Boolean value that indicates 
        // whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex, 
        // and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.

        m = new Mutex(true, mutexName, out createdNew);

        if (!createdNew)
        {
            Current.Shutdown(); // Exit the application
        }

    }
    catch (Exception)
    {
        throw;
    }

    base.OnStartup(e);
}


protected override void OnExit(ExitEventArgs e)
{
    if (m != null)
    {
        m.Dispose();
    }
    base.OnExit(e);
}
}

1

সি # উইনফর্মগুলির জন্য একটি সময় সাশ্রয় সমাধান ...

Program.cs:

using System;
using System.Windows.Forms;
// needs reference to Microsoft.VisualBasic
using Microsoft.VisualBasic.ApplicationServices;  

namespace YourNamespace
{
    public class SingleInstanceController : WindowsFormsApplicationBase
    {
        public SingleInstanceController()
        {
            this.IsSingleInstance = true;
        }

        protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e)
        {
            e.BringToForeground = true;
            base.OnStartupNextInstance(e);
        }

        protected override void OnCreateMainForm()
        {
            this.MainForm = new Form1();
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            string[] args = Environment.GetCommandLineArgs();
            SingleInstanceController controller = new SingleInstanceController();
            controller.Run(args);
        }
    }
}

1

থেকে প্রস্তাবিত সমাধান দয়া করে চেক করুন এখানে যা কোনও বিদ্যমান উদাহরণ ইতিমধ্যে চলছে কিনা তা নির্ধারণের জন্য একটি সেমফোর ব্যবহার করে, ডাব্লুপিএফ অ্যাপ্লিকেশনের জন্য কাজ করে এবং দ্বিতীয় উদাহরণ থেকে টিসিপিলিস্টার এবং একটি টিসিপিপ্লাইন্ট ব্যবহার করে ইতিমধ্যে চলমান প্রথম উদাহরণে যুক্তিগুলি পাস করতে পারে:

এটি শুধুমাত্র নেট নেট ফ্রেমওয়ার্কের জন্য নয়, নেট কোরের জন্যও কাজ করে।


1

আমি এখানে একটি সংক্ষিপ্ত সমাধান খুঁজে পাচ্ছি না তাই আমি আশা করি কেউ এটি পছন্দ করবে:

আপডেটেড 2018-09-20

আপনার এই কোডটি রাখুন Program.cs:

using System.Diagnostics;

static void Main()
{
    Process thisProcess = Process.GetCurrentProcess();
    Process[] allProcesses = Process.GetProcessesByName(thisProcess.ProcessName);
    if (allProcesses.Length > 1)
    {
        // Don't put a MessageBox in here because the user could spam this MessageBox.
        return;
    }

    // Optional code. If you don't want that someone runs your ".exe" with a different name:

    string exeName = AppDomain.CurrentDomain.FriendlyName;
    // in debug mode, don't forget that you don't use your normal .exe name.
    // Debug uses the .vshost.exe.
    if (exeName != "the name of your executable.exe") 
    {
        // You can add a MessageBox here if you want.
        // To point out to users that the name got changed and maybe what the name should be or something like that^^ 
        MessageBox.Show("The executable name should be \"the name of your executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    // Following code is default code:
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new MainForm());
}

এটি একটি রেসের শর্ত প্রবর্তন করবে। অবশ্যই একটি মিটেক্স ব্যবহার করা উচিত।
জর্জিওজড

1
কোনও গ্যারান্টি নেই যে আপনি একই সময়ে দুটি উদাহরণ সরিয়ে ফেললে এটি কাজ করবে। দুটি ভিন্ন থ্রেড থেকে পরিবর্তনশীল আপডেট করার মতো। কৌতুকপূর্ণ ঝুঁকিপূর্ণ ব্যবসা। বাহিনী, লুক :) ব্যবহার করুন
জর্জিওসিস্পে

@ জর্জিওস আহ আমি আপনার বোঝাতে চাইছি যদি কেউ .exe শুরু করে এবং নাম পরিবর্তন করে। হ্যাঁ এটি আরও একবার এটি শুরু করার একটি উপায় হতে পারে তবে নামটি পরিবর্তিত হয়ে গেলে সাধারণভাবে .exe কাজ করে না। আমি আমার উত্তর আপডেট করব Luke আপনাকে ধন্যবাদ লুক: ডি নির্দেশ করার জন্য ডি: :)
ডেনিজ

1
শুধু এটিই নয় @ ডেনিজ। আপনি যদি দুটি প্রক্রিয়া সত্যিই দ্রুত শুরু করেন, তবে সম্ভাবনা রয়েছে যে প্রক্রিয়া তালিকা বা তাদের আনার পদ্ধতিটি কার্যকর হবে যখন এখনও একটি মাত্র উপস্থিত রয়েছে। এটি আপনার পক্ষে অপ্রাসঙ্গিক তবে এটি একটি সাধারণ প্রশ্ন হতে পারে ...
জর্জিয়াস

আপনি কি তা প্রমাণ করতে পারবেন? কারণ Iv'e এটি কেবল আপনার জন্য পরীক্ষা করেছে। তবে এটি আমার পক্ষে সম্ভব ছিল না, এমনকি "সত্যই দ্রুত"! : পি তাই আমি বুঝতে পারি না যে আপনি এমন কোনও কিছুতে কেন বিশ্বাস করেন যা এই ঘটনা নয় এবং এমনকি এই নিরীহ কোডটি অপছন্দ করে: ডি
ডেনিজ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.