[C#] DirectInput gebruiken in C# met custom DeviceDataFormat

Pagina: 1
Acties:
  • 167 views sinds 30-01-2008
  • Reageer

  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
Hallo,

Ik probeer via DirectInput de Hercules DJ Console in C# te gebruiken. Na lang zoeken ben ik er een hele tijd geleden al in geslaagd om dit in Visual Basic 6 te doen, maar nu ik het in C# probeer lukt het nog niet helemaal.
Het probleem is ook dat er op msdn geen informatie over deze functies te vinden is, en dat zoeken erg weinig resultaten oplevert (er wordt zowat altijd vanuit gegaan dat het keyboard, muis of joystick is die je wil gebruiken, en deze zijn door een aantal andere standaardfuncties aan te spreken)

Wat er al wel lukt is de lijst met apparaten doorlopen, daarin de DJ Console vinden, en vervolgens een DirectInput.Device object aanmaken.
Ook setEventNotification gebruiken zodat ik een callback krijg als er een slider bewogen wordt of een knop ingedrukt lukt, alleen kan ik dus nog niet de toestand uitlezen zodat ik weet welke knop, en op welke waardes de sliders staan.

Vanuit mijn ervaring in VB6 weet ik dat ik SetDataFormat zal moeten gebruiken om eerst het juiste data formaat te specifieren, waarna ik normaalgezien via GetDeviceState de toestand zou moeten kunnen opvragen.

Het eerste probleem is SetDataFormat.
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df.DataSize = 116;
int nbControls = 69;
df.Flags = DataFormatFlags.AbsoluteAxis;
df.ObjectDataFormat = new ObjectDataFormat[nbControls];
int i = 0;
foreach (DeviceObjectInstance doi in device.GetObjects(DeviceObjectTypeFlags.All)) {
  if (i < 69) {
    df.ObjectDataFormat[i] = new ObjectDataFormat();
    df.ObjectDataFormat[i].Flags = doi.Flags;
    df.ObjectDataFormat[i].Offset = doi.Offset;
    df.ObjectDataFormat[i].DeviceType = doi.ObjectId;
    df.ObjectDataFormat[i].SourceGuid = doi.ObjectType;
   }
    i++;
}
device.SetDataFormat(df);

In Visual Basic was er voor het DeviceType een functie GetType, maar GetType op zo'n DeviceObjectInstance levert een Type object op, wat niet het bedoelde DeviceType is.
Ik weet niet zeker of het hierdoor fout gaat, maar bij het oproepen van SetDataFormat krijg ik de foutmelding:
ArgumentException: Value does not fall within the expected range.
Update: Na eens de waardes voor GetType in Visual Basic te vergelijken met het doi object, blijkt dat DeviceType gevonden kan worden met doi.ObjectId.
Na deze aanpassing lukt SetDataFormat al. /Update

Het tweede probleem is dan het gebruiken van getDeviceState, want die heeft als argument een "Type", en geeft een "object" terug. Wat voor object ik hiervoor moet geven weet ik niet zeker.
In Visual Basic moest je gewoon een struct, en een lengte in bytes meegeven en de struct werd dan gevuld. In C# kan je onder andere al geen type maken met een fixed array in (tenzij je unsafe werkt, maar dan heeft het weinig nut om de Managed DirectX sdk te gebruiken lijkt me...)

De documentatie die ik op msdn vindt ziet er zo uit dus daar kan ik duidelijk weinig mee.

Als iemand hiermee al ervaring heeft, of misschien een zet in de goede richting kan geven hoor ik het graag.

[ Voor 5% gewijzigd door Adion op 10-04-2006 15:50 ]

VirtualDJ 2026 - Fast Image Resizer - Instagram


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
Niemand?

In Visual Basic ziet de structure die ik wil terugkrijgen er zo uit:
Visual Basic:
1
2
3
4
5
6
Private Type tDJC
    wheels(14) As Long
    buttons(29) As Byte
    leds(23) As Byte
    padding As Long
End Type

Ik heb echter niet echt een idee hoe ik dit in C# moet doen, en wat ik hoe ik dan aan dat 'Type' object kom.

Update : Ik ben er nu in principe al uit op de unsafe manier.
Met behulp van een structure met de juiste grootte (dus ook 116 bytes zoals opgegeven bij het dataformat) lukt het wel.
De structure ziet er dus als volgt uit:
C#:
1
2
3
4
5
6
unsafe private struct tDJC {
    fixed int wheels[15];
    fixed byte buttons[30];
    fixed byte leds[24];
    short padding;
}

Het zou dus alleen nog interessant zijn moest ik dit nu ook volledig met 'safe' code zou kunnen doen.

[ Voor 43% gewijzigd door Adion op 11-04-2006 20:17 ]

VirtualDJ 2026 - Fast Image Resizer - Instagram


Verwijderd

Kun je die fixed modifier niet gewoon weglaten? Dan kun je die struct ook gewoon in een safe context gebruiken. De fixed modifier geeft bij mijn weten aan dat een bepaald object/stuk geheugen tijdens een interop/pinvoke handeling niet verplaatst mag worden (door de CLR, iirc), dus dat lijkt me vrij zinloos in deze context.

Waarom gebruik je trouwens een C# int ipv de Long in VB6 (wheels veld) en een C# short ipv die 2e long (padding veld)? Ik weet de bytegroottes van die types in VB6 niet, maar het lijkt me niet dat die VB6 long en de C# short (padding veld) zomaar uitwisselbaar zijn.

[ Voor 35% gewijzigd door Verwijderd op 11-04-2006 21:54 . Reden: Iets gezien :) ]


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
Als fixed weggelaten wordt kan je geen array met vooraf bepaalde grootte definieren, en ik denk dat dat wel nodig is om de struct op de juiste manier gevuld te krijgen.

