Link to home
Start Free TrialLog in
Avatar of Biss
Biss

asked on

ORA_FFI: How can I read buffers that contain zeroes

I have a problem with the OracleForms Foreign Functions interface (ORA_FFI package).

My foreign function is written in C++ and it returns a buffer of bytes. How can I obtain that buffer in PL/SQL? I have tried using varchar2 but it fails if the buffer contains zeroes. The first encountered zero is interpreted as a string terminator and everything contained in the buffer after that zero is ignored. All I get in the varchar2 variable is the contents of the buffer *before* the zero.

So, my quiestion is: what PL/SQL datatype can I use to process buffers returned by foreign functions?

Thanks.
Avatar of ORACLEtune
ORACLEtune

hi,

What is the nature/substance of the data returned by your foreign function written in c++ -

PL/SQL variables can have any SQL datatype. PL/SQL datatype are similar in nature to other programming languages:  date, char, number, int, boolean ... with many permutations withing these "base" data types.   The real question here may be:  "what is data type" being returned by the foreign function -".

Try "piping", sending the foreign function output to a string variable, then writing the string variable to your varchar2.

PL/SQL ONLINE DOC:

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96624/toc.htm

SQL ONLINE DOC:
http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96590/adfnstyp.htm#417949

good luck, sounds like an interesting integration project.  Eric.
Avatar of Biss

ASKER

Hi! Here are more details:

The project I'm working on is written in Oracle Forms (hardly my favorite development tool but we cannot always choose our projects). Oracle Forms has many limitations and for some of them simply there are no workarounds. Therefore, we decided to write part of the application in another language - Java. When the main Forms application is started, it automatically starts the Java application as a separate background OS process. Java remains hidden and silent until Forms requests something - different things can be requested, some of them invovling GUI.

Now comes the interesting part - Forms and Java must be able to communicate. There are a number of ways for two OS processes to communicate - some better than others.

First we decided to communicate through Oracle Advanced Queueing and everything was fine but an unexpected obstacle occurred -- for such communication to work, both Forms and Java had to establish a connection to Oracle. Well, the company I work for is really large and they had a huge amount of Oracle connections. Now they had to have twice that number of connections -- for each Forms application there has to be also a Java application. The database servers ran out of resources and also the number of allowed connections was exceeded. We had to find another solution.

The current solution for the MS Windows platform is to use Inter-Process Communication (through shared memory). I wrote a DLL which takes care of the communication. Java uses the DLL through JNI (Java Native Interface). Forms uses the DLL through ORA_FFI. To say it simple, the DLL provides a buffer in memory accessible by both Forms and Java. They communicate by writing data in that buffer for the other process to read. Each process stays suspended until data appears for it in the buffer. Then it reads the data, parses it and performs some action specified by the data.

Although Java's idea of buffers is greatly limited, I can still use byte array -- byte[] -- to store the data before parsing.

Forms, however, have a huge problem with buffers. Here's the some of the DLL interface to Forms:

extern "C" IPC_API int sendMessage(char *ppData, int piDataSize, long pdwTimeout);
extern "C" IPC_API int receiveMessage(char *ppData, int *piDataSize, long pdwTimeout);

As you can see, plain char* pointer is used. Just a buffer of chars (bytes). Here's my current PL/SQL interface to it:

function  sendMessage(pnMsgType in pls_integer, psMsgContents in varchar2 default NULL, piTimeout in pls_integer) return pls_integer;
function receiveMessage(pnMsgType out pls_integer, psMsgContents out varchar2, piTimeout in pls_integer) return pls_integer;

I am receiving the data into a VARCHAR2 variable.

And that's causing problems, if the buffer contains zeroes. Because the zeroes are string terminators in PL/SQL (just like in C++). In C++, however, I don't view the buffer as a string. I view it as a sequence of bytes and I can do whatever I wish with it by just moving a pointer around.

In PL/SQL that is NOT the case. If PL/SQL encounters a zero, it decides that this is the end of the string and everything after that zero is dropped out. Since I'm using a VARCHAR2 to access the buffer (through the ORA_FFI built-in package), this means that I can only see that part of the buffer that comes *before* the zero.

