如何解决"没有更多文件"在Windows 10 1803上使用Paradox表的Delphi应用程序出错?

时间:2018-06-07 12:47:33

标签: delphi bde

在旧的Delphi应用程序中使用旧的和已弃用但仍使用的BDE数据库引擎,其中Paradox数据库文件驻留在Windows 10计算机上,该计算机文件已更新到1803" Spring Creators Update"版本,但客户端计算机使用任何旧版本的Windows,如Windows 10 1709或Windows 7,打开一个Paradox表有时会失败,并且#34;没有更多文件"错误,idapi32.dll错误代码DBIERR_OSENMFILE。这会在DBTables.pas / TTable.GetHandle()中引发EDBEngineError异常,该异常由TTable.CreateHandle调用,由TBDEDataSet.OpenCursor()调用。

该错误似乎是由Windows 10 1803更新中的某些与文件共享相关的更改引起的。从文件共享Windows 10计算机中删除1803更新,或将所有客户端计算机更新到Windows 10 + 1803似乎会使错误消失。

人们推测这些变化与SMB协议有关,可能与Windows Defender和/或其他安全相关的问题有关。这是Google Plus的讨论 https://plus.google.com/106831056534874810288/posts/F4nsoTz2pDi

"没有更多文件"通过Delphi应用程序中一些相当容易实现的更改来解决错误,同时允许文件共享客户端和服务器计算机继续使用异构Windows版本?

请尽量避免回答或评论不言而喻的事情,例如"天空是蓝色的"或者" BDE已经过时并已弃用"。保持BDE是一个无法改变的决定,当然不是一个错误修复"。

作为紧急修复,当它返回DBIERR_OSENMFILE错误代码时,我们只是简单地重新尝试DbiOpenTable。我发布了一个带有源代码的答案到idapi32.dll hack。到目前为止,似乎第一个DbiOpenTable说'#34;没有更多文件",第二次尝试成功,应用程序工作没有注意到任何事情。

2 个答案:

答案 0 :(得分:0)

  • 警告 :以下是黑客行为。一个kludge。创可贴,胶水,胶带和口香糖。 BDE很老了。如果您使用BDE和/或尝试此黑客攻击,则完全靠您自己。我对其使用不承担任何责任。如果它适合你,对你有好处。如果它破坏了你的生意,对你不利。

由于Paradox表仍然大部分工作,并且错误似乎有点随机触发,并且因为有人怀疑Windows Defender与它有关,我想也许它只是需要一些踢。如果DbiOpenTable()突然启动有时失败了某个SMB客户端/服务器版本的组合,因为“没有更多的文件”...那么为什么不再尝试文件操作。我把“如果它返回一个DBIERR_OSENMFILE错误,然后Sleep()并再次尝试”围绕DbiOpenTable函数的逻辑,并猜测它 - 它似乎工作。

对于必须维护基于BDE的应用程序的人来说,熟悉BDE的“功能”是很常见的。所以我围绕idapi32.dll的DbiOpenTable函数进行了修补,从ReinaldoYañez编写的旧例程开始,当可用磁盘空间处于4 GB边界时,用BDE修复“磁盘空间不足”错误。见https://cc.embarcadero.com/Item/21475

要使用它,请在uses子句中添加Fix1803,并在开始打开Paradox表之前在某处调用PatchBDE。可能在你完成后调用UnPatchBDE,尽管我认为这不是必要的。

但请记住,你是独立的,这是非常实验性的代码。

unit Fix1803;
// * KLUDGE WARNING * 
// Patch (hack) idapi32.dll DbiOpenTable() to try harder, to work with Windows 10 1803 "Spring Creators Update".
//
// The patching routine is an extension of code originally written by Reinaldo Yañez.
//  see https://cc.embarcadero.com/Item/21475
//
// Some original Spanish comments are left in place.

interface

procedure PatchBDE;
procedure UnPatchBDE;

implementation

uses
  Windows, Db, DbTables, BDE, SysUtils;

// -------------------------------------------  DbiOpenTable hook
var DbiOpenTable_address_plus_9 : Pointer;
function Actual_DbiOpenTable_CallStub(hDb: hDBIDb; pszTableName: PChar; pszDriverType: PChar; pszIndexName: PChar; pszIndexTagName: PChar; iIndexId: Word; eOpenMode: DBIOpenMode; eShareMode: DBIShareMode; exltMode: XLTMode; bUniDirectional: Bool; pOptParams: Pointer; var hCursor: hDBICur): DBIResult stdcall; assembler;
asm
// these two instructions are implicitly contained in the start of the function
//        push ebp
//        mov ebp, esp
        add esp, $fffffee8
        jmp  dword ptr [DbiOpenTable_address_plus_9]
end;

function LogHook_DbiOpenTable (hDb: hDBIDb; pszTableName: PChar; pszDriverType: PChar; pszIndexName: PChar; pszIndexTagName: PChar; iIndexId: Word; eOpenMode: DBIOpenMode; eShareMode: DBIShareMode; exltMode: XLTMode; bUniDirectional: Bool; pOptParams: Pointer; var hCursor: hDBICur): DBIResult stdcall;
var
  i : Integer;
begin
  Result := Actual_DbiOpenTable_CallStub(hDb, pszTableName, pszDriverType, pszIndexName, pszIndexTagName, iIndexId, eOpenMode, eShareMode, exltMode, bUniDirectional, pOptParams, hCursor);
  // if we got the "No more files" error, try again... and again.
  i := 1;
  while (Result = DBIERR_OSENMFILE) and (i < 10) do
  begin
    Windows.Sleep(i);
    Result := Actual_DbiOpenTable_CallStub(hDb, pszTableName, pszDriverType, pszIndexName, pszIndexTagName, iIndexId, eOpenMode, eShareMode, exltMode, bUniDirectional, pOptParams, hCursor);
    Inc(i);
  end;
