Phát triển ứng dụng WinForm C # .NET 2.0. Cần ứng dụng tự đóng và khởi động lại.
Application.Restart();
Phương pháp trên đã được chứng minh là không đáng tin cậy .
Cách tốt hơn để khởi động lại ứng dụng là gì?
Phát triển ứng dụng WinForm C # .NET 2.0. Cần ứng dụng tự đóng và khởi động lại.
Application.Restart();
Phương pháp trên đã được chứng minh là không đáng tin cậy .
Cách tốt hơn để khởi động lại ứng dụng là gì?
Câu trả lời:
Rất tiếc, bạn không thể sử dụng Process.Start () để bắt đầu một phiên bản của quy trình hiện đang chạy. Theo tài liệu Process.Start (): "Nếu quá trình đang chạy, không có tài nguyên quy trình bổ sung nào được bắt đầu ..."
Kỹ thuật này sẽ hoạt động tốt trong trình gỡ lỗi VS (vì VS thực hiện một số phép thuật khiến Process.Start nghĩ rằng tiến trình chưa chạy), nhưng sẽ thất bại khi không chạy dưới trình gỡ lỗi. (Lưu ý rằng điều này có thể dành riêng cho hệ điều hành - dường như tôi nhớ rằng trong một số thử nghiệm của mình, nó hoạt động trên XP hoặc Vista, nhưng tôi có thể chỉ nhớ đang chạy nó trong trình gỡ lỗi.)
Kỹ thuật này chính xác là kỹ thuật được sử dụng bởi lập trình viên cuối cùng trong dự án mà tôi hiện đang làm việc và tôi đã cố gắng tìm ra giải pháp cho điều này trong một thời gian khá dài. Cho đến nay, tôi chỉ tìm thấy một giải pháp và nó chỉ cảm thấy bẩn thỉu và khó chịu với tôi: khởi động ứng dụng thứ hai, chờ ứng dụng đầu tiên kết thúc trong nền, sau đó khởi chạy lại ứng dụng đầu tiên. Tôi chắc rằng nó sẽ hoạt động, nhưng, yuck.
Chỉnh sửa: Sử dụng ứng dụng thứ 2 hoạt động. Tất cả những gì tôi đã làm trong ứng dụng thứ hai là:
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, "");
}
(Đây là một ví dụ rất đơn giản. Mã thực có rất nhiều kiểm tra tỉnh táo, xử lý lỗi, v.v.)
Process.Startkhông nhìn vào danh sách các tiến trình HĐH đang chạy. Câu lệnh tài liệu này chỉ nói về thể hiện đối tượng đó của Processlớp. Các Processlớp có thể được gắn vào một quá trình hoạt động nhưng nó cũng có thể được trong tình trạng Unstarted. Theo tôi đây là một thiết kế vặn lên. Cách tốt nhất, IMO, là không bao giờ sử dụng lại một Process phiên bản và bắt đầu ngay lập tức nó sau khi tạo. Tốt nhất, hãy sử dụng Process.Startphương pháp tĩnh . Sau đó, tài liệu và lỗi thiết kế này không bao giờ phát huy tác dụng.
WaitForExit(1000). Nhưng toàn bộ thời gian chờ đợi là không cần thiết để bắt đầu một quy trình mới. Đó có thể là hành vi bổ sung mà bạn muốn, nhưng không bắt buộc phải bắt đầu một quy trình mới.
Một cách tiếp cận đơn giản hơn nhiều phù hợp với tôi là:
Application.Restart();
Environment.Exit(0);
Điều này bảo toàn các đối số dòng lệnh và hoạt động bất chấp các trình xử lý sự kiện thường ngăn ứng dụng đóng.
Lời gọi Restart () cố gắng thoát, vẫn bắt đầu một phiên bản mới và quay trở lại. Sau đó, lệnh Exit () kết thúc quá trình mà không cho bất kỳ trình xử lý sự kiện nào có cơ hội chạy. Có một khoảng thời gian rất ngắn trong đó cả hai quá trình đang chạy, đây không phải là vấn đề trong trường hợp của tôi, nhưng có thể trong các trường hợp khác.
Mã thoát 0 trong Environment.Exit(0);chỉ định tắt máy hoàn toàn. Bạn cũng có thể thoát bằng 1 để xác định lỗi đã xảy ra.
OnClose()các sự kiện biểu mẫu và tương tự.
Nếu bạn đang ở biểu mẫu ứng dụng chính, hãy thử sử dụng
System.Diagnostics.Process.Start( Application.ExecutablePath); // to start new instance of application
this.Close(); //to turn off current app
Enviorment.Exit(0)cũng sẽ làm được việc.
Enviorment.Exitlà một lối thoát bẩn và khá xâm lấn vì nó ngăn không cho mã dọn dẹp ứng dụng chạy. Không phải là lựa chọn đúng trong hầu hết thời gian.
Tôi có thể đến muộn bữa tiệc nhưng đây là giải pháp đơn giản của tôi và nó hoạt động như một sự quyến rũ với mọi ứng dụng tôi có:
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
{ }
Tôi đã gặp vấn đề chính xác tương tự và tôi cũng có yêu cầu ngăn chặn các trường hợp trùng lặp - tôi đề xuất một giải pháp thay thế cho giải pháp mà HiredMind đang đề xuất (sẽ hoạt động tốt).
Những gì tôi đang làm là bắt đầu quy trình mới với processId của quy trình cũ (quy trình kích hoạt khởi động lại) dưới dạng đối số dòng 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);
Sau đó, khi ứng dụng mới khởi động, trước tiên, tôi phân tích cú pháp dòng cm args và kiểm tra xem cờ khởi động lại có ở đó với processId hay không, sau đó đợi quá trình đó thoát:
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
}
}
Tôi rõ ràng là không hiển thị tất cả các kiểm tra an toàn mà tôi có, v.v.
Ngay cả khi không lý tưởng - tôi thấy đây là một giải pháp thay thế hợp lệ để bạn không cần phải có một ứng dụng riêng chỉ để xử lý việc khởi động lại.
/allowMultipleInstanceslá cờ hơn là một lá cờ kỳ lạ /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);
Điều này dường như hoạt động tốt hơn Application.Restart ();
Không chắc điều này sẽ xử lý như thế nào nếu chương trình của bạn bảo vệ khỏi nhiều phiên bản. Tôi đoán là bạn nên khởi chạy tệp .exe thứ hai tạm dừng và sau đó khởi động ứng dụng chính cho bạn.
Đơn giản của nó, bạn chỉ cần gọi các Application.Restart()phương thức có xu hướng gọi ứng dụng của bạn để được khởi động lại. Nhưng bạn phải thoát khỏi môi trường Cục bộ với mã lỗi của chúng:
Application.Restart();
Environment.exit(int errorcode);
bạn có thể tạo một bảng liệt kê mã lỗi mà bạn có thể sử dụng nó một cách hiệu quả.
Một phương pháp khác là chỉ cần thoát khỏi biểu mẫu ứng dụng và bắt đầu quá trình có đường dẫn thực thi:
Application.exit();
System.Diagnostics.Process.Start(Application.ExecutablePath);
Hãy thử mã này:
bool appNotRestarted = true;
Mã này cũng phải có trong hàm:
if (appNotRestarted == true) {
appNotRestarted = false;
Application.Restart();
Application.ExitThread();
}
Tôi đã tìm ra một giải pháp khác, có lẽ ai cũng có thể sử dụng nó.
string batchContent = "/c \"@ECHO OFF & timeout /t 6 > nul & start \"\" \"$[APPPATH]$\" & exit\"";
batchContent = batchContent.Replace("$[APPPATH]$", Application.ExecutablePath);
Process.Start("cmd", batchContent);
Application.Exit();
Mã được đơn giản hóa nên hãy quan tâm đến Ngoại lệ và nội dung;)
Bạn đang quên các tùy chọn / tham số dòng lệnh đã được chuyển vào phiên bản hiện đang chạy của bạn. Nếu bạn không vượt qua những điều đó, bạn không thực hiện khởi động lại thực sự. Đặt Process.StartInfomột bản sao các thông số quy trình của bạn, sau đó bắt đầu.
Ví dụ: nếu quy trình của bạn được bắt đầu myexe -f -nosplash myfile.txt, phương thức của bạn sẽ chỉ thực thi myexemà không có tất cả các cờ và tham số đó.
Tôi muốn ứng dụng mới khởi động sau khi ứng dụng cũ tắt.
Sử dụng process.WaitForExit () để đợi quá trình của riêng bạn tắt không có ý nghĩa gì. Nó sẽ luôn luôn hết thời gian.
Vì vậy, cách tiếp cận của tôi là sử dụng Application.Exit () sau đó đợi, nhưng cho phép các sự kiện được xử lý, trong một khoảng thời gian. Sau đó, khởi động một ứng dụng mới với các đối số giống như ứng dụng cũ.
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 );
}
Bạn cũng có thể sử dụng Restarter .
Restarter là ứng dụng tự động theo dõi và khởi động lại các chương trình, ứng dụng bị treo hoặc bị treo. Ban đầu nó được phát triển để giám sát và khởi động lại máy chủ trò chơi, nhưng nó sẽ thực hiện công việc đối với bất kỳ bảng điều khiển hoặc chương trình hoặc ứng dụng dựa trên biểu mẫu nào
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 trả về đường dẫn tệp .exe aplication của bạn Vui lòng thực hiện theo thứ tự lệnh gọi. Bạn có thể muốn đặt nó trong một mệnh đề try-catch.
Làm thế nào về tạo một tệp bat, chạy tệp loạt trước khi đóng, sau đó đóng phiên bản hiện tại.
Tệp lô thực hiện điều này:
Đây là 2 xu của tôi:
Trình tự Bắt đầu phiên bản mới-> Đóng phiên bản hiện tại sẽ hoạt động ngay cả đối với các ứng dụng không cho phép chạy nhiều bản sao đồng thời vì trong trường hợp này, phiên bản mới có thể được truyền một đối số dòng lệnh sẽ cho biết đang có khởi động lại vì vậy việc kiểm tra các trường hợp khác đang chạy sẽ không cần thiết. Việc chờ đợi phiên bản đầu tiên thực sự hoàn thành của tôi cũng được thực hiện nếu nó hoàn toàn bắt buộc rằng không có hai nội dung nào đang chạy song song.
Tôi sợ rằng việc khởi động lại toàn bộ ứng dụng bằng Quy trình đang tiếp cận vấn đề của bạn theo cách sai.
Một cách dễ dàng hơn là sửa đổi tệp Program.cs để khởi động lại:
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;
}
}
}
Tôi đã gặp sự cố tương tự, nhưng vấn đề của tôi liên quan đến rò rỉ bộ nhớ không thể quản lý mà tôi không thể tìm thấy trên một ứng dụng phải chạy 24/7. Với khách hàng, tôi đồng ý rằng thời gian an toàn để khởi động lại ứng dụng là 03:00 sáng nếu mức tiêu thụ bộ nhớ vượt quá giá trị đã xác định.
Tôi đã thử Application.Restart, nhưng vì nó dường như sử dụng một số cơ chế khởi động phiên bản mới trong khi nó đang chạy, tôi đã chuyển sang một chương trình khác. Tôi đã sử dụng thủ thuật rằng hệ thống tệp xử lý vẫn tồn tại cho đến khi quá trình tạo ra chúng chết. Vì vậy, từ Ứng dụng, tôi đã thả tệp vào đĩa chứ không phải Dispose()xử lý. Tôi đã sử dụng tệp để gửi thư mục thực thi và thư mục bắt đầu của 'chính tôi' (để thêm tính linh hoạt).
Mã:
_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()ở cuối sẽ bắt đầu tắt ứng dụng và xử lý tệp mà tôi sử dụng StreamWriterở đây sẽ được giữ mở cho đến khi quá trình thực sự chết. Sau đó...
Restarter.exe bắt đầu hoạt động. Nó TRIES để đọc tệp ở chế độ độc quyền, ngăn nó có quyền truy cập cho đến khi ứng dụng chính chưa chết, sau đó khởi động ứng dụng chính, xóa tệp và tồn tại. Tôi đoán rằng nó không thể đơn giản hơn:
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);
}
}
Tôi sử dụng những thứ sau và nó thực hiện chính xác những gì bạn đang tìm kiếm:
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);
}
để sử dụng Khi đăng xuất, bạn cần phải chấm dứt tất cả ứng dụng khỏi Ram Cache vì vậy hãy đóng Ứng dụng trước rồi chạy lại nó
// khi nhấp vào nút Đăng xuất
foreach(Form frm in Application.OpenForms.Cast<Form>().ToList())
{
frm.Close();
}
System.Diagnostics.Process.Start(Application.ExecutablePath);
Vấn đề của việc sử dụng Application.Restart () là nó bắt đầu một quy trình mới nhưng quy trình "cũ" vẫn còn sót lại. Vì vậy, tôi quyết định Hủy quy trình cũ bằng cách sử dụng đoạn mã sau:
if(Condition){
Application.Restart();
Process.GetCurrentProcess().Kill();
}
Và nó hoạt động tốt. Trong trường hợp của tôi, MATLAB và Ứng dụng C # đang chia sẻ cùng một cơ sở dữ liệu SQLite. Nếu MATLAB đang sử dụng cơ sở dữ liệu, thì Biểu mẫu-Ứng dụng sẽ khởi động lại (+ Đếm ngược), cho đến khi MATLAB đặt lại bit bận của nó trong cơ sở dữ liệu. (Chỉ để biết thông tin phụ)