Тут находится официальная инструкция по написанию внешних процедур с помощью ODAC.
Ниже я привожу пример исходников, который будет работать с несколькими Oracle Home.
Работоспособность проверена в окружении Delphi XE5, ODAC 9.3.9 и двух OracleHome, в одном сервер Оракл 10g, а во втором - клиент Оракл 10g. Оба физически установлены на одном компьютере на разных дисках. Надеюсь, это вам поможет.
Да, чуть не забыл: dll-ку совсем не обязательно укладывать в папку bin - в моем случае она находилась вообще вне папки с установкой Ораклов, хотя и на той же машине. Но вот путь к dll-ке при регистрации библиотеки в Оракл нужно указывать полный.
Код: Выделить всё
library AISDAUtils;
uses
SysUtils,
OraCall,
OraClasses,
OraError;
{$R *.res}
procedure ExportLob(Context: POCIExtProcContext; Locator: POCILobLocator; Params: PAnsiChar); cdecl;
var
OraPath: string;
OraEnv: TOCIEnvironment;
OraSvcCtx: TOCISvcCtx;
OraLob: TOraLob;
I: Integer;
hEnv: POCIEnv;
hSvcCtx: POCISvcCtx;
hError: POCIError;
begin
// Инициируем OracleHomes.Default на тот OracleHome, из которого был осуществлен вызов процедуры по пути основного процесса, который залинковал мою библиотеку
OraPath := ExtractFilePath(ParamStr(0));
for I := 0 to OracleHomes.Count - 1 do
if Pos(OracleHomes[I].Path, OraPath) = 1 then
begin
OracleHomes.Default := OracleHomes[I];
Break;
end;
// Инициируем OracleHomes.Default, с которым далее и будем работать
if not OracleHomes.Default.Inited then
OracleHomes.Default.Init;
try
// Получаем указатели на окружение, контекст и ошибки
OracleHomes.Default.OCI8.OCIExtProcGetEnv(Context, hEnv, hSvcCtx, hError);
// Инициализируем окружение
OraEnv := OracleHomes.Default.AllocEnvironment(hEnv);
// Создаем и инициализируем контекст
OraSvcCtx := TOCISvcCtx.Create(OraEnv, hSvcCtx, OraEnv.UnicodeEnv);
try
// ВНИМАНИЕ! В в официальном примере тут в параметре почему-то передается указатель на контекст hSvcCtx, а не на экземпляр класса TOCISvcCtx
OraLob := TOraLob.Create(OraSvcCtx);
// Далее все по примеру за исключением того, что при обработке ошибок необходимо приводить сообщение к Ansi-строке
try
OraLob.OCILobLocator := Locator;
OraLob.ReadLob;
// Далее делаем то, что нам нужно с полученным LOB
// ...
finally
OraLob.Free;
end;
finally
// Мы контекс создали, мы же его и освобождаем
OraSvcCtx.Free;
end;
except
on E: EOraError do
OracleHomes.Default.OCI8.OCIExtProcRaiseExcpWithMsg(Context, E.ErrorCode, PAnsiChar(AnsiString(E.Message)), Length(E.Message));
on E: Exception do
OracleHomes.Default.OCI8.OCIExtProcRaiseExcpWithMsg(Context, 20000, PAnsiChar(AnsiString(E.Message)), Length(E.Message));
end;
end;
exports
ExportLob;
begin
OracleHomes.Init;
end.
И да... если есть необходимость экспортировать NULL/EMPTY_CLOB/EMPTY_BLOB, то в Оракле надо бы вначале проинициализировать локатор с помощью dbms_lob.createtemporary и, после вызова внешней процедуры, финализировать его с помощью dbms_lob.freetemporary. В противном случае, во внешней процедуре можно поймать исключительную ситуацию с "Invalid LOB locator".
С уважением,
Владимир.