发送实例回调作为函数指针导致C#中的“this”为“null”

时间:2012-03-22 19:26:06

标签: c# macos mono filesystemwatcher fsevents

我正在使用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

1 个答案:

答案 0 :(得分:2)

当您将委托传递给本机方法时,运行时无法知道本机方法将保留多长时间的函数指针,因此您可以保持委托的活动状态。如果您不维护对它的引用,则垃圾收集器可能会收集该委托,从而在本机代码尝试调用它时导致未定义的行为。