گفتگو هایی در باب سی شارپ

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام
آها اینو فراموش کردم بپرسم
اینکه رویدادها (دلیگیت ها) مثلا شی (رویداد) Click ، بعد از مقداردهی ، باید جایی فراخونی بشه و پارامترهای آرگومان متد (رویدادی) که میخواد اجرا کنه ، بهش داده بشه تا اینکه رویداد یا متد مورد نظر رو اجرا کنه دیگه؟
خوب ما فقط دلیگیت یا رویدادها رو مقداردهی میکنیم . مثلا وقتی مینویسیم :

کد:
button1.Click += new EventHandler(MyFunctionTest1);
مقداردهی اش کردیم . ولی بعدش نیازه که این شی دلیگیت (رویداد) که نامش اینجا button1.Click هست رو فراخونی و آرگومان های متد مورد نظرش رو بدیم اما این کار رو نمیکنیم. یعنی در واقع وقتی کلیک میکنیم ، کی و چجوری فهمیده میشه که متد مورد نظر اجرا بشه؟
 

the_king

مدیرکل انجمن
سلام استاد علی
میشه شوخی کرد؟ :green:میگم چند روز نبودم ، فکر کنم دلتون برام تنگ شد :green:
ممنون استاد علی

نه تو انجمن های تخصصی شوخی نداریم.
قضیه ی دلیگیت و همینطور کدتون که در پست های قبلی دادین رو بررسی کردم ، و این طور که متوجه شدم ، دلیگیت ، همون رویداد خودمونه . یعنی بعد از تعریف دلیگیت (که معمولا در سطح فضای نام تعریف میشن) ، موقع ساختن شی از دلیگیت مون ، متدهایی که میخوایم اجرا بشن (متدهایی که ورودی و خروجی اش با تعریف دلیگیت سازگار باشه) ، رو بهش میدیم و از اون به بعد ، شی دلیگیت ، اشاره گری میشه به متدهایی که موقع ساختن شی دلیگیت داده بودیم . یعنی در واقع شی دلیگیت ، همون متدهامون میشن که مثل فراخونی متد که پارامتر میدیم ، با شی دلیگیت مون هم همین برخورد رو میکنیم . منتها رویداد ، تنها تفاوتی که با دلیگیت داره اینه که عملگر مساوی به تنهایی براش کار نمیکنه و همینطور موقع تعریف شی دلیگیت ، اگه رویداد میخوایم ، باید کلمه ی event رو هم بنویسیم
درسته؟

در یک کلام خیر. از delegate درک درستی دارید، اما مفهوم و کاربرد event رو درست متوجه نشدید. از اونجایی که در تعریف event از یک delegate استفاده میشه و کارکردش بر اساس اون delegate ئه، شما تشابه اون delegate ها رو به حساب تشابه delegate و event گذاشته اید وگرنه event صرفا بخاطر اینکه از delegate استفاده می کنه به delegate شبیه به نظر میاد. event یک رویداد داخل کلاس رو تعریف می کنه که بنا بر شرایط تعریف شده بروز می کنه و می توانید به یک یا چند متد اصولا بیرون از کلاس مرتبط اش کنید تا با بروز اون رخداد فراخوانی بشوند. event اون متد ها رو فراخوانی نمی کنه، رخداد رو بوجود میاره. به اون عملگر =+ که در اتصال رخداد ها وجود داره نگاهی بندازید. این عملکرد شباهتی به delegate نداره.
کد:
    class Class1
    {
        public delegate void TestEventHandler(Class1 sender);
        public event TestEventHandler Test;
        public string Text = "";

        public void InvokeTest()
        {
            var test = Test;
            if (test != null)
            {
                test.Invoke(this);
            }
        }
    }

کد:
            var c = new Class1();
            c.Test += M1;
            c.Test += M2;
            c.Test += M3;
            c.InvokeTest();
            MessageBox.Show(c.Text);
 

the_king

مدیرکل انجمن
ولی چند تا سئوال داشتم :
اولا من متوجه EventHandler بجای نام (نوع) دلیگیت برای رویدادها نشدم . چرا همه جا موقع تعریف دلیگیت ، نام رو خودمون دلخواهی انتخاب میکردیم ولی اینجا بجای نام ، از اسم کلاس EventHandler استفاده میکنیم و آیا میشه اسمش رو تغییر داد به نام دلخواه؟

بهتون استفاده از کلید F12 رو توصیه کرده بودم، استفاده نمی کنید. اگه کد EventHandler رو ببینید متوجه می شوید که کلاس نیست، delegate ئه. شما می توانید از هر delegate ای با هر نامی استفاده کنید اما در NET. بصورت عمومی از EventHandler استفاده شده، اگه ازش استفاده کنید رخداد تون حالت استاندارد زبان و NET. رو خواهد داشت ولی الزامی وجود نداره.


دوم اینکه در کد زیر :

کد:
namespace Practice_1
{
    public delegate MatchCollection Messager(string a);
    public partial class Form1 : Form
    {
        public event Messager myMessanger2;

        public Form1()
        {
            InitializeComponent();
            myMessanger2 = new Messager( (new Father()).EventExecuteMe);
        }

        private void btnCallerEvent2_Click(object sender, EventArgs e)
        {
            MatchCollection numberInText = myMessanger2(txtboxGeneralUsage.Text);
        }
    }

    public class Father
    {
        public MatchCollection EventExecuteMe(string changeText)
        {
            MessageBox.Show("EventExecuteMe Method In Father Class :\n\n" + changeText);
            Regex pattern = new Regex("\\d+");
            MatchCollection numberInchangeText = pattern.Matches(changeText);
            return numberInchangeText;
        }
    }
}

چرا در متد سازنده ی کلاس Form1 و در موقع مقداردهی شی myMessanger2 ، با اونکه این شی ، یک event تعریف شده ، ولی عملگر = رو قبول کرد و اشکالی نگرفت و کدش کار میکنه؟

کاری که کردید اصولی و معقول نیست اما از نظر منطق زبان اشکالی نداره. جایی که رخداد myMessanger2 (که اصولا نباید با حروف کوچیک شروع می شد) رخ میده داخل کلاس ئه، در نتیجه در داخل خود کلاس که کاملا در جریان زمان وقوع رخداد هستید، احتیاجی به متصل کردن متدی به رخداد نیست، مثل اینه که به کسی بسپارید که وقتی گشنه ام شد به خودم خبر بده. برای همین اینکه myMessanger2 رو داخل خود کلاس به رویدادی متصل کنید عملی هست ولی با کاربرد event و اصول کارش جور در نمیاد. اون event مناسب دسترسی از بیرون کلاس ئه تا بقیه در جریان بروز رخداد باشند.

سوم اینکه در قضیه ی کدهای Thread (نخ) که دادین ، ببینین درست میگم :
کلا نخ ها ، ورودی متد میخوان و بعد از ایجاد نخ ، میشه با متد Start از شی این نخ ها ، این متد ها رو اجرا کرد . خوب میدونیم که دلیگیت ها همون اشاره گر به متدهان پس میشه چندین متد از یه دلیگیت اجرا کرد بناراین بهتره که بجای متد ، از دلیگیت برای اشیاء نخ ها استفاده بشه که میشه
درسته دیگه؟

یک اشکالی هست، delegate در مورد اینکه کدوم نخ اجرا کننده متد باشه تصمیمی نمی گیره، در هر نخی که اجرا بشه همون نخ متد رو فراخوانی می کنه. بنابر این delegate مستقیما ربطی به ایجاد نخ یا انتخاب نخ نداره. شما با delegate می توانید متدی که نخ اجرا می کنه رو نشانه برید ولی نمی توانید بین دو تا نخ متفاوت که هر دوشون اون متد رو اجرا می کنند فرقی قائل بشوید. اگر دو تا شیء delegate بسازید که هر دو به یک متد اشاره می کنند می بینید که با هم برابرند.
از طرف دیگه اون متدی ایجاد کننده نخ در کلاس Thread قرار داره که متد اش محدود شده به استفاده از دو تا delegate ثابت ThreadStart و ParameterizedThreadStart. بنابر این هر delegate متفاوت دیگه ای که بسازید بدرد ساختن نخ نمیخوره و باز برای ساختن Thread ناچارید از همین دو تا delegate کمک بگیرید.
 

the_king

مدیرکل انجمن
چهارم اینکه ، بازم در قضیه ی کدهای Thread که دادین ، اولا نمیشه خودمون یه دلیگیت بنویسیم و به متد سازنده ی Thread بدیم؟ البته خودم میدونم توی این کلاس نمیشه . منظورم اینه که کلاسی جایگزین کلاس Thread هست که این مشکل رو رفع کرده باشه؟ چون مثلا اگه بخوایم چند وروی متدمون داشته باشه ، یا خروجی ای متدمون داشته باشه ، با دلیگیت های تعریف شده برای این متد ، کاری نمیشه کرد (حداقل ورودی دلیگیت ParameterizedThreadStart رو بصورت params تعریف نکردن که آدم دستش برای تعداد آرگومان های مختلف باز باشه) و دوم اینکه کد زیر رو طبق راهنمایی کدهایی که دادین ، برای ادامه ی اجرای کدها نوشتم :
کد:
namespace Practice_1
{
    public delegate MatchCollection Messager(string a);
    public partial class Form1 : Form
    {
        ThreadStart threadDelegate;
        Thread suspendThread;

        public Form1()
        {
            InitializeComponent();
            threadDelegate = new ThreadStart(SuspendThread2);
        }

        public void SuspendThread2()
        {
            Thread.Sleep(1500);
            MessageBox.Show("1");
            suspendThread.Suspend();
            MessageBox.Show("2");
            suspendThread.Suspend();
            MessageBox.Show("3");
            suspendThread.Suspend();
            MessageBox.Show("4");

        }

        private void btnThread3Timer_Suspend_Click(object sender, EventArgs e)
        {
            suspendThread = new Thread(threadDelegate);
            suspendThread.Start();
            timerSuspendThread.Enabled = true;
        }

        private void timerSuspendThread_Tick(object sender, EventArgs e)
        {
            suspendThread.Resume();
        }
    }
}
مساله فنی ئه، خودتون فکر کنید اگه جای طراح Thread بودید چطوری تعداد پارامتر متغیر رو پیاده سازی می کردید. برای کسی که اون متد Start رو طراحی کرده، اضافه کردن یک params ارسال تعداد متغیر پارامتر رو حل می کنه ولی به هر حال در متدی که قراره اجرا بشه فقط یک آرایه دریافت می شد. می توانید به Start یک آرایه []object رو به عنوان یک object بفرستید و در متد تون اون پارامتر رو به آرایه ([]object) تبدیل کنید و هر پارامتر رو از یک اندیس این آرایه بردارید.

به نظرم نسبت به کدهای لوا ، 3 تا مشکل داره که ممنون میشم برای حل اش راهنمایی کنین . توی لوا ، وقتی متد Resume رو مینوشتیم ، خودش میفهمید ، این بار اولشه که این نخ اجرا شد یا بار دوم به بعد و به نسبت اون ، اقدام میکرد و اجرا میشد ولی اینجا برای بار اول حتما باید متد Start رو بنویسیم و برای بار دوم به بعد هم حتما باید متد Resume رو وگرنه ارور میده . هر چند این قضیه اش زیاد مهم نیست . مشکل دوم اینکه توی لوا هر بار که متد Resume اجرا میشد ، اول میرفت نخ رو اجرا میکرد و بعد توقف کد داخل نخ ، میومد ادامه ی کدها رو اجرا میکرد ولی اینجا وقتی مینویسیم :
کد:
suspendThread.Start();

suspendThread.Resume();
کد بالا اگه توی یک رویداد نوشته شده باشه و وقتی خط اول رو اجرا کنه ، نمیره نخ رو اجرا کنه واسه همین با اجرای کد خط دوم ، ارور پیش میاد که فقط چاره اش این میشه که هر کد رو توی رویدادهای متفاوت بنویسیم . این مشکل برم مهم هست و نمیشه کاری کرد که در یک رویداد ، کدهای متفاوتی از نخ رو اجرا کرد مثل بالا؟
سوم اینکه توی لوا ، متد Suspend که باعث توقف کد (درون نخ) میشه ، میتونست رشته ای برگردونه که وضعیت اجرای نخ (به تعلیق در آمده یا تمام شده و ...) رو گزارش کنه و همینطور میتونست از متدهای دیگه ، رشته ها رو برگردونه و به متد Resume در رویدادی که باعث فراخونی اش شد ، برگردونه ولی اینجا متد Suspend نمیتونه وضعیت و چیزی رو برگردونه . برای برگردوندن وضعیت ، یا حتی در صورت اتمام نخ وقتی نخ (متد) مون ، چیزی رو برگردونه ، چجوری باید متد Resume (یا متد Start) که نخ رو به اتمام رسوند ، از مقدار بازگشتی نخ مورد نظر مطلع بشه؟
اولا از متد Suspend و Resume استفاده نکنید. هم منسوخ شده و هم کارکرد دقیقی نداره. ثانیا suspendThread.ThreadState وضعیت نخ رو نشون میده. ثالثا متدی که نخ اجرا می کنه void ئه، مقدار بازگشتی نداره که. من جای شما بودم حداقل برای شروع کار از BackgroundWorker استفاده می کردم. علاوه بر سادگی مزایاش اینه که در حین اجرای نخ می توانید با نخ اصلی به سادگی ارتباط برقرار کنید و در ضمن موقع اتمام اجرای نخ، رخداد مناسبی داره که متوجه پایان اش بشید.

