Autor Beitrag
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: So 25.06.17 06:51 
Hallo Forum

Problemstellung:
Wie in dem Thema hier beschrieben, ist eine C# Klasse gegeben, sowie eine Klasse vom Typ NativeWindow, die als "Windows-Nachrichten Vermittler" dient, d.h. ich benutze ihre WndProc() Methode, um auf Windows-Nachrichten zu reagieren.
Bei bestimmten Nachrichten sollen auch Ereignisse in Kraft treten, und so bin ich gezwungen, statische Ereignisse bereitzustellen:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
public class MainClass
{
    private WndClass _wndClass;
    public static event EventHandler DoFirst, DoSecond;
    
    public MainClass()
    {
        _wndClass = new WndClass();
    }

    public static void WndProc(ref Message m)
    {
            
        if (m.Msg == 537)
        {
            if ((ushort)m.WParam == 0x8000)
                OnDoFirst(new EventArgs());
            if ((ushort)m.WParam == 0x8004)
                OnDoSecond(new EventArgs());
        }
    }
   
    public static void OnDoFirst(EventArgs e)
    {
        EventHandler eh = DoFirst;
        if (eh != null)
            eh(typeof(EventHandler), e);
    }

    public static void OnDoSecond(EventArgs e)
    {
        EventHandler eh = DoSecond;
        if (eh != null)
            eh(typeof(EventHandler), e);
    }
}

class WndClass : NativeWindow 
{
    public WndClass() 
    {
        CreateParams Params = new CreateParams();
        CreateHandle(Params);
    }

    protected override void WndProc(ref Message m)
    {
        MainClass.WndProc(ref m);  // statische WndProc Methode
        base.WndProc(ref m);
    }
}

Statische Methoden lassen sich nicht überschreiben oder "virtual" setzen, daher die Frage: Wie könnte ich dennoch weitere Ereignisse nachträglich setzen, oder noch besser ... hätte ich zumindest eine Möglichkeit auf die statischen Elemente zu verzichten und dennoch auf Windows-Nachrichten reagieren zu können?

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3741
Erhaltene Danke: 762

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: So 25.06.17 10:26 
Hallo,

das war mir schon bei deinem anderen Beitrag dazu aufgefallen: das ist kein gutes Design mit der statischen Methode MainClass.WndProc, da sich die beiden Klassen gegenseitig referenzieren.

Ich würde ein Event (bzw. Delegat) in WndClass deklarieren und mittels der MainClass daran binden (und diese kann dann wiederum eine virtuelle Methode aufrufen).
Hier (in Kürze) der Code dazu:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
public class MainClass
{
    public MainClass()
    {
        _wndClass = new WndClass();
        _wndClass.OnWndProc = OnWndProc;
    }

    protected void OnWndProc(ref Message m)
    {
        WndProc(ref m);
    }

    protected virtual void WndProc(ref Message m)
    {
        // ...
    }
}

class WndClass : NativeWindow 
{
    public delegate void WndProcDelegate(ref Message m);

    public WndProcDelegate OnWndProc;

    protected override void WndProc(ref Message m)
    {
        OnWndProc?.Invoke(ref m);

        base.WndProc(ref m);
    }
}

