Simple Delphi VST3 host

Hi!

I would like to create a simple VST3 effect host in Delphi, just to show the editor, pass samples and get back the effected samples for start.

Examining the SDK tutorials and Googleing for examples, I managed to achieve the basic steps, but I am new to this, although I have a little experience with COM, and I run into a problem that I can’t get the IConnectionPoint interfaces. When I then try to open the editor it crashes with a $18 address AV in the plugin DLL.

Here’s the code that I have right now:

type
    TPluginInitExit = function: LongBool; cdecl;
    TGetPluginFactory = function: Pointer; stdcall;

type
    THostApplication = class (TInterfacedObject, IHostApplication, IPlugFrame, IComponentHandler)
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        // Gets host application name.
        function GetName(name: PString128): TResult; stdcall;
        // Create host object (e.g. Vst::IMessage).
        function CreateInstance(cid: TUID; iid: TUID; var obj: pointer): TResult; stdcall;
        (** Called to inform the host about the resize of a given view. *)
        function ResizeView(view: IPlugView; newSize: PViewRect): TResult; stdcall;
        // Transfer parameter editing to component via host.
        // To be called before calling a performEdit (when mouse down for example).
        function BeginEdit(tag: TParamID): TResult; stdcall;
        // Called between beginEdit and endEdit to inform the handler that a given parameter has a new value.
        function PerformEdit(tag: TParamID; valueNormalized: TParamValue): TResult; stdcall;
        // To be called after calling a performEdit (when mouse up for example).
        function EndEdit(tag: TParamID): TResult; stdcall;
        // Instruct host to restart the component.
        // - flags is a combination of RestartFlags
        function RestartComponent(flags: int32): TResult; stdcall;
    end;

procedure TForm1.Button1Click(Sender: TObject);
var
    DLLHandle: THandle;
    PluginInit: TPluginInitExit;
    //PluginExit: TPluginInitExit;
    PluginFactoryCall: TGetPluginFactory;
    PluginFactoryCallResult: Pointer;
    PluginFactory: IPluginFactory;
    FactoryInfo: TPFactoryInfo;
    ClassInfo: TPClassInfo;
    Component: IComponent;
    PluginInstance: Pointer;
    AudioProcessor: IAudioProcessor;
    HostApplication: THostApplication;
    EditController: IEditController;
    cp_comp: IConnectionPoint;
    cp_edit: IConnectionPoint;
    PlugView: IPlugView;
    ViewRect: TViewRect;
    PanelHandle: HWND;
    ProcessSetup: TProcessSetup;
    SpeakerArrangement: TSpeakerArrangement;
    Res: TResult;
    BStream: TVST3Stream;