پنجم اینکه در کدی که دادین ، من از قضیه ی کلاس ManualResetEvent متوجه نشدم . بجاش میشه از کلاس Thread استفاده کرد دیگه؟! پس چرا کلاس Thread تعریف کردین ولی از کلاس ManualResetEvent استفاده کردین؟!
نه، چون در مورد مباحث هماهنگ سازی نخ ها اطلاعاتی ندارید شاید توضیح دادنش ساده نباشه. ManualResetEvent هیچ جایگزینی برای Thread نیست، فقط یک رخداد هماهنگ ساز ئه که بین نخ ها قابل استفاده است. فرض کنید که می خواستید وقتی نخ تون خاتمه یافت به نخ دیگری اطلاع بدید. شاید به فکر یک event باشید که قراره اتمام نخ رو اعلام کنه. شما رخداد رو در انتهای نخ ایجاد می کنید ولی سایر نخ ها مطلع نمی شوند، چون در هر نخ ای که رخداد رخ بده به همون نخ محدود میمونه، یعنی در Thread ها از event نمیشه اینطوری استفاده کرد. اشاره کردم که event از delegate کمک می گرفت که متوجه تفاوت نخ ها نمیشه و نمی توانید به delegate بگید در فلان نخ اجرا بشو. برای همین بین نخ ها اگر از event استفاده کنید می بینید که فقط داخل همون نخ فعلی اجرا میشه. ManualResetEvent علاوه بر اینکه میتونه نخ رو به حالت suspend ببره میتونه با بروز رخدادی مجددا از حالت suspend درش بیاره. خودش نخ نیست، رخداد هماهنگ ساز نخ ئه. به کمکش می توانید نخ ها رو در موقع لزوم منتظر یک رخدادی در نخ دیگری نگهدارید. جایگزین همون Suspend و Resume ای است که نباید بکارشون ببرید.

شیشم اینکه من خیلی از متد استاتیک Sleep که در کلاس Thread استفاده کردین ، خوشم اومد . در اتوپلی ، این متد ، هم مصرف پردازنده رو بسیار بالا میبرد و هم باعث not respond شدن برنامه تا پایان اجرای این کد میشد ولی در اینجا هیچ کدوم از این مشکلات رو نداره!
دقت کنید که شما دارید یک نخ مستقل رو Sleep می کنید، طبیعی است که این نخ اصلی فرم نیست که با Sleep شدنش not respond بشه، شما اگر نخ اصلی فرم رو Sleep کنید همون اتفاقی می افته که نامطلوب ئه.

