Autor Beitrag
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2292
Erhaltene Danke: 420

[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: Do 13.09.18 07:30 
Hallo Forum

Problemstellung:
Ich möchte einer Methode einen Parameter mitgeben, der eine Variable einer x-beliebigen Struktur (struct) aufnehmen soll, jedoch fällt mir kein passender Typ dafür ein:
ausblenden C#-Quelltext
1:
CallingMethod(ref ? someStruct);					

Wende ich an der Stelle den Typ object an und caste es mit dem jeweiligen Typ der dafür vorgesehenen Struktur, kommt die Meldung - Ein ref- oder out-Argument muss eine zuweisbare Variable sein.
Wie könnte man das nun umsetzen?

_________________
„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)
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 455
Erhaltene Danke: 85

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 13.09.18 07:42 
Ein Generic geht nicht ?

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.

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

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Do 13.09.18 08:07 
Und diese Generic-Methode könntest du dann mittels where T : struct passend einschränken (s.a. Einschränkungen für Typparameter).

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

[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: Do 13.09.18 08:45 
Vielen Dank für die Hinweise, OlafSt und Th69!
Bräuchte ich dafür nicht auch eine generische Klasse? Wenn ja, dann geht das nicht auf dem Wege.
Der Parameter ist an sich ein untypisierter Zeiger (void*) und das versuche ich irgendwie ohne unsafe zu lösen.
Und wenn das, wie gedacht, auch nicht geht, werde ich wohl doch auf unsafe zurückgreifen müssen.
Am Abend kann ich genauer darauf eingehen.

_________________
„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: 3995
Erhaltene Danke: 820

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Do 13.09.18 09:16 
Nein, die Klasse muß nicht zwangsläufig generisch sein.

Was meinst du aber jetzt mit "untypisierter Zeiger (void*)"? Zeige mal am besten ein Beispiel.

Für diesen Beitrag haben gedankt: Frühlingsrolle
OlafSt
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 455
Erhaltene Danke: 85

Win7, Win81, Win10
Tokyo, VS2017
BeitragVerfasst: Do 13.09.18 09:26 
Eine generische Klasse braucht es nicht. Eine List<T>, später als List<integer> instanziiert, hat auch keinen generischen Integer ;)

_________________
Lies, was da steht. Denk dann drüber nach. Dann erst fragen.

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