I have considered a LOB, but I simply don't know how to map a LOB to a C++ buffer through ORA_FFI. At least I haven't found anything mentioned in the ORA_FFI documentation.

That's why I need help. I would appreciate any suggestion as to how I can access ALL my data. Not just the part before the first zero. It sounded so simple. Just a plain buffer containing arbitrary bytes. But PL/SQL has made matters disastrous. Thanks to everybody who can tell me what datatype I can use to access arbitrary data contained in a C++ buffer of bytes.

(Yes, we can escape all zeroes by encoding them with special non-zero-containing sequences and later we can decode these sequences back to the corresponding value -- that's what I'm doing now. But that's terrible. I have a huge amount of hard-to-maintain code dealing with encoding and decoding. All that because PL/SQL cannot handle even the simplest data structures.)
Avatar of Biss

ASKER

Oh, just a little clarification:

Of course, the actual PL/SQL interface to the DLL's functions is:


function ff_sendMessage(phFuncHandle ORA_FFI.FuncHandleType,
                                     psData      in out varchar2,
                                     piDataSize in     pls_integer,
                                     piTimeout  in      pls_integer) return pls_integer;
pragma interface(C, ff_sendMessage, 11265);


function ff_receiveMessage(phFuncHandle ORA_FFI.FuncHandleType,
                                        psData      in out varchar2,
                                        piDataSize in out pls_integer,
                                        piTimeout   in      pls_integer) return pls_integer;
pragma interface(C, ff_receiveMessage, 11265);


But, of course, I use much more "civilized" functions (like the ones mentioned in my previous post) to access these lowest-level interfaces.
hi,

What is the nature/substance of the data returned by your foreign function written in c++ -

PL/SQL variables can have any SQL datatype. PL/SQL datatype are similar in nature to other programming languages:  date, char, number, int, boolean ... with many permutations withing these "base" data types.   The real question here may be:  "what is data type" being returned by the foreign function -".

Try "piping", sending the foreign function output to a string variable, then writing the string variable to your varchar2.

PL/SQL ONLINE DOC:

http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96624/toc.htm

SQL ONLINE DOC:
http://otn.oracle.com/docs/products/oracle9i/doc_library/release2/appdev.920/a96590/adfnstyp.htm#417949

good luck, sounds like an interesting integration project.  Eric.
i see,

here's an ORA_FFI A Worked Example DLL-> Trigger example.


1.  The C Code that is the DLL function:
----------------------------------------

#include <string.h>
#include <windows.h>



//Simple C function that just puts something in a string
// and returns the length of that string both as the RC and as a param
// Keeping the C interface as simple as possible.
// The function is exported from the DLL
// You can use the Visual C utility DUMPBIN to check that the
// Function is correctly exported.

__declspec( dllexport ) int PopulateString ( char *FormsBuffer, int *BuffLen)
{
        int LocalLength;
        strcpy(FormsBuffer,"A Fixed string from within the C program");
        *BuffLen    = strlen(FormsBuffer);
        LocalLength = strlen(FormsBuffer);
        return LocalLength;
}


2.  The package Header for the FFI code:
----------------------------------------

PACKAGE FFISample IS
/* So this is the Pubic Interface */
FUNCTION PopulateString ( PopulateString IN OUT VARCHAR2,
                          BuffLen IN OUT PLS_INTEGER)    
                          RETURN PLS_INTEGER;
END FFISample;


3.  The package Body for the FFI code:
--------------------------------------

PACKAGE BODY FFISample IS

 -- Define the various Handles
        lh_ffisamp              ora_ffi.libHandleType;
        fh_PopulateString       ora_ffi.funcHandleType;

 -- Define internal versions of the functions

    /* The interface function, note how the C Pointers map to
       IN OUT variables! */
    FUNCTION i_PopulateString ( funcHandle      IN ora_ffi.funcHandleType,
                                PopulateString  IN OUT VARCHAR2,
                                BuffLen         IN OUT PLS_INTEGER)
                                RETURN PLS_INTEGER;
 -- interface the internal function to the C call interface
    PRAGMA INTERFACE(C,i_PopulateString,11265);

 -- Body of the Externalised function
    FUNCTION PopulateString (   PopulateString  IN OUT VARCHAR2,
                                BuffLen         IN OUT PLS_INTEGER)
                                RETURN PLS_INTEGER IS
    -- declare the buffer padded to the max possible length of the string
    -- This is an In Out var so PopulateString may already have some
    --  content
        /* This RPAD ensures that the VARCHAR string occupies
           contiguous memory */
        PopulateString_l        VARCHAR2(255)  :=  
RPAD(SUBSTR(NVL(PopulateString,' '),1,255),255,CHR(0));

    -- internal buffer for the Int no padding required here.
        BuffLen_l               PLS_INTEGER := BuffLen;
        rc                      PLS_INTEGER;
   BEGIN
     -- Call the internal version of the function with the local buffers
     rc  := i_PopulateString( fh_PopulateString, PopulateString_l, BuffLen_l);

     -- Reset the Real variables with the contents of the buffer
     PopulateString := PopulateString_l;
     BuffLen := BuffLen_l;

   RETURN (rc);
   END ;

-- Package Body Block, handles loading the DLL into memory and registering the
Functions
BEGIN
     BEGIN
        -- Try and fin the DLL already loaded
        lh_ffisamp := ora_ffi.find_library('ffisamp.dll');
     EXCEPTION WHEN ora_ffi.FFI_ERROR THEN
        -- Ok not found lets load it
        -- The null argument for the LOAD library is the DLL location so if
        -- the DLL was not in
        -- the working DIR or not in the PATH soemwhere we might want to
        -- put something in here.
        lh_ffisamp := ora_ffi.load_library(NULL,'ffisamp.dll');
     END ;
     
     -- Now register the PopulateString
     fh_PopulateString :=  ora_ffi.register_function(lh_ffisamp,
                           'PopulateString',ora_ffi.C_STD);
     ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_CHAR_PTR);
     ora_ffi.register_parameter(fh_PopulateString,ORA_FFI.C_INT_PTR);
     ora_ffi.register_return(fh_PopulateString,ORA_FFI.C_INT);

