在映射之后,文件不能关闭,只有在所有映射结束后才可以关闭,错误类型就是这么导致的。
还有,你既然是多线程上传,为什么还要用CriticalSection? 每个线程操作各自那部分就可以了,分配好了应该没有冲突的。我建议你设置一个全局标志,文件只打开一次,同时建立映射,在各个线程里打开映射就可以了,上传完毕后再关闭文件。这样就没有文件共享冲突的问题。ERROR 1006:
The volume for a file has been externally altered so that the opened file is no longer valid.  ERROR_FILE_INVALID 

解决方案 »

  1.   

    Kronus(Kronus):现在的问题是如果我只打开一次文件,第二次如果只打开映射,怎么知道映射文件的长度呢?我上面的代码里面是用打开文件来取得文件长度的:
            ......
            else
            {
                CFile SrcFile(szSrcName,CFile::modeRead);
                dwSize=SrcFile.GetLength();
                SrcFile.Close();
            }
            
    还有,就是如果不用CriticalSection的话,几个线程用OpenFileMapping打开同一个映射会不会冲突?谢谢!
      

  2.   

    对不起,开始我没有仔细看你提出的问题,我以为是多线程上传到同一个服务器。
    如果你想上传到多个服务器,每个服务器开一个线程,可以如下处理:方法一 还是用FileMapping, 就是只建立一次,在每个线程中new一份内存,用memcpy()把FileMapping所影射的内容拷贝到这块内存中,在复制过程中可以用互斥对象来防止内存访问冲突。然后就可以上传了。所有线程都复制完毕后,就可以关闭映射了。至于文件大小,最好设置全局变量。方法二 不用FileMapping,在进入线程前把文件拷贝到内存中,在每一个线程中先申请一块同样大小的内存,内存大小可以用 把文件内容所在内存复制一份,所有线程复制完毕后,就可以释放掉分配的全局内存了。在复制的过程可以用互斥对象Mutex来防止内存访问冲突。注:其实实质和映射差不多,但是避免了用映射函数的麻烦。我现在想到的方法比较笨,就是比较耗内存。
      

  3.   

    附:方法二中的在线程中文件大小可以用获取分配内存大小_msize(...)的方法获得
      

  4.   

    Kronus(Kronus):也许是我没有把问题描述清楚,现在的情况是这样的: 我有一批文件要上传(文件的数量可能很多,不可能在进入线程前把每一个文件都打开并分配内存)到几个FTP服务器,并且需要上传的文件被放在一个任务链表中,为每个FTP服务器启动线程的时候就是把这个任务链表作为参数传给线程的,然后每个线程独立地按照任务链表来打开并上传文件(上传的实际上是同一批文件)对于这样的情况是不是应该有更好的做法?
      

  5.   

    那还是用文件映射吧,就是在线程里判断该文件是否已经映射,就是OpenFileMapping(...),如没有,则OpenFile和CreateFileMapping, 在该文件在该线程结束的时候,判断是否所有的线程都传输完此文件,如是,则关闭文件,如没有,则不关闭文件。不过在传输此文件的时候,还是要分配内存,不过是只分配正在传输文件,传输完此文件就释放掉,然后同样处理下一个文件。实际上各个线程只分配一个文件的空间,为了防止内存访问冲突,我现在还没有想到其它更好的方法,也许高手有更好的方法。
      

  6.   

    实质就是如下:
    (openfile->malloc->memcpy->closefile)
    ~~~~~~~~~~~~~~~mutex~~~~~~~~~~~~~~~~~->upload->free->next file......
      

  7.   

    谢谢Kronus(Kronus),我现在的做法是(效果很不错,看来在映射之后,文件是可以关闭的):
    在这里放一天大家看一下再给分:BOOL WriteFile(CFtpConnection *pConnectionDes,LPCTSTR szSrcName,LPCTSTR szDesName,DWORD dwSize)
    {
    try
    {
    if(dwSize==0)
    {
    CInternetFile* pDesFile=pConnectionDes->OpenFile(szDesName,GENERIC_WRITE);
    pDesFile->Close();
    return TRUE;
    } CString strname(szSrcName);
    strname.Replace('\\','&');
    strname.Replace('/','&'); EnterCriticalSection(&g_csection); HANDLE hMap=OpenFileMapping(FILE_MAP_READ,FALSE,strname);
    if(hMap==NULL)
    {
    CFile SrcFile(szSrcName,CFile::modeRead);
    hMap=CreateFileMapping((HANDLE)SrcFile.m_hFile,NULL,PAGE_READONLY,0,0,strname); 
    SrcFile.Close();
    if(hMap==NULL)
    {
                                         // 出错处理
    LeaveCriticalSection(&g_csection);
    return FALSE;
    }
    }
    LPVOID lpMapAddress=MapViewOfFile(hMap,FILE_MAP_READ,0,0,0); LeaveCriticalSection(&g_csection); CInternetFile* pDesFile=pConnectionDes->OpenFile(szDesName,GENERIC_WRITE);
    DWORD dwCount=0;
    while(dwSize!=0)
    {
    if(dwSize>=1024)
    {
    dwSize-=1024;
    pDesFile->Write((LPVOID)((char*)lpMapAddress+dwCount),1024);
    dwCount+=1024;
    }
    else
    {
    pDesFile->Write((LPVOID)((char*)lpMapAddress+dwCount),dwSize);
    dwSize=0;
    }
    } pDesFile->Close();

    UnmapViewOfFile(lpMapAddress);
    CloseHandle(hMap); }
    catch (...)
    {
             }
    }
      

  8.   

    Kronus(Kronus): 如果几个线程同时  写  同一文件的内存映像的同一字段时,那么一定会发生冲突,但是
    如果几个线程同时  读  同一文件的内存映像的同一字段时,是否会有问题呢?是不是会引发冲突呢?在这一点上面我一直不太清楚。
      

  9.   

    我对内核对象也不是非常了解,因为我以前在用Event的时候,如果没有Mutex,就有偶尔可能发生程序莫名其妙退出的问题,而隔离一下就没有问题了,这个问题我花了好几天功夫才找出来。所以后来FileMapping我一直是用隔离,因为他们都是内核对象。至于什么原因,我一直也很想知道,但愿高手能指点一下。