My observation in practice has been that GC.SuppressFinalize does not always suppress the call to the finalizer. It could be that the finalizer gets called nontheless. I won
I have used the exact same pattern many times and GC.SupressFinalize has always appeared to work.
Keep in mind that a call to GC.ReRegisterForFinalize will cause objects to re-register for finalisation.
Whenever I use the technique above I always ensure that include a full stack trace during object construction so I can track down the method that allocated the non-disposed object.
Eg. in the constructor use
StackFrame frame = new StackFrame(1);
and report that in your debug message during the finaliser.
Also, I notice your GC.SupressFinalize is not in a finally clause, if an exception is thrown during dispose, your objects finalizer will not be suppressed.