EXCEPTION /* Generic FFI Error Handler to unwind the TOOL_ERR stack */
        when others then
                for iCounter in 1..tool_err.nerrors LOOP
                        message(tool_err.message);
                        tool_err.pop;
                end loop;
END FFISample;


4.  Finally a sample forms trigger that calls the C code:
---------------------------------------------------------
   (this might be say on a When-Button-Pressed  trigger)

declare
  StringBuffer varchar2(255)
           := 'An initial value that will be overwritten';
  StringLength pls_integer   := length(StringBuffer);
  RC           pls_integer;
begin
  message('Before Calling My c the value of the string is:'||
           StringBuffer);
  message('Old Length '||to_char(StringLength));
  RC := FFISample.PopulateString(StringBuffer,StringLength);
  message('The New value for String is: '||StringBuffer);
  message('New Length '||to_char(StringLength));
  message('RC was '||to_char(RC));
end;
.

the end.
another doc, may be useful:

ORAFFI: PASSING CHARACTER STRING POINTERS BACK FROM ORA_FFI FUNCTIONS
 
Problem Description:
====================
 
You need know the details on passing a character string pointer (char*) back
from a function into ORA_FFI.
 
Symptoms of improper implementations of this may be a GPF in DE15WIN.DLL.
 
 
Related Terms:
==============
ORAFFI
PTR
LPCTSTR
DLL

Solution Description:
=====================
 
The memory for the char pointer must be globally allocated using a GlobalAlloc
(with the GMEM_FIXED flag) such that the memory is not resident on the stack.
The problem with allocating memory on the stack for the pointer is that this
memory block will be freed when the function exits and the returned pointer
will become invalid.
 
Here is a complete example showing a successful implementation of this
functionality. Although the following code demonstrates a 16bit
implementation, the same holds true for 32bit implementations.
 