هفتم اینکه ولی با این حال ، متوجه نشدم چطور کلا دستوری (مثل MessageBox.Show) که در نخ جداگانه اجرا میشه ، دیگه هندل ویندوز مربوطه شو نمیتونه بگیره و به اون ویندوز وابسته نمیشه و ممنون میشم کاربرد استفاده از نخ ها رو بجز در این دو مورد ، بیشتر باز کنین
اصلا MessageBox.Show رو فراموش کنید، ذهن تون رو درگیر پنجره و Handle می کنید در حالی که ربطی به این قضیه نداره. شما تصور می کنید که Modal بودن پنجره Show است که فرم تون رو قفل نگهداشته در حالی که مساله خود متد Show ئه که به این زودی خاتمه پیدا نمی کنه. تا وقتی از متد Show بازگشتی صورت نگیره کد های بعدی اجرا نمیشه. اصلا پنجره رو بیخیال بشید، فرض کنید بجای MessageBox.Show یک متد ساده دارید که (Sleep(10000 رو اجرا می کنه. تا زمانی که این 10 ثانیه نگذره که نخ ئه کد های بعدی رو اجرا نخواهد کرد. شما یک نخ جداگانه ساخته اید و MessageBox.Show رو داخلش اجرا می کنید، اجرای کد اش در همون موقعیت Show میمونه تا زمانی که متد Show مقدار بازگشتی برگردونه. Show هم اینکار رو وقتی می کنه که پنجره رو ببندید. حالا این نخ اگر نخ اصلی فرم بود، هیچکاری در فرم نمی توانستید بکنید، چون اجرای کد های بعدی نخ گیر Show بود و تا کارش تموم نمیشه نخ اصلی فرم کار دیگه ای نمی کرد. اما اگه نخ یک نخ مستقل باشه، نخ اصلی فرم آزاد ئه و هر کاری می تونه انجام بده، گیر این نخ که نیست، حالا میخواد اون نخ هر چقدر در Show معطل بمونه.

هشتم اینکه ، یه قضیه ی Attributes رو خوب درک کنم و اینکه کلا چیه ، عالی میشد (البته هنوز درباره اش تحقیقات اولیه نکردم)
خیلی اساس ساده ای داره، برای هر عضو می توانید یک یا چند Attribute در نظر بگیرید، حالا Attribute آماده یا دست ساز. بعضی از این Attribute ها رو فقط ویژوال استدیو میشناسه و براشون قابلیت خاص در نظر گرفته، مثلا اینکه مقدار پیشفرض یک مشخصه چه مقداری ئه، یا فلان متد این توضیح رو داشته باشه، یا رخداد پیشفرض کلاس کدوم رخداد باشه، بعضی هاشون هم استاندارد زبان #C اند، بعضی هاشون هم مثل اونهایی که خودتان می سازید کاری انجام نمی دهند مگر اینکه خودتان کدی بنویسید که فرضا اگر فلان صفت وجود داشت فلان کار انجام بشه.

نهم اینکه (ربطی به کد نویسی نداره) ، من وقتی میام انجمن ، یه صفحه ی دیگه میاد و فقط در صورتی که اول توی یه تاپیک دیگه مستقیما بیام ، میتونم انجمن رو باز کنم (چند روزی هست این طور شد)

آخر نشد به ده تا برسه :green:
ممنون استاد علی . بازم عذر میخوام
:rose:
در همین حد میدونم که انتقال سرور انجام شده و یکسری خطا در نمایش صفحه هنوز رفع نشده که گاهی رخ میده.

سلام
آها اینو فراموش کردم بپرسم
اینکه رویدادها (دلیگیت ها) مثلا شی (رویداد) Click ، بعد از مقداردهی ، باید جایی فراخونی بشه و پارامترهای آرگومان متد (رویدادی) که میخواد اجرا کنه ، بهش داده بشه تا اینکه رویداد یا متد مورد نظر رو اجرا کنه دیگه؟
خوب ما فقط دلیگیت یا رویدادها رو مقداردهی میکنیم . مثلا وقتی مینویسیم :

کد:
button1.Click += new EventHandler(MyFunctionTest1);
مقداردهی اش کردیم . ولی بعدش نیازه که این شی دلیگیت (رویداد) که نامش اینجا button1.Click هست رو فراخونی و آرگومان های متد مورد نظرش رو بدیم اما این کار رو نمیکنیم. یعنی در واقع وقتی کلیک میکنیم ، کی و چجوری فهمیده میشه که متد مورد نظر اجرا بشه؟
بله دیگه، اگه شما در داخل کلاس تون رخداد رو در جایی و در شرایطی Invoke نکنید رخدادی رخ نمیده و بلااستفاده می مونه. برای همینه که event خودش شباهتی به delegate نداره، صرفا از delegate استفاده می کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
مساله فنی ئه، خودتون فکر کنید اگه جای طراح Thread بودید چطوری تعداد پارامتر متغیر رو پیاده سازی می کردید. برای کسی که اون متد Start رو طراحی کرده، اضافه کردن یک params ارسال تعداد متغیر پارامتر رو حل می کنه ولی به هر حال در متدی که قراره اجرا بشه فقط یک آرایه دریافت می شد. می توانید به Start یک آرایه []object رو به عنوان یک object بفرستید و در متد تون اون پارامتر رو به آرایه ([]object) تبدیل کنید و هر پارامتر رو از یک اندیس این آرایه بردارید.

سلام
ممنون استاد علی :rose:
من الان تست کردم ؛ نمیتونم برای دلیگیت ParameterizedThreadStart ، متدی تعریف کنم که آرایه ای از آبجکت ها بگیره!!
در این صورت خطای مچ نبودن دلیگیت با متد رو میده


اولا از متد Suspend و Resume استفاده نکنید. هم منسوخ شده و هم کارکرد دقیقی نداره. ثانیا suspendThread.ThreadState وضعیت نخ رو نشون میده. ثالثا متدی که نخ اجرا می کنه void ئه، مقدار بازگشتی نداره که. من جای شما بودم حداقل برای شروع کار از BackgroundWorker استفاده می کردم. علاوه بر سادگی مزایاش اینه که در حین اجرای نخ می توانید با نخ اصلی به سادگی ارتباط برقرار کنید و در ضمن موقع اتمام اجرای نخ، رخداد مناسبی داره که متوجه پایان اش بشید.
این کد چرا کار نمیکنه ؟ :
کد:
private void button1_Click(object sender, EventArgs e)
        {
            BackgroundWorker separateThread = new BackgroundWorker();
            separateThread.DoWork += new DoWorkEventHandler(SeparateThread);
            separateThread.RunWorkerAsync("این پیام در نخ جداگانه ای در بک گراند وورکر فراخونی میشه که پیام ربطی به نخ اصلی فرم1 نداره");
        }

        private void SeparateThread(object sender, DoWorkEventArgs e)
        {
            if (sender is string)
                MessageBox.Show( (string)sender );
        }

اصلا MessageBox.Show رو فراموش کنید، ذهن تون رو درگیر پنجره و Handle می کنید در حالی که ربطی به این قضیه نداره. شما تصور می کنید که Modal بودن پنجره Show است که فرم تون رو قفل نگهداشته در حالی که مساله خود متد Show ئه که به این زودی خاتمه پیدا نمی کنه. تا وقتی از متد Show بازگشتی صورت نگیره کد های بعدی اجرا نمیشه. اصلا پنجره رو بیخیال بشید، فرض کنید بجای MessageBox.Show یک متد ساده دارید که (Sleep(10000 رو اجرا می کنه. تا زمانی که این 10 ثانیه نگذره که نخ ئه کد های بعدی رو اجرا نخواهد کرد. شما یک نخ جداگانه ساخته اید و MessageBox.Show رو داخلش اجرا می کنید، اجرای کد اش در همون موقعیت Show میمونه تا زمانی که متد Show مقدار بازگشتی برگردونه. Show هم اینکار رو وقتی می کنه که پنجره رو ببندید. حالا این نخ اگر نخ اصلی فرم بود، هیچکاری در فرم نمی توانستید بکنید، چون اجرای کد های بعدی نخ گیر Show بود و تا کارش تموم نمیشه نخ اصلی فرم کار دیگه ای نمی کرد. اما اگه نخ یک نخ مستقل باشه، نخ اصلی فرم آزاد ئه و هر کاری می تونه انجام بده، گیر این نخ که نیست، حالا میخواد اون نخ هر چقدر در Show معطل بمونه.

آها ؛ ربطی به هندل ویندوز نداره و ربط به نخ ای داره که در اون اجرا میشه و چون نخ اش با نخ اصلی فرم یکی بوده پس تا کدهای داخل متد Show تموم نشه ، به کدهای بعدی در فرم نمیرسه و وقتی نخ جداگانه ای ساخته بشه ، کدهای هر نخ (در صورت فراخوانی) همزمان اجرا میشن و ربطی به هم ندارن؟!
چون API ویندوز یه متدی شبیه این داره (messagebox) که در آرگومانش هندل ویندوز رو میخواد، من فکر میکردم به هندل ویندوز مربوط هه . پس در واقع ، اون متد API ، هندل ویندوز رو درخواست میکنه تا به نخ اصلی فرم اش دست پیدا کنه ولی در متد MessageBox.Show در سی شارپ ، خودش هندل ویندوز اصلی رو داره و بنابراین به نخ اصلی فرم1 دسترسی داره . درسته؟
خوب حالا آیا نخ اصلی فرم رو اصلا خودمون میتونیم گیر بیاریم که چیه؟ مثلا با پروپرتی Application.OpenForms میتونیم ، شی فرم جاری ای که باز هست رو بدست بیاریم ؛ شی نخ فرم اصلی جاری که باز هست رو میشه بدست آورد؟ اگه آره ، چجوری؟


دقت کنید که شما دارید یک نخ مستقل رو Sleep می کنید، طبیعی است که این نخ اصلی فرم نیست که با Sleep شدنش not respond بشه، شما اگر نخ اصلی فرم رو Sleep کنید همون اتفاقی می افته که نامطلوب ئه.

من چک کردم ، فقط Thread.Sleep هست که sleep داره . یعنی کلاس application و environment رو چک کردم ، هیچ کدوم متد sleep ندارن که فرم رو sleep کنن تا همون not responding اتفاق بیفته . یعنی مثل همون قضیه ی بالا ، باید اول نخ اصلی فرم رو بدست آورد؟
کلا قضیه ی sleep کردن فرم اصلی چجوری هه و کدش توی کدوم کلاس هه؟



------------------------------

راهی هست که در جایی که داریم کد مینویسیم (مثلا Form1 و هر کلاس و چیز دیگه ای) ، با زدن یک دکمه یا دستور و هر چیز دیگه ای، علامت منفی کنار هر شرط و متد و کلاس و... بسته شن؟ (منظورم نظم دهی به کدهاست) . آخه کدهای یه پروژه ممکنه چند هزار خط بشه و تعداد زیاد کدها باعث سردرگمی میشه اگه همه باز باشن . یعنی جوری بسته شم که از داخلی ترین کدها به سمت بیرونی ترین کدها بسته شن مثلا اول شرط های داخل متد بسته (جمع) شن و بعد متدها و بعد کلاس ها و فضای نام ها و ...
نمیدونم چرا وقتی همه ی کدها رو جمع میکنم و بعد ویژال استودیو رو میبندم و دوباره باز میکنم ، بعضی وقت ها دوباره همه ی کدهای جمع و بسته شده ، باز میشن و دوباره خیلی وقت برای بستن شون میزارم
----------------------------
یه سئوال درباره ی ویژال استودیو اینکه من اگه نخوام فقط وقتی دکمه ی start رو میزنم (زمانی که برنامه رو اجرا میکنم) ، نخوام در اون زمان کدهایی که نوشتم (حتی در صورت درست بودن و ایراد نگرفتن کمپایلر) ، ذخیره بشه ، باید چی کار کنم؟ یعنی فقط وقتی خودم دکمه ی save رو زدم ذخیره بشه (و البته auto save هم داشته باشه که هر 5 دیقه سیو شه اما سیو رسمی نباشه این اتو سیو یعنی برای زمان های خاص که برق رفت و دوباره وارد ویژال استودیو شدم ، پیام بده که میخوای سیو قبلی رو برگردونی و در صورت تایید من ، برگردونده شه)
باید چی کار کنم؟
 

the_king

مدیرکل انجمن
سلام
ممنون استاد علی :rose:
من الان تست کردم ؛ نمیتونم برای دلیگیت ParameterizedThreadStart ، متدی تعریف کنم که آرایه ای از آبجکت ها بگیره!!
در این صورت خطای مچ نبودن دلیگیت با متد رو میده
متد رو با همون تک object تعریف کنید و داخلش به []object تبدیل کنید.
کد:
        private void Start(params object[] args)
        {
            suspendThread.Start(args);
        }

        public void SuspendThread2(object obj)
        {
            var args = (object[])obj;
        }

            Start(123, "test", 4.5);

این کد چرا کار نمیکنه ؟ :
کد:
private void button1_Click(object sender, EventArgs e)
        {
            BackgroundWorker separateThread = new BackgroundWorker();
            separateThread.DoWork += new DoWorkEventHandler(SeparateThread);
            separateThread.RunWorkerAsync("این پیام در نخ جداگانه ای در بک گراند وورکر فراخونی میشه که پیام ربطی به نخ اصلی فرم1 نداره");
        }

        private void SeparateThread(object sender, DoWorkEventArgs e)
        {
            if (sender is string)
                MessageBox.Show( (string)sender );
        }
چون sender همون BackgroundWorker تونه و string نیست، sender رو با e.Argument اشتباه گرفته اید :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            BackgroundWorker separateThread = new BackgroundWorker();
            separateThread.DoWork += new DoWorkEventHandler(SeparateThread);
            separateThread.RunWorkerAsync("این پیام در نخ جداگانه ای در بک گراند وورکر فراخونی میشه که پیام ربطی به نخ اصلی فرم1 نداره");
        }

        private void SeparateThread(object sender, DoWorkEventArgs e)
        {
            if (e.Argument is string)
                MessageBox.Show((string)e.Argument);
        }


آها ؛ ربطی به هندل ویندوز نداره و ربط به نخ ای داره که در اون اجرا میشه و چون نخ اش با نخ اصلی فرم یکی بوده پس تا کدهای داخل متد Show تموم نشه ، به کدهای بعدی در فرم نمیرسه و وقتی نخ جداگانه ای ساخته بشه ، کدهای هر نخ (در صورت فراخوانی) همزمان اجرا میشن و ربطی به هم ندارن؟!

همینطوره.

چون API ویندوز یه متدی شبیه این داره (messagebox) که در آرگومانش هندل ویندوز رو میخواد، من فکر میکردم به هندل ویندوز مربوط هه . پس در واقع ، اون متد API ، هندل ویندوز رو درخواست میکنه تا به نخ اصلی فرم اش دست پیدا کنه ولی در متد MessageBox.Show در سی شارپ ، خودش هندل ویندوز اصلی رو داره و بنابراین به نخ اصلی فرم1 دسترسی داره . درسته؟
نه، Handle چه در MessageBox.Show و چه در اون تابع API ویندوز همون Window ئه و فرقی از این نظر ندارند، Handle برای اینکه که بدونه کدوم پنجره صاحب این پنجره است، فقط همین. اصلا ربطی به نخ نداره.
شما وقتی یک متد عادی می نویسید و اجرا می کنید چطوری تصمیم می گیرید که نخ اصلی اجراش کنه؟ هیچ طوری، نخ اصلی به اون قسمت فراخوانی در کد می رسه، پس خودش اجراش می کنه. برای MessageBox هم همینطوره، برای MessageBox مطرح نیست که اجرا کننده کدوم نخ ئه یا پنجره اصلی با کدوم نخ ایجاد شده، اصلا کاری با نخ نداره. با هر نخی فراخوانی اش کنید اجرا کننده اش همون نخ ئه. برای اغلب متد اصلا مهم نیست که کدوم نخ داره اجراشون می کنه به همین جهت اصلا مفهوم نخ براشون مطرح نیست، مثل همون متد هایی که شما قبل از این می نوشتید و در نخ اصلی اجرا می شوند. شما برای اینکه یک متد رو اجرا کنید که نمی روید بگردید ببینید نخ اصلی کدومه، اصلا درگیر مفهوم نخ نمی شوید، MessageBox.Show هم همینطور. براش مهم نیست، هر نخی که میخواد اجراش کنه. اصلا روالی ندارند که بخواهند به نخ چیزی دسترسی پیدا کنند. هر نخی هر کاری که انجام بده خودش انجام داده، این رفتار عادی احتیاجی به هندل و درخواست کردن و دسترسی نداره. به ندرت تابعی پیدا می کنید که کاری با نخ فعلی و غیر فعلی داشته باشه که اونها هم توابع اختصاصی مرتبط با نخ اند.

خوب حالا آیا نخ اصلی فرم رو اصلا خودمون میتونیم گیر بیاریم که چیه؟ مثلا با پروپرتی Application.OpenForms میتونیم ، شی فرم جاری ای که باز هست رو بدست بیاریم ؛ شی نخ فرم اصلی جاری که باز هست رو میشه بدست آورد؟ اگه آره ، چجوری؟
در هر قسمت از کد با Thread.CurrentThread می توانید نخ اجرا کننده رو مشخص کنید. طبیعتا داخل هر فرمی اگه درون متد هایی مثل Form_Load اجرا بشه نخ اصلی فرم خواهد بود. در ضمن کنترل ها (مثل فرم ها) متد Invoke دارند. با Invoke می توانید متدی رو الزاما در نخ اجرا اصلی شون اجرا کنید. از این جهت میگم که معمولا اینکه بدانید نخ اصلی فرم کدومه و بدستش بیاورید مستقیما بدرد تان نخواهد خورد.


من چک کردم ، فقط Thread.Sleep هست که sleep داره . یعنی کلاس application و environment رو چک کردم ، هیچ کدوم متد sleep ندارن که فرم رو sleep کنن تا همون not responding اتفاق بیفته . یعنی مثل همون قضیه ی بالا ، باید اول نخ اصلی فرم رو بدست آورد؟
کلا قضیه ی sleep کردن فرم اصلی چجوری هه و کدش توی کدوم کلاس هه؟
عملا مفهومی به نام Sleep کردن برای برنامه و فرم و کنترل و ... بی معنی است، برای همین برای Thread در نظر گرفته اند. فرم ماهیت اجرایی نداره که Sleep بشه، اون Thread ئه که اجراش متوقف میشه، شما یک دکمه رو روی فرم فشار بدید و در رخدادش Thread.Sleep کنید، همون نخ اصلی فرم رو Sleep خواهید کرد چون رخداد فشار دکمه توسط اون نخ مدیریت میشه.


راهی هست که در جایی که داریم کد مینویسیم (مثلا Form1 و هر کلاس و چیز دیگه ای) ، با زدن یک دکمه یا دستور و هر چیز دیگه ای، علامت منفی کنار هر شرط و متد و کلاس و... بسته شن؟ (منظورم نظم دهی به کدهاست) . آخه کدهای یه پروژه ممکنه چند هزار خط بشه و تعداد زیاد کدها باعث سردرگمی میشه اگه همه باز باشن . یعنی جوری بسته شم که از داخلی ترین کدها به سمت بیرونی ترین کدها بسته شن مثلا اول شرط های داخل متد بسته (جمع) شن و بعد متدها و بعد کلاس ها و فضای نام ها و ...
نمیدونم چرا وقتی همه ی کدها رو جمع میکنم و بعد ویژال استودیو رو میبندم و دوباره باز میکنم ، بعضی وقت ها دوباره همه ی کدهای جمع و بسته شده ، باز میشن و دوباره خیلی وقت برای بستن شون میزارم

هر بار که یک تغییر در فرمت کد پیش میاد و { } ها از نو چیده بشن این اتفاق می افته. در منوی Edit > Outlining گزینه هاش هست، می توانید در تنظیمات ویژوال استدیو ترکیب کلیدی متفاوتی بهشان بدهید تا ساده تر انتخاب شون کنید.

یه سئوال درباره ی ویژال استودیو اینکه من اگه نخوام فقط وقتی دکمه ی start رو میزنم (زمانی که برنامه رو اجرا میکنم) ، نخوام در اون زمان کدهایی که نوشتم (حتی در صورت درست بودن و ایراد نگرفتن کمپایلر) ، ذخیره بشه ، باید چی کار کنم؟ یعنی فقط وقتی خودم دکمه ی save رو زدم ذخیره بشه (و البته auto save هم داشته باشه که هر 5 دیقه سیو شه اما سیو رسمی نباشه این اتو سیو یعنی برای زمان های خاص که برق رفت و دوباره وارد ویژال استودیو شدم ، پیام بده که میخوای سیو قبلی رو برگردونی و در صورت تایید من ، برگردونده شه)
باید چی کار کنم؟
Tools > Options > Project and Solutions > Build and Run > Before building > Don't save any changes
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
متد رو با همون تک object تعریف کنید و داخلش به []object تبدیل کنید.
کد:
        private void Start(params object[] args)
        {
            suspendThread.Start(args);
        }

        public void SuspendThread2(object obj)
        {
            var args = (object[])obj;
        }

            Start(123, "test", 4.5);

سلام
ممنون آقا علی
چه چیز عجیبی؟!!!
من 2 تا سئوال دارم . اول اینکه توی راهنمای متد Start گفت میتونیم object بفرستیم نه آرایه ای از آبجکت ها پس شما چطور متوجه شدین که میشه آرایه هم فرستاد؟!
و مهم تر اینکه در آرگومان متد Start که args رو فرستادیم ، تا اینجا و قبل از فرستادن به متد SuspendThread2 ، آرایه هست اما بعد از رسیدن این آرگومان به متد SuspendThread2 ، خوب اونجا گفتیم که تک عضو object بشه نه اینکه بصورت آرایه ای از آبجکت ها بهش برسه . اصلا مگه میشه یه آرگومان ای ، تک عضو بخواد ولی ما براش آرایه ارسال کنیم!!!!!!!! قضیه اش چجوری هه؟ من تا حالا با این نوع کد کار نکردم
حالا آرایه ای از آبجکت ها که فرستادیم ، حالا چطور یک شی (حالا هر شی ای مثل عدد و آبجکت و ...) رو میشه به آرایه ای از شی ها تبدیل کرد؟!!!! منظورم از چطور میشه ، کدی که ما مینویسیم نیست . یعنی چطور میشه که کمپایلر اشکال نمیگیره و میفهمه؟ درکش واسه ما آسون نیست چه برسه به کمپایلر!
این قضیه مثل این میمونه که یه مغازه ای از یه کارخونه ای یک بسته مواد غذایی بخواد ولی اون کارخونه یک کامیونی از اون مواد غذایی رو بفرسته براش ولی به دست مغازه دار که میرسه ، باز بصورت یک بسته برسه (نه بصورت یک کامیون) ولی مغازه دار بعد از تحویل اون بسته اون یک بسته رو دوباره با جادو بتونه تبدیل به یک کامیون کنه o_O


چون sender همون BackgroundWorker تونه و string نیست، sender رو با e.Argument اشتباه گرفته اید :
کد:
        private void button1_Click(object sender, EventArgs e)
        {
            BackgroundWorker separateThread = new BackgroundWorker();
            separateThread.DoWork += new DoWorkEventHandler(SeparateThread);
            separateThread.RunWorkerAsync("این پیام در نخ جداگانه ای در بک گراند وورکر فراخونی میشه که پیام ربطی به نخ اصلی فرم1 نداره");
        }

        private void SeparateThread(object sender, DoWorkEventArgs e)
        {
            if (e.Argument is string)
                MessageBox.Show((string)e.Argument);
        }
آره قاتی کردم :)


در هر قسمت از کد با Thread.CurrentThread می توانید نخ اجرا کننده رو مشخص کنید. طبیعتا داخل هر فرمی اگه درون متد هایی مثل Form_Load اجرا بشه نخ اصلی فرم خواهد بود. در ضمن کنترل ها (مثل فرم ها) متد Invoke دارند. با Invoke می توانید متدی رو الزاما در نخ اجرا اصلی شون اجرا کنید. از این جهت میگم که معمولا اینکه بدانید نخ اصلی فرم کدومه و بدستش بیاورید مستقیما بدرد تان نخواهد خورد.

این کد چرا کار نمیکنه و ارور TargetParameterCountException رو میده؟ :

کد:
    public partial class Form1 : Form
    {
        FreeDelegate mainThreadDelegate;
        public delegate int FreeDelegate(params object[] a);
        [DllImport("User32.dll", EntryPoint = "MessageBoxA")]public static extern int MessageBoxAPI(IntPtr hWnd, string Text, string Caption, int uType);

        public int CustomMessageBox(params object[] argoments)
        {
            return MessageBoxAPI((IntPtr)argoments[0], (string)argoments[1], (string)argoments[2], (int)argoments[3]);
        }

        public Form1()
        {
            mainThreadDelegate += new FreeDelegate(CustomMessageBox);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            this.Invoke(mainThreadDelegate, IntPtr.Zero, text, "", 0);
        }
    }

Tools > Options > Project and Solutions > Build and Run > Before building > Don't save any changes
تا آدرس Build and Run ای که دادین ، درسته ولی بقیه اش برام نیست . نسخه ی ویژال استودیو ام 2015 Enterprise Update One هست

-----------------------
این attributes چرا کار نمیکنه؟ :

کد:
//در کلاس Form1 :
private void button2_Click(object sender, EventArgs e)
        {
            MessageBoxAPI(this.Handle, (new Father()).DefaultProperty, "", 0);
        }


    public class Father
    {
        private string defaultProperty;
       
        [DefaultValue("مقدار پیش فرض")] public string DefaultProperty
        {
            get
            {
                return defaultProperty;
            }
            set
            {
                defaultProperty = value;
            }
        }
    }

مگه اتریباتس DefaultValue قبل از یک پروپرتی بیاد ، مقدار پیش فرضش رو مشخص نمیکنه؟ پس چرا وقتی بهش مقداری نمیدم ، خالی هه؟

------------------------------------------
درباره ی پارامترهای DllImportAttributes که مهمترین اتریباتس هست برای dll های unmanaged ، اینا رو میگم درسته و اگه نکته ی مهم ای درباره ی پارامتری که دونستن اش مهمه رو نمیدونم ، ممنون میشم بگین :
پارامتر اولش که مسیر (کامل) فایل dll رو میخواد
CallingConvention هم که نوع فایلی که با چه روشی ساخته شد که معمولا Unmanaged ها بصورت StdCall ساخته میشن
CharSet که مشخص کننده ی نوع سازگاری (فونت) هست یعنی Unicode (فارسی ساپورت) باشه یا Ansi
EntryPoint هم مشخص کننده ی نام متد در dll هه
 

the_king

مدیرکل انجمن
سلام
ممنون آقا علی
چه چیز عجیبی؟!!!
من 2 تا سئوال دارم . اول اینکه توی راهنمای متد Start گفت میتونیم object بفرستیم نه آرایه ای از آبجکت ها پس شما چطور متوجه شدین که میشه آرایه هم فرستاد؟!
هر داده ای می تونه بصورت یک object دربیاد، حتی یک آرایه چند بعدی، object هر نوع داده ای رو داخل یک کپسول قرار میده که صرفا بر اساس اندازه داده کپسول بزرگتر یا کوچکتر میشه، دیگه اینجا اهمیتی نداره که ساختار اون داده چقدر ساده یا پیچیده است. object از این لحاظ محدودیتی نداره.

و مهم تر اینکه در آرگومان متد Start که args رو فرستادیم ، تا اینجا و قبل از فرستادن به متد SuspendThread2 ، آرایه هست اما بعد از رسیدن این آرگومان به متد SuspendThread2 ، خوب اونجا گفتیم که تک عضو object بشه نه اینکه بصورت آرایه ای از آبجکت ها بهش برسه . اصلا مگه میشه یه آرگومان ای ، تک عضو بخواد ولی ما براش آرایه ارسال کنیم!!!!!!!! قضیه اش چجوری هه؟ من تا حالا با این نوع کد کار نکردم
حالا آرایه ای از آبجکت ها که فرستادیم ، حالا چطور یک شی (حالا هر شی ای مثل عدد و آبجکت و ...) رو میشه به آرایه ای از شی ها تبدیل کرد؟!!!! منظورم از چطور میشه ، کدی که ما مینویسیم نیست . یعنی چطور میشه که کمپایلر اشکال نمیگیره و میفهمه؟ درکش واسه ما آسون نیست چه برسه به کمپایلر!
این قضیه مثل این میمونه که یه مغازه ای از یه کارخونه ای یک بسته مواد غذایی بخواد ولی اون کارخونه یک کامیونی از اون مواد غذایی رو بفرسته براش ولی به دست مغازه دار که میرسه ، باز بصورت یک بسته برسه (نه بصورت یک کامیون) ولی مغازه دار بعد از تحویل اون بسته اون یک بسته رو دوباره با جادو بتونه تبدیل به یک کامیون کنه o_O

آره قاتی کردم
:)
object یک کپسول می سازه که به اندازه ای که یک کامیون با بارش درسته توش جا میشه، هم کل داده کامیون داخلش قرار می گیره و هم نوع داده کامیون روی کپسول نوشته میشه، در مقصد کپسول مثل هر کپسول بزرگ یا کوچیکی قابل دریافت ئه. کپسول کپسول ئه، اندازه اش فرق می کنه ولی فرمت ظاهری اش یکسان ئه. بعد شما به کامپایلر اعلام می کنید که محتویات کپسول یک کامیون ئه. کامپایلر تضمین شما رو چه درست و چه نادرست به هر حال قبول می کنه، اگه اشتباه کرده باشین در زمان اجرا و موقع تبدیل محتویات کپسول خطا میده وگرنه کامپایلر در همین حد که شما ضمانت کرده اید برایش کافی است. موقع تبدیل داده به نوع کامیون، محتویات کپسول از داخل object درمیاد و با اون نوع داده اعلام شده تطبیق داده میشه، اگر نوع داده عینا همون بود که بدون کار اضافی قابل تبدیل ئه، وگرنه با بروز خطا اجرای کد متوقف میشه.

این کد چرا کار نمیکنه و ارور TargetParameterCountException رو میده؟ :

کد:
    public partial class Form1 : Form
    {
        FreeDelegate mainThreadDelegate;
        public delegate int FreeDelegate(params object[] a);
        [DllImport("User32.dll", EntryPoint = "MessageBoxA")]public static extern int MessageBoxAPI(IntPtr hWnd, string Text, string Caption, int uType);

        public int CustomMessageBox(params object[] argoments)
        {
            return MessageBoxAPI((IntPtr)argoments[0], (string)argoments[1], (string)argoments[2], (int)argoments[3]);
        }

        public Form1()
        {
            mainThreadDelegate += new FreeDelegate(CustomMessageBox);
        }

        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            this.Invoke(mainThreadDelegate, IntPtr.Zero, text, "", 0);
        }
    }
ایراد در اصل نه از mainThreadDelegate.Invoke ئه و نه از []params object. اون this.Invoke یکم ایراد داره. با delegate هایی که params دارند به مشکل بر میخوره.
خاصیت params اینه که اگه یک آرایه رو به عنوان تنها پارامتر دریافت کنه بصورت پارامتر های متوالی محسوب می شوند، نه فقط یک آرایه برای پارامتر اول.
کد:
        private delegate void TestDelegate(params string[] args);

        private void Test(params string[] args)
        {
            MessageBox.Show(string.Join(", ", args));
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            new TestDelegate(Test).Invoke("one-two-three".Split('-'));
        }
و موقع استفاده از متدی مثل Control.Invoke ناچارید که از این خصوصیت استفاده کنید :
کد:
        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            this.Invoke(mainThreadDelegate, (object)new object[] { IntPtr.Zero, text, "", 0 });
        }
دقت کنید که ایراد از Control.Invoke ئه وگرنه خود delegate تون همچین مشکلی نداره :
کد:
        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            mainThreadDelegate.Invoke(IntPtr.Zero, text, "", 0);
        }

تا آدرس Build and Run ای که دادین ، درسته ولی بقیه اش برام نیست . نسخه ی ویژال استودیو ام 2015 Enterprise Update One هست
لابد حذف اش کردن که درخواست برگردوندن یا اضافه کردن این option رو دادن :
Do not save any changes before building

این attributes چرا کار نمیکنه؟ :
کد:
//در کلاس Form1 :
private void button2_Click(object sender, EventArgs e)
        {
            MessageBoxAPI(this.Handle, (new Father()).DefaultProperty, "", 0);
        }


    public class Father
    {
        private string defaultProperty;
     
        [DefaultValue("مقدار پیش فرض")] public string DefaultProperty
        {
            get
            {
                return defaultProperty;
            }
            set
            {
                defaultProperty = value;
            }
        }
    }

مگه اتریباتس DefaultValue قبل از یک پروپرتی بیاد ، مقدار پیش فرضش رو مشخص نمیکنه؟ پس چرا وقتی بهش مقداری نمیدم ، خالی هه؟
کار که می کنه، ولی کاربرد اش اون نیست. DefaultValue در مقدار اولیه ای که به متغیر defaultProperty تون داده اید یا می توانستید بدهید هیچ دخالتی نخواهد کرد. DefaultValue فرضا برای اینه که ویژوال استدیو بدونه موقع Serialize کردن یک شیء Father نیازی به ذخیره کردن مقدار DefaultProperty هست یا خیر. اگه مقدارش در حالت پیشفرض باشه مقدار اون پروپرتی رو ذخیره نمی کنه. یا موقع ریست کردن مقدار پروپرتی برای اینه که بدونه به چه مقداری تغییرش بده. کلاس تون رو اول در یک فایل جداگانه کلاس قرار بدید که قاطی Form1 یا جزئی از اون نباشه و وارث System.ComponentModel.Component اش کنید تا بعد از کمپایل بتوانید از بالای نوار Toolbox که مخصوص پروژه خودتونه برش دارید و پایین فرم تون یک نمونه اش رو قرار بدید (مثل Timer یا BackgroundWorker)، بعد ببینید وقتی دارید فرم رو طراحی می کنید در پنجره Properties وقتی روی DefaultProperty راست کلید می کنید و Reset رو انتخاب می کنید چه اتفاقی می افته :
کد:
public class Father : System.ComponentModel.Component

