Philipp Sumi의 훌륭한 NotifyIcon 을 사용 하는 창없는 WPF 응용 프로그램이 있습니다. 이전 WinForms 기반 버전을 기꺼이 교체하고 있지만 한 가지 문제가 있습니다.
내 앱에는 실행 파일을 제거하기 전에 트레이 응용 프로그램을 닫는 제거 프로그램이 있습니다. 닫기 작업은 트레이 응용 프로그램 프로세스에 WM_CLOSE 메시지를 보내는 것으로 완료됩니다. 그러나 WinForms를 사용하여 이러한 메시지를 듣는 것은 비교적 쉽습니다. WPF를 사용하여 수행하는 방법, 그리고 다시 WPF 트레이 응용 프로그램을 종료하도록 원격으로 알리는 더 좋은 방법이 있을까요? 내 말은, 또 뭐가 있지? 파이프?
이것은 중복이 아닙니다. 내 앱에 "메인 창"이 없습니다. 따라서 주 창을 참조하는 것은 작동하지 않습니다.
시스템이 다시 시작하거나 종료하는 경우 앱이 자동으로 종료되도록 지시하는 방법이 있어야한다고 생각합니다. 그렇기 때문에 "이 앱이 응답하지 않습니다. 종료 하시겠습니까?"또는 이와 유사한 메시지가 표시되는 이유입니다.
내 순진한 접근 방식은 다음과 같습니다.
using (var process = Process.GetProcessesByName("MyApp").FirstOrDefault()) {
const uint WM_SYSCOMMAND = 0x0112;
const uint SC_CLOSE = 0xF060;
const uint WM_CLOSE = 0x0010;
var hwnd = process.MainWindowHandle;
NativeMethods.SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
foreach (ProcessThread thread in process.Threads) {
NativeMethods.PostThreadMessage(
(uint)thread.Id,
WM_SYSCOMMAND,
(IntPtr)SC_CLOSE,
IntPtr.Zero
);
}
}
작동하지 않습니다. 물론 hwnd는 항상 IntPtr.Zero입니다. 창을 만들지 않으면 창을 만들고 싶지 않습니다. 응용 프로그램 스레드는 SC_CLOSE를 무시하므로 여기서도 기쁨이 없습니다.
네, 보이지 않는 창을 만들려고했습니다. 이 방법은 창에 ShowInTaskBar가 true로 설정된 경우 작동합니다. 안좋다.
그런 다음 System.Windows.Forms.NativeWindow에서 스폰지 창을 만들었습니다.
물론 보이지 않는 창은 WM_CLOSE 및 기타 메시지를 완벽하게 수신 할 수 있지만 프로세스 기본 창으로 설정되어 있지 않으므로 제거 앱으로 대상을 지정할 수 없습니다.
현재 나는 아이디어가 없습니다.
대부분의 경우 나는 스스로 알아 내야했다. 방법은 다음과 같습니다.
첫째, 프로세스가 있습니다. 이 프로세스에는 주 창이없고 창도 전혀 없으므로 창과의 모든 통신은 테이블에서 벗어납니다. 그러나 프로세스에는 스레드가 있습니다. 스레드에는 메시지 큐가 있지만 Win32 API로 액세스 할 수 있습니다.
그래서 여기에 수신 끝이 있습니다.
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application {
// (...)
/// <summary>
/// Sent as a signal that a window or an application should terminate.
/// </summary>
const uint WM_CLOSE = 0x0010;
/// <summary>
/// Retrieves a message from the calling thread's message queue. The function dispatches incoming sent messages until a posted message is available for retrieval.
/// </summary>
/// <param name="lpMsg">MSG structure that receives message information from the thread's message queue.</param>
/// <param name="hWnd">A handle to the window whose messages are to be retrieved. The window must belong to the current thread. Use <see cref="IntPtr.Zero"/> to retrieve thread message.</param>
/// <param name="wMsgFilterMin">The integer value of the lowest message value to be retrieved.</param>
/// <param name="wMsgFilterMax">The integer value of the highest message value to be retrieved.</param>
/// <returns>Non-zero for any message but WM_QUIT, zero for WM_QUIT, -1 for error.</returns>
[DllImport("user32.dll")]
static extern int GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
/// <summary>
/// Waits indefinitely for a specific thread message.
/// </summary>
/// <param name="signal">Signal, message value.</param>
private void WaitForSignal(uint signal) => GetMessage(out var msg, IntPtr.Zero, signal, signal);
/// <summary>
/// WPF application startup.
/// </summary>
/// <param name="e">Event arguments.</param>
protected override async void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
// ... initialization code ...
await Task.Run(() => WaitForSignal(WM_CLOSE));
Shutdown();
}
// (...)
}
보내는 끝 :
/// <summary>
/// Demo.
/// </summary>
class Program {
/// <summary>
/// Sent as a signal that a window or an application should terminate.
/// </summary>
const uint WM_CLOSE = 0x0010;
/// <summary>
/// Posts a message to the message queue of the specified thread. It returns without waiting for the thread to process the message.
/// </summary>
/// <param name="threadId">The identifier of the thread to which the message is to be posted.</param>
/// <param name="msg">The type of message to be posted.</param>
/// <param name="wParam">Additional message-specific information.</param>
/// <param name="lParam">Additional message-specific information.</param>
/// <returns></returns>
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostThreadMessage(uint threadId, uint msg, IntPtr wParam, IntPtr lParam);
/// <summary>
/// Closes a windowless application pointed with process name.
/// </summary>
/// <param name="processName">The name of the process to close.</param>
static void CloseWindowless(string processName) {
foreach (var process in Process.GetProcessesByName(processName)) {
using (process) {
foreach (ProcessThread thread in process.Threads) PostThreadMessage((uint)thread.Id, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
}
/// <summary>
/// Main application entry point.
/// </summary>
/// <param name="args">Command line arguments.</param>
static void Main(string[] args) => CloseWindowless("MyAppName");
}
그리고 뒤에있는 부두 마술 : P / Invoke 부분은 꽤 분명합니다. 더 흥미로운 것은 WPF Application
입니다. OnStartup
무시 동작.
메서드가 동기적이고 종료되지 않으면 응용 프로그램이 중단됩니다. 그러나 비동기로 표시된 경우 종료 할 필요가 없습니다. 기다릴 수있는 모든 것을 무기한 기다립니다. Shutdown()
메인 UI 스레드에서만 호출 할 수 있기 때문에 이것이 정확히 필요한 것 입니다. 해당 스레드를 차단할 수 없으므로 다른 스레드에서 메시지를 기다려야합니다. 우리는 WM_CLOSE를 모든 프로세스 스레드로 보내서 그것을 얻을 것입니다. 종료되면 Shutdown
메서드는 메인 스레드에서 호출됩니다.
이 솔루션에서 가장 좋은 것은 System.Windows.Forms 참조가 필요하지 않습니다.
이 기사는 인터넷에서 수집됩니다. 재 인쇄 할 때 출처를 알려주십시오.
침해가 발생한 경우 연락 주시기 바랍니다[email protected] 삭제
몇 마디 만하겠습니다