各位大虾, 本人需要在win2000下用delphi6写一个读写软盘扇区的程序,该程序要求用virtualalloc分配内存作为扇区数据保存区,令人百思不得其解的是,读写该内存区竟然会出现access violation at 0xXXXX:read address 0xXXXX 这样的异常(而读写用Getmem分配的内存或用字节数组却不会出现这种错误),不知为什么?请指点指点!
delphi6 (试过delphi2,delphi4都不行)代码如下:
procedure TForm1.Button1Click(Sender: TObject);//下面的注解摘自Microsoft的support网站,大意是读扇区的时候必须按扇区字节数的倍数读取,缓冲区的地址也必须是扇区字节数的倍数,用virtualalloc来实现是一种较简便的方法(是不是还有其他方法可实现这些扇区读写要求?)
{With floppy and hard disks, all sector reads must start on sector boundaries on the disk and must be an integral number of sectors long. Furthermore, the buffers used for the reads must be aligned on addresses that fall on sector boundaries. For example, because a sector on a floppy disk is normally 512 bytes, the buffer that receives the sector data must be a multiple of 512 and must start on an address that is a multiple of 512. An easy way to guarantee that the buffer will start on a multiple of 512 is to allocate it with VirtualAlloc.}
 
//本代码读出软盘第0、1扇区并保存到硬盘的Sector.dat文件下type
  LPBYTE = ^BYTE;var
     hFD, hFile: THANDLE;
     dwNotUsed: DWORD;
     lpSector: LPBYTE;
     dwSize:DWORD;begin
   //  创建 Sector.dat 以保存读出的扇区数据.
   hFile := CreateFile ('Sector.dat',
                       GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
                       FILE_ATTRIBUTE_NORMAL, 0);   // 打开软驱a:
  hFD := CreateFile ('\\.\a:', GENERIC_READ,
                     FILE_SHARE_READ OR FILE_SHARE_WRITE,
                     nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);   if (hFD <> INVALID_HANDLE_VALUE) then BEGIN  //如果成功打开         dwSize := 2*512;  // 2个扇区
        
    //用virtualAlloc分配内存,把内存指针保存到lpSector
         lpSector := LPBYTE(VirtualAlloc (nil, dwSize,
                                  MEM_COMMIT or MEM_RESERVE,
                                  PAGE_READWRITE));         // 把文件指针移到需要读取的软盘扇区.
         SetFilePointer (hFD, 0, nil, FILE_BEGIN) ;         //读扇区,如成功则把读出的数据写到Sector.dat.
       if ReadFile (hFD, lpsector, dwSize, dwNotUsed, nil)  //在这里发生AV异常
       then  WriteFile (hFile, lpsector, dwSize, dwNotUsed, nil);        VirtualFree(lpSector, 0, MEM_RELEASE);
   END  ;   CloseHandle (hFD);
   CloseHandle (hFile); 
   
END; //End of Delphi code更为奇怪的是,如把上述代码翻译为c++代码,并在c++builder6下,在同样的机器上执行则不会发生任何异常。
我的电脑配置:cpu:Celeron 433, mem:128M,os:win2000

