generic where T : value class Result BaseEffect::SetValue( EffectHandle^ parameter, T value ) { HRESULT hr; D3DXHANDLE handle = parameter != nullptr ? parameter->InternalHandle : NULL; if( T::typeid == bool::typeid ) { BOOL newValue = Convert::ToInt32( value, CultureInfo::InvariantCulture ); hr = InternalPointer->SetBool( handle, newValue ); } else if( T::typeid == float::typeid ) { hr = InternalPointer->SetFloat( handle, static_cast<FLOAT>( value ) ); } else if( T::typeid == int::typeid ) { hr = InternalPointer->SetInt( handle, static_cast<INT>( value ) ); } else if( T::typeid == Matrix::typeid ) { hr = InternalPointer->SetMatrix( handle, reinterpret_cast( &value ) ); } else if( T::typeid == Vector4::typeid ) { hr = InternalPointer->SetVector( handle, reinterpret_cast( &value ) ); } else { hr = InternalPointer->SetValue( handle, &value, static_cast( sizeof(T) ) ); } return RECORD_D3D9( hr ); }
This function will crash randomly on good input. Specifically, D3D will return a D3DERR_INVALIDCALL, which then gets translated to an exception. Again, that's on known-good input. The call to the function looks like this:
effect.SetValue("alpha", 0.5f);
When this call is made, an EffectHandle is implicitly constructed that copies the string into native memory. Here's the relevant bits of EffectHandle:
EffectHandle::EffectHandle( String^ name ) { m_StringData = Marshal::StringToHGlobalAnsi( name ); m_HasString = true; m_StringDataSize = name->Length; GC::AddMemoryPressure( m_StringDataSize ); m_Handle = reinterpret_cast( m_StringData.ToPointer() ); }//Called by both ~EffectHandle and !EffectHandle. void EffectHandle::Destruct() { if( m_HasString ) { Marshal::FreeHGlobal( m_StringData ); GC::RemoveMemoryPressure( m_StringDataSize ); } }
So. Do you see where we screwed this up?
In case it's not clear:
* EffectHandle::InternalHandle returns EffectHandle::m_Handle
* D3DXHANDLE is a typedef for char*
* Although there's plenty of interop, the interop is NOT the root cause.
HINT: EffectHandle is finalizable. Answer is in comments.