代码之家  ›  专栏  ›  技术社区  ›  Ray Brian Agnew

在C#app中收到封送的结构后不久发生堆损坏

  •  2
  • Ray Brian Agnew  · 技术社区  · 6 年前

    我想把新的windows10包装起来 ProjFS C#中的功能。第一步, as outlined on MSDN ,工作正常:我设置一个目录作为虚拟化根目录,然后注册投影提供程序,包括其回调,如下所示:

    static void Main(string[] args)
    {
        // Create and mark clean directory as virtualization root.
        const string directory = @"C:\test_projfs"; // Warning: Deleted to get clean directory.
        if (Directory.Exists(directory))
            Directory.Delete(directory);
        Directory.CreateDirectory(directory);
    
        Guid guid = Guid.NewGuid();
        Marshal.ThrowExceptionForHR(PrjMarkDirectoryAsPlaceholder(directory, null, IntPtr.Zero,
            ref guid));
    
        // Set up the callback table for the projection provider.
        PrjCallbacks callbackTable = new PrjCallbacks
        {
            StartDirectoryEnumerationCallback = StartDirectoryEnumerationCallback,
            EndDirectoryEnumerationCallback = EndDirectoryEnumerationCallback,
            GetDirectoryEnumerationCallback = GetDirectoryEnumerationCallback,
            GetPlaceholderInfoCallback = GetPlaceholderInfoCallback,
            GetFileDataCallback = GetFileDataCallback
        };
        // Start the projection provider.
        IntPtr instanceHandle = IntPtr.Zero;
        Marshal.ThrowExceptionForHR(PrjStartVirtualizing(directory, ref callbackTable,
            IntPtr.Zero, IntPtr.Zero, ref instanceHandle));
    
        // Keep a test console application running.
        Console.ReadLine();
    }
    
    // Managed callbacks, simply returning S_OK for now.
    static int StartDirectoryEnumerationCallback(ref PrjCallbackData callbackData, ref Guid enumerationId)
    {
        return 0;
    }
    static int EndDirectoryEnumerationCallback(ref PrjCallbackData callbackData, ref Guid enumerationId) => 0;
    static int GetDirectoryEnumerationCallback(ref PrjCallbackData callbackData, ref Guid enumerationId, string searchExpression, IntPtr dirEntryBufferHandle) => 0;
    static int GetPlaceholderInfoCallback(ref PrjCallbackData callbackData) => 0;
    static int GetFileDataCallback(ref PrjCallbackData callbackData, ulong byteOffset, uint length) => 0;
    

    本机声明如下所示(很抱歉,这个代码块可以滚动-基本的东西已经很多了):

    // Methods to mark directory as virtualization root, and start the projection provider.
    [DllImport("ProjectedFSLib.dll", CharSet = CharSet.Unicode)]
    static extern int PrjMarkDirectoryAsPlaceholder(string rootPathName,
        string targetPathName, IntPtr versionInfo, ref Guid virtualizationInstanceID);
    [DllImport("ProjectedFSLib.dll", CharSet = CharSet.Unicode)]
    static extern int PrjStartVirtualizing(string virtualizationRootPath,
        ref PrjCallbacks callbacks, IntPtr instanceContext, IntPtr options,
        ref IntPtr namespaceVirtualizationContext);
    
    // Structure configuring the projection provider callbacks.
    [StructLayout(LayoutKind.Sequential)]
    struct PrjCallbacks
    {
        public PrjStartDirectoryEnumerationCb StartDirectoryEnumerationCallback;
        public PrjEndDirectoryEnumerationCb EndDirectoryEnumerationCallback;
        public PrjGetDirectoryEnumerationCb GetDirectoryEnumerationCallback;
        public PrjGetPlaceholderInfoCb GetPlaceholderInfoCallback;
        public PrjGetFileDataCb GetFileDataCallback;
        public PrjQueryFileNameCb QueryFileNameCallback;
        public PrjNotificationCb NotificationCallback;
        public PrjCancelCommandCb CancelCommandCallback;
    }
    
    // Callback signatures.
    delegate int PrjStartDirectoryEnumerationCb(ref PrjCallbackData callbackData, ref Guid enumerationId);
    delegate int PrjEndDirectoryEnumerationCb(ref PrjCallbackData callbackData, ref Guid enumerationId);
    delegate int PrjGetDirectoryEnumerationCb(ref PrjCallbackData callbackData, ref Guid enumerationId, string searchExpression, IntPtr dirEntryBufferHandle);
    delegate int PrjGetPlaceholderInfoCb(ref PrjCallbackData callbackData);
    delegate int PrjGetFileDataCb(ref PrjCallbackData callbackData, ulong byteOffset, uint length);
    delegate int PrjQueryFileNameCb(ref PrjCallbackData callbackData);
    delegate int PrjNotificationCb(ref PrjCallbackData callbackData, bool isDirectory, int notification, string destinationFileName, IntPtr operationParameters);
    delegate int PrjCancelCommandCb(ref PrjCallbackData callbackData);
    
    // Callback data passed to each of the callbacks above.
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct PrjCallbackData
    {
        public uint Size;
        public uint Flags;
        public IntPtr NamespaceVirtualizationContext;
        public int CommandId;
        public Guid FileId;
        public Guid DataStreamId;
        public string FilePathName;
        public IntPtr VersionInfo;
        public uint TriggeringProcessId;
        public string TriggeringProcessImageFileName;
        public IntPtr InstanceContext;
    }
    

    PRJ_START_DIRECTORY_ENUMERATION_CB 回拨( PrjStartDirectoryEnumerationCb 开始枚举我的目录。它将指针传递给 PRJ_CALLBACK_DATA PrjCallbackData 在我的代码中)。

    现在,虽然我显然很好地将结构接收到了托管回调中,但所有值直到最后一个成员都有意义 InstanceContext S_OK ).

    static int StartDirectoryEnumerationCallback(ref PrjCallbackData callbackData, ref Guid enumerationId)
    {
        return 0; // Crashes here or when stepping out of this method.
    }
    

    我试图找出错误所在,但由于调试立即停止,没有任何异常(我没有过滤掉任何异常),我没有走多远。我意识到当我把回调改为默默地接受 IntPtr 而不是 ref PrjCallbackData ,应用程序不会崩溃。

    static int StartDirectoryEnumerationCallback(IntPtr callbackData, ref Guid enumerationId)
    {
        return 0; // No crash executing this with IntPtr passed in.
    }
    
    delegate int PrjStartDirectoryEnumerationCb(IntPtr callbackData, ref Guid enumerationId);
    

    从逻辑上讲,如果没有我可以访问的重要信息,这不会让我走远。

    我是不是漏了一步?这样简单的结构映射是不是不可能直接映射?

    如果感兴趣,事件查看器条目如下所示。我使用.NETFramework 4.6在一个新型的C#项目文件中运行应用程序(它应该解释“dotnet.exe”可执行文件名)。如果需要其他信息,我很乐意提供。

    Faulting application name: dotnet.exe, version: 2.1.26919.1, time stamp: 0x5ba1bb46
    Faulting module name: ntdll.dll, version: 10.0.17763.1, time stamp: 0xa369e897
    Exception code: 0xc0000374
    Fault offset: 0x00000000000fb349
    Faulting process id: 0xfa8
    Faulting application start time: 0x01d46d623aee076d
    Faulting application path: C:\Program Files\dotnet\dotnet.exe
    Faulting module path: C:\WINDOWS\SYSTEM32\ntdll.dll
    Report Id: adcfba5c-dfd4-428d-8eb5-81aceada1983
    Faulting package full name: 
    Faulting package-relative application ID: 
    

    请注意,如果要尝试上面的示例代码,则必须在windows1809中安装投影的文件系统功能,并将其编译为x64(x86/AnyCPU配置没有本机库)。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Ray Brian Agnew    6 年前

    正如西蒙所说,只有 IntPtr Marshal.PtrToStructure<PrjCallbackData>(callbackData) 检索 传递给回调函数的结构的一部分可以正常工作。

    或者,使用Hans的解决方案 对于结构中的字符串字段(以及将结构保留在签名中)也有效,但使我无法简单地访问字符串数据。

    幸运的是,我不必写任何东西回这个结构,否则我会遇到问题写回原来的,而不是原来的 复制

    here .