我正在使用Mono编写 FileSystemWatcher ,但出于某种原因,我发送到FSEvents库的回调并没有保留这个引用,它始终是在回调中为null,即使回调是实例方法。
知道为什么会这样吗?
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using MonoMac.Foundation;
using System.Threading;
using NUnit.Framework;
using System.Text;
namespace Test
{
class MainClass
{
public void Main ()
{
string testFolder = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments), "mono-test");
if (Directory.Exists (testFolder)) {
Directory.Delete (testFolder, true);
}
Directory.CreateDirectory (testFolder);
IntPtr path = CFStringCreateWithCString (IntPtr.Zero, testFolder, 0);
IntPtr paths = CFArrayCreate (IntPtr.Zero, new IntPtr [1] { path }, 1, IntPtr.Zero);
IntPtr stream = FSEventStreamCreate (IntPtr.Zero, this.Callback, IntPtr.Zero, paths, FSEventStreamEventIdSinceNow, 0, FSEventStreamCreateFlags.WatchRoot | FSEventStreamCreateFlags.FileEvents);
CFRelease (paths);
CFRelease (path);
Thread runLoop = new Thread (delegate() {
FSEventStreamScheduleWithRunLoop (stream, CFRunLoopGetCurrent (), kCFRunLoopDefaultMode);
FSEventStreamStart (stream);
CFRunLoopRun ();
});
runLoop.Name = "FSEventStream";
runLoop.Start ();
Thread.Sleep (3000);
string file1 = Path.Combine (testFolder, "file1.txt");
//Thread.Sleep(1000);
using (System.IO.File.Create(file1)) {
}
//Thread.Sleep(1000);
System.IO.File.WriteAllText (file1, "file1");
//Thread.Sleep(1000);
System.IO.File.Delete (file1);
}
public static void Main (string[] args)
{
new MainClass().Main();
}
private static IDictionary<IntPtr, MainClass> thisDict = new Dictionary<IntPtr, MainClass>();
private void Callback (IntPtr streamRef, IntPtr clientCallBackInfo, int numEvents, IntPtr eventPaths, IntPtr eventFlags, IntPtr eventIds)
{
MainClass thisObj;
if (this != null) {
thisDict.Add(streamRef, this);
thisObj = this;
} else {
thisObj = thisDict[streamRef];
}
Console.WriteLine("\n{0}", this != null ? "this is not null" : "this is null");
Console.WriteLine("{0}\n", thisObj != null ? "thisObj is not null" : "thisObj is null");
string[] paths = new string[numEvents];
UInt32[] flags = new UInt32[numEvents];
UInt64[] ids = new UInt64[numEvents];
unsafe
{
char** eventPathsPointer = (char**) eventPaths.ToPointer();
uint* eventFlagsPointer = (uint*) eventFlags.ToPointer();
ulong* eventIdsPointer = (ulong*) eventIds.ToPointer();
for (int i = 0; i < numEvents; i++)
{
paths[i] = Marshal.PtrToStringAuto(new IntPtr(eventPathsPointer[i]));
flags[i] = eventFlagsPointer[i];
ids[i] = eventIdsPointer[i];
}
}
Console.WriteLine("Number of events: {0}", numEvents);
for (int i = 0; i < numEvents; i++)
{
Console.WriteLine("{0} {1:x8} {2}", ids[i], flags[i], paths[i]);
Console.WriteLine("Modified: {0:x8}", (flags[i] & (uint) FSEventStreamEventFlagItem.Modified));
Console.WriteLine("Created: {0:x8}", (flags[i] & (uint) FSEventStreamEventFlagItem.Created));
Console.WriteLine("Removed: {0:x8}", (flags[i] & (uint) FSEventStreamEventFlagItem.Removed));
Console.WriteLine("Renamed: {0:x8}", (flags[i] & (uint) FSEventStreamEventFlagItem.Renamed));
Console.WriteLine();
}
}
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static IntPtr CFStringCreateWithCString (IntPtr allocator, string value, int encoding);
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static IntPtr CFArrayCreate (IntPtr allocator, IntPtr [] values, int numValues, IntPtr callBacks);
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static IntPtr CFArrayGetValueAtIndex(IntPtr array, int index);
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static void CFRelease(IntPtr cf);
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static IntPtr CFRunLoopGetCurrent ();
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static IntPtr CFRunLoopGetMain();
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static void CFRunLoopRun ();
[DllImport ("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation")]
extern static int CFRunLoopRunInMode (IntPtr mode, double seconds, int returnAfterSourceHandled);
delegate void FSEventStreamCallback (IntPtr streamRef, IntPtr clientCallBackInfo, int numEvents, IntPtr eventPaths, IntPtr eventFlags, IntPtr eventIds);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static IntPtr FSEventStreamCreate (IntPtr allocator, FSEventStreamCallback callback, IntPtr context, IntPtr pathsToWatch, ulong sinceWhen, double latency, FSEventStreamCreateFlags flags);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static int FSEventStreamStart (IntPtr streamRef);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static void FSEventStreamStop (IntPtr streamRef);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static void FSEventStreamRelease (IntPtr streamRef);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static void FSEventStreamScheduleWithRunLoop (IntPtr streamRef, IntPtr runLoop, IntPtr runLoopMode);
[DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
extern static void FSEventStreamUnscheduleFromRunLoop (IntPtr streamRef, IntPtr runLoop, IntPtr runLoopMode);
const ulong FSEventStreamEventIdSinceNow = ulong.MaxValue;
private static IntPtr kCFRunLoopDefaultMode = CFStringCreateWithCString(IntPtr.Zero, "kCFRunLoopDefaultMode", 0);
[Flags()]
enum FSEventStreamCreateFlags : uint {
None = 0x00000000,
UseCFTypes = 0x00000001,
NoDefer = 0x00000002,
WatchRoot = 0x00000004,
IgnoreSelf = 0x00000008,
FileEvents = 0x00000010
}
[Flags()]
enum FSEventStreamEventFlag : uint {
None = 0x00000000,
MustScanSubDirs = 0x00000001,
UserDropped = 0x00000002,
KernelDropped = 0x00000004,
EventIdsWrapped = 0x00000008,
HistoryDone = 0x00000010,
RootChanged = 0x00000020,
FlagMount = 0x00000040,
Unmount = 0x00000080
}
[Flags()]
enum FSEventStreamEventFlagItem : uint {
Created = 0x00000100,
Removed = 0x00000200,
InodeMetaMod = 0x00000400,
Renamed = 0x00000800,
Modified = 0x00001000,
FinderInfoMod = 0x00002000,
ChangeOwner = 0x00004000,
XattrMod = 0x00008000,
IsFile = 0x00010000,
IsDir = 0x00020000,
IsSymlink = 0x00040000
}
}
}
更新
对于任何有兴趣的人,固定代码为here。
答案 0 :(得分:2)
当您将委托传递给本机方法时,运行时无法知道本机方法将保留多长时间的函数指针,因此您可以保持委托的活动状态。如果您不维护对它的引用,则垃圾收集器可能会收集该委托,从而在本机代码尝试调用它时导致未定义的行为。