درباره ی پارامترهای DllImportAttributes که مهمترین اتریباتس هست برای dll های unmanaged ، اینا رو میگم درسته و اگه نکته ی مهم ای درباره ی پارامتری که دونستن اش مهمه رو نمیدونم ، ممنون میشم بگین :
پارامتر اولش که مسیر (کامل) فایل dll رو میخواد
CallingConvention هم که نوع فایلی که با چه روشی ساخته شد که معمولا Unmanaged ها بصورت StdCall ساخته میشن
CharSet که مشخص کننده ی نوع سازگاری (فونت) هست یعنی Unicode (فارسی ساپورت) باشه یا Ansi
EntryPoint هم مشخص کننده ی نام متد در dll هه
الزاما مسیر کامل فایل نیست و اصلا اصولی نیست که کسی مسیر کامل فایل dll رو بده چون محل اش الزاما ثابت نیست، مگر در موقع debug کردن و اشکال یابی در پروژه که فایل همواره در یک مسیر ثابت قرار داره. موقع اجرا اول مسیر فعلی کنار فایل اجرایی و بعد مسیر های پیشفرض سیستم رو به دنبال کتابخانه مورد نظر چک می کنه.
اتفاقا از دید آماری در اغلب موارد Cdecl اند، چون تعداد کتابخانه هایی که انحصاری برای خود زبان ++C یا C ساخته شده اند خیلی زیاد تره، ولی در مورد کتابخانه هایی که عموما بین زبان ها مشترک ئه بله، StdCall اند.
اینکه در ANSI فارسی ساپورت بشه یا نه بستگی به تنظیمات سیستم و البته کتابخانه داره، در کتابخانه های فارسی که برای ویندوز های قدیمی نوشته میشد انتخابی بجز ANSI وجود نداره.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام
ممنون آقا علی
قبلا میگفتم mp3 رو چجوری میشه بدون شی بصری windows media player بالا آورد ، الان فهمیدم روش راحت ترش رو . داخل همون فایل wmp.dll (در پوشه ی system32) ، اگه از کلاس WMPLib.WindowsMediaPlayerClass شی درست کنیم ، میشه بدون ایجاد کنترل بصری ، از امکانات windows media player که پخش آهنگ mp3 و ... هم هست استفاده کرد (نمیدونستم) اما اگه از کلاس AxWMPLib.AxWindowsMediaPlayer شی درست شه ، یه کنترل بصری هست که باید به کنترل add شه تا باهاش کار کرد
حالا سئوالم اینه که اولا چجوری میشه فهمید یه کلاسی از نوع دات نت هست یا از نوع Unmanaged ؟ اگه از نوع Unmanaged باشه ، حتما موقع فراخونی ، به attributes DllImport نیاز داره دیگه؟
دوم اینکه در همین کلاس WMPLib.WindowsMediaPlayerClass ، چجوری میشه آهنگ رو جلو برد؟ مثلا ثانیه ی 35 ام رو بره پخش کنه یا مثلا 20 درصد از زمان آهنگ رو بره جلوتر یا عقب تر پخش کنه؟ اصلا اعضای این کلاس هیچ توضیحی نوشته نیست براش . متد یا پروپرتی ای بنام Seek هم نداره . یه متدی که انگار بهش بیاد این کار رو کنه ، متد step هست که انگار هم اونم این کار رو نمیکنه و هم ارور میده نمیدونم چرا !
کلا کدوم متد یا پرورتی در این کلاس ، باعث جلو یا عقب رفتن روند زمان آهنگ میشه؟


هر داده ای می تونه بصورت یک object دربیاد، حتی یک آرایه چند بعدی، object هر نوع داده ای رو داخل یک کپسول قرار میده که صرفا بر اساس اندازه داده کپسول بزرگتر یا کوچکتر میشه، دیگه اینجا اهمیتی نداره که ساختار اون داده چقدر ساده یا پیچیده است. object از این لحاظ محدودیتی نداره.


object یک کپسول می سازه که به اندازه ای که یک کامیون با بارش درسته توش جا میشه، هم کل داده کامیون داخلش قرار می گیره و هم نوع داده کامیون روی کپسول نوشته میشه، در مقصد کپسول مثل هر کپسول بزرگ یا کوچیکی قابل دریافت ئه. کپسول کپسول ئه، اندازه اش فرق می کنه ولی فرمت ظاهری اش یکسان ئه. بعد شما به کامپایلر اعلام می کنید که محتویات کپسول یک کامیون ئه. کامپایلر تضمین شما رو چه درست و چه نادرست به هر حال قبول می کنه، اگه اشتباه کرده باشین در زمان اجرا و موقع تبدیل محتویات کپسول خطا میده وگرنه کامپایلر در همین حد که شما ضمانت کرده اید برایش کافی است. موقع تبدیل داده به نوع کامیون، محتویات کپسول از داخل object درمیاد و با اون نوع داده اعلام شده تطبیق داده میشه، اگر نوع داده عینا همون بود که بدون کار اضافی قابل تبدیل ئه، وگرنه با بروز خطا اجرای کد متوقف میشه.

اگه اشتباه نکنم ، آرایه ای که میسازیم ، در واقع ای از کلاس Array ساخته میشه . حالا مثلا آرایه ای از عدد []int یا هر شی دیگه ای که میسازیم ، دقیقا نمیدونم خودش از کلاس Array شی ای میسازه که باز کلاس Array از کلاس int پیاده سازی شده باشه (که احتمالا همین جوره فکر کنم بخاطر اینترفیس های متنوعی که به ارث میبره) و یا اینکه واقعا وقتی آرایه ای از عددها []int که میسازیم ، هم از کلاس int و هم از کلاس Array شی میسازه
به هر حال ، به یکی از دلیل های بالا ، نمیشه آرایه ای از عدد رو داخل یه نوع عدد (بدون آرایه) ریخت یعنی نمیشه :

کد:
int a = new int[] { 1, 5 };
چون آرایه خودش از کلاس Array هست ولی int فقط از کلاس int هست و شی کلاس Array توی نوع کلاس int ریخته نمیشه
ولی نوع object چون همه ی کلاس ها من جمله کلاس Array رو هم شامل میشه و پدر همه شونه ، میشه شی ای از کلاس Array (که آرایه ای از یک نوع هست مثلا آرایه ای از اعداد و ...) رو ریخت توی نوع کلاس object (نه آرایه ای از object) یعنی کد زیر درست هه :
کد:
object a = new int[] { 1, 5 };
یا
کد:
Array a = new int[] { 1, 5 };
این فرضیه درسته دیگه؟

ایراد در اصل نه از mainThreadDelegate.Invoke ئه و نه از []params object. اون this.Invoke یکم ایراد داره. با delegate هایی که params دارند به مشکل بر میخوره.
خاصیت params اینه که اگه یک آرایه رو به عنوان تنها پارامتر دریافت کنه بصورت پارامتر های متوالی محسوب می شوند، نه فقط یک آرایه برای پارامتر اول.
کد:
        private delegate void TestDelegate(params string[] args);

        private void Test(params string[] args)
        {
            MessageBox.Show(string.Join(", ", args));
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            new TestDelegate(Test).Invoke("one-two-three".Split('-'));
        }
و موقع استفاده از متدی مثل Control.Invoke ناچارید که از این خصوصیت استفاده کنید :
کد:
        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            this.Invoke(mainThreadDelegate, (object)new object[] { IntPtr.Zero, text, "", 0 });
        }
دقت کنید که ایراد از Control.Invoke ئه وگرنه خود delegate تون همچین مشکلی نداره :
کد:
        private void button3_Click(object sender, EventArgs e)
        {
            string text = "از متد اینووک در فرم1 اجرا میشه برای اجرا در نخ اصلی با آرگومان هندل صفر";
            mainThreadDelegate.Invoke(IntPtr.Zero, text, "", 0);
        }

حالا این کد زیر ، ارور نمیده ولی کار اصلی اش که قرار بود اتوماتیک متد مورد نظر رو در نخ اصلی خودش فراخونی کنه رو انجام نمیده چرا ؟؟! :

کد:
mainThreadDelegate.Invoke(new object[] { IntPtr.Zero, text, "", 0 });

کار که می کنه، ولی کاربرد اش اون نیست. DefaultValue در مقدار اولیه ای که به متغیر defaultProperty تون داده اید یا می توانستید بدهید هیچ دخالتی نخواهد کرد. DefaultValue فرضا برای اینه که ویژوال استدیو بدونه موقع Serialize کردن یک شیء Father نیازی به ذخیره کردن مقدار DefaultProperty هست یا خیر. اگه مقدارش در حالت پیشفرض باشه مقدار اون پروپرتی رو ذخیره نمی کنه. یا موقع ریست کردن مقدار پروپرتی برای اینه که بدونه به چه مقداری تغییرش بده. کلاس تون رو اول در یک فایل جداگانه کلاس قرار بدید که قاطی Form1 یا جزئی از اون نباشه و وارث System.ComponentModel.Component اش کنید تا بعد از کمپایل بتوانید از بالای نوار Toolbox که مخصوص پروژه خودتونه برش دارید و پایین فرم تون یک نمونه اش رو قرار بدید (مثل Timer یا BackgroundWorker)، بعد ببینید وقتی دارید فرم رو طراحی می کنید در پنجره Properties وقتی روی DefaultProperty راست کلید می کنید و Reset رو انتخاب می کنید چه اتفاقی می افته :
کد:
public class Father : System.ComponentModel.Component

پس attributes ای نیست که بشه اونچه که مد نظرم هست یعنی مقدار پیش فرض به متد یا پروپرتی یا فیلد ای داد؟
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
2 تا سئوال جانبی دیگه هم داشتم
اینکه توی ویژال استودیو انگار درست حسابی متن ادغام فارسی و انگلیسی رو ساپورت نمیکنه و با بهم ریختگی از لحاظ ترتیب همراه میشه :

کد:
string a = "تست test یک متن";
راهی برای درست شدنش هست؟
دوم اینکه شما Visual Studio 2017 RC رو دارین؟ فایل آفلاین کامل شو میشه گیر آورد ؟ اگه آره ، لینک میدین (گشتم ، پیدا نکردم)
سوم اینکه فایل راهنمای آفلاین نسخه ی 2015 رو میشه گیر آورد؟ من راهنمای آفلاین نسخه 2010 رو دارم ولی برای 2015 هر کجا چرخیدم گیر نیاوردم . اگه میدونین ، بی زحمت لینک اینم میدین؟
ممنون
 

the_king

مدیرکل انجمن
سلام
ممنون آقا علی
قبلا میگفتم mp3 رو چجوری میشه بدون شی بصری windows media player بالا آورد ، الان فهمیدم روش راحت ترش رو . داخل همون فایل wmp.dll (در پوشه ی system32) ، اگه از کلاس WMPLib.WindowsMediaPlayerClass شی درست کنیم ، میشه بدون ایجاد کنترل بصری ، از امکانات windows media player که پخش آهنگ mp3 و ... هم هست استفاده کرد (نمیدونستم) اما اگه از کلاس AxWMPLib.AxWindowsMediaPlayer شی درست شه ، یه کنترل بصری هست که باید به کنترل add شه تا باهاش کار کرد
ActiveX ها بر اساس یک تکنولوژی COM یا Component Object Model طراحی شده اند که شما از امکانات COM اش برای شیء ساختن از کلاس ها استفاده کرده اید. الزامی نداره که کلاس ها در همه کتابخانه های ActiveX به این صورت قابل استفاده باشند، بستگی به طراحی شون داره ولی بعضی هاشون این امکان رو دارند.
حالا سئوالم اینه که اولا چجوری میشه فهمید یه کلاسی از نوع دات نت هست یا از نوع Unmanaged ؟ اگه از نوع Unmanaged باشه ، حتما موقع فراخونی ، به attributes DllImport نیاز داره دیگه؟
هر موردی که در پنجره Object Browser ویژوال استدیو لیست میشه Managed ئه، بقیه Unmanaged اند و با DllImport بهشون دسترسی پیدا می کنید.
COM ها در اصل Unmanaged اند ولی NET. بصورت خودکار و با تفسیر شون رابطی ایجاد می کنه که بصورت Managed قابل استفاده باشند. اون چیزی که تحت عنوان interop در اسامی میاد مربوط به همین رابط ئه که نقش واسطه رو داره.

دوم اینکه در همین کلاس WMPLib.WindowsMediaPlayerClass ، چجوری میشه آهنگ رو جلو برد؟ مثلا ثانیه ی 35 ام رو بره پخش کنه یا مثلا 20 درصد از زمان آهنگ رو بره جلوتر یا عقب تر پخش کنه؟ اصلا اعضای این کلاس هیچ توضیحی نوشته نیست براش . متد یا پروپرتی ای بنام Seek هم نداره . یه متدی که انگار بهش بیاد این کار رو کنه ، متد step هست که انگار هم اونم این کار رو نمیکنه و هم ارور میده نمیدونم چرا !
کلا کدوم متد یا پرورتی در این کلاس ، باعث جلو یا عقب رفتن روند زمان آهنگ میشه؟
فکر کنم بار سوم ئه که یادآوری می کنم، هر مستندی که مربوط به محصولات مایکروسافت باشه در سایت مایکروسافت ئه، نه تو گوگل و نه این فروم و نه هیچ جای دیگه ای دنبال مستندات محصولات مایکروسافت نگردید. از جستجوی سایت مایکروسافت استفاده کنید.
Object Model Reference for Scripting
مثلا چیزی که شما دنبالش می گردید Controls.currentPosition ئه.

اگه اشتباه نکنم ، آرایه ای که میسازیم ، در واقع ای از کلاس Array ساخته میشه . حالا مثلا آرایه ای از عدد []int یا هر شی دیگه ای که میسازیم ، دقیقا نمیدونم خودش از کلاس Array شی ای میسازه که باز کلاس Array از کلاس int پیاده سازی شده باشه (که احتمالا همین جوره فکر کنم بخاطر اینترفیس های متنوعی که به ارث میبره) و یا اینکه واقعا وقتی آرایه ای از عددها []int که میسازیم ، هم از کلاس int و هم از کلاس Array شی میسازه
به هر حال ، به یکی از دلیل های بالا ، نمیشه آرایه ای از عدد رو داخل یه نوع عدد (بدون آرایه) ریخت یعنی نمیشه :

