MogreFreeSL causes PInvokeStackImbalance while debugging

walterbing1

04-05-2011 12:25:58

I have a project using Mogre for graphics and MogreFreeSL for sound. I am using Visual Studio 2010 to develop my application, and I am compiling all of my managed libraries under .NET 4.0 Client Profile using the C# language.

If I run my program by running the executable file directly, if runs fine. If I run the program within Visual Studio 2010 without debugging (Ctrl+F5), it runs fine. But, if I debug the program in Visual Studio by pressing (F5), it crashes with a PInvokeStackImbalance, saying something like:

Managed Debugging Assistant 'PInvokeStackImbalance' has detected a problem in 'C:\path\to\my\program.exe'.
Additional Information: A call to PInvoke function 'FSLOgreCS!FSLOgreCS.FreeSL::fslInit' has unbalanced the stack.
This is likely because the managed PInvoke signature does not match the unmanaged target signature.
Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.


Although my application works fine when running by itself, I need a way to debug the rest of it. Any suggestions would be helpful.

P.S. To forum moderators - If this is in the wrong place, please move it.

Meharin

04-05-2011 18:35:19

I just ran into this! Are you sure it crashes, and doesn't just break into the debugger? I thought it was broken until I realized I could press continue and keep going.

Still, I wanted it fixed. For me, the problem went away by tweaking MogreFreeSL as follows and recompiling.