-- ********************************************************* --
-- This calling trigger's code                                --
-- ********************************************************* --
 
declare
  -- Parameters
  parm1 VARCHAR2(128) := 'X';
  -- Return value
  rt VARCHAR2(512) := 'Hello Folks';
begin
  rt := MyLib.MyFunc1( parm1 );
  Message(rt);
end;
 
-- ********************************************************* --
-- This wrapper package's header                             --
-- ********************************************************* --
 
PACKAGE MyLib IS
  FUNCTION MyFunc1( parm1 IN OUT VARCHAR2 )
                    RETURN VARCHAR2;  
END;
 
 
-- ********************************************************* --
-- This wrapper package's body                               --
-- ********************************************************* --
 
PACKAGE BODY MyLib IS
  lib_hndl  ora_ffi.libHandleType;  
  func_hndl ora_ffi.funcHandleType;  
   
  FUNCTION i_MyFunc1( func_hndl IN ora_ffi.funcHandleType,  
                      parm1 IN OUT VARCHAR2 )
                      RETURN VARCHAR2;  
   
  PRAGMA INTERFACE(C,i_MyFunc1,11265);  
   
  FUNCTION MyFunc1( parm1 IN OUT VARCHAR2 )
                      RETURN VARCHAR2  
  IS    
    parm1_l  VARCHAR2 := parm1;
    rc       VARCHAR2(512);
  BEGIN    
    rc  := i_MyFunc1(func_hndl, parm1_l);
    RETURN (rc);  
  END ;  
   
BEGIN    
  lib_hndl := ora_ffi.load_library(NULL,'DLL16.DLL');  
  func_hndl :=
ora_ffi.register_function(lib_hndl,'PASCAL_TEST',ora_ffi.PASCAL_STD);
  ora_ffi.register_parameter(func_hndl,ORA_FFI.C_CHAR_PTR);
  ora_ffi.register_return(func_hndl,ORA_FFI.C_CHAR_PTR);
END;
 
 
-- ********************************************************* --
-- This DLL's DEF file                                       --
-- ********************************************************* --
 
LIBRARY      DLL16
 
EXETYPE      WINDOWS
 
CODE         PRELOAD MOVEABLE DISCARDABLE
DATA         PRELOAD SINGLE
           
HEAPSIZE     4096
 
EXPORTS
             WEP PRIVATE
             Pascal_Test  @1
 
 
-- ********************************************************* --
-- This DLL's CPP file                                       --
-- ********************************************************* --
 
#include <windows.h>
#include <string.h>
 
int CALLBACK LibMain(HANDLE hInstance, WORD wDataSeg, WORD wHeapSize, LPSTR
lpszCmdLine)
{
        if (wHeapSize > 0 )
                UnlockData (0);  // Unlocks the library's data segment
        return 1;
}
 
