Получение сообщения Windows на С# из управляемой библиотеки DLL с помощью p/invoke

Мне нужно вызвать некоторые нативные функции C из C#, используя p/invoke. До сих пор у меня не было проблем с маршалингом различных методов и структур в C#. Моя проблема заключается в том, что многие методы, которые я должен вызывать, являются асинхронными и возвращают свои окончательные результаты моему приложению WinForms через сообщения Windows. Например, у меня есть вызов метода со следующей подписью на C:

HRESULT AsyncOpenSession( LPSTR lpszLogicalName,
          HANDLE hApp,
          LPSTR lpszAppID,
          DWORD dwTraceLevel,
          DWORD dwTimeOut,
          USHORT lphService,
          HWND hWnd,
          DWORD dwSrvcVersionsRequired,
          LPWFSVERSION lpSrvcVersion,
          LPWFSVERSION lpSPIVersion,
          ULONG lpRequestID );

Где lpszAppID ожидает получить ИМЯ моего приложения (MyApp.exe), а hWnd — это указатель на РУЧКУ ОКНА моего приложения, которую я получаю с помощью вызова

[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr GetCurrentWindowHandle()
{
    IntPtr handle = GetForegroundWindow();
    return handle;
}

Импортированная подпись:

[DllImport("MSXFS.DLL", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern int AsyncOpenSession([MarshalAs(UnmanagedType.LPStr)] string lpszLogicalName,
                                       IntPtr hApp,
                                       [MarshalAs(UnmanagedType.LPStr)] string lpszAppID,
                                       UInt32 dwTraceLevel,
                                       UInt32 dwTimeOut,
                                       out Int32 lphService,
                                       IntPtr hWnd,
                                       UInt32 dwSrvcVersionsRequired,
                                       out WFSVersion lpSrvcVersion,
                                       out WFSVersion lpSPIVersion,
                                       out Int32 lpRequestID); 

Я вызываю метод следующим образом:

Int32 r = AsyncOpenSession(lpszLogicalName,
                               appHanlder,
                               "MyApp.exe",
                               dwTraceLevel,
                               dwTimeOut,
                               out hServ,
                               GetCurrentWindowHandle(),
                               dwSrvcVersionsRequired,
                               out srvcVersion,
                               out spiVersion,
                               out requestID);

Первоначально вызов метода возвращает код результата, указывающий, что запрошенный вызов был помещен в очередь выполнения. В любое время после первоначального вызова собственная Dll завершает выполнение запроса и отправляет сообщение Windows приложению с помощью своего дескриптора и имени окна. Код сообщения Windows для этого метода определяется следующим образом:

#define WM_USER               0x0400
#define OPEN_SESSION_COMPLETE (WM_USER + 1)

Дополнительные сведения о сообщениях Windows см. здесь: http://msdn.microsoft.com/en-us/library/windows/desktop/ms644931(v=vs.85).aspx

В моем приложении я переопределил метод WndProc, чтобы иметь возможность управлять сообщениями Windows следующим образом:

protected override void WndProc(ref Message m)
{
    const int wm_user = 0x0400;
    const int OPEN_SESSION_COMPLETE = wm_user + 1;

    switch (m.Msg)
    {
        case OPEN_SESSION_COMPLETE:
        txtEventsState.Text = m.ToString();
        break;
    }
    base.WndProc(ref m);
}

Но я никогда не получаю сообщение Windows с этим кодом. Ошибок в логах и просмотрщике событий нет. Я знаю, что мой вызов метода прошел успешно, потому что я не получаю никакого кода ошибки и потому, что перед вызовом других методов необходимо иметь открытый сеанс, и я могу успешно вызывать другие методы, которым требуется обработчик сеанса (код ошибки также отсутствует).

Моя импортированная подпись и вызов метода находятся в проекте библиотеки классов, на который ссылается мое приложение. Я делаю что-то не так здесь? Может ли кто-нибудь пролить свет на то, что может происходить? У меня нет доступа к собственному коду, только к тестовому приложению, которое показывает, что сообщения Windows отправляются родной C Dll.

Спасибо всем заранее.


person Gabe Thorns    schedule 10.10.2011    source источник
comment
Не используйте GetForegroundWindow(), вы можете получить дескриптор окна совершенно другого приложения. Используйте свойство Handle вашей формы. Используйте инструмент Spy++, чтобы убедиться, что сообщение действительно публикуется.   -  person Hans Passant    schedule 10.10.2011
comment
Гансу: Гений!!! Спасибо!!! Как я не сделал это в первую очередь??   -  person Gabe Thorns    schedule 10.10.2011
comment
@HansPassant: Не могли бы вы опубликовать свой комментарий в качестве ответа? Я приму это и закрою этот вопрос. Благодарить!   -  person Gabe Thorns    schedule 26.10.2011
comment
Пожалуйста, не стесняйтесь опубликовать свой собственный ответ и отметить его.   -  person Hans Passant    schedule 26.10.2011


Ответы (1)


Итак, вот в чем дело: GetForegroundWindow() не извлекает правильный дескриптор окна. Бывает, что у моего приложения два окна. Одна является основной формой, которую я использую для своего теста, а другая отображает все результаты и журналы, которые я делаю. Таким образом, метод фактически возвращал дескриптор окна входа в систему, а не окна формы.

Использование this.Handle и передача этого значения нативным методам заставили все работать правильно.

Спасибо Гансу за указание на это.

person Gabe Thorns    schedule 26.10.2011