문제

WPF로 작성된 새 응용 프로그램이있어 이전 API를 지원 해야하는 이전 API가 숨겨진 창에 게시 된 메시지를 수신 할 수 있습니다. 일반적으로 다른 응용 프로그램은 FindWindow를 사용하여 사용자 정의 창 클래스의 이름을 사용하여 숨겨진 창을 식별합니다.

1) 구식 Win32 통화를 사용해야하는 사용자 정의 창 클래스를 구현한다고 가정합니다.

이전 C ++ 응용 프로그램은 RegisterClass 및 CreateWindow를 사용하여 가장 간단한 보이지 않는 창을 만들었습니다.

나는 C#내에서 똑같이 할 수 있어야한다고 생각합니다. 내 프로젝트가 관리되지 않는 코드를 컴파일 해야하는 것을 원하지 않습니다.

System.windows.interop.hwndhost에서 상속을 시도하고 System.runtime.interopservices.dllimport를 사용하여 위의 API 메소드를 가져 왔습니다.

이렇게하면 표준 Win32 창을 성공적으로 호스팅 할 수 있습니다. 그러나 사용자 정의 창에 대해 CreateWindowEx를 호출하면 항상 NULL을 반환합니다.

RegisterClass에 대한 나의 호출은 성공하지만 wndclass.lpfnwndproc 멤버로 무엇을 설정 해야하는지 잘 모르겠습니다.

2) 누구든지 성공적으로 수행하는 방법을 아는 사람이 있습니까?

도움이 되었습니까?

해결책

기록을 위해 나는 마침내 이것을 일하게했다. 내가 마샬링 문제를 현악화하는 데 어려움이 있는데 어려움이 밝혀졌습니다. Win32 기능의 가져 오기에 더 정확해야했습니다.

아래는 C#에서 사용자 정의 창 클래스를 생성하는 코드입니다. 구식 API를 지원하는 데 유용합니다. 사용자 정의 창 클래스에 의존 할 수 있습니다.

메시지 펌프가 스레드에서 실행되는 한 WPF 또는 Winforms에서 작동해야합니다.

편집 : 콜백을 감싸는 대의원의 초기 컬렉션으로 인해보고 된 충돌을 해결하기 위해 업데이트되었습니다. 대의원은 이제 회원으로 개최되며 대의원은 기능 포인터로 명시 적으로 마샬링되었습니다. 이것은 문제를 해결하고 행동을 더 쉽게 이해할 수있게합니다.

class CustomWindow : IDisposable
{
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.StructLayout(
        System.Runtime.InteropServices.LayoutKind.Sequential,
       CharSet = System.Runtime.InteropServices.CharSet.Unicode
    )]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
        public string lpszClassName;
    }

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.UInt16 RegisterClassW(
        [System.Runtime.InteropServices.In] ref WNDCLASS lpWndClass
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr CreateWindowExW(
       UInt32 dwExStyle,
       [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
       string lpClassName,
       [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)]
       string lpWindowName,
       UInt32 dwStyle,
       Int32 x,
       Int32 y,
       Int32 nWidth,
       Int32 nHeight,
       IntPtr hWndParent,
       IntPtr hMenu,
       IntPtr hInstance,
       IntPtr lpParam
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern System.IntPtr DefWindowProcW(
        IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam
    );

    [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyWindow(
        IntPtr hWnd
    );

    private const int ERROR_CLASS_ALREADY_EXISTS = 1410;

    private bool m_disposed;
    private IntPtr m_hwnd;

    public void Dispose() 
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing) 
    {
        if (!m_disposed) {
            if (disposing) {
                // Dispose managed resources
            }

            // Dispose unmanaged resources
            if (m_hwnd != IntPtr.Zero) {
                DestroyWindow(m_hwnd);
                m_hwnd = IntPtr.Zero;
            }

        }
    }

    public CustomWindow(string class_name){

        if (class_name == null) throw new System.Exception("class_name is null");
        if (class_name == String.Empty) throw new System.Exception("class_name is empty");

        m_wnd_proc_delegate = CustomWndProc;

        // Create WNDCLASS
        WNDCLASS wind_class = new WNDCLASS();
        wind_class.lpszClassName = class_name;
        wind_class.lpfnWndProc = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(m_wnd_proc_delegate);

        UInt16 class_atom = RegisterClassW(ref wind_class);

        int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

        if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
            throw new System.Exception("Could not register window class");
        }

        // Create window
        m_hwnd = CreateWindowExW(
            0,
            class_name,
            String.Empty,
            0,
            0,
            0,
            0,
            0,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero
        );
    }

    private static IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) 
    {
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }

    private WndProc m_wnd_proc_delegate;
}

다른 팁

Morechilli의 답변을 언급하고 싶습니다 :

public CustomWindow(string class_name){

    if (class_name == null) throw new System.Exception("class_name is null");
    if (class_name == String.Empty) throw new System.Exception("class_name is empty");

    // Create WNDCLASS
    WNDCLASS wind_class = new WNDCLASS();
    wind_class.lpszClassName = class_name;
    wind_class.lpfnWndProc = CustomWndProc;

    UInt16 class_atom = RegisterClassW(ref wind_class);

    int last_error = System.Runtime.InteropServices.Marshal.GetLastWin32Error();

    if (class_atom == 0 && last_error != ERROR_CLASS_ALREADY_EXISTS) {
        throw new System.Exception("Could not register window class");
    }

    // Create window
    m_hwnd = CreateWindowExW(
        0,
        class_name,
        String.Empty,
        0,
        0,
        0,
        0,
        0,
        IntPtr.Zero,
        IntPtr.Zero,
        IntPtr.Zero,
        IntPtr.Zero
    );
}

위에서 복사 한 생성자에서 약간의 오류가 있습니다. WNDClass 인스턴스가 생성되었지만 저장되지 않았습니다. 결국 쓰레기가 수집 될 것입니다. 그러나 WNDClass는 WNDPROC 대의원을 보유하고 있습니다. 이로 인해 WNDClass가 쓰레기가 수집 되 자마자 오류가 발생합니다. WNDClass의 인스턴스는 창이 파괴 될 때까지 멤버 변수에서 유지해야합니다.

1) 일반적인 Windows Forms 클래스를 서브 클래스 할 수 있습니다 ... 모든 Win32 호출이 필요하지 않으며, WNDProc 메시지를 수동으로 구문 분석하면됩니다.

2) System.windows.forms 네임 스페이스를 가져 와서 WPF와 함께 사용할 수 있습니다. WPF 양식을 너무 많은 Windows 양식에 얽매이지 않는 한 아무런 문제가 없다고 생각합니다. 메시지를 받기 위해 사용자 정의 숨겨진 양식을 인스턴스화하고 싶습니까?

WNDPROC 서브 클래스의 예 :

protected override void WndProc(ref System.Windows.Forms.Message m)
{
   // *always* let the base class process the message
   base.WndProc(ref m);

   const int WM_NCHITTEST = 0x84;
   const int HTCAPTION = 2;
   const int HTCLIENT = 1;

   // if Windows is querying where the mouse is and the base form class said
   // it's on the client area, let's cheat and say it's on the title bar instead
   if ( m.Msg == WM_NCHITTEST && m.Result.ToInt32() == HTCLIENT )
      m.Result = new IntPtr(HTCAPTION);
}

이미 RegisterClass와 모든 Win32 호출을 알고 있으므로 WNDPROC 메시지가 문제가되지 않을 것이라고 가정합니다 ...

WNDCLASS WIND_CLASS; 함수가 아닌 클래스에 정의를 넣으면 충돌이 고정됩니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top