In VB6 is een Long 32-bits, dus dat hoort een int te worden in c#.
De reden dat ik in c# een short gebruik voor de padding, is om in totaal aan de 116 bytes te komen die ik opgeef bij de constructie.
De VB6 structure is in feite 2 bytes te groot, maar omdat daar nog gewoon pointers werden doorgegeven maakte dat geen verschil.
In C# merkt hij blijkbaar echter dat het opgegeven type niet de juiste grootte heeft waardoor hij weigert dat te vullen (met de foutboodschap die ik in m'n eerste post vermelde)

VirtualDJ 2026 - Fast Image Resizer - Instagram


Verwijderd

Ahja, ik zie het hier nu ook net in de docs staan. Sorry voor de verwarring, dat gebruik van het fixed keyword kon ik nog niet. Weer wat geleerd :)

Ik heb pas 1 keer wat met die custom data formats geprutst voor DirectInput hooking en daaruit kwamen vooral crashende applicaties, dus ik ben bang dat ik je niet echt verder kan helpen. Maar wat is nu precies het probleem? Werkt het wel met unsafe C# code? Dat hoeft toch geen probleem te zijn, de helft van m'n image/texture copies gebruiken ook unsafe code, net zoals bijvoorbeeld het DirectX SampleFramework voor C# van Microsoft, dus unsafe code is zeker geen schande ;)

  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 14-02 12:54
Als het nodig is de volgorde van de struct af te dwingen, kan dat (in ieder geval) ook op de volgende manier:
C#:
1
2
3
4
5
6
7
8
9
10
[Serializable()]
[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
    public int vkCode;
    public int scanCode;
    public int flags;
    public int time;
    public IntPtr dwExtraInfo;  
}


Als je de offset van de variabelen wilt opgeven, kan dat op de volgende manier. Zo kan je variabelen ook elkaar laten overlappen.
C#:
1
2
3
4
5
6
7
8
[StructLayout(LayoutKind.Explicit)] 
public struct INPUT
{
    [FieldOffset(0)] public int type;
    [FieldOffset(4)] public MOUSEINPUT mi;
    [FieldOffset(4)] public KEYBDINPUT ki;
    [FieldOffset(4)] public HARDWAREINPUT hi;
}

Zoals je kan zien komen mijn voorbeelden uit de P/Invoke met de Win32 API. Weet niet wat het voordeel ten opzichte van fixed is.

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
De bedoeling van fixed is niet dat de plaats vast staat, maar gewoon dat je de mogelijkheid hebt om arrays met vaste grootte te maken.
Ik denk wel dat ik het zou kunnen oplossen door er bijvoorbeeld int wheel0, int wheel1, int wheel2, ... van te maken, maar dan is de code om dat allemaal na te kijken nodeloos 15 keer dezelfde regel.

VirtualDJ 2026 - Fast Image Resizer - Instagram


Verwijderd

