การพัฒนาโปรแกรม C # .NET 2.0 WinForm ต้องการให้แอปพลิเคชันปิดและรีสตาร์ทตัวเอง
Application.Restart();
วิธีการดังกล่าวได้รับการพิสูจน์จะไม่น่าเชื่อถือ
วิธีที่ดีกว่าในการรีสตาร์ทแอปพลิเคชันคืออะไร?
คำตอบ:
น่าเสียดายที่คุณไม่สามารถใช้ Process.Start () เพื่อเริ่มอินสแตนซ์ของกระบวนการที่กำลังทำงานอยู่ ตามเอกสาร Process.Start (): "หากกระบวนการทำงานอยู่แล้วจะไม่มีการเริ่มทรัพยากรกระบวนการเพิ่มเติม ... "
เทคนิคนี้จะทำงานได้ดีภายใต้ดีบักเกอร์ VS (เนื่องจาก VS ทำเวทมนตร์บางอย่างที่ทำให้เกิดกระบวนการเริ่มคิดว่ากระบวนการยังไม่ทำงาน) แต่จะล้มเหลวเมื่อไม่ได้ทำงานภายใต้ดีบักเกอร์ (โปรดทราบว่านี่อาจเป็นเฉพาะระบบปฏิบัติการ - ฉันดูเหมือนจะจำได้ว่าในการทดสอบบางอย่างของฉันมันทำงานบน XP หรือ Vista แต่ฉันอาจจำได้ว่ากำลังรันภายใต้ดีบักเกอร์)
เทคนิคนี้เป็นเทคนิคที่โปรแกรมเมอร์คนสุดท้ายใช้ในโครงการที่ฉันกำลังทำงานอยู่และฉันพยายามหาวิธีแก้ปัญหานี้มาระยะหนึ่งแล้ว จนถึงตอนนี้ฉันพบวิธีแก้ปัญหาเพียงวิธีเดียวและมันก็รู้สึกสกปรกและไม่ดีสำหรับฉัน: เริ่มแอปพลิเคชั่นที่ 2 ซึ่งรออยู่เบื้องหลังเพื่อให้แอปพลิเคชันแรกยุติจากนั้นจึงเปิดแอปพลิเคชันตัวแรกอีกครั้ง ฉันแน่ใจว่ามันจะใช้งานได้ แต่แย่แล้ว
แก้ไข: การใช้แอปพลิเคชันที่ 2 ใช้งานได้ สิ่งที่ฉันทำในแอพที่สองคือ:
static void RestartApp(int pid, string applicationName )
{
// Wait for the process to terminate
Process process = null;
try
{
process = Process.GetProcessById(pid);
process.WaitForExit(1000);
}
catch (ArgumentException ex)
{
// ArgumentException to indicate that the
// process doesn't exist? LAME!!
}
Process.Start(applicationName, "");
}
(นี่เป็นตัวอย่างที่เรียบง่ายมากรหัสจริงมีการตรวจสอบความมีสติการจัดการข้อผิดพลาด ฯลฯ )
Process.Startไม่ดูรายการของกระบวนการ OS ที่รันอยู่ คำสั่งเอกสารนี้พูดถึงเฉพาะอินสแตนซ์วัตถุของProcessคลาส Processระดับสามารถแนบไปกับกระบวนการทำงาน แต่ก็ยังสามารถอยู่ในสถานะที่ยกเลิกการเริ่ม ในความคิดของฉันนี่เป็นการออกแบบที่สกรูขึ้น แนวทางปฏิบัติที่ดีที่สุด IMO คือห้ามใช้Process อินสแตนซ์ซ้ำ และเริ่มต้นทันทีหลังจากสร้าง ตามหลักการแล้วให้ใช้Process.Startวิธีคงที่ จากนั้นเอกสารและข้อบกพร่องในการออกแบบนี้จะไม่เข้ามามีบทบาท
WaitForExit(1000)รหัสที่แสดงที่นี่ยังเป็นที่ไม่น่าเชื่อถือเพราะ แต่ไม่จำเป็นต้องรอทั้งหมดเพื่อเริ่มกระบวนการใหม่ อาจเป็นพฤติกรรมเพิ่มเติมที่คุณต้องการ แต่ไม่จำเป็นต้องเริ่มกระบวนการใหม่
วิธีที่ง่ายกว่ามากที่ได้ผลสำหรับฉันคือ:
Application.Restart();
Environment.Exit(0);
สิ่งนี้รักษาอาร์กิวเมนต์บรรทัดคำสั่งและใช้งานได้แม้จะมีตัวจัดการเหตุการณ์ที่โดยปกติจะป้องกันไม่ให้แอปพลิเคชันปิด
การเรียกรีสตาร์ท () พยายามออกเริ่มอินสแตนซ์ใหม่ต่อไปและส่งคืน จากนั้นการเรียก Exit () จะยุติกระบวนการโดยไม่ให้โอกาสตัวจัดการเหตุการณ์ใด ๆ ในการเรียกใช้ มีช่วงเวลาสั้น ๆ ที่กระบวนการทั้งสองกำลังทำงานอยู่ซึ่งไม่ใช่ปัญหาในกรณีของฉัน แต่อาจเป็นในกรณีอื่น
รหัสทางออก 0 ในEnvironment.Exit(0);ระบุการปิดระบบทั้งหมด คุณยังสามารถออกด้วย 1 เพื่อระบุข้อผิดพลาดที่เกิดขึ้น
OnClose()เกิดเหตุการณ์แบบฟอร์มและสิ่งที่คล้ายกัน
หากคุณอยู่ในรูปแบบแอปหลักลองใช้
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)ก็จะทำหน้าที่
Enviorment.Exitเป็นทางออกที่สกปรกและค่อนข้างรุกรานเนื่องจากป้องกันไม่ให้รหัสล้างแอปพลิเคชันทำงาน ไม่ใช่ทางเลือกที่ถูกต้องเกือบตลอดเวลา
ฉันอาจจะไปงานปาร์ตี้สาย แต่นี่คือวิธีง่ายๆของฉันและมันใช้งานได้ดีกับทุกแอปพลิเคชันที่ฉันมี:
try
{
//run the program again and close this one
Process.Start(Application.StartupPath + "\\blabla.exe");
//or you can use Application.ExecutablePath
//close this one
Process.GetCurrentProcess().Kill();
}
catch
{ }
ฉันมีปัญหาเหมือนกันและฉันก็มีข้อกำหนดในการป้องกันไม่ให้เกิดอินสแตนซ์ที่ซ้ำกันด้วยเช่นกันฉันขอเสนอทางเลือกอื่นให้กับที่ HiredMind เสนอ (ซึ่งจะใช้ได้ดี)
สิ่งที่ฉันกำลังทำคือเริ่มต้นกระบวนการใหม่ด้วย processId ของกระบวนการเก่า (กระบวนการที่ทริกเกอร์การรีสตาร์ท) เป็นอาร์กิวเมนต์บรรทัด cmd:
// Shut down the current app instance.
Application.Exit();
// Restart the app passing "/restart [processId]" as cmd line args
Process.Start(Application.ExecutablePath, "/restart" + Process.GetCurrentProcess().Id);
จากนั้นเมื่อแอปใหม่เริ่มต้นฉันจะแยกวิเคราะห์อาร์เรย์บรรทัด cm ก่อนและตรวจสอบว่าแฟล็กรีสตาร์ทอยู่ที่นั่นด้วย processId หรือไม่จากนั้นรอให้กระบวนการนั้นออก:
if (_isRestart)
{
try
{
// get old process and wait UP TO 5 secs then give up!
Process oldProcess = Process.GetProcessById(_restartProcessId);
oldProcess.WaitForExit(5000);
}
catch (Exception ex)
{
// the process did not exist - probably already closed!
//TODO: --> LOG
}
}
เห็นได้ชัดว่าฉันไม่ได้แสดงการตรวจสอบความปลอดภัยทั้งหมดที่ฉันมีอยู่เป็นต้น
แม้ว่าจะไม่เหมาะ - ฉันพบว่านี่เป็นทางเลือกที่ถูกต้องเพื่อที่คุณจะได้ไม่ต้องมีแอพแยกต่างหากเพื่อจัดการกับการรีสตาร์ท
/allowMultipleInstancesธงกว่าค่อนข้างแปลก/restartหนึ่ง
// Get the parameters/arguments passed to program if any
string arguments = string.Empty;
string[] args = Environment.GetCommandLineArgs();
for (int i = 1; i < args.Length; i++) // args[0] is always exe path/filename
arguments += args[i] + " ";
// Restart current application, with same arguments/parameters
Application.Exit();
System.Diagnostics.Process.Start(Application.ExecutablePath, arguments);
ดูเหมือนว่าจะทำงานได้ดีกว่า Application.Restart ();
ไม่แน่ใจว่าจะจัดการอย่างไรหากโปรแกรมของคุณป้องกันหลายอินสแตนซ์ ฉันเดาว่าคุณจะดีกว่าถ้าเปิด. exe ตัวที่สองซึ่งจะหยุดชั่วคราวจากนั้นเริ่มแอปพลิเคชันหลักให้คุณ
มันง่ายที่คุณเพียงแค่ต้องเรียกApplication.Restart()วิธีการที่มีแนวโน้มที่จะเรียกใช้แอปพลิเคชันของคุณเพื่อเริ่มต้นใหม่ แต่คุณต้องออกจากสภาพแวดล้อม Local ด้วยรหัสข้อผิดพลาด:
Application.Restart();
Environment.exit(int errorcode);
คุณสามารถสร้างการแจกแจงรหัสข้อผิดพลาดที่คุณสามารถใช้อย่างมีประสิทธิภาพ
อีกวิธีหนึ่งคือเพียงแค่ออกจากฟอร์มแอปพลิเคชันและเริ่มกระบวนการที่มีเส้นทางปฏิบัติการ:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
ลองใช้รหัสนี้:
bool appNotRestarted = true;
รหัสนี้ต้องอยู่ในฟังก์ชันด้วย:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
ฉันคิดหาวิธีแก้ปัญหาอื่นบางทีใคร ๆ ก็สามารถใช้ได้เช่นกัน
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
รหัสถูกทำให้ง่ายขึ้นดังนั้นโปรดดูแลข้อยกเว้นและสิ่งต่างๆ;)
คุณกำลังลืมตัวเลือก / พารามิเตอร์บรรทัดคำสั่งที่ส่งผ่านไปยังอินสแตนซ์ที่กำลังทำงานอยู่ หากคุณไม่ผ่านสิ่งเหล่านั้นแสดงว่าคุณไม่ได้ทำการรีสตาร์ทจริง ตั้งค่าProcess.StartInfoด้วยการโคลนพารามิเตอร์กระบวนการของคุณจากนั้นเริ่มต้น
ตัวอย่างเช่นหากกระบวนการของคุณเริ่มต้นเมื่อmyexe -f -nosplash myfile.txtวิธีของคุณจะดำเนินการmyexeโดยไม่มีแฟล็กและพารามิเตอร์เหล่านั้นทั้งหมด
ฉันต้องการให้แอปพลิเคชันใหม่เริ่มทำงานหลังจากที่แอปพลิเคชันเก่าปิดตัวลง
การใช้ process.WaitForExit () เพื่อรอให้กระบวนการของคุณปิดระบบนั้นไม่มีเหตุผล มันจะหมดเวลาเสมอ
ดังนั้นแนวทางของฉันคือใช้ Application.Exit () จากนั้นรอ แต่อนุญาตให้ประมวลผลเหตุการณ์เป็นระยะเวลาหนึ่ง จากนั้นเริ่มแอปพลิเคชันใหม่โดยใช้อาร์กิวเมนต์เดียวกันกับแอปพลิเคชันเก่า
static void restartApp() {
string commandLineArgs = getCommandLineArgs();
string exePath = Application.ExecutablePath;
try {
Application.Exit();
wait_allowingEvents( 1000 );
} catch( ArgumentException ex ) {
throw;
}
Process.Start( exePath, commandLineArgs );
}
static string getCommandLineArgs() {
Queue<string> args = new Queue<string>( Environment.GetCommandLineArgs() );
args.Dequeue(); // args[0] is always exe path/filename
return string.Join( " ", args.ToArray() );
}
static void wait_allowingEvents( int durationMS ) {
DateTime start = DateTime.Now;
do {
Application.DoEvents();
} while( start.Subtract( DateTime.Now ).TotalMilliseconds > durationMS );
}
public static void appReloader()
{
//Start a new instance of the current program
Process.Start(Application.ExecutablePath);
//close the current application process
Process.GetCurrentProcess().Kill();
}
Application.ExecutablePathส่งคืนไฟล์ aplication .exe ของคุณโปรดทำตามลำดับการโทร คุณอาจต้องการวางไว้ในประโยค try-catch
วิธีสร้างไฟล์ bat รันไฟล์แบตช์ก่อนปิดจากนั้นปิดอินสแตนซ์ปัจจุบัน
ไฟล์แบตช์ทำสิ่งนี้:
นี่คือ 2 เซ็นต์ของฉัน:
ลำดับเริ่มต้นอินสแตนซ์ใหม่ -> ปิดอินสแตนซ์ปัจจุบันควรใช้งานได้แม้กระทั่งสำหรับแอปพลิเคชันที่ไม่อนุญาตให้เรียกใช้หลายสำเนาพร้อมกันในกรณีนี้อินสแตนซ์ใหม่อาจส่งผ่านอาร์กิวเมนต์บรรทัดคำสั่งซึ่งจะระบุว่ากำลังดำเนินการรีสตาร์ท ดังนั้นการตรวจสอบอินสแตนซ์อื่น ๆ ที่ทำงานอยู่จึงไม่จำเป็น กำลังรอให้อินสแตนซ์แรกเสร็จสิ้นการใช้งานจริงของฉันด้วยเช่นกันหากจำเป็นอย่างยิ่งที่จะต้องไม่มีสองสถานการณ์ทำงานควบคู่กัน
ปัญหาในการใช้Application.Restart ()คือจะเริ่มกระบวนการใหม่ แต่ "เก่า" ยังคงมีอยู่ ด้วยเหตุนี้ฉันจึงตัดสินใจฆ่ากระบวนการเก่าโดยใช้ข้อมูลโค้ดต่อไปนี้:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
และทำงานได้ดีอย่างเหมาะสม ในกรณีของฉัน MATLAB และแอปพลิเคชัน C # กำลังแชร์ฐานข้อมูล SQLite เดียวกัน ถ้า MATLAB กำลังใช้ฐานข้อมูล Form-App ควรรีสตาร์ท (+ นับถอยหลัง) อีกครั้งจนกว่า MATLAB จะรีเซ็ตบิตที่ไม่ว่างในฐานข้อมูล (สำหรับข้อมูลด้านข้าง)
ฉันกลัวว่าการรีสตาร์ทแอปพลิเคชันทั้งหมดโดยใช้กระบวนการกำลังเข้าใกล้ปัญหาของคุณในทางที่ผิด
วิธีที่ง่ายกว่าคือการแก้ไขไฟล์ Program.cs เพื่อรีสตาร์ท:
static bool restart = true; // A variable that is accessible from program
static int restartCount = 0; // Count the number of restarts
static int maxRestarts = 3; // Maximum restarts before quitting the program
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
while (restart && restartCount < maxRestarts)
{
restart = false; // if you like.. the program can set it to true again
restartCount++; // mark another restart,
// if you want to limit the number of restarts
// this is useful if your program is crashing on
// startup and cannot close normally as it will avoid
// a potential infinite loop
try {
Application.Run(new YourMainForm());
}
catch { // Application has crashed
restart = true;
}
}
}
ฉันมีปัญหาที่คล้ายกัน แต่ของฉันเกี่ยวข้องกับการรั่วไหลของหน่วยความจำที่ไม่สามารถจัดการได้ซึ่งฉันไม่พบในแอปที่ต้องทำงานตลอด 24 ชั่วโมงทุกวัน กับลูกค้าฉันตกลงว่าเวลาที่ปลอดภัยในการรีสตาร์ทแอปคือ 03:00 น. หากการใช้หน่วยความจำเกินค่าที่กำหนด
ฉันลองApplication.Restartแล้ว แต่เนื่องจากดูเหมือนว่าจะใช้กลไกบางอย่างที่เริ่มอินสแตนซ์ใหม่ในขณะที่กำลังทำงานอยู่ฉันจึงไปใช้โครงการอื่น ฉันใช้เคล็ดลับที่จัดการระบบไฟล์ยังคงมีอยู่จนกว่ากระบวนการที่สร้างขึ้นจะตาย ดังนั้นจากแอปพลิเคชันฉันทิ้งไฟล์ลงในดิสก์และไม่ได้Dispose()จัดการ ฉันใช้ไฟล์เพื่อส่งไฟล์ปฏิบัติการ 'ตัวเอง' และไดเร็กทอรีเริ่มต้นด้วย (เพื่อเพิ่มความยืดหยุ่น)
รหัส:
_restartInProgress = true;
string dropFilename = Path.Combine(Application.StartupPath, "restart.dat");
StreamWriter sw = new StreamWriter(new FileStream(dropFilename, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite));
sw.WriteLine(Application.ExecutablePath);
sw.WriteLine(Application.StartupPath);
sw.Flush();
Process.Start(new ProcessStartInfo
{
FileName = Path.Combine(Application.StartupPath, "VideoPhill.Restarter.exe"),
WorkingDirectory = Application.StartupPath,
Arguments = string.Format("\"{0}\"", dropFilename)
});
Close();
Close()ในตอนท้ายจะเป็นการเริ่มต้นการปิดแอปและการจัดการไฟล์ที่ฉันใช้สำหรับStreamWriterที่นี่จะเปิดค้างไว้จนกว่ากระบวนการจะตายจริงๆ จากนั้น ...
Restarter.exe เข้ามาดำเนินการ พยายามอ่านไฟล์ในโหมดพิเศษป้องกันไม่ให้เข้าถึงได้จนกว่าแอปหลักจะไม่ตายจากนั้นเริ่มแอปหลักลบไฟล์และมีอยู่ ฉันเดาว่ามันไม่ง่ายไปกว่านี้:
static void Main(string[] args)
{
string filename = args[0];
DateTime start = DateTime.Now;
bool done = false;
while ((DateTime.Now - start).TotalSeconds < 30 && !done)
{
try
{
StreamReader sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));
string[] runData = new string[2];
runData[0] = sr.ReadLine();
runData[1] = sr.ReadLine();
Thread.Sleep(1000);
Process.Start(new ProcessStartInfo { FileName = runData[0], WorkingDirectory = runData[1] });
sr.Dispose();
File.Delete(filename);
done = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Thread.Sleep(1000);
}
}
ฉันใช้สิ่งต่อไปนี้และทำในสิ่งที่คุณกำลังมองหา:
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
UpdateCheckInfo info = null;
info = ad.CheckForDetailedUpdate();
if (info.IsUpdateRequired)
{
ad.UpdateAsync(); // I like the update dialog
MessageBox.Show("Application was upgraded and will now restart.");
Environment.Exit(0);
}
สำหรับการใช้ As logout คุณจะต้องยุติแอพทั้งหมดจาก Ram Cache ดังนั้นให้ปิดแอปพลิเคชันก่อนแล้วจึงรันใหม่
// เมื่อคลิกปุ่มออกจากระบบ
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
คุณสามารถใส่รหัสของคุณไว้ในฟังก์ชันและเมื่อจำเป็นต้องรีสตาร์ทคุณก็สามารถเรียกใช้ฟังก์ชันได้
ยกตัวอย่างเช่นแอปพลิเคชันที่:
ในขณะที่ไม่ได้ลงทะเบียนแอปพลิเคชัน (เมื่อเริ่มต้น) แอปพลิเคชันควรแจ้งให้ผู้ใช้ลงทะเบียนแอปพลิเคชันและสร้างบัญชีเข้าสู่ระบบ
เมื่อส่งการลงทะเบียนและสร้างข้อมูลรับรองการเข้าสู่ระบบแล้ว แอปพลิเคชันควรรีสตาร์ทตรวจสอบการลงทะเบียนและแจ้งให้ผู้ใช้เข้าสู่ระบบด้วยข้อมูลรับรองที่ใส่ไว้ (เพื่อให้ผู้ใช้สามารถเข้าถึงคุณสมบัติทั้งหมดของแอปพลิเคชัน)
ปัญหา: โดยการสร้างและเปิดแอปพลิเคชันจาก Visual Studio การร้องทางเลือกใด ๆ จาก 4 ทางเลือกจะไม่สามารถบรรลุภารกิจที่ต้องการได้
/*
* Note(s):
* Take into consideration that the lines bellow don't represent a code block.
* They are just a representation of possibilities,
* that can be used to restart the application.
*/
Application.Restart();
Application.Exit();
Environment.Exit(int errorCode);
Process.GetCurrentProcess().Kill();
สิ่งที่เกิดขึ้นคือ: หลังจากสร้างการลงทะเบียนเข้าสู่ระบบและเรียกใช้ Application.Restart (); แอปพลิเคชันจะ (แปลก ๆ ) เปิดแบบฟอร์มการลงทะเบียนอีกครั้งและข้ามข้อมูลในฐานข้อมูล (แม้ว่าทรัพยากรจะถูกตั้งค่าเป็น "คัดลอกถ้าใหม่กว่า")
วิธีการแก้: สร้างแบทช์แอปพลิเคชัน (สำหรับฉัน) เป็นข้อพิสูจน์ว่าบรรทัดใด ๆ ข้างต้นทำงานได้จริงตามที่คาดไว้ ไม่ใช่เมื่อสร้างและเรียกใช้แอปพลิเคชันด้วย Visual Studio
ในตอนแรกฉันจะลองสร้างแอปพลิเคชันเป็นกลุ่ม เรียกใช้ภายนอก Visual Studio และตรวจสอบว่า Application.Restart () ใช้งานได้จริงตามที่คาดไว้หรือไม่
ตรวจสอบข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ด้วย: ฉันจะรีสตาร์ทแอปพลิเคชัน C # WinForm ของฉันได้อย่างไร