NilObject thrown in built but not during debug

I have a list of files with (sometimes) sound files attached that will each be played whenever the list/row changes. For some reason on the built app (maybe only Mac), if I attempt to open another list, and there is no sound file, a nilobject is thrown.

How do I debug this?

The other problem is it freezes.

Sprinkle your code with System.DebugLog messages to track what it’s doing.

How are the sound files “attached” to the app? Did you drag them into the IDE, or are they external?

Not sure why it would not throw the exception in the IDE as you say. Otherwise it would be easy to break on the exception. Maybe just set the IDE (if you haven’t) to “Break on Exceptions” anyway.

Off the top of my head, since you say that some rows will not have sounds anyway, I would nest the code that plays the sound in a Try/Catch

I thought the debuglog only worked in debug mode.
I know the line of code which is being jumped over. I have no proof. The sounds are being ran if there a text path is there. If there is no text, then the “if” statement is NO.
How do I find out what is happening?

That is true. I did not mean to imply otherwise.

The sounds are being ran if there a text path is there. If there is no text, then the “if” statement is NO.

Is the “text path” the file path to the sound file?

Given the failure to find the NEO, my suggestion was to just handle it. Something like

Try

//put in your code that plays the sound

Catch somethingGoofy as nilObjectException

// We don't know what threw the exception, but we won't crash trying to play a nil sound

End Try

In a compiled app, DebugLog messages can be found through the Console app on the Mac, and something similar in windows and Linux I’d think.

2 Likes

Maybe post some of the relevant code here so we can see what you’re doing.

Good Night. If you respond, I’ll respond after celebrations tomorrow.
I’ll post the code for playing the sound. When I look at it I have got it covered with Trys, but maybe someone has ideas.

This is the method inside of the method which does the actual playing of the sound. There are 2 possible sounds held in the array WhchSound. This is all where the NilObject is being probably being thrown, but it shouldn’t even get here.

//Public Function PlayngSnd(Idx As Integer) As Integer
//stStop is True if starting sound False if stopping
//idx is the index being requested.  If -1 then whatever
If idx = 0 Then
  If me.WhchSound(0) = Nil Then 
    me.WSIdx = -1
    Return me.WSIdx//This hold the index of the pair of sounds
  Else
    me.WSIdx = 0
  End If
ElseIf idx = 1 Then
  If me.WhchSound(1) = Nil Then 
    me.WSIdx = -1
    Return me.WSIdx
  Else
    me.WSIdx = 1
  End If
ElseIf idx = -1 Then
  If me.WhchSound(0) <> Nil Then 
    me.WSIdx = 0
  ElseIf me.WhchSound(1) <> Nil Then 
    me.WSIdx = 1
  Else
    me.WSIdx = -1
    Return me.WSIdx
  End If
End If//If ids = 0 Then

If me.WhchSound(me.WSIdx).IsPlaying Then//Triangle
  me.WhchSound(WSIdx).Stop
  //dWin.TurnOff.RunMode = Timer.RunModes.Off
  
Else
  Try
    me.WhchSound(WSIdx).Play
    //dWin.TurnOff.RunMode = Timer.RunModes.Multiple
    //me.SegmentAt(segmentIndex).Title = &u25a0
  Catch
    me.WSIdx = -1
    MessageBox "An error occured while attempting to play this sound." + EOL + "The sound file may be corrupt."
  End Try
End If
Return me.WSIdx

This is the part of the method which calls the other.

if App.ExstsTruu(ArMnt.WhchSndFItm(0)) Then
  ArMnt.WhchSound(0) = ArMnt.WhchSound(0).Open(ArMnt.WhchSndFItm(0))
  SoundBttons.SegmentAt(0).Enabled = True
  Try
    If SndAutoPlay And ArMnt.PlayngSnd(0) = 0 Then 
      TurnOff(0).RunMode = Timer.RunModes.Multiple//This is a timer to turn it
      
    End If 
    
  Catch  //If an error occurs, code here will be run.
    'WordSndTimer.Mode = Xojo.Core.Timer.Modes.Off
    MessageBox "An Error occured while Attempting to Play the Sound " + ArMnt.WhchSndFItm(0).Name + EOL + "The Sound File may be Corrupt."
    SoundBttons.SegmentAt(0).Enabled = False
  End Try