(falls dir ?. nichts sagt, dies ist ab C# 6 möglich: Null-Conditional Operator - ansonsten einfach wie in deinen bisherigen OnDo...-Methoden aufrufen)

Und die MainClass-Ereignisse dann nicht-statisch deklarieren.

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 26.06.17 01:26 
Besten Dank Th69! Das hast du sehr schlau und einfach gelöst :!: Der ?-Operator ist mir bekannt. Danke dennoch für den verlinkten Beitrag.
Die Zeile werde ich anpassen müssen, da mein Projekt auf dem .NET Framework 3.5 basiert und ich nicht weiss, ob und wie es mit dem Operator klar kommen wird, daher:

ausblenden C#-Quelltext
1:
2:
3:
4:
OnWndProc?.Invoke(ref m);
// wird zu
if (OnWndProc!= null)
    OnWndProc.Invoke(ref m);

Ich möchte das Projekt bis zu einer gewissen Grenze, abwärtskompatibel halten.

Das Thema hat sich erledigt !!!

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3741
Erhaltene Danke: 762

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Mo 26.06.17 09:04 
Nur noch als Hinweis. Die C#-Version (also welchen Compiler du nutzt) hat nichts mit dem eingesetzten .NET-Framework zu tun...

Für diesen Beitrag haben gedankt: Frühlingsrolle
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20215
Erhaltene Danke: 2034

Win 10
C# (VS 2017)
BeitragVerfasst: Mo 26.06.17 09:10 
Und noch ein Hinweis :D

Die korrekte Umsetzung des ?-Operators ist:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
OnWndProc?.Invoke(ref m);
// wird zu
var tmp = OnWndProc;
if (tmp != null)
    tmp.Invoke(ref m);

Dann ist es auch thread-safe ;)

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 26.06.17 09:22 
Hinweise sind immer gut! :zustimm:
Ok, wenn du es schon ansprichst: Warum ist es anders herum(wie bei mir) nicht thread sicher?

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Chefentwickler
Beiträge: 20215
Erhaltene Danke: 2034

Win 10
C# (VS 2017)
BeitragVerfasst: Mo 26.06.17 09:33 
Ohne die temporäre Variable könnte zwischen der if-Abfrage und dem Invoke ein anderer Thread das Feld nullen und es würde eine NullReferenceException geben. Durch die temporäre, lokale Variable kann ein anderer Thread nicht mehr zugreifen.

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".

Für diesen Beitrag haben gedankt: Frühlingsrolle
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 3741
Erhaltene Danke: 762

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Mo 26.06.17 09:34 
Wenn genau zwischen den 2 Zeilen ein Threadwechsel stattfindet und dann die interne Eventmethoden-Liste geändert wird (im schlimmsten Fall das letzte Element gelöscht wird => Liste ist dann doch null => NullReferenceException).
Ob man das Event-De-/Abonnieren aber aus verschiedenen Threads überhaupt verwenden sollte, ist für mich eher designtechnisch zu hinterfragen.

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Mo 26.06.17 12:06 
Habt Dank Christian S. und Th69! Das gibt mir doch etwas zu denken, ob nicht auch andere Delphi und C# Projekte davon betroffen sind.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4263
Erhaltene Danke: 851


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mo 26.06.17 20:10 
Zitat:
Das gibt mir doch etwas zu denken, ob nicht auch andere Delphi und C# Projekte davon betroffen sind.


Das gilt eigentlich in jedem System. Sobald etwas prüfen und dann etwas mit dem geprüften tun 2 Dinge (nicht atomar) sind hat man ein potentielles Threading Problem.

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Di 27.06.17 07:47 
Gilt das auch, wenn man einem Paramter keinen "fixen" Wert mitgibt, sondern stattdessen etwas mit new ... übergibt?

ausblenden C#-Quelltext
1:
2:
3:
4:
void Beispiel(string[] s, int i);

// Aufruf
Beispiel(new string[] { "a, b" }, 3);

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4263
Erhaltene Danke: 851


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 27.06.17 09:38 
Die einzige Referenz auf das neu erzeugte Array hat jetzt die Beispiel Methode insofern ist das potentiell ungefährlich.

Denn Zusammenhang zum vorher besprochenen verstehe ich aber nicht ganz :gruebel:

Für diesen Beitrag haben gedankt: Frühlingsrolle
Frühlingsrolle Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2008
Erhaltene Danke: 368

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Di 27.06.17 09:55 
Meine Überlegung war, wenn im Ernstfall eine Threading Klasse im Spiel wäre, und es würde sich eine Methode anbieten die als Parameter nicht unbedingt ein Array, aber ein Objekt verlangt, welches ich noch nicht erzeugt bzw. an eine Variable übergeben habe, dann hätte es sein können, dass ein new ... für den besagten Paramter ebenso wenig thread-sicher ist.
So dachte ich es mir zumindest.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)