PDA

View Full Version : سوال: نحوه اجرا شدن خودکار پروژه در زمان خاص



md3848
یک شنبه 24 اسفند 1399, 02:26 صبح
سلام

کاری که میخوام بکنم : کاربر تنظیم میکنه که ساعت 12:15 یاد آوری مطالعه انجام بشه حالا من میخوام این کارو بکنم.

توضیح اولیه : معمولا پروژه ها توسط کاربر اجرا میشه ( رو فایل exe کلیک میکنند ) یا موقع startup پروژه ها اجرا میشن ( زمانی که ویندوز تازه روشن شده )،

مشکل من : اما چطور میشه پروژه رو در زمان خاص اجرا کرد؟ با فرض این که کامپیوتر روشن هستش ( چون بعید میدونم بشه کامپیوتر رو به کمک کدنویسی روشن کرد، اگه هم بشه چیز جالبی بعید میدونم بشه و قطعا رو مخی میشه برای کاربر و بعضا ممکنه مشکل ایجاد کنه و ... پس فرضمون اینه که کامپیوتر حتما روشنه )؛ اگه پروژه موقع startup ایجاد شده باشه و توسط کاربر بسته نشده باشه که خب هیچی، در ساعت مقرر یه اعلامه ( notification ) نمایش میدیم؛ اما اگه پروژه موقع startup اجرا نشده بود و یا توسط کاربر بسته شد چی؟ اون وقت بازم میشه در ساعت خاص پروژه رو اجرا کرد و اعلامیه مون رو نمایش داد؟

the king
یک شنبه 24 اسفند 1399, 05:58 صبح
سلام

کاری که میخوام بکنم : کاربر تنظیم میکنه که ساعت 12:15 یاد آوری مطالعه انجام بشه حالا من میخوام این کارو بکنم.

توضیح اولیه : معمولا پروژه ها توسط کاربر اجرا میشه ( رو فایل exe کلیک میکنند ) یا موقع startup پروژه ها اجرا میشن ( زمانی که ویندوز تازه روشن شده )،

مشکل من : اما چطور میشه پروژه رو در زمان خاص اجرا کرد؟ با فرض این که کامپیوتر روشن هستش ( چون بعید میدونم بشه کامپیوتر رو به کمک کدنویسی روشن کرد، اگه هم بشه چیز جالبی بعید میدونم بشه و قطعا رو مخی میشه برای کاربر و بعضا ممکنه مشکل ایجاد کنه و ... پس فرضمون اینه که کامپیوتر حتما روشنه )؛ اگه پروژه موقع startup ایجاد شده باشه و توسط کاربر بسته نشده باشه که خب هیچی، در ساعت مقرر یه اعلامه ( notification ) نمایش میدیم؛ اما اگه پروژه موقع startup اجرا نشده بود و یا توسط کاربر بسته شد چی؟ اون وقت بازم میشه در ساعت خاص پروژه رو اجرا کرد و اعلامیه مون رو نمایش داد؟