[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: Do 13.09.18 13:36 
Der untypisierte Zeiger zeigt allgemein auf die Adresse von Irgendetwas, wie z.B. hier bei mir auf eine Struktur, sinngemäß:

ausblenden C++-Quelltext
1:
2:
SOMESTRUCT someStruct = new SOMESTRUCT();
void* somePointer = &someStruct;

Konkret geht es mir um eine Übersetzung, die ich hier schon in Delphi umgesetzt habe und nun auch in C# umgesetzt bekommen haben möchte.

Beispiel: capDriverGetCaps macro hat folgendes geschrieben:

ausblenden C++-Quelltext
1:
2:
3:
4:
5:
void capDriverGetCaps(
   hwnd,
   psCaps,
   wSize
);

hwnd
Handle to a capture window.
psCaps
Pointer to the CAPDRIVERCAPS structure to contain the hardware capabilities.
wSize
Size, in bytes, of the structure referenced by psCaps.

Die anderen Makros sind überwiegend so aufgebaut und verlangen, je nachdem, eine andere Struktur.
In den Makros wird dabei die Funktion SendMessage() ausgeführt, die sinngemäß folgendes übergeben bekommt:

ausblenden Quelltext
1:
SendMessage(HANDLE, SOME_CAP_MESSAGE, SIZE_OF_STRUCT, POINTER_TO_STRUCT);					

In Delphi habe ich mehrere überladene Versionen von SendMessage() vorliegen, um mir die zahlreichen Casts zu ersparen, darunter auch:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
const
  {$IFDEF UNICODE}
  SEND_MESSAGE = 'SendMessageW';
  {$ELSE}
  SEND_MESSAGE = 'SendMessageA';
  {$ENDIF}

function SendMsgB(hWnd: HWND; Msg: DWord; wParam: NativeInt;
  lParam: Pointer): Boolean; overload;
  stdcallexternal 'User32.dll' name SEND_MESSAGE;

Das Makro sieht in Delphi wie folgt aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
  PCAPDRIVERCAPS = ^CAPDRIVERCAPS;
  CAPDRIVERCAPS = record
  // ...
  end;

const
  WM_CAP_START                    = $400;  // 0x400
  WM_CAP_DRIVER_GET_CAPS          = WM_CAP_START + 14;

function capDriverGetCaps(hwnd: HWND; psCaps: PCAPDRIVERCAPS;
  wSize: NativeInt): Boolean;
begin
  result := SendMsgB(hwnd, WM_CAP_DRIVER_GET_CAPS, wSize, psCaps);
end;

Genau das möchte ich nun umsetzen! Ich benötige für den lParam-Parameter in der SendMessage() Funktion einen allgemeinen Typ, der die jeweilige Struktur akzeptiert, wenn ich schon in C# nicht direkt mit Zeigern arbeite.

Ansonsten könnte ich auch so vorgehen und jedes Mal eine neue Überladung bereitstellen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern bool SendMsgB(IntPtr hWnd, int Msg, int wParam, ref CAPDRIVERCAPS lParam);

public static bool capDriverGetCaps(IntPtr hwnd, ref CAPDRIVERCAPS psCaps, int wSize)
{
    return SendMsgB(hwnd, WM_CAP_DRIVER_GET_CAPS, wSize, ref psCaps);
}

Das artet dann aber ganz schön aus, und das gilt es zu vermeiden, wenn's geht.

_________________
„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: 3995
Erhaltene Danke: 820

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Do 13.09.18 14:48 
Einfach so:
ausblenden C#-Quelltext
1:
2:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern bool SendMsgB<T>(IntPtr hWnd, int Msg, int wParam, ref T lParam) where T : struct;

Und der Aufruf dazu:
ausblenden C#-Quelltext
1:
2:
3:
4:
public static bool capDriverGetCaps(IntPtr hwnd, ref CAPDRIVERCAPS psCaps, int wSize)
{
  return SendMsgB(hwnd, WM_CAP_DRIVER_GET_CAPS, wSize, ref psCaps);
}

s.a. Ideone-Code (wobei dabei DllImportAttribute sowie CAPDRIVERCAPS nur Fakes sind).

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

[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: Fr 14.09.18 00:10 
Vielen Dank für die Hilfestellung.
Dass man es so auch auf Methoden anwenden kann, wusste ich nicht.
Ich bekomme an der Stelle eine TypeLoadException, wenn ich die generische SendMessage() Funktion anwende.
Mit dem kleinen Testprojekt ist es ersichtlicher, benötigt dafür eine angeschlossene Webcam:

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:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        IntPtr hCam = IntPtr.Zero;

        private void button1_Click(object sender, EventArgs e)
        {
            hCam = VfW.capCreateCaptureWindow("MyCam", VfW.WS_CHILD | VfW.WS_VISIBLE, 00300225, Handle, 0);
            try
            {
                VfW.capDriverConnect(hCam, 0);
                VfW.CAPDRIVERCAPS cap = new VfW.CAPDRIVERCAPS();
                VfW.capDriverGetCaps(hCam, ref cap, Marshal.SizeOf(cap)); // <-- TypeLoadException
                MessageBox.Show(cap.wDeviceIndex.ToString());
            }
            finally
            {
                VfW.capDriverDisconnect(hCam);
                VfW.capDestroyCaptureWindow(hCam);
            }
        }
    }

    public static class VfW
    {
        // Video Capture Messages
        private const int WM_CAP_START = 0x400;
        private const int WM_CAP_DRIVER_CONNECT = WM_CAP_START + 10;
        private const int WM_CAP_DRIVER_DISCONNECT = WM_CAP_START + 11;
        private const int WM_CAP_DRIVER_GET_CAPS = WM_CAP_START + 14;


        // Window Styles
        public const uint WS_VISIBLE = 0x10000000;
        public const uint WS_CHILD = 0x40000000;


        // Video Capture Structs
        [StructLayout(LayoutKind.Sequential)]
        public struct CAPDRIVERCAPS
        {
            public uint wDeviceIndex;
            public bool fHasOverlay,
                fHasDlgVideoSource,
                fHasDlgVideoFormat,
                fHasDlgVideoDisplay,
                fCaptureInitialized,
                fDriverSuppliesPalettes;
            public IntPtr hVideoIn,
                hVideoOut,
                hVideoExtIn,
                hVideoExtOut;
        }


        // Video Capture Functions
        [DllImport("Avicap32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr capCreateCaptureWindow(
            [MarshalAs(UnmanagedType.LPTStr)]
            string lpszWindowName,
            uint dwStyle,
            int x,
            int y,
            int nWidth,
            int nHeight,
            IntPtr hwndParent,
            int nID);


        // Additional Functions
        [DllImport("User32.dll", EntryPoint = "DestroyWindow")]
        public static extern bool capDestroyCaptureWindow(IntPtr hwnd);
        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        private static extern bool SendMsgB(IntPtr hWnd, int Msg, int wParam, int lParam);

        [DllImport("User32.dll", EntryPoint = "SendMessage")]
        //private static extern bool SendMsgB<T>(IntPtr hWnd, int Msg, int wParam, ref T lParam) where T : struct; // <-- TypeLoadException
        private static extern bool SendMsgB(IntPtr hWnd, int Msg, int wParam, ref CAPDRIVERCAPS lParam);  // OK


        // Video Capture Macros
        public static bool capDriverConnect(IntPtr hwnd, int iIndex)
        {
            return SendMsgB(hwnd, WM_CAP_DRIVER_CONNECT, iIndex, 0);
        }

        public static bool capDriverDisconnect(IntPtr hwnd)
        {
            return SendMsgB(hwnd, WM_CAP_DRIVER_DISCONNECT, 00);
        }

        public static bool capDriverGetCaps(IntPtr hwnd, ref CAPDRIVERCAPS psCaps, int wSize)
        {
            return SendMsgB(hwnd, WM_CAP_DRIVER_GET_CAPS, wSize, ref psCaps);
        }
    }
}

_________________
„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: 3995
Erhaltene Danke: 820

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Fr 14.09.18 08:17 
Puh, da scheint wohl P/Invoke nicht mit generischen Methoden umgehen zu können.

Dann bleibt wohl nur (ohne unsafe zu benutzen):
ausblenden C#-Quelltext
1:
2:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern bool SendMsgB(IntPtr hWnd, int Msg, int wParam, IntPtr lParam);

Und dann eine generische Methode erstellen, welche mittels Marshal.AllocHGlobal diesen IntPtr erstellt und nach dem Aufruf von SendMsgB dann Marshal.PtrToStructure aufruft (s.a. die Beispiele)

PS: Laß dich nicht verwirren von "This API is now obsolete.": dies bezieht sich nur auf die Methoden-Überladung mittels object (je nachdem also welche Framework-Version du nutzt, wird die passende Methode verwendet).

PPS: Die Größe der Struktur würde ich direkt in capDriverGetCaps vornehmen (und nicht wie du es bisher machst, jedesmal als Parameter übergeben).

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

[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: Fr 14.09.18 20:38 
Gehört zwischen .AllocHGlobal und .PtrToStructure nicht auch noch .StructureToPtr?

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
[DllImport("Kernel32.dll")]
public static extern void ZeroMemory(IntPtr Destination, int Length);

public static bool capDriverGetCaps(IntPtr hwnd, ref CAPDRIVERCAPS psCaps)
{
    int wSize = Marshal.SizeOf(psCaps);
    IntPtr hCap = Marshal.AllocHGlobal(wSize);
    ZeroMemory(hCap, Marshal.SizeOf(psCaps));

    Marshal.StructureToPtr(psCaps, hCap, true);
    SendMsgB(hwnd, WM_CAP_DRIVER_GET_CAPS, wSize, hCap);
    psCaps = (CAPDRIVERCAPS)Marshal.PtrToStructure(hCap, typeof(CAPDRIVERCAPS));
    Marshal.FreeHGlobal(hCap);
    return true;  // an der Stelle muss ich mir etwas überlegen bzw. eine "void" daraus machen.
}

_________________
„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: 3995
Erhaltene Danke: 820

Win7
C++, C# (VS 2015/17)
BeitragVerfasst: Sa 15.09.18 07:53 
Ja stimmt, zumindestens wenn es auch ein Input-Parameter sein soll. Bei capDriverGetCaps bin ich jedoch von einem reinen Output(out)-Parameter ausgegangen.
Aber als generelle Methode sicherlich empfehlenswert.

Du könntest aber jetzt einfach 2 verschiedene generische Methoden anbieten, einmal mit ref und einmal mit out:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
public static bool SendMsgRef<T>(IntPtr hwnd, int msg, ref T param) where T : struct
{
    int wSize = Marshal.SizeOf(param);
    IntPtr hMem = Marshal.AllocHGlobal(wSize);
    // ZeroMemory(hMem, wSize); // sehe ich nicht als notwendig hier an (wegen anschließendem StructureToPtr)

    Marshal.StructureToPtr(param, hMem, true);
    bool result = SendMsgB(hwnd, msg, wSize, hMem);
    param = (T)Marshal.PtrToStructure(hMem, typeof(T));

    Marshal.FreeHGlobal(hMem);

    return result;
}

public static bool SendMsgOut<T>(IntPtr hwnd, int msg, out T param) where T : struct
{
  // wie oben, nur ohne StructureToPtr
}

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

[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: Sa 15.09.18 21:11 
Hab' vielen Dank, Th69!
Der Umgang mit Zeigern unter C# ist mir anhand des Beispiels viel klarer geworden. :beer:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
[DllImport("User32.dll", EntryPoint = "SendMessage")]
private static extern bool SendMsgB(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); 

private static bool SendMsgRef<T>(IntPtr hwnd, int msg, ref T param) where T : struct
{
    int wSize = Marshal.SizeOf(param);
    IntPtr hMem = Marshal.AllocHGlobal(wSize);

    Marshal.StructureToPtr(param, hMem, true);
    bool result = SendMsgB(hwnd, msg, wSize, hMem);
    param = (T)Marshal.PtrToStructure(hMem, typeof(T));

    Marshal.FreeHGlobal(hMem);

    return result;
}

public static bool capDriverGetCaps(IntPtr hwnd, ref CAPDRIVERCAPS psCaps)
{
    return SendMsgRef(hwnd, WM_CAP_DRIVER_GET_CAPS, ref psCaps);
}


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)