///////////////////////////// Joystick control panel program ///////////////////////////// a) When unloading conditional effects in joy.cpl, the program crashes: When unloading the conditional effect array entries, Wine tries to access an invalid (high) address because dieffect.lpvTypeSpecificParams was not set at the time the effect was created. So declare a valid 'standard' (maxed out) conditional effect. For other unknown effects: - The same can happen when CustomForce effects are available, but because Wine's FF dinput->linux translation does not support this yet, we simply print a FIXME to indicate a possible cause if a crash would happen. - The same for other (unsupported) effects (this is probably not safe?), but then we print a WARN. => This is probably not safe, but these effects won't be unloaded, because they will never be allowed to be created: I tested this by using a dummy FF device that advertised to support CustomForce, but because JoystickAImpl_EnumEffects() does not handle CustomForce, no CustomForce entry was generated in the joy.cpl application, and there was no crash at exit. Solution: After the code of joy.cpl/main.c:766 : else if (IsEqualGUID(&pdei->guid, &GUID_Sine) || IsEqualGUID(&pdei->guid, &GUID_Square) || IsEqualGUID(&pdei->guid, &GUID_Triangle) || IsEqualGUID(&pdei->guid, &GUID_SawtoothUp) || IsEqualGUID(&pdei->guid, &GUID_SawtoothDown)) { DIPERIODIC pforce; pforce.dwMagnitude = DI_FFNOMINALMAX; pforce.lOffset = 0; pforce.dwPhase = 0; pforce.dwPeriod = FF_PERIOD_TIME; dieffect.cbTypeSpecificParams = sizeof(pforce); dieffect.lpvTypeSpecificParams = &pforce; dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS; } add the following code : else if (IsEqualGUID(&pdei->guid, &GUID_Spring) || IsEqualGUID(&pdei->guid, &GUID_Damper) || IsEqualGUID(&pdei->guid, &GUID_Inertia) || IsEqualGUID(&pdei->guid, &GUID_Friction)) { DICONDITION cxyforce[2]; int i; for (i = 0; i < 2; ++i) { cxyforce[i].lOffset = 0; cxyforce[i].lPositiveCoefficient = DI_FFNOMINALMAX; cxyforce[i].lNegativeCoefficient = DI_FFNOMINALMAX; cxyforce[i].dwPositiveSaturation = DI_FFNOMINALMAX; cxyforce[i].dwNegativeSaturation = DI_FFNOMINALMAX; cxyforce[i].lDeadBand = 0; } dieffect.cbTypeSpecificParams = sizeof(cxyforce); dieffect.lpvTypeSpecificParams = cxyforce; dieffect.dwFlags |= DIEP_TYPESPECIFICPARAMS; } else if (IsEqualGUID(&pdei->guid, &GUID_CustomForce)) { FIXME("Unhandled type specific parameters of CustomForce effect, this may lead to memory errors later on.\n"); } else WARN("Possibly unhandled type specific parameters of effect with guid %x.\n", pdei->guid); b) Weirdly, I had to add these commands that evaluates the DIEFFECT pointers at the end of each if-case, otherwise the pointers seemed to point to other address or data on that address was getting overwritten: TRACE("&rforce=0x%x\n", &rforce); TRACE("&cforce=0x%x\n", &cforce); TRACE("&pforce=0x%x\n", &pforce); TRACE("&cxyforce[0]=0x%x; &cxyforce[1]=0x%x\n", &(cxyforce[0]), &(cxyforce[1]));