尝试打开位于win2k8共享上的文件时,文件打开最初失败,但最终可以成功 [英] File open fails initially when trying to open a file located on a win2k8 share but eventually can succeeed

查看:110
本文介绍了尝试打开位于win2k8共享上的文件时,文件打开最初失败,但最终可以成功的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

问题的核心:在成功打开文件之前,大约8到9秒钟,我收到(0x80070002)系统找不到指定的文件".

简而言之,我们有两个com组件.组件A调用组件B,并要求写入UNC文件名-返回的文件名尚不存在,但路径确实存在-然后它完成其工作,创建并填充文件,并通过执行以下操作告诉组件B:另一个电话.届时,组件B将调用MoveFile将文件重命名为其正式"名称.

此代码已经(字面上)使用了几年.在win2k3上可以正常工作.在win2k8上运行时,它可以正常工作,并指向win2k3服务器上的共享.但是,如果您在win2k8上运行它并将其指向(不同的)win2k8服务器上的共享,它将失败.如果共享实际上位于运行代码的同一台win2k8计算机上,那么它也可以正常运行.

A组份和A组份组件B存在于它们自己的Windows服务中,并以域管理员帐户运行.共享在所有测试环境中均配置为所有人/完全控制",类似地,共享指向的基础文件夹也是如此.所有机器都在同一个域中.

在调试过程中,我意识到在手动检查该文件时该文件确实存在-经过几次迭代后,我发现该文件直到经过一些延迟后才显示"-因此我放入如下所示,在组件B中的循环如下:

void Demo()
{
    int nCounter = 0;

    while (true)
    {
        CFileStatus fs;
        if (CFile::GetStatus(tempname, fs)) break;

        SleepEx(100, FALSE);
        nCounter++;
    }

}

实际上,此代码确实退出了,nCounter通常在80& amp;之间.当它确实指示文件出现"大约90至8秒后,将进行90次迭代.一旦该循环退出,代码就可以成功地重命名该文件,并且所有进一步的处理似乎都可以正常进行.

我在调用组件B之前将CFile :: GetStatus放在组件A中,这表明成功-它可以看到文件并获得其真实大小,但是在调用组件b之后立即看不到文件,直到以上指示的延迟时间过去了.我已经验证了路径名是完全一样的,尽管显然必须要经过8到9秒钟的暂停才能使调用最终成功.

当发生这种情况时,我始终认为代码中存在错误,除非另行证明,但鉴于此代码已执行了很长时间且(未添加诊断循环)未更改,因此它可以正常工作在所有环境中,除了win2k8-> win2k8共享,我猜这里有些操作系统问题我不理解.

任何见解都会有所帮助-谢谢

解决方案

问题原来是SMB2协议缓存更改-仅当双方同意时才使用SMB2,这就是为什么win2k8-> win2k8仅是一个问题( Vista和Windows 7也实现了SMB2)

在SMB2中,客户端具有仅每10秒更新一次(默认情况下)的本地缓存,这意味着其他计算机可以更新服务器共享,并且直到缓存变为无效时才会显示.我认为,但目前尚未与Microsoft确认,运行32位应用程序时,win2k8的WindowsOnWindows层中也存在一些问题-看来它们可能也获得了自己的缓存,这也许可以解释为什么我的两个交互应用程序在同一台计算机上看不到服务器份额的相同视图.

Microsoft提供了(至少)几个解决此问题的方法

  • 重写代码以使用无缓冲文件访问
  • 完成写入后(但在关闭文件之前)调用FlushFileBuffers
  • 在一台或两台计算机上禁用SMB2

在Windows Server 2008上运行"regedit"
基于计算机.展开并找到 子树如下.
HKLM \ System \ CurrentControlSet \ Services \ LanmanServer \ Parameters 添加一个新的REG_DWORD键,名称为 "Smb2"(不带引号)的说明
值名称:Smb2值类型:
REG_DWORD 0 =禁用1 =启用
将该值设置为0以禁用SMB 2.0,或将其设置为1以重新启用SMB 2.0.重新启动服务器.

  • 在客户端计算机上禁用SMB2缓存