ElseIf BeingEdited = False Then
  SoundBttons.SegmentAt(0).Enabled = False
End If

I’m curious as to how the WhchSound() array is populated. My initial thought when you first posted is that this could be related to the fact that you get an exception when running the compiled app but not while debugging, and is why I asked “How are the sound files “attached” to the app? Did you drag them into the IDE, or are they external?”, but your posted code doesn’t answer that question as far as I can see.

I note that there’s no Nil check before

If me.WhchSound(me.WSIdx).IsPlaying

so if WhchSound(me.WSIdx) is Nil, it will definitely throw an NoE on that line.

1 Like

This makes sense, regarding the difference between debug and build behavior. If your sound files are in the project folder, it’s possible that the .debug app can see them, but a built app (originally in the Build folder but ultimately sent anywhere) cannot. But like Julia says, we don’t know how you acquire the sound files, so this is guesswork.

However you do it, I would think that the audio files need to wind up in your app’s Resources folder when you build.

But right now, you don’t know for sure it happens here…

Anyway…

these lines don’t check for nil:

If me.WhchSound(me.WSIdx).IsPlaying Then//Triangle
  me.WhchSound(WSIdx).Stop

And I suspect of any of the code above has set me.WSIdx = -1 , then asking
If me.WhchSound(-1).IsPlaying is a problem.
That should probably say

If me.WSIdx > -1 and me.WhchSound(me.WSIdx) <> Nil and me.WhchSound(me.WSIdx).IsPlaying

Personally, Im not a fan of ELSEIF. It’s perfectly valid, but it can make the code hard to follow.
I also note that in some places you use me.WSIdx and in others just WSIdx… if there is a global WSIdx property somewhere, these are different variables.

Can I suggest this (feel free to ignore)


dim Ret as integer  = -1    //assume the worst
//kill the current sound

if  me.WhchSound(idx) <> nil and  me.WhchSound(idx).IsPlaying then
 me.WhchSound(idx).Stop
end if

// swap -1 for 0 if 0 is valid
if idx = -1 and me.WhchSound(0) <> Nil  then 
   Ret = 0 
else
//idx is not -1, so check if it is valid
if me.WhchSound(idx)  <> nil then
 Ret = idx  
end if
end if

me.WSIdx = Ret

if Ret > -1 and me.WhchSound(Ret) <> nil then


  //start playing
  Try
    me.WhchSound(Ret).Play
  Catch
    Ret = -1
    MessageBox "An error occured while attempting to play this sound." + EOL + "The sound file may be corrupt."
  End Try
End if
Return Ret
1 Like

FWIW, the array itself may be nil. While unlikely to be the problem here, I’d add a “if me.WhchSound<>nil then” just to be safe.

1 Like

this WhchSound array should only contain valid sounds.

i would use a method to access current sound

var sound As Sound = CurrentSound()
if sound <>Nil then sound.Play

If me.WSIdx > -1 and me.WhchSound(me.WSIdx) <> Nil and me.WhchSound(me.WSIdx).IsPlaying

Might be. Later.

Something to keep in mind is that the location of the app when running is typically right next to the app. If you’ve hard coded any paths to files, you can easily get a nilobjectexception from the built app if the files are not in the same relative place. Try moving your built app into the same directory as your project and see if it still fails.

3 Likes

I had a similar problem and discovered that something created in an Open event doesn’t necessarily exist when accessed in the Activated event.

Thanks. Everything is stored as text and I have a system of partial paths for sound and image files. That partial path is stored as text and recreated as Native when I need it.

I haven’t added network capability yet, because I keep on finding little bugs.

I tweaked this line and the problem stopped

If me.WhchSndFItm(me.WSIdx) <> Nil And me.WhchSound(me.WSIdx).IsPlaying Then//Triangle

WhchSndFItm is the folder item.

Now I have to figure out how it even got there.

Even partial paths are likely going to be an issue cross platform. It could be better to add items to the resources folder and access them via SpecialFolder.Resource( “Somefile.extension” )

1 Like