여러 GUI 스레드를 '스핀오프'할 수 있나요? (Application.Run에서 시스템을 중단하지 않음)

나의 목표

메인 처리 스레드(비 GUI)가 있고, 필요에 따라 자체 백그라운드 스레드에서 GUI를 스핀오프할 수 있고, 메인 비 GUI 스레드가 계속 작동하도록 하고 싶습니다. 다시 말해, 비 GUI 스레드가 GUI 스레드의 소유자가되기를 원하며 그 반대의 경우도 마찬가지입니다. 이것이 Windows Forms(?)에서도 가능한지 잘 모르겠습니다.

배경

컨트롤러가 어셈블리를 동적으로로드하고 단일 메서드 DoStuff()로 공통 IComponent 인터페이스를 구현하는 클래스를 인스턴스화하고 실행하는 구성 요소 기반 시스템이 있습니다.

로드되는 컴포넌트는 xml 구성 파일을 통해 그리고 IComponent의 다른 구현을 포함하는 새 어셈블리를 추가하여 구성됩니다. 컴포넌트는 메인 애플리케이션에 유틸리티 기능을 제공합니다. 메인 프로그램이 원자력 발전소 제어와 같은 작업을 수행하는 동안 구성 요소는 데이터베이스 정리, 이메일 전송, 프린터에 재미있는 농담 인쇄 등과 같은 유틸리티 작업을 (자체 스레드에서) 수행할 수 있습니다. 제가 원하는 것은 이러한 컴포넌트 중 하나가 이메일 전송 컴포넌트의 상태 정보와 같은 GUI를 표시할 수 있도록 하는 것입니다.

전체 시스템의 수명은 다음과 같습니다.

  1. 애플리케이션이 시작됩니다.
  2. 로드할 구성 요소에 대한 구성 파일을 확인합니다. 로드합니다.
  3. **각 컴포넌트에 대해 DoStuff()를 실행하여 초기화하고 자체 스레드에서 자체 수명을 살도록 합니다.
  4. 메인 애플리케이션인 작업의 왕을 계속 수행합니다.

컴포넌트가 DoStuff()에서 GUI를 실행하는 경우 아직 포인트 3을 성공적으로 수행하지 못했습니다. 단순히 GUI가 닫힐 때까지 멈출 뿐입니다. 그리고 GUI가 닫힐 때까지는 프로그램이 포인트 4로 진행되지 않습니다.

이러한 컴포넌트가 자체 Windows Forms GUI를 시작할 수 있다면 좋을 것입니다.

문제

컴포넌트가 DoStuff()에서 GUI를 실행하려고 할 때(정확한 코드 줄은 컴포넌트가 Application.Run(theForm)을 실행할 때입니다), 컴포넌트와 우리 시스템은 GUI가 닫힐 때까지 Application.Run() 줄에서 "멈춥니다". 방금 실행한 GUI는 예상대로 정상적으로 작동합니다.

컴포넌트의 예시. 하나는 GUI와 아무 관련이 없지만, 두 번째 컴포넌트는 분홍색 솜털 토끼가 있는 귀여운 창을 실행합니다.

public class MyComponent1: IComponent
{
    public string DoStuff(...) { // write something to the database  }
}

public class MyComponent2: IComponent
{
    public void DoStuff()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form());

        // I want the thread to immediately return after the GUI 
        // is fired up, so that my main thread can continue to work.
    }
}

나는 이것을 시도했지만 운이 없었습니다. 자체 스레드에서 GUI를 실행하려고 해도 GUI가 닫힐 때까지 실행이 중지됩니다.

public void DoStuff()
{
    new Thread(ThreadedInitialize).Start()
}

private void ThreadedInitialize()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form());
}

GUI를 스핀오프하고 Application.Run() 이후에 반환하는 것이 가능합니까?

해결책

Application.Run 메서드는 하나 이상의 양식을 표시하고 모든 양식이 닫힐 때까지 실행되는 표준 메시지 루프를 시작합니다. 모든 양식을 닫거나 애플리케이션을 강제로 종료하는 경우를 제외하고는 해당 메서드에서 강제로 반환할 수 없습니다.

그러나 새 Form() 대신 ApplicationContext를 Application.Run 메서드에 전달할 수 있으며 ApplicationContext를 사용하여 여러 양식을 한 번에 실행할 수 있습니다. 모든 양식이 닫힐 때만 애플리케이션이 종료됩니다. 여기를 참조하세요: http://msdn.microsoft.com/en-us/library/system.windows.forms.application.run.aspx>

또한 모달로 표시하지 않는 양식은 기본 양식과 함께 계속 실행되므로 서로를 차단하지 않는 두 개 이상의 창을 가질 수 있습니다. 이것이 실제로 여러분이 달성하려는 목표라고 생각합니다.

해설 (0)

충분히 열심히 해킹하면 가능하겠지만, 좋은 생각은 아니라고 생각합니다.

(화면에 표시되는) 윈도우는 프로세스와 매우 밀접하게 연결되어 있습니다. 즉, GUI를 표시하는 각 프로세스에는 창을 만들고 관리하는 데 관련된 모든 메시지(예: '버튼을 클릭했다', '앱을 닫았다', '화면을 다시 그렸다' 등)를 처리하는 메시지 루프가 있을 것으로 예상됩니다.

따라서 메시지 루프가 있는 경우 프로세스의 수명 기간 동안 사용할 수 있어야 한다고 가정합니다. 예를 들어 창에서 '종료' 메시지를 보낼 수 있는데, 화면에 아무 것도 없는 경우에도 이를 처리할 수 있는 메시지 루프가 있어야 합니다.

가장 좋은 방법은 다음과 같이 하는 것입니다:

'기본 앱'으로 표시되지 않는 가짜 양식을 만드세요; 시작하기 Application.Run을 호출하고 이 가짜 양식을 전달하세요. 다른 스레드에서 작업을 수행하고, Gui 작업을 수행해야 할 때 메인 스레드에서 이벤트를 실행하세요.

해설 (1)

이것이 맞는지 확실하지 않지만 콘솔 응용 프로그램에서 창 양식을 실행할 때 양식을 새로 만들고 newForm.Show()를 호출하는 것으로 기억하는데, 구성 요소에서 Application.Run() 대신 이를 사용하는 경우 새 양식이 차단되지 않아야 합니다.

물론 컴포넌트는 생성한 폼에 대한 참조를 유지 관리할 책임이 있습니다.

해설 (0)