使用 刷新所需的超时值 本地缓存.将这些键设置为 零将禁用相应的 缓存.

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Services \ LanmanWorkstation \ Parameters:

FileInfoCacheLifetime FileNotFoundCacheLifetime DirectoryCacheLifetime 所有值都是DWORD,以秒为单位指定

SMB2注册表设置

Core of the problem: I receive "(0x80070002) The system cannot find the file specified" for roughly 8 to 9 seconds before it can open it successfully.

In a nutshell, we have two com components. Component A calls into Component B and asks for a UNC filename to write to - the filename returned doesn't exist yet, but the path does - it then does its work, creates and populates the file, and tells Component B its done by making another com call. At that point Component B will call MoveFile to rename the file to its "official" name.

This code has worked for (literally) years. Its works fine on win2k3. Its works fine when its running on win2k8 and points to a share on a win2k3 server. But if you run it on win2k8 and point it to a share on a (different) win2k8 server it fails. It also runs fine, if there share is actually located on the same win2k8 machine that the code is running on.

Both Component A & component B exist in their own Windows Service running with as a domain admin account. The shares are configured for "Everyone/Full control" in all test environments, similarly so are the underlying folders that the share points to. All machines are in the same domain.

During debugging i realized the file actually does exist by the time i get to checking manually for it - after several iterations it occurred to me that the file doesn't "show up" until some delay passes by - so i put in the loop below in component B as shown below:

void Demo()
{
    int nCounter = 0;

    while (true)
    {
        CFileStatus fs;
        if (CFile::GetStatus(tempname, fs)) break;

        SleepEx(100, FALSE);
        nCounter++;
    }

}

This code does, in fact, exit and nCounter is generally between 80 & 90 iterations when it does indicating the file "appears" approx 8 to 9 seconds later. Once that loop exits the code can successfully rename the file and all further processing appears to work.

I put a CFile::GetStatus in component A immediately before it calls into Component B and that indicates success - it can see the file and get its true size yet the call into component b made immediately after can not see the file until the above indicated delay passes. I have verified the pathnames are precisely the same, even though it would clearly have to be for the calls to eventually succeed after a pause of 8 to 9 seconds...

When something like this occurs I always assume there is a bug in my code until proven otherwise, but given this code has executed properly for a very long time and (other than my diagnostic loop added) has not changed, and it works in all environments except the win2k8 - > win2k8 share i'm guessing there is some OS issue in here that i do not understand.

Any insight would be helpful - thanks

解决方案

The problem turns out to be SMB2 protocol caching changes - SMB2 is only used when both sides agree which is why it is only a problem for win2k8->win2k8 (vista & windows 7 also implement SMB2)

In SMB2 the client has a local cache that is only updated (by default) every 10 seconds, this means other machines can update the server share and it will not be seen until the cache becomes invalidated. I think, but have not confirmed with Microsoft at this point, that there is also some issue in the WindowsOnWindows layer in win2k8 when running 32bit applications - it would appear that they may get their own cache as well which might explain why my two interacting applications on the same machine didn't see the same view of the sever's share.

There a (at least) a couple work-arounds to this problem as provided by microsoft

  • Rewrite your code to use unbuffered file access
  • call FlushFileBuffers when you are done writing (but before you close the file)
  • Disable SMB2 on one or both machines

Run "regedit" on Windows Server 2008
based computer. Expand and locate the sub tree as follows.
HKLM\System\CurrentControlSet\Services\LanmanServer\Parameters Add a new REG_DWORD key with the name of "Smb2" (without quotation mark)
Value name: Smb2 Value type:
REG_DWORD 0 = disabled 1 = enabled
Set the value to 0 to disable SMB 2.0, or set it to 1 to re-enable SMB 2.0. Reboot the server.

  • Disable the SMB2 caching on the client machine

Configure the below keys with the desired timeout value for refreshing the local cache. Setting these keys to zero will disable the respective cache.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters:

FileInfoCacheLifetime FileNotFoundCacheLifetime DirectoryCacheLifetime All values are DWORD, specified in seconds

SMB2 Registry settings

这篇关于尝试打开位于win2k8共享上的文件时,文件打开最初失败,但最终可以成功的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