char* FAR PASCAL __export Pascal_Test (char* xyz)  
{
    HGLOBAL hgl;
    char FAR* abc;
 
    hgl = GlobalAlloc(GPTR, 256);
    abc = (char*)GlobalLock(hgl);
    strcpy(abc,"Hello World");
        GlobalUnlock(hgl);
 
        MessageBox(NULL,abc,"Pascal",MB_OK);
    return abc;

. the end.
more stuff, see ***** below on "scalar" only variables being support by ORA_FFI.

Problem Description:
====================
 
What is ORA_FFI?
Where do you find documentation on ORA_FFI?
What are the differences between ORA_FFI and user exits?

ORA_FFI ENABLES PL/SQL TO CALL 3GL ROUTINES IN EXTERNAL LIBRARIES

Solution Description:
=====================
 
This is an excerpt from the Oracle Procedure Builder Release Notes:
 
Q: What is ORA_FFI?  Does it offer any benefits over user_exits?
     
A: ORA_FFI stands for Oracle Foreign Function Interface.  It is a built-in  
   PL/SQL package that provides services for integrating with third-party  
   Dynamic Link Libraries.
     
   The bindings from PL/SQL program units to foreign functions are
   established DYNAMICALLY at runtime -- NOT compile time -- obviating
   the need to relink executables each time updates are made.  
     
   Because bindings are dynamic, ORA_FFI provides a much more  
   extensible solution than user exits.  If two companies wanted to  
   build forms applications that both had a user exit DLL, it was a  
   real problem because there can only be one forms user exit DLL  
   (F40XTB.DLL).
     
   In addition, there are already many third-party DLLs available,
   including the Windows API library.  ORA_FFI enables developers
   to integrate with these DLLs with little or no additional C
   coding.  User exits may require a substantial amount of C coding
   apart from the work that still must be done in PL/SQL.
 
 
Note:  The ORA_FFI call is only available on the client side PL/SQL
       engine, not the server side.  Typically, it is used from the
       Forms environment.
*******Q: Does ORA_FFI offer support for passing pointers or structures?
     
A: ORA_FFI only offers support for passing scalar values.
   Many people do not consider this a hinderance because it is
   relatively easy to write a small wrapper DLL.  Functions in
   this auxiliary DLL would accept arguments which are used to establish
   the necessary structure(s) that gets passed to the primary
   DLL.
     
   Also, there are already many useful Windows calls which do not
   require structured arguments.  For example, all of the Profile
   functions (GetPrivateProfileString) do not require structured
   arguments.
 
 
Q: Does ORA_FFI work on platforms other than Microsoft Windows?
     
A: ORA_FFI will work on any platform that supports dynamic loading.
   Besides Windows, SunOS, VMS, and Mac are examples of other
   platforms where developers can exploit ORA_FFI.
 
 
Additional Information:
=======================
 
Oracle Documentation:
---------------------
Oracle Procedure Builder 1.5 Developer's Guide Manual
Chapter 8, Oracle Procedure Builder Packages
The ORA_FFI Package
 
Oracle Support Bulletins:  
-------------------------  
9247635.61  
CALLING WINDOWS APPLICATIONS FROM ORACLE FORMS 4.5
 
Oracle Web Sites:
-----------------
You can download examples along with the White Paper "Boosting Development
Productivity with Foreign Function Calls" from the following web sites:  
 
16-bit  
------  
ftp://support.oracle.com/desktop/download/woraffi.exe 
 
32-bit  
------  
ftp://support.oracle.com/desktop/download/ntoraffi.exe 
 
If you have difficulties connecting to the server,  
substitute the IP address for the server name (192.86.154.93).
more stuff, see ***** below on "scalar" only variables being support by ORA_FFI.

Problem Description:
====================
 
What is ORA_FFI?
Where do you find documentation on ORA_FFI?
What are the differences between ORA_FFI and user exits?

ORA_FFI ENABLES PL/SQL TO CALL 3GL ROUTINES IN EXTERNAL LIBRARIES

Solution Description:
=====================
 
This is an excerpt from the Oracle Procedure Builder Release Notes:
 
Q: What is ORA_FFI?  Does it offer any benefits over user_exits?
     
A: ORA_FFI stands for Oracle Foreign Function Interface.  It is a built-in  
   PL/SQL package that provides services for integrating with third-party  
   Dynamic Link Libraries.
     
   The bindings from PL/SQL program units to foreign functions are
   established DYNAMICALLY at runtime -- NOT compile time -- obviating
   the need to relink executables each time updates are made.  
     
   Because bindings are dynamic, ORA_FFI provides a much more  
   extensible solution than user exits.  If two companies wanted to  
   build forms applications that both had a user exit DLL, it was a  
   real problem because there can only be one forms user exit DLL  
   (F40XTB.DLL).
     
   In addition, there are already many third-party DLLs available,
   including the Windows API library.  ORA_FFI enables developers
   to integrate with these DLLs with little or no additional C
   coding.  User exits may require a substantial amount of C coding
   apart from the work that still must be done in PL/SQL.
 
 
Note:  The ORA_FFI call is only available on the client side PL/SQL
       engine, not the server side.  Typically, it is used from the
       Forms environment.
*******Q: Does ORA_FFI offer support for passing pointers or structures?
     
A: ORA_FFI only offers support for passing scalar values.
   Many people do not consider this a hinderance because it is
   relatively easy to write a small wrapper DLL.  Functions in
   this auxiliary DLL would accept arguments which are used to establish
   the necessary structure(s) that gets passed to the primary
   DLL.
     
   Also, there are already many useful Windows calls which do not
   require structured arguments.  For example, all of the Profile
   functions (GetPrivateProfileString) do not require structured
   arguments.
 
 
Q: Does ORA_FFI work on platforms other than Microsoft Windows?
     
A: ORA_FFI will work on any platform that supports dynamic loading.
   Besides Windows, SunOS, VMS, and Mac are examples of other
   platforms where developers can exploit ORA_FFI.
 
 
Additional Information:
=======================
 
Oracle Documentation:
---------------------
Oracle Procedure Builder 1.5 Developer's Guide Manual
Chapter 8, Oracle Procedure Builder Packages
The ORA_FFI Package
 
Oracle Support Bulletins:  
-------------------------  
9247635.61  
CALLING WINDOWS APPLICATIONS FROM ORACLE FORMS 4.5
 
Oracle Web Sites:
-----------------
You can download examples along with the White Paper "Boosting Development
Productivity with Foreign Function Calls" from the following web sites:  
 
16-bit  
------  
ftp://support.oracle.com/desktop/download/woraffi.exe 
 
32-bit  
------  
ftp://support.oracle.com/desktop/download/ntoraffi.exe 
 
If you have difficulties connecting to the server,  
substitute the IP address for the server name (192.86.154.93).
more stuff, see ***** below on "scalar" only variables being support by ORA_FFI.

Problem Description:
====================
 
What is ORA_FFI?
Where do you find documentation on ORA_FFI?
What are the differences between ORA_FFI and user exits?

ORA_FFI ENABLES PL/SQL TO CALL 3GL ROUTINES IN EXTERNAL LIBRARIES

Solution Description:
=====================
 
This is an excerpt from the Oracle Procedure Builder Release Notes:
 
Q: What is ORA_FFI?  Does it offer any benefits over user_exits?
     
A: ORA_FFI stands for Oracle Foreign Function Interface.  It is a built-in  
   PL/SQL package that provides services for integrating with third-party  
   Dynamic Link Libraries.
     
   The bindings from PL/SQL program units to foreign functions are
   established DYNAMICALLY at runtime -- NOT compile time -- obviating
   the need to relink executables each time updates are made.  
     
   Because bindings are dynamic, ORA_FFI provides a much more  
   extensible solution than user exits.  If two companies wanted to  
   build forms applications that both had a user exit DLL, it was a  
   real problem because there can only be one forms user exit DLL  
   (F40XTB.DLL).
     
   In addition, there are already many third-party DLLs available,
   including the Windows API library.  ORA_FFI enables developers
   to integrate with these DLLs with little or no additional C
   coding.  User exits may require a substantial amount of C coding
   apart from the work that still must be done in PL/SQL.
 
 
Note:  The ORA_FFI call is only available on the client side PL/SQL
       engine, not the server side.  Typically, it is used from the
       Forms environment.
*******Q: Does ORA_FFI offer support for passing pointers or structures?
     
A: ORA_FFI only offers support for passing scalar values.
   Many people do not consider this a hinderance because it is
   relatively easy to write a small wrapper DLL.  Functions in
   this auxiliary DLL would accept arguments which are used to establish
   the necessary structure(s) that gets passed to the primary
   DLL.
     
   Also, there are already many useful Windows calls which do not
   require structured arguments.  For example, all of the Profile
   functions (GetPrivateProfileString) do not require structured
   arguments.
 
 
Q: Does ORA_FFI work on platforms other than Microsoft Windows?
     
A: ORA_FFI will work on any platform that supports dynamic loading.
   Besides Windows, SunOS, VMS, and Mac are examples of other
   platforms where developers can exploit ORA_FFI.
 
 
Additional Information:
=======================
 
Oracle Documentation:
---------------------
Oracle Procedure Builder 1.5 Developer's Guide Manual
Chapter 8, Oracle Procedure Builder Packages
The ORA_FFI Package
 
Oracle Support Bulletins:  
-------------------------  
9247635.61  
CALLING WINDOWS APPLICATIONS FROM ORACLE FORMS 4.5
 
Oracle Web Sites:
-----------------
You can download examples along with the White Paper "Boosting Development
Productivity with Foreign Function Calls" from the following web sites:  
 
16-bit  
------  
ftp://support.oracle.com/desktop/download/woraffi.exe 
 
32-bit  
------  
ftp://support.oracle.com/desktop/download/ntoraffi.exe 
 
If you have difficulties connecting to the server,  
substitute the IP address for the server name (192.86.154.93).
more stuff, see ***** below on "scalar" only variables being support by ORA_FFI.

Problem Description:
====================
 
What is ORA_FFI?
Where do you find documentation on ORA_FFI?
What are the differences between ORA_FFI and user exits?

ORA_FFI ENABLES PL/SQL TO CALL 3GL ROUTINES IN EXTERNAL LIBRARIES

Solution Description:
=====================
 
This is an excerpt from the Oracle Procedure Builder Release Notes:
 
Q: What is ORA_FFI?  Does it offer any benefits over user_exits?
     
A: ORA_FFI stands for Oracle Foreign Function Interface.  It is a built-in  
   PL/SQL package that provides services for integrating with third-party  
   Dynamic Link Libraries.
     
   The bindings from PL/SQL program units to foreign functions are
   established DYNAMICALLY at runtime -- NOT compile time -- obviating
   the need to relink executables each time updates are made.  
     
   Because bindings are dynamic, ORA_FFI provides a much more  
   extensible solution than user exits.  If two companies wanted to  
   build forms applications that both had a user exit DLL, it was a  
   real problem because there can only be one forms user exit DLL  
   (F40XTB.DLL).
     
   In addition, there are already many third-party DLLs available,
   including the Windows API library.  ORA_FFI enables developers
   to integrate with these DLLs with little or no additional C
   coding.  User exits may require a substantial amount of C coding
   apart from the work that still must be done in PL/SQL.
 
 
Note:  The ORA_FFI call is only available on the client side PL/SQL
       engine, not the server side.  Typically, it is used from the
       Forms environment.
*******Q: Does ORA_FFI offer support for passing pointers or structures?
     
A: ORA_FFI only offers support for passing scalar values.
   Many people do not consider this a hinderance because it is
   relatively easy to write a small wrapper DLL.  Functions in
   this auxiliary DLL would accept arguments which are used to establish
   the necessary structure(s) that gets passed to the primary
   DLL.
     
   Also, there are already many useful Windows calls which do not
   require structured arguments.  For example, all of the Profile
   functions (GetPrivateProfileString) do not require structured
   arguments.
 
 
Q: Does ORA_FFI work on platforms other than Microsoft Windows?
     
A: ORA_FFI will work on any platform that supports dynamic loading.
   Besides Windows, SunOS, VMS, and Mac are examples of other
   platforms where developers can exploit ORA_FFI.
 
 
Additional Information:
=======================
 
Oracle Documentation:
---------------------
Oracle Procedure Builder 1.5 Developer's Guide Manual
Chapter 8, Oracle Procedure Builder Packages
The ORA_FFI Package
 
Oracle Support Bulletins:  
-------------------------  
9247635.61  
CALLING WINDOWS APPLICATIONS FROM ORACLE FORMS 4.5
 
Oracle Web Sites:
-----------------
You can download examples along with the White Paper "Boosting Development
Productivity with Foreign Function Calls" from the following web sites:  
 
16-bit  
------  
ftp://support.oracle.com/desktop/download/woraffi.exe 
 
32-bit  
------  
ftp://support.oracle.com/desktop/download/ntoraffi.exe 
 
If you have difficulties connecting to the server,  
substitute the IP address for the server name (192.86.154.93).
maybe i write what was alredy written before (have not time to read all comments) : store that into raw data type and use (write your owns) conversion functions to convert it into varchar2
Avatar of Biss

ASKER

That's all nice but:

1. The first example deals with plain strings, i.e. strings that do not contain zeroes - only characters. That's not good for me. I need a way to combat zeroes.

2. The second exerpt deals with memory allocation problems (which I do not have).

3. The third one tells me I can pass only scalar variables. That I know. I have also considered the possibility of doing all processing inside the DLL and returning scalar types. But imagine my buffer contains an array of numbers (which sometimes happens). An array of random length. I cannot use scalar types to obtain that array. All the more so because I don't know in advance its size. Even sending random-length Unicode strings is a problem because most often the hi-bytes of the Unicode characters will be zeroes.

I need a way to pass arbitrary unstructured data from the DLL to PL/SQL. Random-length random-data. And I would need a way to do that effectively. I have already solved the problem in a terrible way - encoding and subsequently decoding the data and thus working with buffers that do not contain zeroes. But that is a very time-consuming, resource-consuming, hard-to-maintain process. That's why I'm hoping I can find a way to pass the data directly through some datatype or algorithm.

Of course, I thank ORACLEtune for the great effort so far. I do appreciate that. But I do not feel willing to close the question still - I would like to see if anyone could suggest something interesting.
Avatar of Biss

ASKER

Regarding konektor's comment:

Do you mean Oracle's RAW datatype? That sounds good. I myself have been considering LOBs. But could you tell me what C++ datatype I can map to a RAW datatype in PL/SQL? I have not been able to find any ORA_FFI documentation that mentions the RAW datatype.

Here's what I've found:

C++ type     ORA_FFI type       PL/SQL types
--------     ------------       ------------
char         C_CHAR             VARCHAR2
char*        C_CHAR_PTR         VARCHAR2
double       C_DOUBLE           NUMBER
double*      C_DOUBLE_PTR       NUMBER
float        C_FLOAT            NUMBER
float*       C_FLOAT_PTR        NUMBER
int          C_INT              BINARY_INTEGER
int*         C_INT_PTR          BINARY_INTEGER
long         C_LONG             PLS_INTEGER
long*        C_LONG_PTR         PLS_INTEGER
short        C_SHORT            PLS_INTEGER
short*       C_SHORT_PTR        PLS_INTEGER
void*        C_VOID_PTR         N/A


If you know of any other mapping, could you tell them to me?
Hi!

What "ZERO" is in returning string?
Binary "ZERO" is the terminator_string.
By avoiding binary "zero" you can read all string.

Regards,
pm
Avatar of Biss

ASKER

Precisely! The binary zero is causing problems. And I am avoiding it by encoding/decoding all zeroes with combinations of bytes that do not contain zeroes.

Just to avoid writing it again, please, refer to my comment from 06/24/2002 05:57AM PST . Encoding and decoding is a terrible thing and the application has become burdened with tons of stupid and seemingly unnecessary code.

I posted this question with the hopes of finding someone who knows a way to pass arbitrary buffers without having to first remove the zeroes from it.
Hi!

Probe with this:

SELECT REPLACE('abc\0defg', '\0', Null) aaa FROM dual
/

AAA
-------
abcdefg


If you replace 0x00 in C++ with '\0' and then REPLACE in PL/SQL the returned string as shown above...???

Regards,
pm
Avatar of Biss

ASKER

Yes, that's what I'm doing now. I encode, although not with '\0'. The coding sequences are different but the principle is *exactly* the same. However, that brings a lot of disgusting code into the application -- all because of the stupid inability of Forms to process arrays containing zeroes.

I was thinking that perhaps there is a way to pass arbitrary data through ORA_FFI without having to encode it first. But it looks like PL/SQL is not really designed for power. Instead, it's designed for simplicity and inefficiency.

I wish I knew a way to receive the data into a LOB or a RAW variable instead of in a VARCHAR2 variable. But if there's a way to do that, I don't know it and I haven't been able to find any such thing in any documentation I've read.  :(
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:
PAQ/Refund
Please leave any comments here within the next seven days.
 
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!
 
jpkemp
EE Cleanup Volunteer
ASKER CERTIFIED SOLUTION
Avatar of YensidMod
YensidMod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial