Как исправить ошибку «Нет больше файлов» в приложении Delphi с таблицами Paradox в Windows 10 1803? - PullRequest
0 голосов
/ 07 июня 2018

В старых приложениях Delphi, которые используют старый и устаревший, но все еще используемый механизм баз данных BDE с файлами базы данных Paradox, находящимися на компьютере Windows 10, который обновлен до версии 1803 «Spring Creators Update», но на клиентских компьютерах используется любая более старая версияВ Windows, такой как Windows 10 1709 или Windows 7, открытие таблицы Paradox иногда завершается с ошибкой «No more files», код ошибки idapi32.dll DBIERR_OSENMFILE.Это вызывает исключение EDBEngineError в DBTables.pas / TTable.GetHandle (), которое вызывается TTable.CreateHandle, вызывается TBDEDataSet.OpenCursor ().

Кажется, что ошибка вызвана некоторыми связанными с совместным доступом к файламИзменения в обновлении Windows 10 1803.Удаление обновления 1803 с компьютера с общим доступом к файлам Windows 10 или обновление всех клиентских компьютеров до Windows 10 + 1803, похоже, устраняет ошибку.

Люди предполагают, что изменения как-то связаны сПротокол SMB, может быть Защитник Windows и / или другие вопросы, связанные с безопасностью.Вот обсуждение Google Plus https://plus.google.com/106831056534874810288/posts/F4nsoTz2pDi

Как можно обойти ошибку «Нет больше файлов» с помощью некоторых достаточно легко выполнимых изменений в приложении Delphi, позволяя клиентским и серверным компьютерам общего доступа к файлам продолжать использоватьразнородные версии для Windows?

Пожалуйста, старайтесь не отвечать и не комментировать такие очевидные вещи, как "небо голубое" или "BDE стар и устарел".Сохранение BDE - это решение, которое не может быть изменено, конечно, не как «исправление ошибки».

В качестве экстренного исправления мы прибегли к простой повторной попытке DbiOpenTable, когда он возвращает код ошибки DBIERR_OSENMFILE.Я отправил ответ с исходным кодом взломать idapi32.dll.Пока что кажется, что если первая таблица DbiOpenTable сообщает «Нет больше файлов», вторая попытка завершается успешно, и приложение работает, ничего не замечая.

Ответы [ 2 ]

0 голосов
/ 15 июня 2018

VMWare, Virtual Box и т. Д. Для виртуализации Windows 7. Если, как вы говорите, W7 работает безупречно, это решит проблему.

0 голосов
/ 07 июня 2018
  • ПРЕДУПРЕЖДЕНИЕ : далее следует взломать.Кладж.Пластырь, клей, клейкая лента и жвачка.BDE старый.Вы полностью одиноки, если используете BDE и / или пытаетесь взломать.Я не несу ответственности за его использование.Если это работает для вас, хорошо для вас.Если это разрушает ваш бизнес, это плохо для вас.

Поскольку таблицы Paradox по-прежнему в основном работали и ошибка, по-видимому, была слегка случайной, а поскольку кто-то подозревал, что Защитник Windows как-то с этим связан, яподумал, может быть, это просто нужно немного покататься вокруг.Если DbiOpenTable () внезапно запускается , иногда , происходит сбой при определенной комбинации версий клиент / сервер SMB, потому что "Нет больше файлов" ... тогда почему бы просто не попробовать снова выполнить файловую операцию.Я помещаю логику «если она возвращает ошибку DBIERR_OSENMFILE, затем Sleep () и попробуйте снова» вокруг функции DbiOpenTable, и угадываю, что, похоже, сработало.любой, кто должен поддерживать приложения на основе BDE.Поэтому я сделал исправление для функции DbiOpenTable idapi32.dll, начиная со старой подпрограммы, изначально написанной Рейнальдо Яньесом, для исправления ошибки «недостаточно места на диске» в BDE, когда свободное место на диске находится на границе 4 ГБ.См. https://cc.embarcadero.com/Item/21475

Чтобы использовать это, добавьте Fix1803 в предложении использования и вызовите PatchBDE где-нибудь перед началом открытия таблиц Paradox.Может быть, когда вы закончите, позвоните 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.
...