Einzelnen Beitrag anzeigen

Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#11

AW: C# Delegates in Delphi konsumieren

  Alt 18. Feb 2013, 16:23
Zitat:
Sieht gut aus oder ?
Nein, gar nicht.
Ein Event ist sowas wie eine Property. Man hat keinen Zugriff auf das Delegate, kann nur Handler hinzufügen oder entfernen (wenn man sie auch kennt!)
Der Hack auf der Seite führt macht doch das Deklarieren des Events als EVENT (nicht Property) komplett banane.
Das wirkt auf mich als hätte er solange rumprobiert, bis irgendwas ging...
Ein einfacher Event-Subscription-Mechanismus, der in .Net und anderen Umgebungen (wie Delphi) sehr gut funktioniert, sind Subscription wie man sie aus Java kennt: http://www.delphipraxis.net/173317-%...ml#post1204018
Außerdem musst du IDispatch nehmen, also bekommst du weder Code-Completion, noch Compiler-Meldungen wenn du dich vertippst.

Ich bleibe mal bei deinem Hundebeispiel...
Hier die Interfaces:
Code:
[ComVisible(true),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    Guid("22396704-484A-43CE-AA8D-0765DB317562")]
public interface INotifyEventHandler
{
    void Invoke([MarshalAs(UnmanagedType.IUnknown)] object sender);
}

[ComVisible(true),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
    Guid("5B127B2D-4433-43DA-87A6-28B8CBF725D7")]
public interface INotifyEvent
{
    void Add(INotifyEventHandler handler);
    void Remove(INotifyEventHandler handler);
}

[ComVisible(true),
    Guid("45A15623-E99C-466E-AE76-BFB4500CC900"),
    InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDog
{
    INotifyEvent Bark { get; }
    INotifyEvent Howl { get; }
    INotifyEvent Eat { get; }
    void PerformBark(int howOften);
}
Delphi-Quellcode:
INotifyEventHandler = interface(IUnknown)
  ['{22396704-484A-43CE-AA8D-0765DB317562}']
  procedure Invoke(const sender : IUnknown); safecall;
end;

INotifyEvent = interface(IUnknown)
  ['{5B127B2D-4433-43DA-87A6-28B8CBF725D7}']
  procedure Add(const handler: INotifyEventHandler); safecall;
  procedure Remove(const handler: INotifyEventHandler); safecall;
end;

IDog = interface(IUnknown)
['{45A15623-E99C-466E-AE76-BFB4500CC900}']
  function GetOnBark : INotifyEvent; safecall;
  function GetOnHowl : INotifyEvent; safecall;
  function GetOnEat : INotifyEvent; safecall;

  property OnBark : INotifyEvent read GetOnBark;
  property OnHowl : INotifyEvent read GetOnHowl;
  property OnEat : INotifyEvent read GetOnEat;

  procedure Bark(aHowOften : Integer); safecall;
end;
hier die Implmentierung in C#. Achte auf das "DllExport", um das zu kriegen musst du einen Rechtsklick auf dein Projekt machen, "manage Nuget Packages" und in dem Dialog suchst du nach dllexport (siehe screenie).

Code:
public class NotifyEvent : INotifyEvent
{
    private readonly ISet<INotifyEventHandler> _Handlers = new HashSet<INotifyEventHandler>();

    public void Add(INotifyEventHandler handler)
    {
        _Handlers.Add(handler);
    }

    public void Remove(INotifyEventHandler handler)
    {
        _Handlers.Remove(handler);
    }

    public void Invoke(object sender)
    {
        var handlersCopy = _Handlers.ToList();
        handlersCopy.ForEach(h => h.Invoke(sender));
    }
}

public class CSharpDog : IDog
{
    public INotifyEvent Bark
    {
        get { return _Bark; }
    }

    public INotifyEvent Howl
    {
        get { return _Howl; }
    }

    public INotifyEvent Eat
    {
        get { return _Eat; }
    }

    readonly NotifyEvent _Bark = new NotifyEvent();
    readonly NotifyEvent _Howl = new NotifyEvent();
    readonly NotifyEvent _Eat = new NotifyEvent();

    public void PerformBark(int howOften)
    {
        for (int i = 0; i < howOften; i += 1)
        {
            _Bark.Invoke(this);
        }
    }

    [DllExport]
    static void CreateDog([MarshalAs(UnmanagedType.Interface)]out IDog dog)
    {
        dog = new CSharpDog();
    }
}
Auf der Delphi-Seit brauchen wir eine Implementierung für einen Eventhandler:

Delphi-Quellcode:
TNotifyEventHandler = class(TInterfacedObject, INotifyEventHandler)
private
  fCallback : TProc<IUnknown>;
protected
  procedure Invoke(const sender : IUnknown); safecall;
public
  constructor Create(aCallBack: TProc<IUnknown>); overload;
  constructor Create(aCallBack: TProc); overload;
end;

{ TNotifyEventHandler }

constructor TNotifyEventHandler.Create(aCallBack: TProc<IUnknown>);
begin
  fCallback := aCallBack;
end;

constructor TNotifyEventHandler.Create(aCallBack: TProc);
begin
  fCallback := procedure(sender : IUnknown) begin
    aCallBack();
  end;
end;

procedure TNotifyEventHandler.Invoke(const sender: IInterface);
begin
  fCallback(sender);
end;
Und das war's dann fast schon. Im C# Projekt musst du noch die CPU-Plattform passend zu deinem Delphi-Projekt (wohl x86) wählen.


Delphi-Quellcode:
procedure CreateDog(out dog : IDog); stdcall; external 'DeineCSharpClassLibrary';

var
  dog : IDog;
  barkCounter : Integer;
begin
  CoInitialize(nil);
  CreateDog(dog);
  barkCounter := 0;

  dog.OnBark.Add(TNotifyEventHandler.Create(procedure begin
      inc(barkCounter);
  end));

  dog.Bark(6);
  dog.Bark(2);

  Writeln('dog barked ', barkCounter, ' times...');
end.
Zitat von Ouput:
dog barked 8 times...
In der Tat, ja. Dann bleiben Dir komplizierte Nicht-COM Lösungen erspart.
Genau, weil Com-Registrierungen auch immer problemlos funzen und jeder sich so gut mit SxS auskennt, dass auch wirklich jeder alles ohne globale Registrierung lösen kann...

Sicherlich ist COM/Interop ein sehr guter Weg für viele, gerade komplexere, Szenarios. aber COM ist auch saumäßig frickelig und kommt mit seinen eigenen Problemen daher. Zum Glück kann man fast alle COM-Goodies aus .Net ohne COM benutzen. Macht C++/CLI die meiste Zeit über auch nicht anders.

Einfach so zu behaupten, dass man keine RCWs über Reverse P/Invoke kriegt, oder es nur als Notlösung taugt, halte ich für ein Indiz einer sehr dogmatische Sichtweise. Ich kann mir kaum vorstellen, dass du wirklich so engstirnig bist.
Miniaturansicht angehängter Grafiken
untitled.png  
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”
  Mit Zitat antworten Zitat