Внешние процедуры Oracle с Delphi DLL - PullRequest
1 голос
/ 10 мая 2019

Я пытался настроить функцию «Внешние процедуры» для моей базы данных Oracle 18c в Windows 10.

В документации утверждается, что для настройки этой функции просто необходимо установить значения среды в extrproc.ora - в моем случае это всего лишь одна строка: SET EXTPROC_DLLS=ANY (для целей тестирования).

Я создал DLL для проверки своей конфигурации с помощью Delphi со следующим кодом:

library testdll;

function Sum(x, y: Integer): Integer; stdcall;
begin
  Result := x + y;
end;

function Subtract(x, y: Integer): Integer; stdcall;
begin
  Result := x - y;
end;

procedure TEST; stdcall;
begin

end;

exports
  TEST,
  Sum,
  Subtract;

begin

end.

Итак, мой первый вопрос: нужно ли мне скомпилировать DLL как x64 или как x86? Кроме того, является ли соглашение о вызовах stdcall совместимым с Oracle 18c?

Кроме того, я создал псевдоним библиотеки в своей базе данных, используя:

CREATE OR REPLACE LIBRARY MySchema.TESTDLL AS 'C:\testdll.dll'

Внешняя процедура была опубликована

create or replace PROCEDURE TESTPROCEDURE
AS LANGUAGE C 
NAME "TEST"
LIBRARY TESTDLL;

Теперь, когда у меня все настроено в базе данных, я попытался вызвать процедуру:

begin
TESTPROCEDURE();
end;

Но когда я попытался позвонить, я получил следующую ошибку:

ORA-06520: PL/SQL: Fehler beim Laden der externen Library
ORA-06522: Unable to load DLL
ORA-06512: in "MySchema.TESTPROCEDURE", Zeile 1
ORA-06512: in Zeile 2
06520. 00000 -  "PL/SQL: Error loading external library"
*Cause:    An error was detected by PL/SQL trying to load the external
           library dynamically.
*Action:   Check the stacked error (if any) for more details.

Информация:

  • База данных правильно порождает extproc.exe, после того как я вызову процедуру

Как мне правильно работать и совместимы ли библиотеки Delphi DLL?

Это мой listener.ora файл:

# listener.ora Network Configuration File: C:\app\oracle\product\18.0.0\dbhomeXE\NETWORK\ADMIN\listener.ora
# Generated by Oracle configuration tools.

DEFAULT_SERVICE_LISTENER = XE

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (SID_NAME = CLRExtProc)
      (ORACLE_HOME = C:\app\oracle\product\18.0.0\dbhomeXE)
      (PROGRAM = extproc)
      (ENVS = "EXTPROC_DLLS=ONLY:C:\app\oracle\product\18.0.0\dbhomeXE\bin\oraclr18.dll")
    )
  )

LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )

А это мой файл tnsnames.ora:

# tnsnames.ora Network Configuration File: C:\app\oracle\product\18.0.0\dbhomeXE\NETWORK\ADMIN\tnsnames.ora
# Generated by Oracle configuration tools.

XE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XE)
    )
  )

LISTENER_XE =
  (ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 1521))


ORACLR_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
    (CONNECT_DATA =
      (SID = CLRExtProc)
      (PRESENTATION = RO)
    )
  )

Есть идеи, что может быть не так с моей конфигурацией?

Edit: Это пример кода .c dll из базы данных:

/*
** Copyright (c) 1997 by Oracle Corporation
**
** NAME
**   EXTERN.C 
**
** DESCRIPTION
**   Sample Windows NT External Procedure: find_max 
**
*/

#include <windows.h>

#define NullValue -1

/*
  This function simply returns the returns the larger of x and y.
*/

long __declspec(dllexport) find_max(long    x, 
                    short   x_indicator, 
                                    long    y, 
                        short       y_indicator, 
                    short       *ret_indicator)
{
   /* It can be tricky to debug DLL's that are being called by a process
      that is spawned only when needed, as in this case.  
      Therefore try using the DebugBreak(); command.  
      This will start your debugger.  Uncomment the following line and
      you can step right into your code.
   */
   /* DebugBreak();  */

   /* first check to see if you have any nulls */
   /* Just return a null if either x or y is null */

   if ( x_indicator==NullValue || y_indicator==NullValue) {
      *ret_indicator = NullValue;   
      return(0);
   } else { 
      *ret_indicator = 0;        /* Signify that return value is not null */
      if (x >= y) return x;
      else return y;
   }
}

А это файл make.bat:

REM USAGE: just type MAKE
if (%PROCESSOR_ARCHITECTURE%)==(IA64) goto win64_ia64
if (%PROCESSOR_ARCHITECTURE%)==(AMD64) goto win64_amd64
 cl -I. /LD -Zi extern.c /link msvcrt.lib /nod:libcmt /DLL
 goto fi
:win64_ia64
 cl /DWIN64 /D_WIN64 /DSS_64BIT_SERVER /D_IA64_=1 -I. /LD -Zi extern.c /link msvcrt.lib /nod:libcmt /DLL
 goto fi
:win64_amd64
 cl /GS- /DWIN64 /D_WIN64 /DSS_64BIT_SERVER /D_AMD64_=1 -I. /LD -Zi extern.c /link msvcrt.lib /MACHINE:AMD64 /nod:libcmt /DLL
 goto fi
:fi

1 Ответ

1 голос
/ 10 мая 2019

На основании предоставленной информации я вижу следующее:

  • Пример C скомпилирован как 64-битная DLL.
  • В примере C не указано соглашение о вызовахи поэтому использует значение по умолчанию cdecl.
  • . Функции примера C принимают 5 аргументов, но ваши функции принимают только 2.
...