The culprit was a missing CallingConvention=CallingConvention.Cdecl on the [DllImport ...] attributes. I simply updated all the DllImport (search and replace, and then fixing a few non DllImport), and it seemed to work. (I don't know enough about P/Invoke and calling conventions to know if this is the proper solution, or just something that worked for me.) In case it helps, here's my copy of FreeSL.cs:


using System;
using System.Collections.Generic;
using System.Text;

namespace FSLOgreCS
{
using System;
using System.Runtime.InteropServices;

public sealed class FreeSL
{
// Sound System
public enum FSL_SOUND_SYSTEM
{
FSL_SS_EAX2, // EAX 2.0 (Direct Sound 3D)
FSL_SS_DIRECTSOUND3D, // Direct Sound 3D
FSL_SS_DIRECTSOUND, // Direct Sound
FSL_SS_NVIDIA_NFORCE_2, // nVidia nForce 2
FSL_SS_CREATIVE_AUDIGY_2, // Creative Audigy 2
FSL_SS_MMSYSTEM, // Microsoft
FSL_SS_ALUT, // ALUT

FSL_SS_NOSYSTEM // no sound system
};

public enum AL_DISTANCE_MODEL
{
/// <summary>
/// Inverse distance rolloff model, which is equivalent to the IASIG I3DL2 model with the exception that referenceDistance does not imply any clamping.
/// <code>gain = referenceDistance / (referenceDistance + rolloffFactor * (distance - referenceDistance))</code>
/// </summary>
AL_INVERSE_DISTANCE = 90,

/// <summary>
/// Inverse Distance clamped model, which is essentially the inverse distance rolloff model, extended to guarantee that for distances below referenceDistance, gain is clamped. This mode is equivalent to the IASIG I3DL2 distance model.
/// </summary>
AL_INVERSE_DISTANCE_CLAMPED,

/// <summary>
/// Linear distance rolloff model, modeling a linear dropoff in gain as distance increases between the source and listener.
/// <code>gain = (1 - rolloffFactor * (distance - referenceDistance) / (maxDistance - referenceDistance))</code>
/// </summary>
AL_LINEAR_DISTANCE,

/// <summary>
/// Linear Distance clamped model, which is the linear model, extended to guarantee that for distances below referenceDistance, gain is clamped.
/// </summary>
AL_LINEAR_DISTANCE_CLAMPED,

/// <summary>
/// Exponential distance rolloff model, modeling an exponential dropoff in gain as distance increases between the source and listener.
/// <code>gain = (distance / referenceDistance) ** (- rolloffFactor)</code>
/// </summary>
AL_EXPONENT_DISTANCE,

/// <summary>
/// Exponential Distance clamped model, which is the exponential model, extended to guarantee that for distances below referenceDistance, gain is clamped.
/// </summary>
AL_EXPONENT_DISTANCE_CLAMPED,
};


[DllImport("FreeSL.dll", EntryPoint = "?fslInit@@YA_NW4FSL_SOUND_SYSTEM@@@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern bool fslInit(FSL_SOUND_SYSTEM val);

/// <summary>
/// Gets the current memory usage of all non-streaming sounds. This method does not seem to work properly.
/// </summary>
/// <returns></returns>
[DllImport("FreeSL.dll", EntryPoint = "?fslGetSoundMemoryUsage@@YAKXZ", CallingConvention=CallingConvention.Cdecl)]
public static extern ulong fslGetSoundMemoryUsage();

/// <summary>
/// Pauses all sounds currently playing.
/// </summary>
[DllImport("FreeSL.dll", EntryPoint = "?fslSoundPauseAllSounds@@YAXXZ", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSoundPauseAllSounds();

/// <summary>
/// Unpauses all sounds currently paused.
/// </summary>
[DllImport("FreeSL.dll", EntryPoint = "?fslSoundUnPauseAllSounds@@YAXXZ", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSoundUnPauseAllSounds();

/// <summary>
/// Stops all sounds currently playing.
/// </summary>
[DllImport("FreeSL.dll", EntryPoint = "?fslSoundStopAllSounds@@YAXXZ", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSoundStopAllSounds();

/// <summary>
/// Sets the speed of all sounds.
/// </summary>
/// <param name="pitch">Speed between 0 and 1 at which to play the sound.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetPitchAllSounds@@YAXM@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSoundSetSpeedAllSounds(float pitch);

/// <summary>
/// Sets the gain of all sounds.
/// </summary>
/// <param name="gain">Positive or negative gain to apply to all sounds.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetGainAllSounds@@YAXM@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSoundSetGainAllSounds(float gain);

/// <summary>
/// Sets parameters for the doppler effect.
/// </summary>
/// <param name="factor">Factor by which to scale the Doppler effect. 0 = off, 1 = normal.</param>
/// <param name="velocity">A leftover input variable from OpenAL 1.0. Default value is 1.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSetDopplerParameters@@YAXMM@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetDopplerParameters(float factor, float velocity);

/// <summary>
/// Sets speed of sound for use by the doppler effect.
/// </summary>
/// <param name="val">Speed of sound to assign. The default value is 343.3.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSetSpeedOfSound@@YAXM@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetSpeedOfSound(float val);

/// <summary>
/// Sets the distance-based sound attenuation model. Controls how the gain of sound sources is affected by distance from the listener.
/// </summary>
/// <param name="model">Model to apply as the distance sound attenuation model. The default attenuation model is AL_INVERSE_DISTANCE_CLAMPED.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerDistanceModel@@YAXI@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetListenerDistanceModel(AL_DISTANCE_MODEL model);

[DllImport("FreeSL.dll", EntryPoint = "?fslShutDown@@YAXXZ", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslShutDown();

[DllImport("FreeSL.dll", EntryPoint = "?fslSetVolume@@YAXM@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetVolume(float gain_mult);

[DllImport("FreeSL.dll", EntryPoint = "?fslFreeSound@@YAXI_N@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslFreeSound(uint obj, bool remove_buffer);

[DllImport("FreeSL.dll", EntryPoint = "?fslLoadSound@@YAIPBD@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern uint fslLoadSound(string strFile);

[DllImport("FreeSL.dll", EntryPoint = "?fslStreamSound@@YAIPBD@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern uint fslStreamSound(string strFile);

[DllImport("FreeSL.dll", EntryPoint = "?fslLoadSoundFromZip@@YAIPBD0@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern uint fslLoadSoundFromZip(string strPackage, string strFile);

/// <summary>
/// Enables or disables AutoUpdate, which allows streaming to work.
/// </summary>
/// <param name="auto">Whether to use AutoUpdate.</param>
[DllImport("FreeSL.dll", EntryPoint = "?fslSetAutoUpdate@@YAX_N@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetAutoUpdate(bool auto);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundPlay@@YAXI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundPlay(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundRewind@@YAXI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundRewind(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundStop@@YAXI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundStop(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundPause@@YAXI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundPause(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundIsPlaying@@YA_NI@Z", CallingConvention=CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool fslSoundIsPlaying(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundIsPaused@@YA_NI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern bool fslSoundIsPaused(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetLooping@@YAXI_N@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetLooping(uint obj, bool loop_sound);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundIsLooping@@YA_NI@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern bool fslSoundIsLooping(uint obj);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetPitch@@YAXIM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetSpeed(uint obj, float pitch);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetGain@@YAXIM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetGain(uint obj, float gain);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetSourceRelative@@YAXI_N@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetSourceRelative(uint obj, bool is_relative);

[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerPosition@@YAXMMM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSetListenerPosition(float x, float y, float z);

[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerOrientation@@YAXMMMMMM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSetListenerOrientation(float atx, float aty, float atz, float upx, float upy, float upz);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetPosition@@YAXIMMM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetPosition(uint obj, float x, float y, float z);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetMaxDistance@@YAXIM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetMaxDistance(uint obj, float max_distance);

[DllImport("FreeSL.dll", EntryPoint = "?fslSoundSetReferenceDistance@@YAXIM@Z", CallingConvention=CallingConvention.Cdecl)]
internal static extern void fslSoundSetReferenceDistance(uint obj, float ref_distance);


// Listener Environments
public enum FSL_LISTENER_ENVIRONMENT : int
{
FSL_ENVIRONMENT_GENERIC,
FSL_ENVIRONMENT_PADDEDCELL,
FSL_ENVIRONMENT_ROOM,
FSL_ENVIRONMENT_BATHROOM,
FSL_ENVIRONMENT_LIVINGROOM,
FSL_ENVIRONMENT_STONEROOM,
FSL_ENVIRONMENT_AUDITORIUM,
FSL_ENVIRONMENT_CONCERTHALL,
FSL_ENVIRONMENT_CAVE,
FSL_ENVIRONMENT_ARENA,
FSL_ENVIRONMENT_HANGAR,
FSL_ENVIRONMENT_CARPETEDHALLWAY,
FSL_ENVIRONMENT_HALLWAY,
FSL_ENVIRONMENT_STONECORRIDOR,
FSL_ENVIRONMENT_ALLEY,
FSL_ENVIRONMENT_FOREST,
FSL_ENVIRONMENT_CITY,
FSL_ENVIRONMENT_MOUNTAINS,
FSL_ENVIRONMENT_QUARRY,
FSL_ENVIRONMENT_PLAIN,
FSL_ENVIRONMENT_PARKINGLOT,
FSL_ENVIRONMENT_SEWERPIPE,
FSL_ENVIRONMENT_UNDERWATER,
FSL_ENVIRONMENT_DRUGGED,
FSL_ENVIRONMENT_DIZZY,
FSL_ENVIRONMENT_PSYCHOTIC,

FSL_ENVIRONMENT_COUNT
};

// Structs
[StructLayout(LayoutKind.Sequential)]
public struct FSL_EAX_LISTENER_PROPERTIES
{
public long lRoom; // room effect level at low frequencies
public long lRoomHF; // room effect high-frequency level re. low frequency level
public float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect
public float flDecayTime; // reverberation decay time at low frequencies
public float flDecayHFRatio; // high-frequency to low-frequency decay time ratio
public long lReflections; // early reflections level relative to room effect
public float flReflectionsDelay; // initial reflection delay time
public long lReverb; // late reverberation level relative to room effect
public float flReverbDelay; // late reverberation delay time relative to initial reflection
public ulong dwEnvironment; // sets all listener properties
public float flEnvironmentSize; // environment size in meters
public float flEnvironmentDiffusion; // environment diffusion
public float flAirAbsorptionHF; // change in level per meter at 5 kHz
public ulong dwFlags; // modifies the behavior of properties
};

[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerEnvironment@@YAXPAUFSL_EAX_LISTENER_PROPERTIES@@@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetListenerEnvironment(FSL_EAX_LISTENER_PROPERTIES lpData);

[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerEnvironmentPreset@@YAXW4FSL_LISTENER_ENVIRONMENT@@@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetListenerEnvironmentPreset(FSL_LISTENER_ENVIRONMENT type);

[DllImport("FreeSL.dll", EntryPoint = "?fslSetListenerDefaultEnvironment@@YAXXZ", CallingConvention=CallingConvention.Cdecl)]
public static extern void fslSetListenerDefaultEnvironment();

[DllImport("FreeSL.dll", EntryPoint = "?fslGetCurrentListenerEnvironment@@YA?AUFSL_EAX_LISTENER_PROPERTIES@@XZ", CallingConvention=CallingConvention.Cdecl)]
public static extern FSL_EAX_LISTENER_PROPERTIES fslGetCurrentListenerEnvironment();

[DllImport("FreeSL.dll", EntryPoint = "?fslLoadListenerEnvironment@@YA?AUFSL_EAX_LISTENER_PROPERTIES@@PBD@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern FSL_EAX_LISTENER_PROPERTIES fslLoadListenerEnvironment(string strFile);

[DllImport("FreeSL.dll", EntryPoint = "?fslLoadListenerEnvironmentFromZip@@YA?AUFSL_EAX_LISTENER_PROPERTIES@@PBD0@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern FSL_EAX_LISTENER_PROPERTIES fslLoadListenerEnvironmentFromZip(string strPackage, string strFile);

[DllImport("FreeSL.dll", EntryPoint = "?fslCreateListenerEnvironment@@YA?AUFSL_EAX_LISTENER_PROPERTIES@@PBDI@Z", CallingConvention=CallingConvention.Cdecl)]
public static extern FSL_EAX_LISTENER_PROPERTIES fslCreateListenerEnvironment(string strData, uint size);
}
}

walterbing1

04-05-2011 23:15:15

Sorry for the late response. When I got the error, if I pressed continue, it continued giving more PInvokeStackImbalances.

Thanks for the help with P/Invoke. The CallingConvention.Cdecl parameter worked perfectly.

I found another way in (I think .NET 4.0 only) to ensure that inconsistencies in P/Invoke calling conventions do not cause stack imbalances. What needed to be done was create an "app.config" file for the project and add the following code:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<NetFx40_PInvokeStackResilience enabled="1" />
</runtime>
</configuration>


However, I don't think this works for other versions of the framework, so the CallingConvention.Cdecl parameter would have to be used there.

Thanks again!

Meharin

06-05-2011 01:22:21

Glad I could help!

BTW, I think BitBucket (and Mercurial) is cool beans (and I don't have write access to mogreaddons) so I forked mogreaddons and uploaded my changes and sent some maintainers a pull-request. Here's my fork: https://bitbucket.org/JaredThirsk/mogreaddons-jared.

Sorry for the late response.

I think that on this forum a few hours is fast response :). If not, I will have a lot of apologizing to do.


I found another way in (I think .NET 4.0 only) to ensure that inconsistencies in P/Invoke calling conventions do not cause stack imbalances. What needed to be done was create an "app.config" file for the project and add the following code:

...<NetFx40_PInvokeStackResilience enabled="1" />...

However, I don't think this works for other versions of the framework, so the CallingConvention.Cdecl parameter would have to be used there.


Thanks for bringing this up! It looks to me as though setting this to 1 uses an older approach that was used in previous versions of the framework, so maybe nobody noticed this problem before .NET 4 because it was automatically fixed.

(MSDN docs on NetFx40_PInvokeStackResilience: http://msdn.microsoft.com/en-us/library/ff361650.aspx)

On a tangential note, it also looks like .NET 4 uses a faster approach for marshalling. I wonder how much the improvement is (and if it helps Mogre in general.)

walterbing1

06-05-2011 12:44:21

Sorry for the late response.

I think that on this forum a few hours is fast response :). If not, I will have a lot of apologizing to do.


After browsing around here for a few days, I realize you were right. I'm very new on this forum, so I didn't know that when I first posted. :oops:

Thanks for bringing this up! It looks to me as though setting this to 1 uses an older approach that was used in previous versions of the framework, so maybe nobody noticed this problem before .NET 4 because it was automatically fixed.

On a tangential note, it also looks like .NET 4 uses a faster approach for marshalling. I wonder how much the improvement is (and if it helps Mogre in general.)


Yes, I read that it was because .NET 4 didn't automatically resolve those issues. Maybe I will try the CallingConvention parameter and see if it works faster, but since my program is barely started, I have no timing or profiling code in place yet.

materialDefender

08-05-2011 03:59:39

I'm not too familiar with Mercurial (I've only pushed...3 updates so far?), but I believe I've merged your changes with the main MogreAddons branch.

I myself haven't updated to VS2010 and .NET 4 yet... I think I'll do that now, so I can be sure to fix problems in MFSL before others do it for me. I also need to set up reply notifications on the MFSL thread... :oops:

In any case, thank you for your contribution! :D

Meharin

11-05-2011 04:14:29

I'm not too familiar with Mercurial (I've only pushed...3 updates so far?), but I believe I've merged your changes with the main MogreAddons branch.

I did a Hg update on the official mogreAddons and it looks like it worked! (I think it's my first patch contribution to OSS.) Thanks for maintaining MogreFreeSL!

Beauty

16-03-2012 12:19:30

MaterialDefender,
now after 10 months I found your message to me on BitBucket. (about Pull request)
Thanks for your improvement.
Thanks to Meharin, too for the Hg push.