کد:
int a = new int[] { 1, 5 };
چون آرایه خودش از کلاس Array هست ولی int فقط از کلاس int هست و شی کلاس Array توی نوع کلاس int ریخته نمیشه
ولی نوع object چون همه ی کلاس ها من جمله کلاس Array رو هم شامل میشه و پدر همه شونه ، میشه شی ای از کلاس Array (که آرایه ای از یک نوع هست مثلا آرایه ای از اعداد و ...) رو ریخت توی نوع کلاس object (نه آرایه ای از object) یعنی کد زیر درست هه :
کد:
object a = new int[] { 1, 5 };
یا
کد:
Array a = new int[] { 1, 5 };
این فرضیه درسته دیگه؟
نه دقیقا، تعریف Array رو ببینید، کلاس abstract ئه، شما حتی نمی توانید با new Array ازش شیء بسازید. اما در کل [] به عنوان زیر ساخت از Array استفاده می کنه. به این مساله توجه کنید که Array خودش یک آرایه نیست، شامل امکاناتی است که مدیریت یک آرایه بهشون نیاز داره.
اینکه []int رو داخل int بریزید کلا بی معنی است، عملی نبودنش ارتباطی با دو تا فرض تون نداره. شما حتی Class1 رو وارث object معرفی کنید، همچنان []Class1 رو نمیشه داخل Class1 قرار داد.

حالا این کد زیر ، ارور نمیده ولی کار اصلی اش که قرار بود اتوماتیک متد مورد نظر رو در نخ اصلی خودش فراخونی کنه رو انجام نمیده چرا ؟؟! :
کد:
mainThreadDelegate.Invoke(new object[] { IntPtr.Zero, text, "", 0 });
Invoke برای delegate با Invoke ئه Control فرق میکنه، شما دارید متد تون رو با نخی که به دستور Invoke می رسه فراخوانی می کنید، نخ اصلی ای در کار نیست. قبلا هم اشاره کردم، برای delegate نخ با نخ فرقی نداره، انتخاب نخی صورت نمی گیره.

پس attributes ای نیست که بشه اونچه که مد نظرم هست یعنی مقدار پیش فرض به متد یا پروپرتی یا فیلد ای داد؟
نه، ساختن attribute مورد نظرتون کد نویسی می خواد، شما می توانید از هر Attribute ای که خواستید برای اون منظور استفاده کنید، حتی از همون DefaultValue، ولی این Attribute نمی تونه موقعی که شیء از کلاس تون ساخته میشه کاری انجام بده، خودتون باید موقعی که متد سازنده کلاس اجرا میشه کدی بنویسید که Property ها رو بگرده و هر کدوم اون صفت مورد نظر رو داشتند بهشون مقدار بده. همه Attribute همینطور اند، اگه کدی براشون نوشته نشده بود خودش به تنهایی نمی توانند موقع ساختن شیء کاری انجام بدهند.

2 تا سئوال جانبی دیگه هم داشتم
اینکه توی ویژال استودیو انگار درست حسابی متن ادغام فارسی و انگلیسی رو ساپورت نمیکنه و با بهم ریختگی از لحاظ ترتیب همراه میشه :

کد:
string a = "تست test یک متن";
راهی برای درست شدنش هست؟
در Unicode کاراکتر های کنترلی مثل Right to left override داریم که جهت نمایش متن رو تغییر میده، ولی فقط مناسب فایل های متنی مثل txt است، با ویرایشگر کد سازگاری نداره چون نمایش عبارات انگلیسی رو هم برعکس می کنه.

دوم اینکه شما Visual Studio 2017 RC رو دارین؟ فایل آفلاین کامل شو میشه گیر آورد ؟ اگه آره ، لینک میدین (گشتم ، پیدا نکردم)
سوم اینکه فایل راهنمای آفلاین نسخه ی 2015 رو میشه گیر آورد؟ من راهنمای آفلاین نسخه 2010 رو دارم ولی برای 2015 هر کجا چرخیدم گیر نیاوردم . اگه میدونین ، بی زحمت لینک اینم میدین؟
ممنون
خیر. کلا نحوه ارتباط Visual Studio با MSDN آنلاین شده، از این ابزار استفاده کنید :
Visual Studio 2012/2013/2015 Help Downloader
 

پیوست ها

  • VisualStudioHelperDownloader-v1.2.0.0.zip
    24.7 کیلوبایت · بازدیدها: 0

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ActiveX ها بر اساس یک تکنولوژی COM یا Component Object Model طراحی شده اند که شما از امکانات COM اش برای شیء ساختن از کلاس ها استفاده کرده اید. الزامی نداره که کلاس ها در همه کتابخانه های ActiveX به این صورت قابل استفاده باشند، بستگی به طراحی شون داره ولی بعضی هاشون این امکان رو دارند.

هر موردی که در پنجره Object Browser ویژوال استدیو لیست میشه Managed ئه، بقیه Unmanaged اند و با DllImport بهشون دسترسی پیدا می کنید.
COM ها در اصل Unmanaged اند ولی NET. بصورت خودکار و با تفسیر شون رابطی ایجاد می کنه که بصورت Managed قابل استفاده باشند. اون چیزی که تحت عنوان interop در اسامی میاد مربوط به همین رابط ئه که نقش واسطه رو داره.

فکر کنم بار سوم ئه که یادآوری می کنم، هر مستندی که مربوط به محصولات مایکروسافت باشه در سایت مایکروسافت ئه، نه تو گوگل و نه این فروم و نه هیچ جای دیگه ای دنبال مستندات محصولات مایکروسافت نگردید. از جستجوی سایت مایکروسافت استفاده کنید.
Object Model Reference for Scripting
مثلا چیزی که شما دنبالش می گردید Controls.currentPosition ئه.
سلام
ممنون استاد علی
والا همونطور که میدونین ، من مبتدی هستم . درک درستی از COM ها ندارم و این قدر هم به زور باهاشون کار میکنم :green:مخصوصا هم که راهنمای شون از راهنمای کلاس های معمولی ، سخت تر هست یه کمی
جستجوی سایتش هم همه ی چیز رو قشنگ میاره جز راهنمای COM ها رو که به زور باید پیداش کرد
همونطور که گفتین "COM ها در اصل Unmanaged اند" ، بخاطر همین هست که خیلی از اعضای همین کلاس WMPLib.WindowsMediaPlayerClass نمیتونن تریس بشن و پیام میده که برای تریس شدن باید گزینه ی "Enable Native Code Debugging" رو باید در منوی Project>Properties>Debug فعال کنیم؟


نه دقیقا، تعریف Array رو ببینید، کلاس abstract ئه، شما حتی نمی توانید با new Array ازش شیء بسازید. اما در کل [] به عنوان زیر ساخت از Array استفاده می کنه. به این مساله توجه کنید که Array خودش یک آرایه نیست، شامل امکاناتی است که مدیریت یک آرایه بهشون نیاز داره.
اینکه []int رو داخل int بریزید کلا بی معنی است، عملی نبودنش ارتباطی با دو تا فرض تون نداره. شما حتی Class1 رو وارث object معرفی کنید، همچنان []Class1 رو نمیشه داخل Class1 قرار داد.
راستی آره .
همه ی کلاس ها که از object ارث میبرن پس همونطور که گفتین ، چرا فقط آرایه ای از آبجکت ها رو میشه در نوع آبجکت ریخت و مثلا نمیشه آرایه ای از int ها را در int ریخت؟
من قشنگ درک نکردم این موضوع رو


Invoke برای delegate با Invoke ئه Control فرق میکنه، شما دارید متد تون رو با نخی که به دستور Invoke می رسه فراخوانی می کنید، نخ اصلی ای در کار نیست. قبلا هم اشاره کردم، برای delegate نخ با نخ فرقی نداره، انتخاب نخی صورت نمی گیره.
با متد Invoke در Controls هم فراخونی کنم ، بازم همین مشکل هست (مثل this.Invoke)

در Unicode کاراکتر های کنترلی مثل Right to left override داریم که جهت نمایش متن رو تغییر میده، ولی فقط مناسب فایل های متنی مثل txt است، با ویرایشگر کد سازگاری نداره چون نمایش عبارات انگلیسی رو هم برعکس می کنه.

الان شما همه ، توی همه ی پروژه هاشون مشکل ترکیب فارسی با انگلیسی رو دارن یعنی؟!!
اینکه نه این طرف مشکل پیدا شه نه اون طرف
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
سلام
این کد پروژه ی بسیار کوچیکی هست که با استفاده از کلاس غیر بصری WMPLib.WindowsMediaPlayerClass (کمپوننت ویندوز مدیا پلیر) ، نوشته شد و کدهاش شامل انتخاب انواع فایل صوتی و اضافه کردن به پلی لیست و همینطور پلی و پاوز کردن آهنگ و جلو و عقب بردن آهنگ و رفتن به آهنگ بعدی و قبلی در پلی لیست و کم و زیاد کردن صدا و گرفتن زمان آهنگ هست .
گفتم شاید به درد کسی خورد .
توضیحات بصورت کامنت در کد هست
پروژه در User Control ای به ابعاد 980 در 675 هست (با تشکر از راهنمایی های استاد علی) :