begin

    //Set8087CW($133F);

    DLLHandle := LoadLibrary(PChar('ReLife.vst3'));

    PluginInit := GetProcAddress(DLLHandle, PAnsiChar('InitDLL'));
    //PluginExit := GetProcAddress(DLLHandle, PAnsiChar('ExitDLL'));
    if Assigned(PluginInit) then begin
        PluginInit;
    end;

    PluginFactoryCall := GetProcAddress(DLLHandle, PAnsiChar('GetPluginFactory'));
    PluginFactoryCallResult := PluginFactoryCall;
    PluginFactory := IPluginFactory(PluginFactoryCallResult);
    PluginFactory.GetFactoryInfo(FactoryInfo);
    PluginFactory.GetClassInfo(0, ClassInfo);
    PluginFactory.CreateInstance(@ClassInfo.cid, @UID_IComponent, PluginInstance);
    Component := IComponent(PluginInstance);
    Component.SetIoMode(kSimple);

    HostApplication := THostApplication.Create;
    Res := Component.Initialize(HostApplication);
    if Res <> 0 then begin
        Showmessage('Error: Component.Initialize()');
    end;

    PluginFactory.CreateInstance(@ClassInfo.cid, @UID_IAudioProcessor, PluginInstance);
    AudioProcessor := IAudioProcessor(PluginInstance);
    ProcessSetup.processMode := kRealtime;
    ProcessSetup.symbolicSampleSize := kSample32;
    ProcessSetup.maxSamplesPerBlock := 1024;
    ProcessSetup.sampleRate := 44100;
    SpeakerArrangement := 3;
    AudioProcessor.SetBusArrangements(@SpeakerArrangement, 2, @SpeakerArrangement, 2);
    AudioProcessor.SetupProcessing(ProcessSetup);
    AudioProcessor.SetProcessing(1);

    PluginFactory.CreateInstance(@ClassInfo.cid, @UID_IEditController, PluginInstance);
    EditController := IEditController(PluginInstance);
    EditController.SetComponentHandler(HostApplication);
    Res := EditController.Initialize(HostApplication);
    if Res <> 0 then begin
        Showmessage('Error: EditController.Initialize()');
    end;

    Res := Component.QueryInterface(UID_IConnectionPoint, cp_comp);
    if Res <> 0 then begin
        if Res = kNoInterface then begin
            Showmessage('kNoInterface'); //* ERROR HERE
        end else begin
            Showmessage(IntToStr(Res));
        end;
    end;
    Res := EditController.QueryInterface(UID_IConnectionPoint, cp_edit);
    if Res <> 0 then begin
        if Res = kNoInterface then begin
            Showmessage('kNoInterface'); //* ERROR HERE
        end else begin
            Showmessage(IntToStr(Res));
        end;
    end;
    if Assigned(cp_comp)
    AND Assigned(cp_edit)
    then begin
        Res := cp_comp.Connect(cp_edit);
        if Res <> kResultOk then begin
            Showmessage(IntToStr(Res));
        end;
        cp_edit.Connect(cp_comp);
        if Res <> kResultOk then begin
            Showmessage(IntToStr(Res));
        end;
    end;

    // synchronize controller to component by using setComponentState
    BStream := TVST3Stream.Create;
    try
        //BStream.setByteOrder(kLittleEndian);
        if Component.GetState(BStream) = kResultTrue then begin
            BStream.Seek(0, kIBSeekSet);
            if EditController.SetComponentState(BStream) <> kResultTrue then begin
                Showmessage('Error: EditController.SetComponentState()');
            end;
        end;
    finally
        FreeAndNil(BStream);
    end;

    Component.ActivateBus(kAudio, kInput, 0, 1);
    Component.ActivateBus(kAudio, kOutput, 0, 1);
    Component.SetActive(1);

    PlugView := IPlugView(EditController.CreateView(PANSIChar(kEditor)));
    PlugView.GetSize(@ViewRect);
    PlugView.SetFrame(HostApplication);
    PanelHandle := Panel1.Handle;
    PlugView.Attached(Pointer(PanelHandle), kPlatformTypeHWND); //* CRASH HERE

end;

{ THostApplication }

function THostApplication.BeginEdit(tag: TParamID): TResult;
begin
    Result := kNotImplemented;
end;

function THostApplication.CreateInstance(cid, iid: TUID; var obj: pointer): TResult;
begin
    //* Don't know what to put here?
    Result := 0;
end;

function THostApplication.EndEdit(tag: TParamID): TResult;
begin
    Result := kNotImplemented;
end;

function THostApplication.GetName(name: PString128): TResult;
begin
    name^ := HostName;
    Result := kNotImplemented;
end;

function THostApplication.PerformEdit(tag: TParamID; valueNormalized: TParamValue): TResult;
begin
    Result := kNotImplemented;
end;

function THostApplication.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
    //* Not sure if this is needed at all
    if (IID = UID_IPlugFrame)
    OR (IID = UID_FUnknown)
    then begin
        Pointer(Obj) := Self;
		Result := kResultTrue;
    end else begin
        Result := inherited;
    end;
end;

function THostApplication.ResizeView(view: IPlugView; newSize: PViewRect): TResult;
begin
    //* TODO
    Result := 0;
end;

function THostApplication.RestartComponent(flags: int32): TResult;
begin
    Result := kNotImplemented;
end;

Thank you!