می توانید یک کتابخانه یا پکیجی که برای ارتباط با Windows Task Scheduler طراحی شده انتخاب کنید و با اون یک Task جدید بسازید که فلان فایل اجرایی exe در فلان تاریخ یا فلان فواصل زمانی اجرا بشه. Task Scheduler (https://en.wikipedia.org/wiki/Windows_Task_Scheduler) یک ابزار اساسی در ویندوز ئه که می توانید اجرایش کنید و لیست Task ها رو ببینید و ویرایش کنید. برای هر Task تنظیمات و قابلیت های زیادی داره.
خیلی از نرم افزار ها و ابزار های سیستم عامل زمان بندی اجراشون رو در اون مشخص کرده اند.
https://github.com/dahall/taskscheduler
https://stackoverflow.com/questions/2489999/how-can-i-schedule-tasks-in-a-winforms-app/2490142#2490142

md3848
جمعه 27 فروردین 1400, 20:19 عصر
توضیح : الان یه کدی نوشتم ( کد زیر ) که یه Task رو در زمان خاصی اجرا کنه، که کارش اینه که پروژمو سر زمان خاصی با آرگومان ReadQuran اجرا کنه، کدم درست کار میکنه و پروژه ام اجرا میشه و منم آرگومان فوق رو تو Constructor فایل اصلی پروژه ام ( MainWindow ) میخونم و پیغام مناسب باهاشو نمایش میدم؛
// Add Action To Task
task.Actions.Add(new ExecAction(
@"F:\Program Files\Quran Project\Window Project\WpfApp4\WpfApp4\bin\Debug\net5.0-windows\WpfApp4.exe",
"-ReadQuran"));
مشکلی که دارم اینه که اگه از قبل پروژه ام باز بوده باشه، دوباره پروژه ام اجرا میشه.
من میخوام اگه پروژه ام درحال اجرا بوده، پروژه ام دوباره اجرا نشه و بجاش یه تابعی وقفه ای چیزی رخ بده که من بفهمم که Task من رخ داده و برم آرگومانشو بخونم و پیغام مناسب باهاش رو نمایش بدم.

barnamenevisjavan
جمعه 27 فروردین 1400, 22:36 عصر
توضیح : الان یه کدی نوشتم ( کد زیر ) که یه Task رو در زمان خاصی اجرا کنه، که کارش اینه که پروژمو سر زمان خاصی با آرگومان ReadQuran اجرا کنه، کدم درست کار میکنه و پروژه ام اجرا میشه و منم آرگومان فوق رو تو Constructor فایل اصلی پروژه ام ( MainWindow ) میخونم و پیغام مناسب باهاشو نمایش میدم؛
// Add Action To Task
task.Actions.Add(new ExecAction(
@"F:\Program Files\Quran Project\Window Project\WpfApp4\WpfApp4\bin\Debug\net5.0-windows\WpfApp4.exe",
"-ReadQuran"));
مشکلی که دارم اینه که اگه از قبل پروژه ام باز بوده باشه، دوباره پروژه ام اجرا میشه.
من میخوام اگه پروژه ام درحال اجرا بوده، پروژه ام دوباره اجرا نشه و بجاش یه تابعی وقفه ای چیزی رخ بده که من بفهمم که Task من رخ داده و برم آرگومانشو بخونم و پیغام مناسب باهاش رو نمایش بدم.
کدهای زیر رو در کلاس App.xaml.cs کپی کنید


internal static Mutex mutex;


/// <summary>
/// Check that only one instance of the program runs
/// </summary>
/// <param name="AssemblyName"></param>
/// <returns></returns>
public static bool IsSingleInstance(string AssemblyName = null)
{
if (string.IsNullOrEmpty(AssemblyName))
{
AssemblyName = Assembly.GetCallingAssembly().GetName().Name;
}
mutex = new Mutex(true, AssemblyName);
if (mutex.WaitOne(TimeSpan.Zero, true))
{
mutex.ReleaseMutex();
return true;
}
else
{
BringWindowToFront();
Environment.Exit(0);
return false;
}
}




[DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool SetForegroundWindow(IntPtr hWnd);


public static void BringWindowToFront()
{
var currentProcess = Process.GetCurrentProcess();
var processes = Process.GetProcessesByName(currentProcess.ProcessN ame);
var process = processes.FirstOrDefault(p => p.Id != currentProcess.Id);
if (process == null) return;
SetForegroundWindow(process.MainWindowHandle);
}


کد StartupUri="MainWindow.xaml"> رو از فایل App.xaml حذف کنید


متد OnStartup رو override کنید و اونجا بررسی کنید که ایا یک نسخه در حال اجرا هست یا نه


protected override void OnStartup(StartupEventArgs e)
{
if(!IsSingleInstance){
MessageBox.Show("App is Running");
}else{
Application.Current.MainWindow = new MainWindow();
}
}
وقتی برنامه در حال اجرا باشه و شما دوباره برنامه رو اجرا کنید یک پیغام دریافت میکنید و نسخه ای که در حال اجرا هست فوکوس میشه روش

md3848
جمعه 27 فروردین 1400, 23:06 عصر
خب الان این کدا فقط جلوی اجرای تکراری رو میگیره، ولی اصل مشکل من هنوز پابرجاس، یعنی الان پروژه در حال اجرا هستش، قراره 5 دقیقه دیگه هم به کمک Task پروژه با آرگومان ReadQuran اجرا بشه و بعدش یه پیغامی نمایش داده بشه؛ الان با این کدها فقط جلوی اجرای دوباره پروژه گرفته میشه ولی من متوجه رخ دادن Task فوق نمیشم فلذا نمیتونم هیچ پیغامی رو به کاربر نشون بدم. ( چون تابع MainWindow در دفه دوم اجرای پروژه، اجرا نمیشه، پس منم نمیتونم بررسی کنم که تست فوق با آرگومان مدنظر رخ داده یا نه، پس نمیتونم هیچ پیغامی نشون بدم به کاربر؛ که مثلا زمان قرائت قرآن شما فرا رسیده )

public MainWindow(){
InitializeComponent();


// Show Message on...
if (Array.IndexOf(Environment.GetCommandLineArgs(), "-ReadQuran", 1) >= 0)
{
//...
}
}

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

به تابع BringWindowToFront چندخط کد اضافه کردم تا همونجا بررسی کنه که پارامتر ReadQuran وجود داره یا نه و اگه وجود داشت به فایل پروژه پیغامی ارسال کنه ( که اینطوری میتونم بفهمم که Task با پارامتر مدنظرم رخ داده یا نه ) :
public const int MSG_ReadQuran = 0xDEF2;

public static void BringWindowToFront()
{
//...


if (Array.IndexOf(Environment.GetCommandLineArgs(), "-ReadQuran", 1) >= 0)
SendMessage(process.MainWindowHandle, MSG_ReadQuran, IntPtr.Zero, IntPtr.Zero);
}


[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
در حالت عادی کدم درست کار میکنه اما زمانی که پروژه بره به قسمت Taskbar دیگه درست کار نمیکنه، تابع BringWindowToFront رخ نمیده و پروژه نمایش داده نمیشه و تابع SendMessage هم کار نمیکنه! چطوری این مشکلو حل کنم؟ :گریه:
153237

barnamenevisjavan
شنبه 28 فروردین 1400, 11:39 صبح
برای پاس دادن ارگومان به برنامه در حال اجرا میتونید این راه رو امتحان کنید
Passing Parameters to a Running Application in WPF - CodeProject (https://www.codeproject.com/Articles/1224031/Passing-Parameters-to-a-Running-Application-in-WPF)

شما با API های ویندوز یه Message رو ارسال میکنی، و اونور توی برنامه خودت منتظر دریافت پیام میشینی (GetMessage) حالا هروقت برنامه دوباره اجرا شد کافیه شما مسیج رو ارسال کنی و برنامه دوم رو ببندی، باقی کار هارو همون نسخه در حال اجرا انجام میده و اگه برنامت توی Taskbar بود اونو از حالت تسکبار خارجش میکنی و...

md3848
شنبه 28 فروردین 1400, 11:53 صبح
منم فک کنم از همین کد استفاده کردم، ولی تو حالتی که برنامه تو Taskbar هستش، پیامی دریافت نمیشه / تو همون لینکی هم که گزاشتی، پایان مطلب، یه کاربری پیغام گزاشته و همین مشکل منو داشته.

کد سمت MainWindow :
private void CWindow_Loaded(object sender, RoutedEventArgs e){
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(WndProc);
}


// Meaning of message codes : http://www.pinvoke.net/default.aspx/Constants/WM.html
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Debug.WriteLine("WndProc = " + msg.ToString("X4"));


var mainWindow = (MainWindow)Application.Current.MainWindow;


if (msg == App.MSG_ReadQuran)
{
handled = true;
}
else
{
handled = false;
}

return IntPtr.Zero;
}

کد سمت App :
public const int MSG_ReadQuran = 0xDEF2;
public static void BringWindowToFront()
{
//...


if (Array.IndexOf(Environment.GetCommandLineArgs(), "-ReadQuran", 1) >= 0)
SendMessage(process.MainWindowHandle, MSG_ReadQuran, IntPtr.Zero, IntPtr.Zero);
}


[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);

حالا در کل، آیا روش اصولی نیاز من همینیه که من دارم میرم؟ یعنی بخوام برنامم در زمان خاصی اجرا بشه، و اگه برنامم در حال اجرا بود دیگه دوباره اجرا نشه و همونی که درحال اجرا هستش، متوجه رخ دادن Task من بشه و طبق Task فوق بیاد پیغام ناسب باهاشو نمایش بده.

barnamenevisjavan
شنبه 28 فروردین 1400, 16:48 عصر
منم فک کنم از همین کد استفاده کردم، ولی تو حالتی که برنامه تو Taskbar هستش، پیامی دریافت نمیشه / تو همون لینکی هم که گزاشتی، پایان مطلب، یه کاربری پیغام گزاشته و همین مشکل منو داشته.

کد سمت MainWindow :
private void CWindow_Loaded(object sender, RoutedEventArgs e){
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(WndProc);
}


// Meaning of message codes : http://www.pinvoke.net/default.aspx/Constants/WM.html
private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Debug.WriteLine("WndProc = " + msg.ToString("X4"));


var mainWindow = (MainWindow)Application.Current.MainWindow;


if (msg == App.MSG_ReadQuran)
{
handled = true;
}
else
{
handled = false;
}

return IntPtr.Zero;
}

کد سمت App :
public const int MSG_ReadQuran = 0xDEF2;
public static void BringWindowToFront()
{
//...


if (Array.IndexOf(Environment.GetCommandLineArgs(), "-ReadQuran", 1) >= 0)
SendMessage(process.MainWindowHandle, MSG_ReadQuran, IntPtr.Zero, IntPtr.Zero);
}


[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);

حالا در کل، آیا روش اصولی نیاز من همینیه که من دارم میرم؟ یعنی بخوام برنامم در زمان خاصی اجرا بشه، و اگه برنامم در حال اجرا بود دیگه دوباره اجرا نشه و همونی که درحال اجرا هستش، متوجه رخ دادن Task من بشه و طبق Task فوق بیاد پیغام ناسب باهاشو نمایش بده.
مشکل توی متد FindWindow هستش من توی کتابخونه خودم این ویژگی رو همین امروز اضافه کردم میتونید از اینجا کدهاشو کپی کنید
HandyControls/ApplicationHelper.Message.cs at develop · ghost1372/HandyControls (github.com) (https://github.com/ghost1372/HandyControls/blob/develop/src/Shared/HandyControl_Shared/HandyControls/Tools/Helper/ApplicationHelper.Message.cs)

خیلی شیک و مجلسی ردیفش کردم که کاربر نهایی فقط 1 خط کد بنویسه برای ارسال پیام و 1 خط کد بنویسه برای دریافت پیام :لبخند:
نحوه کار به این صورت هست که شما اسم title که برای mainwindow درنظر گرفتید رو به تابع ارسال میدید همراه با پیامتون و اونور برای دریافت پیام هم کافیه توی متد لود mainwidow از متد listen استفاده کنید

SendMessageToAnotherProcess("title", "hello");

ListenToReceiveMessageFromAnotherProcess(this, x =>
{
MessageBox.Show(x);
});

md3848
شنبه 28 فروردین 1400, 17:19 عصر
اه شما همون ghost1372 هستی خخخ دنیا چقدر کوچیکه :لبخند: آموزش های WPF تو خوندم، ولی از HandyControls استفاده نکردم، نمیدونم چه کرمیه که دارم، دوس دارم کنترل هارو خودم بنویسم :لبخند:
مطمئنی؟ چون FindWindow بنظرم درست کار میکنه و حتی پروژه ام اجرا میشه ولی اون تابع WndProc ( که وضیفش دریافت پیام هستش ) اجرا نمیشه، الان شما وقتی پروژه در حالت Taskbar هستش، تست کردی جواب داد؟
لینک فوق رو الان میرم بررسی میکنم...
private void TaskbarHide(){
this.Hide();
taskbarIcon.Visibility = Visibility.Visible;
this.ShowInTaskbar = false;
}
public void TaskbarShow()
{
this.Show();
taskbarIcon.Visibility = Visibility.Collapsed;
this.ShowInTaskbar = true;
}

barnamenevisjavan
شنبه 28 فروردین 1400, 17:26 عصر
اه شما همون ghost1372 هستی خخخ دنیا چقدر کوچیکه :لبخند: آموزش های WPF تو خوندم، ولی از HandyControls استفاده نکردم، نمیدونم چه کرمیه که دارم، دوس دارم کنترل هارو خودم بنویسم :لبخند:
مطمئنی؟ چون FindWindow بنظرم درست کار میکنه و حتی پروژه ام اجرا میشه ولی اون تابع WndProc ( که وضیفش دریافت پیام هستش ) اجرا نمیشه، الان شما وقتی پروژه در حالت Taskbar هستش، تست کردی جواب داد؟
لینک فوق رو الان میرم بررسی میکنم...
private void TaskbarHide(){
this.Hide();
taskbarIcon.Visibility = Visibility.Visible;
this.ShowInTaskbar = false;
}
public void TaskbarShow()
{
this.Show();
taskbarIcon.Visibility = Visibility.Collapsed;
this.ShowInTaskbar = true;
}
من رو سیستم خودم تست کردم FindWindow درست کار نمیکرد واسه همین نمیرفت رو متد WndProc در حالت تسکبار تست نکردم ولی مشکلی نباید پیش بیاد

md3848
شنبه 28 فروردین 1400, 17:34 عصر
خو اصل داستان سر همین Taskbar هستش دیگه، وگرنه غیر این حالت که مشکلی ندارم که :لبخند:
پروژه بره به حالت Taskbar، دیگه پیغامی دریافت نمیشه. ( دیگه تابع WndProc که کدشو در بالا گزاشتم، رخ نمیده )

barnamenevisjavan
شنبه 28 فروردین 1400, 17:52 عصر
خو اصل داستان سر همین Taskbar هستش دیگه، وگرنه غیر این حالت که مشکلی ندارم که :لبخند:
پروژه بره به حالت Taskbar، دیگه پیغامی دریافت نمیشه. ( دیگه تابع WndProc که کدشو در بالا گزاشتم، رخ نمیده )
بله راست میگید SendMessage فقط پیغام به Window میفرسته در شرایط شما چون Window نیستش پیغام دریافت نمیشه این قضیه برای خودمم جالب شد که یه راه حلی براش پیدا کنم، البته راه ساده تر برای شما اینه که یه پوشه ایجاد کنید و داخلش یه فایل خالی با یه اسم ایجاد کنید (هر موقع که کاربر دوباره برنامه رو اجرا میکنه) بعد به کمک تابع FileSystemWatcher اون پوشه رو زیرنظر بگیرید اگر تغییر کرد به شما اطلاع بده اینجوری میتونید مشکل رو تا حدودی حل کنید زیاد راه جالبی نیست ولی بد هم نیست

barnamenevisjavan
شنبه 28 فروردین 1400, 18:54 عصر
مشکل رو حل کردم
برای ارسال پیام
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);


public static int RegisterWindowMessage(string format, params object[] args)
{
string message = String.Format(format, args);
return RegisterWindowMessage(message);
}


public const int HWND_BROADCAST = 0xffff;


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


public static readonly int WM_SHOWFIRSTINSTANCE = RegisterWindowMessage("WM_SHOWFIRSTINSTANCE|{0}", "test");

protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (!ApplicationHelper.IsSingleInstance())
{
PostMessage((IntPtr)HWND_BROADCAST, WM_SHOWFIRSTINSTANCE, IntPtr.Zero, IntPtr.Zero);
Environment.Exit(0);
}


}



و دریافت پیام

private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
HwndSource hWndSource;
WindowInteropHelper wih = new WindowInteropHelper(this);
hWndSource = HwndSource.FromHwnd(wih.Handle);
HwndSourceHook eventHandler = (IntPtr hwnd, int msg, IntPtr param, IntPtr lParam, ref bool handled) =>
{
if (msg == WM_SHOWFIRSTINSTANCE)
{
Show();


}

return IntPtr.Zero;
};
hWndSource.AddHook(eventHandler);
}