کد:
using System;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace Practice_1
{
    public partial class UserControlMp3Player : UserControl
    {
        public Form1 sourceForm;
        public WMPLib.WindowsMediaPlayerClass mp3PlayerVisualLess;
        OpenFileDialog browseObj;
        //string filePath;
        string[] playList;
        //-----------------------------------------------------------------------------
       
        /// <summary>
        /// هر چند شی ای از Form میشه داد ولی شما باید حتما شی ای از Form1 بدید
        /// </summary>
        /// <param name="sourceForm"></param>
        public UserControlMp3Player(Form sourceForm)
        {
            InitializeComponent();
            this.mp3PlayerVisualLess = new WMPLib.WindowsMediaPlayerClass();
            this.mp3PlayerVisualLess.StatusChange += Mp3PlayerVisualLess_StatusChange;
            browseObj = new OpenFileDialog();
            browseObj.Filter = "Audio Files (*.mp3; *.wma; *.wav; *.wave; *.ogg)|*.mp3;*.wma;*.wav;*.wave;*.ogg";
            browseObj.Multiselect = true;
            browseObj.InitialDirectory = @"G:\Mp3\mp3 flash (golchin)\CD 1";
            Mp3PlayerVisualLess_StatusChange();
            if (sourceForm is Form1)
                this.sourceForm = (Form1)sourceForm;
        }

        private void Mp3PlayerVisualLess_StatusChange()
        {

            if ( (new Regex("^Finished")).IsMatch(this.mp3PlayerVisualLess.status) )
            {
                this.btnPlay.Text = "Play";
                this.panelSeek.Enabled = false;
            }
            else if ( (new Regex("^Playing")).IsMatch(this.mp3PlayerVisualLess.status) )
            {
                this.btnPlay.Text = "Pause";
                this.btnPlay.Enabled = true;
                this.panelSeek.Enabled = true;
            }
            else if ( (new Regex("^Pause")).IsMatch(this.mp3PlayerVisualLess.status) )
            {
                this.btnPlay.Text = "Play";
                this.panelSeek.Enabled = false;
            }
            else if ((new Regex("^Ready")).IsMatch(this.mp3PlayerVisualLess.status))
            {
                this.btnPlay.Text = "Play";
                this.btnPlay.Enabled = true;
            }
            else if (this.mp3PlayerVisualLess.status == "")
            {
                //this.btnPlayMp3.Text = "Play";
                this.btnPlay.Enabled = false;
                this.panelSeek.Enabled = false;
            }
            //else
            //{
            //    this.btnPlayMp3.Enabled = true;
            //    this.btnSetPosition.Enabled = true;
            //}
            //MessageBox.Show(this.mp3PlayerVisualLess.status);
        }
       
        private void btnPage1_Click(object sender, EventArgs e)
        {
            this.Hide();
            this.sourceForm.HideControls(this.sourceForm.pagesControls[0], false);
        }

        private void btnBrowseMp3_Click(object sender, EventArgs e)
        {
            DialogResult selectFile = browseObj.ShowDialog();
            if (selectFile == DialogResult.OK)
            {
                this.playList = browseObj.FileNames;
                browseObj.InitialDirectory = this.playList[0];
                mp3PlayerVisualLess.autoStart = false;
                mp3PlayerVisualLess.URL = this.playList[0];  // در صورتی که پروپرتی autoStart ترو باشه ، با مقدار دادن به پروپرتی Url ، شروع به پلی شدن میکنه
                //this.mp3PlayerVisualLess.currentMedia = this.mp3PlayerVisualLess.newMedia(this.filePath);  // بجای پروپرتی URL برای تعیین فایل رسانه و آهنگ ، میشه از پروپرتی currentMedia هم استفاده کرد که این پروپرتی چون مقداری از اینترفیس IWMPMedia رو میخواد ، میشه از کلاس newMedia استفاده و مقداردهی اش کرد

                for (int i = 1; i < this.playList.Length; i++)  // بجز اولی که در پلی لیست رایج هست ، بقیه ی آیتم و آهنگ های انتخاب شده را در پلی لیست جاری اضافه میکنه (اولی که قبلا وجود داشت)
                {
                    WMPLib.IWMPMedia myMedia = this.mp3PlayerVisualLess.newMedia(this.playList[i]);
                    this.mp3PlayerVisualLess.currentPlaylist.appendItem(myMedia);
                    //this.mp3PlayerVisualLess.controls.next();  // با اجرای این دستور ، آهنگ بعدی در پلی لیست ، پلی میشه
                }
               
                //MessageBox.Show(mp3PlayerVisualLess.currentPlaylist.Item[0].duration + "");  // پلی لیست ، تا زمانی که برای اولین بار ، آهنگ پلی نشه ، وجود نداره پس این کد در این قسمت اطلاعاتی رو نمیتونه برگردونه چون تا به این خط آهنگی پلی نشد که پلی لیستی ساخته شه
            }
        }

        private void btnPlay_Click(object sender, EventArgs e)
        {
            if (btnPlay.Text == "Play")
                mp3PlayerVisualLess.play();
            else
                mp3PlayerVisualLess.pause();
        }

        private void btnSeekForward10Sec_Click(object sender, EventArgs e)
        {
            this.mp3PlayerVisualLess.currentPosition = this.mp3PlayerVisualLess.currentPosition + 10;  // هم از پروپرتی currentPosition ای که مستقیم در کلاس WMPLib.WindowsMediaPlayerClass هست میشه مدیریت زمان آهنگ رو کنترل کرد و هم همین پروپرتی که در اینترفیس IWMPControls وجود داره و با پروپرتی Controls در همین کلاس WindowsMediaPlayerClass در دسترس مون قرار میگیره
        }

        private void btnSeekBackward10Sec_Click(object sender, EventArgs e)
        {
            mp3PlayerVisualLess.controls.currentPosition = mp3PlayerVisualLess.controls.currentPosition - 10;  // هم از پروپرتی currentPosition ای که مستقیم در کلاس WMPLib.WindowsMediaPlayerClass هست میشه مدیریت زمان آهنگ رو کنترل کرد و هم همین پروپرتی که در اینترفیس IWMPControls وجود داره و با پروپرتی Controls در همین کلاس WindowsMediaPlayerClass در دسترس مون قرار میگیره
        }

        private void btnVolumeDown_Click(object sender, EventArgs e)
        {
            if (this.mp3PlayerVisualLess.volume > 0)
                this.mp3PlayerVisualLess.volume -= 10;
        }

        private void btnVolumeUp_Click(object sender, EventArgs e)
        {
            if (this.mp3PlayerVisualLess.volume < 100)
                this.mp3PlayerVisualLess.volume += 10;
        }

        private void btnGetDuration_Click(object sender, EventArgs e)
        {
            lblGetDuration.Text = ((int)this.mp3PlayerVisualLess.controls.currentItem.duration).ToString() + "  Secand\n" + mp3PlayerVisualLess.controls.currentItem.durationString + " Minutes";  // برای گرفتن اطلاعات زمان کلی آهنگ ، باید از اعضای کلاس پلی لیست جاری ، دسترسی پیدا کرد (با پروپرتی آیتم و بعد شماره ی آیتم مورد نظر در پلی لیست و بعد استفاده از اعضای مورد نظر کلاسی که آیتم اون کلاس رو برمیگردونه
            //this.mp3PlayerVisualLess.controls.currentItem  // برای گرفتن خیلی از متدها و پروپرتی های مفید و کاربردی ، باید به پروپرتی controls یعنی در واقع اینترفیس IWMPControls مراجعه کرد مثل پروپرتی گرفتن شماره ی آیتم در حال خوندن و پلی شدن در پلی لیست
        }

        private void btnPreviuse_Click(object sender, EventArgs e)
        {
            this.mp3PlayerVisualLess.controls.previous();  // رسانه (آهنگ) قبلی در پلی لیست رو اجرا میکنه (اگه در حالت play یا autoPlay ترو باشه)
        }

        private void btnNext_Click(object sender, EventArgs e)
        {
            this.mp3PlayerVisualLess.controls.next();  // رسانه (آهنگ) بعدی در پلی لیست رو اجرا میکنه (اگه در حالت play یا autoPlay ترو باشه)
        }


    }  /////////////////////////////////////////////////////////////////end class
}
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
اینم کدهای مربوط به Desiner یوزر کنترل :
کد:
namespace Practice_1
{
    partial class UserControlMp3Player
    {
    
        private System.ComponentModel.IContainer components = null;


        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

   
        private void InitializeComponent()
        {
            this.btnPage1 = new System.Windows.Forms.Button();
            this.btnBrowseMp3 = new System.Windows.Forms.Button();
            this.btnPlay = new System.Windows.Forms.Button();
            this.btnSeekForward10Sec = new System.Windows.Forms.Button();
            this.panelSeek = new System.Windows.Forms.Panel();
            this.btnGetDuration = new System.Windows.Forms.Button();
            this.btnVolumeUp = new System.Windows.Forms.Button();
            this.btnVolumeDown = new System.Windows.Forms.Button();
            this.btnSeekBackward10Sec = new System.Windows.Forms.Button();
            this.lblGetDuration = new System.Windows.Forms.Label();
            this.btnPreviuse = new System.Windows.Forms.Button();
            this.btnNext = new System.Windows.Forms.Button();
            this.panelSeek.SuspendLayout();
            this.SuspendLayout();
     
            this.btnPage1.ForeColor = System.Drawing.Color.MediumBlue;
            this.btnPage1.Location = new System.Drawing.Point(877, 642);
            this.btnPage1.Name = "btnPage1";
            this.btnPage1.Size = new System.Drawing.Size(100, 30);
            this.btnPage1.TabIndex = 1;
            this.btnPage1.Text = "GoTo Page1";
            this.btnPage1.UseVisualStyleBackColor = true;
            this.btnPage1.Click += new System.EventHandler(this.btnPage1_Click);
    
            this.btnBrowseMp3.ForeColor = System.Drawing.Color.Black;
            this.btnBrowseMp3.Location = new System.Drawing.Point(877, 6);
            this.btnBrowseMp3.Name = "btnBrowseMp3";
            this.btnBrowseMp3.Size = new System.Drawing.Size(100, 30);
            this.btnBrowseMp3.TabIndex = 2;
            this.btnBrowseMp3.Text = "Browse Mp3";
            this.btnBrowseMp3.UseVisualStyleBackColor = true;
            this.btnBrowseMp3.Click += new System.EventHandler(this.btnBrowseMp3_Click);
     
            this.btnPlay.ForeColor = System.Drawing.Color.Black;
            this.btnPlay.Location = new System.Drawing.Point(6, 6);
            this.btnPlay.Name = "btnPlay";
            this.btnPlay.Size = new System.Drawing.Size(100, 30);
            this.btnPlay.TabIndex = 3;
            this.btnPlay.Text = "Play";
            this.btnPlay.UseVisualStyleBackColor = true;
            this.btnPlay.Click += new System.EventHandler(this.btnPlay_Click);
      
            this.btnSeekForward10Sec.ForeColor = System.Drawing.Color.Black;
            this.btnSeekForward10Sec.Location = new System.Drawing.Point(141, 3);
            this.btnSeekForward10Sec.Name = "btnSeekForward10Sec";
            this.btnSeekForward10Sec.Size = new System.Drawing.Size(132, 30);
            this.btnSeekForward10Sec.TabIndex = 4;
            this.btnSeekForward10Sec.Text = "Seek Forward 10 Sec";
            this.btnSeekForward10Sec.UseVisualStyleBackColor = true;
            this.btnSeekForward10Sec.Click += new System.EventHandler(this.btnSeekForward10Sec_Click);

            this.panelSeek.Controls.Add(this.btnNext);
            this.panelSeek.Controls.Add(this.btnPreviuse);
            this.panelSeek.Controls.Add(this.btnGetDuration);
            this.panelSeek.Controls.Add(this.btnVolumeUp);
            this.panelSeek.Controls.Add(this.btnVolumeDown);
            this.panelSeek.Controls.Add(this.btnSeekBackward10Sec);
            this.panelSeek.Controls.Add(this.btnSeekForward10Sec);
            this.panelSeek.Location = new System.Drawing.Point(3, 42);
            this.panelSeek.Name = "panelSeek";
            this.panelSeek.Size = new System.Drawing.Size(974, 41);
            this.panelSeek.TabIndex = 5;
 
            this.btnGetDuration.ForeColor = System.Drawing.Color.Black;
            this.btnGetDuration.Location = new System.Drawing.Point(742, 3);
            this.btnGetDuration.Name = "btnGetDuration";
            this.btnGetDuration.Size = new System.Drawing.Size(100, 30);
            this.btnGetDuration.TabIndex = 8;
            this.btnGetDuration.Text = "Get Duration";
            this.btnGetDuration.UseVisualStyleBackColor = true;
            this.btnGetDuration.Click += new System.EventHandler(this.btnGetDuration_Click);

            this.btnVolumeUp.ForeColor = System.Drawing.Color.Black;
            this.btnVolumeUp.Location = new System.Drawing.Point(624, 3);
            this.btnVolumeUp.Name = "btnVolumeUp";
            this.btnVolumeUp.Size = new System.Drawing.Size(100, 30);
            this.btnVolumeUp.TabIndex = 7;
            this.btnVolumeUp.Text = "Volume Up";
            this.btnVolumeUp.UseVisualStyleBackColor = true;
            this.btnVolumeUp.Click += new System.EventHandler(this.btnVolumeUp_Click);

            this.btnVolumeDown.ForeColor = System.Drawing.Color.Black;
            this.btnVolumeDown.Location = new System.Drawing.Point(518, 3);
            this.btnVolumeDown.Name = "btnVolumeDown";
            this.btnVolumeDown.Size = new System.Drawing.Size(100, 30);
            this.btnVolumeDown.TabIndex = 6;
            this.btnVolumeDown.Text = "Volume Down";
            this.btnVolumeDown.UseVisualStyleBackColor = true;
            this.btnVolumeDown.Click += new System.EventHandler(this.btnVolumeDown_Click);

            this.btnSeekBackward10Sec.ForeColor = System.Drawing.Color.Black;
            this.btnSeekBackward10Sec.Location = new System.Drawing.Point(3, 3);
            this.btnSeekBackward10Sec.Name = "btnSeekBackward10Sec";
            this.btnSeekBackward10Sec.Size = new System.Drawing.Size(132, 30);
            this.btnSeekBackward10Sec.TabIndex = 5;
            this.btnSeekBackward10Sec.Text = "Seek Backward 10 Sec";
            this.btnSeekBackward10Sec.UseVisualStyleBackColor = true;
            this.btnSeekBackward10Sec.Click += new System.EventHandler(this.btnSeekBackward10Sec_Click);

            this.lblGetDuration.AutoSize = true;
            this.lblGetDuration.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(178)));
            this.lblGetDuration.Location = new System.Drawing.Point(3, 608);
            this.lblGetDuration.Name = "lblGetDuration";
            this.lblGetDuration.Size = new System.Drawing.Size(0, 25);
            this.lblGetDuration.TabIndex = 6;

            this.btnPreviuse.ForeColor = System.Drawing.Color.Black;
            this.btnPreviuse.Location = new System.Drawing.Point(291, 3);
            this.btnPreviuse.Name = "btnPreviuse";
            this.btnPreviuse.Size = new System.Drawing.Size(100, 30);
            this.btnPreviuse.TabIndex = 9;
            this.btnPreviuse.Text = "Previuse";
            this.btnPreviuse.UseVisualStyleBackColor = true;
            this.btnPreviuse.Click += new System.EventHandler(this.btnPreviuse_Click);
 
            this.btnNext.ForeColor = System.Drawing.Color.Black;
            this.btnNext.Location = new System.Drawing.Point(397, 3);
            this.btnNext.Name = "btnNext";
            this.btnNext.Size = new System.Drawing.Size(100, 30);
            this.btnNext.TabIndex = 10;
            this.btnNext.Text = "Next";
            this.btnNext.UseVisualStyleBackColor = true;
            this.btnNext.Click += new System.EventHandler(this.btnNext_Click);

            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.Controls.Add(this.lblGetDuration);
            this.Controls.Add(this.panelSeek);
            this.Controls.Add(this.btnPlay);
            this.Controls.Add(this.btnBrowseMp3);
            this.Controls.Add(this.btnPage1);
            this.Name = "UserControlMp3Player";
            this.Size = new System.Drawing.Size(980, 675);
            this.panelSeek.ResumeLayout(false);
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        private System.Windows.Forms.Button btnPage1;
        private System.Windows.Forms.Button btnBrowseMp3;
        private System.Windows.Forms.Button btnPlay;
        private System.Windows.Forms.Button btnSeekForward10Sec;
        private System.Windows.Forms.Panel panelSeek;
        private System.Windows.Forms.Button btnSeekBackward10Sec;
        private System.Windows.Forms.Button btnVolumeUp;
        private System.Windows.Forms.Button btnVolumeDown;
        private System.Windows.Forms.Button btnGetDuration;
        private System.Windows.Forms.Label lblGetDuration;
        private System.Windows.Forms.Button btnNext;
        private System.Windows.Forms.Button btnPreviuse;
    }
}
 