end;

// -------------------------------------------  Patching routines
const // The size of the jump instruction written over the start of the original routine is 5 bytes
  NUM_BYTES_OVERWRITTEN_BY_THE_PATCH = 5;

type
  TRYPatch = record
    OrgAddr: Pointer;
    OrgBytes: array[0..NUM_BYTES_OVERWRITTEN_BY_THE_PATCH-1] of Byte;
  end;

procedure TRYPatch_Clear(var ARYPatch : TRYPatch);
begin
  FillChar(ARYPatch, SizeOf(TRYPatch), 0);
end;

function RedirectFunction(OldPtr, NewPtr, CallOrigStub : Pointer; var OriginalRoutineAddressPlusN: Pointer; NumBytesInCompleteInstructionsOverwritten : Integer): TRYPatch;
type
  PPtr=^pointer;
  PPPtr=^PPtr;
  TByteArray=array[0..maxint-1] of byte;
  PByteArray=^TByteArray;

function SameBytes(Ptr1, Ptr2 : Pointer; NumBytes : Integer) : Boolean;
  var
    i : Integer;
  begin
    Result := true;
    i := 0;
    while (Result) and (i < NumBytes) do
    begin
      Result := Result and ((PByteArray(Ptr1)^[i] = PByteArray(Ptr2)^[i]));
      Inc(i);
    end;
  end;

var
  PatchingAddress : Pointer;
  OldProtect,
  Protect   : DWORD;
  p: PByteArray;
  i : Integer;
begin
  PatchingAddress := OldPtr;
  if PWord(PatchingAddress)^ = $25FF then
  begin {Es un JMP DWORD PTR [XXXXXXX](=> Esta utilizando Packages)}
    p := PatchingAddress;
    PatchingAddress := (PPPtr(@p[2])^)^; // PatchingAddress now points to the start of the actual original routine
  end;


// Safety check (as if this thing was "safe"). The given replacement routine must start with the same bytes as the replaced routine.
  // Otherwise something is wrong, maybe a different version of idapi32.dll or something.
  if (CallOrigStub <> nil) and not SameBytes(PatchingAddress, CallOrigStub, NumBytesInCompleteInstructionsOverwritten) then
    raise Exception.Create('Will not redirect function, original call stub doesn''t match.');


// Change memory access protection settings, so we can change the contents
  VirtualProtect(PatchingAddress, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH, PAGE_READWRITE, @OldProtect);


// Save the old contents of the first N bytes of the routine we're hooking
  Result.OrgAddr := PatchingAddress; // Save the address of the code we're patching (which might not be the same as the original OldPtr given as parameter)
  for i := 0 to NUM_BYTES_OVERWRITTEN_BY_THE_PATCH-1 do
    result.OrgBytes[i] := PByte(Integer(PatchingAddress) + i)^;


// Replace the first bytes of the original function with a relative jump to the new replacement hook function
  // First write the instruction opcode, $E9 : JMP rel32
  PByte(PatchingAddress)^:= $E9;
  // Then write the instruction's operand: the relative address of the new function 
  PInteger(Integer(PatchingAddress)+1)^ := Integer(NewPtr) - Integer(PatchingAddress) - 5;


// Address to jump to, for the replacement routine's jump instruction 
  OriginalRoutineAddressPlusN := Pointer(Integer(PatchingAddress) + NumBytesInCompleteInstructionsOverwritten);


// Restore the access protection settings
  VirtualProtect(PatchingAddress, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH, OldProtect, @Protect);
  FlushInstructionCache(GetCurrentProcess, PatchingAddress, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH);
end;


procedure RestorePatch(RestorePatch: TRYPatch);
var
  OldProtect,
  Protect   : DWORD;
  OldPtr: Pointer;
  i : Integer;
begin
  OldPtr := RestorePatch.OrgAddr;
  VirtualProtect(OldPtr, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH, PAGE_READWRITE, @OldProtect);
  for i := 0 to NUM_BYTES_OVERWRITTEN_BY_THE_PATCH-1 do
    PByte(Integer(OldPtr) + i)^ := RestorePatch.OrgBytes[i];

    VirtualProtect(OldPtr, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH, OldProtect, @Protect);
  FlushInstructionCache(GetCurrentProcess, OldPtr, NUM_BYTES_OVERWRITTEN_BY_THE_PATCH);
end;


var
  idapi32_handle: HMODULE;
  Patch_DbiOpenTable : TRYPatch;


procedure PatchBDE;
begin
  if idapi32_handle <> 0 then Exit; // already_patched
  idapi32_handle := LoadLibrary('idapi32');
  if idapi32_handle <> 0 then
  begin
    Patch_DbiOpenTable := RedirectFunction(GetProcAddress(idapi32_handle, 'DbiOpenTable'), @LogHook_DbiOpenTable, @Actual_DbiOpenTable_CallStub, DbiOpenTable_address_plus_9, 9);
  end;
end;

procedure UnPatchBDE;
begin
  if idapi32_handle <> 0 then
  begin
    {Leave everything as before, just in case...}
    if Patch_DbiOpenTable.OrgAddr <> nil then
      RestorePatch(Patch_DbiOpenTable);
    FreeLibrary(idapi32_handle);
    idapi32_handle := 0;
  end;
end;

initialization
  idapi32_handle := 0;
  TRYPatch_Clear(Patch_DbiOpenTable); 

end.

答案 1 :(得分:-1)

VMWare,Virtual Box等虚拟化Windows 7.如果像你说的那样,W7可以完美地工作,从而解决问题。