Конференция "WinAPI" » Подключение плагина на C++ к программе на Delphi [D7, WinXP]
 
  • Bordo (01.09.09 13:52) [0]
    Здравствуйте, коллеги!

    Неделю мучаюсь над странным поведением SendMessage() из WinAPI -- растолкуйте кто знает.

    Условия задачи:
    * есть чужая программа на Delphi7, исходников нет;
    * к программе можно подключать простые плагины в виде DLL с экспортируемой точкой входа вида ShowModForm(appWnd: HWND);
    * подключаемый модуль может общаться с программой посредством SendMessage;
    * тестовый пример на Delphi работает, а на WTL -- нет.

    Код модуля на Delphi:

    unit TestFormUnit;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs;

    type
     TTestForm = class(TForm)
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     TestForm: TTestForm;

    procedure ShowModForm(appWnd: HWND);

    implementation

    {$R *.dfm}

    procedure ShowModuleForm(appWnd: HWND);
    var
     rslt : Integer;
     msgCode : Integer;
    begin
     with TTestForm.Create(Application) do
     try
       msgCode := RegisterWindowMessage('WM_ACME_REGISTERMODWND');

       {WM_ACME_REGISTERMODWND -- одно из сообщений для взаимодействия с приложением,
       {Возвращаемое значение должно быть 1 или 2}

       rslt := SendMessage(appWnd, msgCode, Handle, 0);

       Caption := 'DelphiTestModule - ' + IntToStr(rslt);
       ShowModal
     finally
       Free
     end
    end;

    end.



    Процедура [i]ShowModuleForm[/i] экспортируется так:

    library ModDelphi;

    uses
     ShareMem,
     SysUtils,
     Classes,
     TestFormUnit in 'TestFormUnit.pas' {TestForm};

    {$R *.res}

    exports ShowModuleForm;

    begin
    end.



    При открытии формы этого модуля в заголовке будет написано DelphiTestModule - 1.

    Вот код на C++ c использованием WTL:

    #include "stdafx.h"

    #define MOD_WTL_API __declspec(dllexport)

    #include <windows.h>

    #include <atlbase.h>
    #include <atlapp.h>

    extern CAppModule _Module;
    #include <atlwin.h>
    #include <atlgdi.h>
    #include <atlmisc.h>

    CAppModule _Module;

    using namespace std;

    // Функция вызова плагина
    extern "C" MOD_WTL_API BOOL ShowModForm(HWND parent);

    // Точка входа в DLL
    BOOL APIENTRY DllMain( HMODULE hModule,
                          DWORD  ul_reason_for_call,
                          LPVOID lpReserved
         )
    {
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
     // Инициализируем модуль
     _Module.Init(0, hModule, 0);

     break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
     break;
    case DLL_PROCESS_DETACH:
     _Module.Term();
     break;
    }

       return TRUE;
    }

    // Главное окно плагина
    class CMainWindow :
    public CWindowImpl<CMainWindow, CWindow, CFrameWinTraits>
    {
    BEGIN_MSG_MAP(CMainWindow)
     MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
    END_MSG_MAP()

    LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
     PostQuitMessage(0);
     return 0;
    }

    };

    MOD_WTL_API BOOL ShowModForm(HWND parent)
    {
    // Создаём главное окно модуля.
    CMainWindow wnd;
    wnd.Create(NULL, CWindow::rcDefault);

    const UINT WM_ACME_REGISTERMODWND = ::RegisterWindowMessage(L"WM_ACME_REGISTERMODWND");
    const LRESULT rslt = ::SendMessage(parent, WM_ACME_REGISTERMODWND, (WPARAM)((HWND)wnd), 0); ///< Возвращает всегда 0!

    char buffer[80] = {0}
    ;
    _itoa(rslt, buffer, 10);

    CString title(L"WtlTestModule - ");
    title += rslt;
    ::SetWindowText(wnd, title);

    wnd.ShowWindow(SW_SHOW);
    // Запускаем цикл сообщений
    CMessageLoop loop;
    loop.Run();

    return TRUE;
    }



    При открытии формы этого модуля в заголовке будет написано WtlTestModule - 0. Т.е. объявленный в программе API для подключения модулей не работает.

    Подозреваю, что тайна может заключаться в использовании TTestForm.Create(Application), но как его использовать в C++?
  • clickmaker © (01.09.09 13:58) [1]
    TTestForm.CreateParented(appWnd)
  • Rouse_ © (01.09.09 14:20) [2]
    (WPARAM)((HWND)wnd)
    замени на
    wnd::m_hWnd
  • Bordo (01.09.09 15:35) [3]
    Rouse_, тогда уж на
    reinterpret_cast<WPARAM>(wnd.m_hWnd)

    но к вопросу это не относится.
  • Bordo (01.09.09 15:41) [4]

    > clickmaker ©   (01.09.09 13:58) [1]
    > TTestForm.CreateParented(appWnd)

    Это на CBuilder? К сожалению, не могу его использовать -- работаю на Visual Studio 2005 и gcc 3.4.5 (mingw-vista special r3). Есть способ использовать VCL в этих компиляторах?
  • Bordo (01.09.09 15:44) [5]
    Мне тут указали, что procedure ShowModForm(appWnd: HWND); вызывается как register (__fastcall в С++).

    И правда, если объявить
    procedure ShowModForm(appWnd: HWND); stdcall;

    , то в заголовке Дельфийской формы тоже будет ноль. К сожалению, пока что не удалось подобрать нужный спецификатор вызова для C++ версии.
  • Rouse_ © (01.09.09 16:25) [6]

    > но к вопросу это не относится.

    Это как это не относится? :) У тебя же код разные вещи делает :)

    В дельфи аналоге (который работает) ты передаешь в SendMessage хэндл вновь созданной формы, а в сишном варианте передаешь указатель на CMainWindow который зачем-то еще и к хэндлу приводишь. вместо того чтобы передать тот-же хэндл, хранящийся в параметре m_hWnd. Разница есть?
    Ну а stdcall применить к ShowModForm - это тебя хитро надоумили. Стек на 4 байта уплыл, понятно почему после этого и дельфийский аналог перестал работать.
  • Bordo (01.09.09 17:16) [7]

    > Rouse_ ©   (01.09.09 16:25) [6]
    > > но к вопросу это не относится.Это как это не относится?
    >  :) У тебя же код разные вещи делает :)В дельфи аналоге
    > (который работает) ты передаешь в SendMessage хэндл вновь
    > созданной формы, а в сишном варианте передаешь указатель
    > на CMainWindow который зачем-то еще и к хэндлу приводишь.
    >  вместо того чтобы передать тот-же хэндл, хранящийся в параметре
    > m_hWnd. Разница есть?


    1. не указатель, а локальную переменную;
    2. CWindow автоматически приводится к HWND через тот же m_hWnd.


    >  Ну а stdcall применить к ShowModForm
    > - это тебя хитро надоумили. Стек на 4 байта уплыл, понятно
    > почему после этого и дельфийский аналог перестал работать.


    Да всё дело оказалось в calling conventions. Именно по этому не работает код на С++: я как порядочный человек использовал stdcall для экспорта, а авторы хост-программы плевать хотели на стандарты и соглашения.

    Теперь разбираю примерчик отсюда: http://www.codeguru.com/forum/showthread.php?t=466266
  • Сергей М. © (01.09.09 17:26) [8]

    > авторы хост-программы плевать хотели на стандарты и соглашения


    Имеют полное право плевать.
    А вот то что они якобы не указали в своем PluginSDK требуемые соглашения о вызовах - это довольно подозрительно.
  • Bordo (01.09.09 17:41) [9]

    > Сергей М. ©   (01.09.09 17:26) [8]
    >
    > Имеют полное право плевать. А вот то что они якобы не указали
    > в своем PluginSDK требуемые соглашения о вызовах - это довольно
    > подозрительно.

    Иметь-то имеют, но не надо тогда заявлять, что можно разрабатывать модули на чём угодно. И всё-таки мне кажется, что они были "не в курсе" каких-то там соглашений.
  • Leonid Troyanovsky © (01.09.09 18:04) [10]

    > Bordo   (01.09.09 13:52)  

    > TTestForm.Create(Application), но как его использовать в
    > C++?

    Application - никак. Т.е. влиять на него можно только сабклассив его окно.
    Кроме того, есть глобальные переменные Screen, PopupMenues и т.п.,
    в каждой из которых притаились ржавые грабли.

    Подозреваю даже неправомерность

    > // Запускаем цикл сообщений

    если, конечно, это не модальный диалог.

    --
    Regards, LVT.
  • Bordo (02.09.09 05:46) [11]
    Итак, всё дело было в разъезжании стека из-за несоответствия calling conventions. Проблему удалось решить с помощью простой обёртки на Дельфи:

    unit TestFormUnit;

    interface

    type
     HWND = type LongWord;
     {$EXTERNALSYM HWND}

    procedure ShowModuleFormProxy(appWnd: HWND);

    implementation

    procedure ShowModForm(appWnd: HWND); stdcall; External 'mod_wtl.dll';

    procedure ShowModuleFormProxy(appWnd: HWND);
    begin
     mod_wtl_show(appWnd);
    end;

    end.


    Код на C++ остался без изменений.

    Параллельно пытаюсь разобраться с вариантом, описанным в  http://www.codeguru.com/forum/showthread.php?t=466266 . Если кто-нибудь поделится рабочим вариантом для Visual C++ 2005, а ещё лучше для gcc 3.4.5 под mingw, буду очень признателен, потому что в ассемблере я совершенно не разбираюсь.

    Если кто-нибудь знает, как правильно написать
 
Конференция "WinAPI" » Подключение плагина на C++ к программе на Delphi [D7, WinXP]
Есть новые Нет новых   [134434   +26][b:0][p:0.003]