الان رو حالت تسکبار هم جواب میده

md3848
شنبه 28 فروردین 1400, 20:58 عصر
برا منم فک کنم جواب داد ولی نه با PostMessage، با SendMessage تست کردم جواب داد ( کد زیر رو به SendMessage تغییر دادم و برا منم جواب داد، ولی با PostMessage جواب داد، چرا؟ :متفکر: )؛

PostMessageA function (winuser.h) (https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea)
HWND_BROADCAST ((HWND)0xffff) : The message is posted to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows. The message is not posted to child windows.

ممنون - فعلا فک کنم مشکلم حل شده، حالا پیش بریم ببینیم چی پیش میاد :لبخند:

md3848
یک شنبه 29 فروردین 1400, 01:03 صبح
.............

mohammadProgramerCSharp
جمعه 08 مرداد 1400, 20:01 عصر
سلام یه پیشنهاد
میتونی کنترل باکس رو بر داری و خودت یک باتن ضرب در بزاری و در رویدادش بنویسی که مینیمایز کنه و show in taskbar رو false کنه
و برای چک کردن زمان یاد آوری میتونی این کار رو بکنی
153445

کمکی خواستی بگو

mohammadProgramerCSharp
جمعه 08 مرداد 1400, 20:03 عصر
با این کار برنامه واقعا بسته نمیشه و مخفی میشه