解决方案 »

  1.   

    lpSector := LPBYTE(VirtualAlloc (nil, dwSize,
    MEM_COMMIT or MEM_RESERVE,
    PAGE_READWRITE));
    改为
     lpSector := LPBYTE(VirtualAlloc (nil, dwSize,
    MEM_COMMIT, PAGE_READWRITE));看看如何
      

  2.   

    The following code is exactly translated from the above Delphi code and run well in C++Builder 6 without any exception.#include <vcl.h>
    #include <windows.h>void __fastcall TForm1::Button1Click(TObject *Sender)
    {/*
       This code reads sectors 0 and 1 from a floppy disk and writes
       the contents to a disk file named Sector.dat
    */{
       HANDLE  hFD, hFile;
       DWORD   dwNotUsed;   //  Disk file that will hold the floppy disk sector data.
       hFile = CreateFile ("Sector.dat",
                           GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
                           FILE_ATTRIBUTE_NORMAL, NULL);   // For the purposes of this sample, drive A: is the floppy drive.
       hFD = CreateFile ("\\\\.\\a:", GENERIC_READ,
                         FILE_SHARE_READ|FILE_SHARE_WRITE,
                         NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                         NULL);   // If the floppy disk was successfully opened, read sectors 0
       // and 1 from it and write their contents out to a disk file.
       if (hFD != INVALID_HANDLE_VALUE)
       {
      
             LPBYTE lpSector;
             DWORD  dwSize = 2 * 512;  // 2 sectors         //The following sentence comes from Microsoft developer support web page
             // "Allocate buffer to hold sectors from floppy disk. Note that
             // the buffer will be allocated on a sector boundary because the
             // allocation granularity is larger than the size of a sector on a
             // floppy disk."
             
             lpSector = (LPBYTE)VirtualAlloc (NULL, dwSize,
                                      MEM_COMMIT|MEM_RESERVE,
                                      PAGE_READWRITE);         // Move to the sector intended to read.
             SetFilePointer (hFD, 0,
                             NULL, FILE_BEGIN);         // Read sectors from the floppy disk and write them to a file.
             if (ReadFile (hFD, lpSector, dwSize, &dwNotUsed, NULL)) //without any exception
                WriteFile (hFile, lpSector, dwSize, &dwNotUsed, NULL);         VirtualFree (lpSector, 0, MEM_RELEASE);
          }
        CloseHandle (hFD);
        CloseHandle (hFile);
      }
    }
      

  3.   

    Really from the above delphi code???? I thought the delphi code is translated from C++ code.I'm sorry.. My floppy driver is broken down... I can't debug this code :(
      

  4.   

    此问题的确是先从c++上试验成功再转回delphi的,说“exactly translated”的意思只是说delphi代码和c++代码基本一样。
      

  5.   

    恶鱼杀手,可否帮小弟debug一把?把代码里的a:改成c: 在硬盘上试验可否?
      

  6.   

    我到没什么问题只是生成的sector.dat体积为0...................而且程序运行完乐才发生AV异常
      

  7.   

    sector.dat是可以生成的,有时候体积也可能就是两个扇区。
      

  8.   

    在delphi IDE里,delphi就会提示我产生AV异常,我只能采用run-reset program来强行中止该程序。
      

  9.   

    ReadFile第二个参数的指针的问题.呵呵........我用汇编重写乐代码:const
        FileName:string='C:\Sector.dat';
        Driver:string='\\.\C:';
    var
    hFD, hFile: THANDLE;
    dwNotUsed: DWORD;
    lpSector: PBYTE;
    dwSize:DWORD;
    begin
    asm
      // 创建 Sector.dat 以保存读出的扇区数据.
      PUSH 0
      PUSH FILE_ATTRIBUTE_NORMAL
      PUSH CREATE_ALWAYS
      PUSH 0
      PUSH 0
      PUSH GENERIC_WRITE
      PUSH FileName
      CALL CreateFile
      MOV hFile,EAX
      // 打开软驱a:
      PUSH 0
      PUSH FILE_ATTRIBUTE_NORMAL
      PUSH OPEN_EXISTING
      PUSH 0
      PUSH FILE_SHARE_READ OR FILE_SHARE_WRITE
      PUSH GENERIC_READ
      PUSH DRIVER
      CALL CreateFile
      MOV hFD,EAX
      CMP EAX,INVALID_HANDLE_VALUE
      JE @Error
        //如果成功打开
        MOV dwSize,2*512  // 2个扇区
        //用virtualAlloc分配内存,把内存指针保存到lpSector
        PUSH PAGE_READWRITE
        PUSH MEM_COMMIT
        PUSH dwSize
        PUSH 0
        CALL VirtualAlloc
        MOV lpSector,EAX
        // 把文件指针移到需要读取的软盘扇区.
        PUSH FILE_BEGIN
        PUSH 0
        PUSH 0
        PUSH hFD
        CALL SetFilePointer
        //读扇区,如成功则把读出的数据写到Sector.dat.
        PUSH 0
        LEA EAX,dwNotUsed
        PUSH EAX
        PUSH dwSize
    //    LEA EAX,lpSector
    //    PUSH EAX
        PUSH lpSector
        PUSH hFD
        CALL ReadFile
        CMP EAX,0
        JE @Release
        PUSH 0
        LEA EAX,dwNotUsed
        PUSH EAX
        PUSH dwSize
        PUSH lpSector
        PUSH hFile
        CALL WriteFile
        @Release:
        PUSH MEM_RELEASE
        PUSH 0
        PUSH lpSector
        CALL VirtualFree
      @Error:
      PUSH hFD
      CALL CloseHandle
      PUSH hFile
      CALL CloseHandle
    END; //End of Delphi code
    end;
      

  10.   

    因为ReadFile第二个参数有个var修饰附传引用.而SDK中ReadFile第二个参数是个指向缓冲区的指针,你直接调用你原来的代码,则是将lpSector的指针传递给ReadFile,ReadFile读完数据后将数据写入到lpSector所在的位置,破坏了堆栈,导致RET返回的时候跳到一个未知的地方发生AV错误(我这里运行完后才AV错误,说明是堆栈被破坏乐)
      

  11.   

    :) 好吧...反正我现在专家分再高也加不了星星乐.随便你吧.但还是要揭帖啊.呵呵..这个题目出现的问题就是指针的问题导致堆栈被破坏.你要是闲全部汇编代码麻烦乐,那就直接把ReadFile那句改为汇编代码啊:
      

  12.   

    恶鱼杀手,有乐你的帮助,这个春节可以快快乐乐过了!再次说声谢谢!以后有问题还向你请教!
    Eastunfail是“东方不败”的意思吗?为什么专家分再高也加不了星星呢?
      

  13.   

    呵呵...我初二申请乐QQ,当时星际争霸年级里无人能比,就给自己QQ名字取乐个东方不败的名字,初三在CSDN里面申请ID理所当然就用乐Eastunfail这个名字乐.按照专家分来凭星星,两星是极限乐.