Closed Noire001 closed 10 months ago
Hi @Noire001,
How does the C# and native logs looks like when this happens ?
Attaching logs
Thanks. That's interesting.
I believe NativeMethods.DokanCloseHandle
should not be called on a device that was dismount externally.
DokanInstance
should prevent that somehow. @LTRData any idea how we could achieve this in C# ?
@
Thanks. That's interesting. I believe
NativeMethods.DokanCloseHandle
should not be called on a device that was dismount externally.DokanInstance
should prevent that somehow. @LTRData any idea how we could achieve this in C# ? @
You are right, this is not currently handled in a safe way for dismounting externally and later calls to DokanCloseHandle
from within application itself. From what I can see, this could be an issue when using only native API as well, depending on exact workflow. The issue basically seems to be that RemoveMountPoint
needs to be guarded somehow so that it is not called again, potentially for another drive. The native DokanInstance
structure still needs to be freed though? So, we should either guard RemoveMountPoint
calls with a mutex and verify that the drive letter still points to the expected drive, or use some other mechanism to check whether drive letter has already been removed.
Forgive my naivety, but why not call DokanCloseHandle
immediately when unmounting?
Here's what I tried:
Setting ownsHandle to false in DokanHandle constructor prevents it from calling ReleaseHandle
when finalized:
public DokanHandle() : base(ownsHandle: false)
Calling DokanCloseHandle in a new Unmount method (since it also unmounts the volume anyway):
public void Unmount(DokanInstance instance)
{
NativeMethods.DokanCloseHandle(instance.DokanHandle.DangerousGetHandle());
}
This seems to fix the issue
Also a small observation:
This does not seem to call DokanCloseHandle. The implementation of Dispose in SafeHandle (of which DokanHandle is derived) is this:
public void Dispose() => this.Dispose(true);
protected virtual void Dispose(bool disposing)
{
if (disposing)
this.InternalDispose();
else
this.InternalFinalize();
}
The Dispose
method will call InternalDispose
. However looking at the stack trace in OP, ReleaseHandle
is called from InternalFinalize
.
If we are supposed to call DokanInstance.Dispose() immediately after an unmount, then perhaps overriding Dispose
in DokanHandle
and calling DokanCloseHandle
always would also work.
Yes, DokanCloseHandle
should be called from DokanHandle.ReleaseHandle
. That is how Dispose pattern is supposed to work in .NET, because the SafeHandle
derived object that holds the actual native object should be the one that frees the native object, nothing else should. Otherwise, safe handle reference counting does not work, and everything gets very risky.
Also, all IDisposable
objects should be disposed by user code, otherwise you end up with cleanup called after a while by a finalizer as you see there. When that happens, it means that there is a missed Dispose
call somewhere in your code. So, if you make sure that you really dispose all DokanInstance
objects and DokanInstance.Dispose
calls DokanHandle.Dispose
and that in turn calls DokanCloseHandle
, then we can remove the confusing DokanInstance.Unmount
method and everything should work as expected.
I correct myself (native), it is safe to call DokanCloseHandle
on a device that was RemoveMountPoint
. The DokanInstance
still exists and actually DokanCloseHandle
is the only function (outside DokanMain
but that’s not used here) that can free the instance.
DokanCloseHandle
should be called after WaitForFileSystemClosedAsync
somewhere if the unmount does not come from DokanInstance.Unmount
or remove it so DokanInstance.Dispose
does the job as @LTRData said above.
Okay, disposing DokanInstance (after unmount) does actually call ReleaseHandle and it solves my issue. Don't know why but I tried everything except this. Thanks!
Thanks @Noire001 for the confirmation! Should we do anything here to prevent this (code wise) or just close the issue ?
Hi, my project manages multiple Dokan volumes so I have a class that holds DokanInstance objects in a dictionary when they are mounted. When unmounting a volume, the corresponding DokanInstance is also removed from the dictionary. However when mounting the same volume again using a new DokanInstance (but same mountpoint), the volume is randomly unmounted after a while.
Consider the following Program.cs for mirror sample:
Mirror is mounted, unmounted after 2 seconds then mounted again. After a few more seconds mirror is randomly unmounted without calling Dokan.Unmount a second time.
Also sometimes I get this exception:
This only happens when re-mounting with the same mount point. If I call MountMirror the second time with a different mountpoint there are no issues. There's also no issue if I add all DokanInstance objects to a list and keep them there forever.
I think this is what happens: