0

I have Created a Delphi control with ActiveX Library and hosted it in WPF app using COM type library. I can see the Label and Delphi side background template colors loaded successfully in WPF but it's not showing controls such as button, Datepicker, Checkbox. Only label text and background are rendered properly. Can anyone help if I am missing anything?

This is my sample Delphi UI

enter image description here

After Hosting to WPF it is only loading as below

enter image description here

I am using HwndHost , below code is for WPF hosting

public class DelphiControlHost : HwndHost
{

    [DllImport("user32.dll")]
    static extern IntPtr CreateWindowEx(int dwExStyle, string lpszClassName, string lpszWindowName, int style, int x, int y, int width, int height, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

    [DllImport("user32.dll")]
    static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    static extern bool DestroyWindow(IntPtr hWnd);

    int height;
    int width;
    IntPtr child;

    public DelphiControlHost(double initialWidth, double initialHeight, IntPtr hostedControl)
    {
        width = (int)initialWidth;
        height = (int)initialHeight;
        child = hostedControl;
    }

    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        
        var host = CreateWindowEx(0, "static", null, 0x40000000 | 0x10000000, 0, 0, height, width, 
            hwndParent.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
      
       

        SetParent(child, host);
        ShowWindow(child, 5);
        return new HandleRef(this, host);
    }

    protected override void DestroyWindowCore(HandleRef hwnd)
    {
        DestroyWindow(hwnd.Handle);
    }
}

MainWindow.xaml.cs file

public partial class MainWindow : Window
{
    [DllImport("msvcr110.dll", CallingConvention = CallingConvention.Cdecl)]
    public static extern int _fpreset();

    private ProFileUXCOMImpl service;

    public MainWindow()
    {
        _fpreset();
        InitializeComponent();
        MainWindow_OnActivated(null,null);
    }

    private void MainWindow_OnActivated(object sender, EventArgs e)
    {
        if (service != null)
            return;
        service = new ProFileUXCOMImpl();
        // var control = service.GetMainFrame(DelphiHostElement.ActualWidth, DelphiHostElement.ActualHeight);
        var control = service.GetMainFrame(600, 300);
        DelphiHostElement.Child = new DelphiControlHost(DelphiHostElement.ActualWidth, DelphiHostElement.ActualHeight, control);
    }

    private void MainCanvas_OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        service?.WindowResized();
    }
}

Below Code Snippet for Delphi Side

unit ProFileUXCOMU;

interface

uses
  SysUtils, ComObj, ComServ, ProFileUXCOM_TLB, Winapi.ActiveX, StdVcl,Vcl.Forms, Winapi.Windows,
  MainFormU, MainFrameU;

type

  ProFileUXCOMImpl = class(TComObject, IProFileUXCOM)
  private
    MainFrame: TMainFrame;
    MainHandle: Cardinal;
    MainPointer: Pointer;
    MainForm: TMainForm;
  protected
    function GetMainFrame(width: Double; height: Double): Pointer; stdcall;
    procedure WindowResized(width: Double; height: Double); safecall;  procedure IProFileUXCOM.WindowResized = IProFileUXCOM_WindowResized;

    procedure IProFileUXCOM_WindowResized; safecall;
  end;

implementation

function ProFileUXCOMImpl.GetMainFrame(width: Double; height: Double): Pointer; stdcall;
begin
  MainForm := TMainForm.Create(Application);
  MainForm.ClientWidth := Trunc(width);
  MainForm.ClientHeight := Trunc(height);
  MainHandle := MainForm.Frame.Handle;
  MainPointer := System.Pointer(MainHandle);
  Result := MainPointer;
end;

procedure ProFileUXCOMImpl.WindowResized(width: Double; height: Double); safecall;
begin
  MainForm.ClientWidth := Trunc(width);
  MainForm.ClientHeight := Trunc(height);
end;

procedure ProFileUXCOMImpl.IProFileUXCOM_WindowResized;
begin

end;

initialization

  TComObjectFactory.Create(ComServer, ProFileUXCOMImpl, CLASS_ProFileUXCOMImpl, 'ProFileUXCOMImpl', '', ciMultiInstance, tmApartment);

end.
V J
  • 77
  • 12

1 Answers1

1

I bet WPF app does not spin the message loop. In Delphi apps this is usually done in Application.Run. But this is a blocking call. In your WPF app you can create a timer that will periodically call new exported function of COM object, like WindowResized. Inside that function on Delphi side call Application.ProcessMessages.

If you do not want to deal with all these low level bugs yourself, you can try existing library: https://www.remobjects.com/hydra/

Torbins
  • 2,111
  • 14
  • 16