Zoals je kan zien komen mijn voorbeelden uit de P/Invoke met de Win32 API. Weet niet wat het voordeel ten opzichte van fixed is.
Zo te zien kun je met die "explicit" waarde voor het StructLayout attribuut je struct gewoon in safe code gebruiken, dus daarmee heeft het schijnbaar wel een voordeel tov. het fixed keyword.

Maar goed, wat is dan nou nog precies het probleem? Het ligt ongetwijfeld aan mij, maar door al die updates zie ik door de bomen het bos niet meer :)

[ Voor 5% gewijzigd door Verwijderd op 12-04-2006 12:31 . Reden: zpelling ]


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
Na wat opzoeken over die StructLayout enzo kwam ik tot volgende mogelijke oplossing:

C#:
1
2
3
4
5
6
7
8
9
10
11
[StructLayout(LayoutKind.Sequential)]
public struct tDJC
{
    [MarshalAs (System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=15)]
    public int[] wheels;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=30)]
    public byte[] buttons;
    [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)]
    public byte[] leds;
    short padding;
}

Van die size ben ik nog niet zeker of ze nu in bytes moet of in aantal elementen in de array, maar bij het stukje waar ik de struct wil gebruiken:
C#:
1
djConsoleState = (tDJC)device.GetDeviceState(djConsoleState.GetType());

Krijg ik nu volgende foutboodschap:
code:
1
ArgumentException: Object contains non-primitive or non-blittable data.

Zelfs met die constante arrays lijkt het dus niet mogelijk om daar zomaar in te schrijven voor die getDeviceState.

VirtualDJ 2026 - Fast Image Resizer - Instagram


Verwijderd

Hmm, ik ben bang dat het zo inderdaad niet gaat werken... Ik ken niet alle ins en outs van Mashalling, maar die arrays die je hebt gedefinieerd zijn gewoon niet fixed size, dus niet blittable (iirc). -snip- weer niet goed gelezen :X :)

Wat was er mis met die unsafe struct waarbij je fixed gebruikte? Dat leek me toch nog de beste oplossing... Unsafe code is misschien minder elegant, maar voor dit soort toepassingen kom je er denk ik niet onder uit.

[ Voor 24% gewijzigd door Verwijderd op 12-04-2006 17:40 ]


  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

C#:
1
djConsoleState = (tDJC)device.GetDeviceState(typeof(tDJC));


bovendien raad ik je aan de stukken in de MSDN over marshalling eens heel goed te lezen.

wat jij nu hebt kan niet...

de struct die jij hebt is in realiteit 14byte waarvan 3 voor pointers. het type dat je nodig hebt is 116byte zoals je aangeeft...

[ Voor 62% gewijzigd door H!GHGuY op 12-04-2006 19:28 ]

ASSUME makes an ASS out of U and ME


  • Adion
  • Registratie: Januari 2001
  • Laatst online: 22-02 11:35
Ik moet inderdaad toegeven dat ik verder nog niet echt vertrouwd ben met Marshalling enzo. Doordat er ByValArray bijstond en dat ik deze code ook gevonden had op een forum waar ze het ook hadden over het declareren van structs met fixed size arrays dacht ik dat dit daar een goed alternatief voor zou zijn.

Ik kan inderdaad voorlopig wel unsafe verder, maar het is toch wel vreemd dat Microsoft eerst zelf een Managed interface voor directX uitbrengt, die je vervolgens niet volledig kan gebruiken zonder toch weer unsafe te gaan werken.
Momenteel lijken er ook nog niet zo'n grote nadelen aan unsafe werken, maar misschien zullen in de toekomst mensen die ff een shareware versie van iets downloaden het niet zomaar meer uitvoeren/standaard kunnen uitvoeren in windows als het geen managed code is?

VirtualDJ 2026 - Fast Image Resizer - Instagram


Verwijderd

Ik denk dat je je hier geen zorgen om hoeft te maken. Als je bijvoorbeeld de C++/CLI taal pakt voor het .NET platform, die is (afaik) per definitief unsafe. Unsafe code heeft verder weinig te betekenen voor eindgebruikers, het komt er eigenlijk gewoon op neer dat je pointers kun gebruiken.

Ik stond er zelf ook eerst van te kijken dat je soms unsafe code moet gebruiken in je Managed DirectX applicaties, maar voor sommige dingen is het gewoon de beste (en door MS aangeraden) manier, zoals mijn voorbeeld voor snelle toegang tot bitmap pixel data.
Pagina: 1