the_king

مدیرکل انجمن
همونطور که گفتین "COM ها در اصل Unmanaged اند" ، بخاطر همین هست که خیلی از اعضای همین کلاس WMPLib.WindowsMediaPlayerClass نمیتونن تریس بشن و پیام میده که برای تریس شدن باید گزینه ی "Enable Native Code Debugging" رو باید در منوی Project>Properties>Debug فعال کنیم؟
بله، ولی گمان نکنم فعال کردنش براتون کاربردی داشته باشه.


راستی آره .
همه ی کلاس ها که از object ارث میبرن پس همونطور که گفتین ، چرا فقط آرایه ای از آبجکت ها رو میشه در نوع آبجکت ریخت و مثلا نمیشه آرایه ای از int ها را در int ریخت؟
من قشنگ درک نکردم این موضوع رو
شما می خواهید {1,2,3} []new int رو که سه عدد ئه داخل یک int قرار بدید که صرفا یک عدد ئه؟ از نظر منطقی براتون همچین چیزی بابد عملی باشه؟ به نظر تون اون عدد باید چه مقداری باشه؟
از نظر ساختار داده هم عملی نیست، int یک عدد 4 بایتی ئه، مثل object طول متغیر با محتوای نامعلوم نداره.

با متد Invoke در Controls هم فراخونی کنم ، بازم همین مشکل هست (مثل this.Invoke)

چه مشکلی؟

الان شما همه ، توی همه ی پروژه هاشون مشکل ترکیب فارسی با انگلیسی رو دارن یعنی؟!!
اینکه نه این طرف مشکل پیدا شه نه اون طرف
توضیحات فارسی برای کد مرسوم نیست، با توجه به امکانات ویرایشگر کد هم دردسر سازه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
شما می خواهید {1,2,3} []new int رو که سه عدد ئه داخل یک int قرار بدید که صرفا یک عدد ئه؟ از نظر منطقی براتون همچین چیزی بابد عملی باشه؟ به نظر تون اون عدد باید چه مقداری باشه؟
از نظر ساختار داده هم عملی نیست، int یک عدد 4 بایتی ئه، مثل object طول متغیر با محتوای نامعلوم نداره.
ممنون آقا علی
میدونم این چیزایی که میگین رو .
واسه همین قضیه ی نامحدود بودن فضای object هه که میتونه هر چیزی رو توی خودش ذخیره کنه؟


شما گفته بودین که اگه از متد Invoke در کنترل ها استفاده کنیم (من از کنترل فرم1 استفاده کردم) ، متد مورد نظری که با همین متد Invoke اجرا شده رو اتوماتیک در نخ اصلی اجرا میکنه ولی نکرد!
 

the_king

مدیرکل انجمن
ممنون آقا علی
میدونم این چیزایی که میگین رو .
واسه همین قضیه ی نامحدود بودن فضای object هه که میتونه هر چیزی رو توی خودش ذخیره کنه؟

بله، به سه دلیل، دلیل اول اینه که فضای داده اش نامحدود (متغیر) ئه، و دلیل دوم اینکه داده داخلش میتونه از هر نوعی باشه. مثلا string هم فضای داده اش متغیر ئه ولی نوع داده داخلش فقط تعدادی char ئه. دلیل سوم اینکه نوع داده داخلش رو هم ذخیره می کنه. مثلا اگر داخلش یک Control قرار بگیره بعدا داده داخلش با []byte اشتباه گرفته نمیشه.

شما گفته بودین که اگه از متد Invoke در کنترل ها استفاده کنیم (من از کنترل فرم1 استفاده کردم) ، متد مورد نظری که با همین متد Invoke اجرا شده رو اتوماتیک در نخ اصلی اجرا میکنه ولی نکرد!
همینطور ئه، منظورتون چیه؟ میخواهید بگید Control.Invoke خرابه؟ یا اگه منظورتون اینه که چشم بسته بگم فلان سطر کدتون ایراد داره که مشکل پیش میاد، عملی نیست. باید کدتون بررسی بشه.
مثال ساده اش اینه، ببینید current thread ای که در Test اعلام میشه کدوم نخ ئه، باید thread 1 باشه. اگر thread 2 باشه Invoke درست کار نمی کنه :
کد:
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private System.Threading.Thread _main;
        private System.Threading.Thread _other;
        private delegate void TestDelegate();

        private void Form1_Load(object sender, EventArgs e)
        {
            _main = System.Threading.Thread.CurrentThread;
            _other = new System.Threading.Thread(new System.Threading.ThreadStart(OtherThreadProc));
            _other.Start();
        }

        private void OtherThreadProc()
        {
            _other = System.Threading.Thread.CurrentThread;
            this.Invoke(new TestDelegate(Test));
        }

        private void Test()
        {
            var sb = new StringBuilder();
            var current = System.Threading.Thread.CurrentThread;
            sb.AppendFormat("thread 1 (main thread) = {0:X8}\n", _main.ManagedThreadId);
            sb.AppendFormat("thread 2 (other thread) = {0:X8}\n", _other.ManagedThreadId);
            sb.AppendFormat("current thread = {0:X8}\n", current.ManagedThreadId);
            MessageBox.Show(sb.ToString());
        }
    }
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون استاد علی
میگم ، استاد علی ، این قضیه ی کد زیر به نخ ربط نداره و به هندل ویندوز ربط داره ها :

کد:
[DllImport("User32.dll",EntryPoint = "MessageBoxA")]public static extern int MessageBoxAPI(IntPtr hWnd, string Text, string Caption, int uType);

        private void Form1_Load(object sender, EventArgs e)
        {
            Form1.MessageBoxAPI(IntPtr.Zero, "1", "", 0);
            Form1.MessageBoxAPI(new IntPtr(), "2", "", 0);
        }

میدونم هیچ نخی ایجاد نکردیم ولی شما گفته بودین که وصل نشدن یک پیغام یا ویندوز ، به ویندوز دیگه ربطی به هندل نداره و به نخی که در اون اجرا میشه ربط دارن
 

the_king

مدیرکل انجمن
ممنون استاد علی
میگم ، استاد علی ، این قضیه ی کد زیر به نخ ربط نداره و به هندل ویندوز ربط داره ها :

کد:
[DllImport("User32.dll",EntryPoint = "MessageBoxA")]public static extern int MessageBoxAPI(IntPtr hWnd, string Text, string Caption, int uType);

        private void Form1_Load(object sender, EventArgs e)
        {
            Form1.MessageBoxAPI(IntPtr.Zero, "1", "", 0);
            Form1.MessageBoxAPI(new IntPtr(), "2", "", 0);
        }

میدونم هیچ نخی ایجاد نکردیم ولی شما گفته بودین که وصل نشدن یک پیغام یا ویندوز ، به ویندوز دیگه ربطی به هندل نداره و به نخی که در اون اجرا میشه ربط دارن

پاسخ من رو درست متوجه نشدید، شما گفته بودید که در نخ جداگانه چطور Handle پنجره رو چطور نمی گیره :
هفتم اینکه ولی با این حال ، متوجه نشدم چطور کلا دستوری (مثل MessageBox.Show) که در نخ جداگانه اجرا میشه ، دیگه هندل ویندوز مربوطه شو نمیتونه بگیره و به اون ویندوز وابسته نمیشه و ممنون میشم کاربرد استفاده از نخ ها رو بجز در این دو مورد ، بیشتر باز کنین
در حالی که هندل ویندوز یک عدد ئه، چیزی مانع گرفتن Handle نمیشه و ربطی هم به نخ نداره و پاسخ من هم در ارتباط با not respond ای بود که مطرح کردید :
دقت کنید که شما دارید یک نخ مستقل رو Sleep می کنید، طبیعی است که این نخ اصلی فرم نیست که با Sleep شدنش not respond بشه، شما اگر نخ اصلی فرم رو Sleep کنید همون اتفاقی می افته که نامطلوب ئه.

اصلا MessageBox.Show رو فراموش کنید، ذهن تون رو درگیر پنجره و Handle می کنید در حالی که ربطی به این قضیه نداره. شما تصور می کنید که Modal بودن پنجره Show است که فرم تون رو قفل نگهداشته در حالی که مساله خود متد Show ئه که به این زودی خاتمه پیدا نمی کنه. تا وقتی از متد Show بازگشتی صورت نگیره کد های بعدی اجرا نمیشه. اصلا پنجره رو بیخیال بشید، فرض کنید بجای MessageBox.Show یک متد ساده دارید که (Sleep(10000 رو اجرا می کنه. تا زمانی که این 10 ثانیه نگذره که نخ ئه کد های بعدی رو اجرا نخواهد کرد. شما یک نخ جداگانه ساخته اید و MessageBox.Show رو داخلش اجرا می کنید، اجرای کد اش در همون موقعیت Show میمونه تا زمانی که متد Show مقدار بازگشتی برگردونه. Show هم اینکار رو وقتی می کنه که پنجره رو ببندید. حالا این نخ اگر نخ اصلی فرم بود، هیچکاری در فرم نمی توانستید بکنید، چون اجرای کد های بعدی نخ گیر Show بود و تا کارش تموم نمیشه نخ اصلی فرم کار دیگه ای نمی کرد. اما اگه نخ یک نخ مستقل باشه، نخ اصلی فرم آزاد ئه و هر کاری می تونه انجام بده، گیر این نخ که نیست، حالا میخواد اون نخ هر چقدر در Show معطل بمونه.

وگرنه MessageBox با Handle کار می کنه و صریحا هم اشاره هم کردم که کاری با نخ نداره و ربطی به نخ نداره :
نه، Handle چه در MessageBox.Show و چه در اون تابع API ویندوز همون Window ئه و فرقی از این نظر ندارند، Handle برای اینکه که بدونه کدوم پنجره صاحب این پنجره است، فقط همین. اصلا ربطی به نخ نداره.
شما وقتی یک متد عادی می نویسید و اجرا می کنید چطوری تصمیم می گیرید که نخ اصلی اجراش کنه؟ هیچ طوری، نخ اصلی به اون قسمت فراخوانی در کد می رسه، پس خودش اجراش می کنه. برای MessageBox هم همینطوره، برای MessageBox مطرح نیست که اجرا کننده کدوم نخ ئه یا پنجره اصلی با کدوم نخ ایجاد شده، اصلا کاری با نخ نداره. با هر نخی فراخوانی اش کنید اجرا کننده اش همون نخ ئه. برای اغلب متد اصلا مهم نیست که کدوم نخ داره اجراشون می کنه به همین جهت اصلا مفهوم نخ براشون مطرح نیست، مثل همون متد هایی که شما قبل از این می نوشتید و در نخ اصلی اجرا می شوند. شما برای اینکه یک متد رو اجرا کنید که نمی روید بگردید ببینید نخ اصلی کدومه، اصلا درگیر مفهوم نخ نمی شوید، MessageBox.Show هم همینطور. براش مهم نیست، هر نخی که میخواد اجراش کنه. اصلا روالی ندارند که بخواهند به نخ چیزی دسترسی پیدا کنند. هر نخی هر کاری که انجام بده خودش انجام داده، این رفتار عادی احتیاجی به هندل و درخواست کردن و دسترسی نداره. به ندرت تابعی پیدا می کنید که کاری با نخ فعلی و غیر فعلی داشته باشه که اونها هم توابع اختصاصی مرتبط با نخ اند.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون استاد علی
میگم الان توی پردازنده ها که نخ وجود داره (مثلا پردازنده های 2 یا 4 یا 8 نخی) ، همین قضیه ی نخ ها هستن که استفاده میکنیم؟ یعنی مثلا پردازنده ی 4 نخی ، همزمان میتونه 4 نخ رو اجرا کنه؟
اگه آره ، 2 تا سئوال دارم :
اول اینکه اگه مثلا 4 نخ همزمان نوشته باشیم (که همه شونو Start زده باشیم پی در پی) ، ولی پردازنده ، 2 نخی باشه ؛ اول میاد 2 تای اولی رو اجرا و بعد ، 2 تای دیگه رو اجرا میکنه دیگه؟
دوم اینکه پس توی Task Manager توی سربرگ Performance چیه که یه قسمت نوشته Thread و عددی بالای 1000 همیشه نوشته میشه و دائم تغییر میکنه؟ مثلا الان برای من نوشته 1384 که دائم هم تغییر میکنه! اگه تعداد کل نخ های نوشته شده در برنامه ها و سیستم عامل و اپلیکشن هایی باشه که تا حالا اجرا شد ، پس نباید کم بشه ولی این مدام در حال کم و زیاد شدنه !!
 

جدیدترین ارسال ها

بالا