I was pleasantly surprised to see your quick response.
Let me tell you that I am a newbie to C/C++. I earnestly request to you to
please bear with me if you notice me doing something funny!, I assure you
that I will take a class in C/C++ and also purchase your book Expert One On One
as a valuable reference for my department!
I want to clearly spell out what exactly I did so you would better be able to
help me accomplish my goal.
I downloaded your template from apress.com.
1. I created a new project in MSVC++ selecting Win32 DLL and Simple DLL as
my options from the IDE.
2. I copied the function pass_str from your answer (to my question),and
the myCtx struct definition, the functions lastOciError, init, term,
debugf, and raise_application_error into my new project.
3. I got tons of type conversion compile errors that I fixed by type
casting them. Please see my comments to the right of the statements
where I have made such a change.
4. I prefixed debugf with an _ (underscore) in your pass_str function code.
Did you mean not to have an underscore? There is a _debugf function but
there is no function called debugf.
5. In the init function, I renamed the variable false as false1 as the
compiler wouldn't compile. I felt that the compiler was thinking it to
be a keyword as it appeared in blue font in MSVC++. Did you mean it to be
false and not something else? To have the code compiled, I also had to
prefix (void *) to false1 in the call to OCIExtractSetKey inside
the init function. Is this okay?
When I executed the procedure, here is the error I get:
SQL> variable x varchar2(100)
SQL> exec :x := 'Hello World';
PL/SQL procedure successfully completed.
SQL> exec pass( :x );
BEGIN pass( :x ); END;
*
ERROR at line 1:
ORA-06521: PL/SQL: Error mapping function
ORA-06522: Unable to load symbol from DLL
ORA-06512: at "SAMPLE.PASS", line 0
ORA-06512: at line 1
Now, here are my questions:
1. The _debugf function shows a ... after fmt meaning we can pass addn'al
parameters to it. If I choose not to use additional parameters for now,
could you please verify that the calls to _debugf are proper for use
with just the two shown myCtx and fmt?
void _debugf( myCtxStruct * myCtx, char * fmt)
2. I also notice a similar thing for the function raise_application_error.
For now I would like to have just the 3 parameters as below:
static int raise_application_error( myCtxStruct * myCtx,
int errCode,
char * errMsg)
The bottom line is that I WANT TO SEE MY C CODE WORKING 100% that I could use
with joy and cheer.
I would be very thankful to you from the bottom of my heart if you could
please take a look at my C code and point out the mistakes if any, to make it
working?
I want to see "Hello World ta dah" on my screen! that would be awesome !!!
Thanks - Inayat
------------------------ attached C code compiled with MSVC++ ---------------
I HAVE PUT A COMMENT "added .... ABOVE EACH LINE WHERE I HAVE DONE TYPE-CASTING
-----------------------------------------------------------------------------
// TResponse.cpp : Defines the entry point for the DLL application.
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <oci.h>
#ifdef WIN_NT
#define -_FILE_NAME "c:\\temp\\extproc.ini"
#else
#define INI_FILE_NAME "/export/home/tkyte/src/demo_passing/extproc.ini"
#endif
#define strupr(a) {char * cp; for(cp=a;*cp;*cp=toupper(*cp), cp++);}
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
typedef struct myCtx
{
OCIExtProcContext * ctx; /* Context passed to all external procs */
OCIEnv * envhp; /* OCI environment handle */
OCISvcCtx * svchp; /* OCI Service handle */
OCIError * errhp; /* OCI Error handle */
int curr_lineno;
char * curr_filename;
ub1 debugf_flag;
char debugf_path[255];
char debugf_filename[50];
/* add your own state variables here... */
}
myCtxStruct;
void _debugf( myCtxStruct * myCtx, char * fmt, ... )
{
va_list ap;
OCIFileObject * fp;
time_t theTime = time(NULL);
char msg[8192];
ub4 bytes;
//added (unsigned char *) for parameters 4 and 5 below
if ( OCIFileOpen( myCtx->envhp, myCtx->errhp, &fp,
(unsigned char *)myCtx->debugf_filename,
(unsigned char *)myCtx->debugf_path,
OCI_FILE_WRITE_ONLY, OCI_FILE_APPEND|OCI_FILE_CREATE,
OCI_FILE_TEXT ) != OCI_SUCCESS ) return;
strftime( msg, sizeof(msg),
"%y%m%d %H%M%S GMT ", gmtime(&theTime) );
OCIFileWrite( myCtx->envhp, myCtx->errhp, fp, msg, strlen(msg), &bytes );
va_start(ap,fmt);
vsprintf( msg, fmt, ap );
va_end(ap);
strcat( msg,"\n");
OCIFileWrite( myCtx->envhp, myCtx->errhp, fp, msg, strlen(msg), &bytes );
OCIFileClose( myCtx->envhp, myCtx->errhp, fp );
}
static int raise_application_error( myCtxStruct * myCtx,
int errCode,
char * errMsg,...)
{
char msg[8192];
va_list ap;
va_start(ap,errMsg);
vsprintf( msg, errMsg, ap );
va_end(ap);
_debugf( myCtx, "raise application error( %d, %s )", errCode, msg );
//added (unsigned char *) to parameter 3
if (OCIExtProcRaiseExcpWithMsg(myCtx->ctx,errCode,(unsigned char *)msg,0)==
OCIEXTPROC_ERROR )
{
_debugf( myCtx, "Unable to raise exception" );
}
return -1;
}
static char * lastOciError( myCtxStruct * myCtx )
{
sb4 errcode;
char * errbuf = (char*)OCIExtProcAllocCallMemory( myCtx->ctx, 256 );
strcpy( errbuf, "unable to retrieve message\n" );
//added (unsigned char *) to parameter 5
OCIErrorGet( myCtx->errhp, 1, NULL, &errcode, (unsigned char *)errbuf,
255, OCI_HTYPE_ERROR );
errbuf[strlen(errbuf)-1] = 0;
return errbuf;
}
static myCtxStruct * init( OCIExtProcContext * ctx )
{
ub1 false1 = 0; //225
myCtxStruct *myCtx = NULL;
OCIEnv *envhp;
OCISvcCtx *svchp;
OCIError *errhp;
ub4 key = 1;
if ( OCIExtProcGetEnv( ctx, &envhp, &svchp, &errhp ) != OCI_SUCCESS )
{
//added (unsigned char *) to parameter 3
OCIExtProcRaiseExcpWithMsg(ctx,20000,
(unsigned char *)"failed to get OCI Connection",0);
return NULL;
}
if ( OCIContextGetValue( envhp, errhp, (ub1*)&key, sizeof(key),
(dvoid**)&myCtx ) != OCI_SUCCESS )
{
//added (unsigned char *) to parameter 3
OCIExtProcRaiseExcpWithMsg(ctx,20000,
(unsigned char *)"failed to get OCI Context",0);
return NULL;
}
if ( myCtx == NULL )
{
if ( OCIMemoryAlloc( envhp, errhp, (dvoid**)&myCtx,
OCI_DURATION_PROCESS,
sizeof(myCtxStruct),
OCI_MEMORY_CLEARED ) != OCI_SUCCESS )
{
//added (unsigned char *) to parameter 3
OCIExtProcRaiseExcpWithMsg(ctx,20000,
(unsigned char *)"failed to get OCI Memory",0);
return NULL;
}
myCtx->ctx = ctx;
myCtx->envhp = envhp;
myCtx->svchp = svchp;
myCtx->errhp = errhp;
if ( OCIContextSetValue( envhp, errhp,
OCI_DURATION_SESSION, (ub1*)&key,
sizeof(key), myCtx ) != OCI_SUCCESS )
{
raise_application_error(myCtx, 20000, "%s", lastOciError(myCtx));
return NULL;
}
if (( OCIExtractInit( envhp, errhp ) != OCI_SUCCESS ) ||
( OCIExtractSetNumKeys( envhp, errhp, 3 ) != OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3
( OCIExtractSetKey( envhp, errhp, (unsigned char *)"debugf",
OCI_EXTRACT_TYPE_BOOLEAN,
//added (void *), removed the "&" prefixing false
0, (void *)false1, NULL, NULL ) != OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3
(OCIExtractSetKey(envhp, errhp, (unsigned char *)"debugf_filename",
OCI_EXTRACT_TYPE_STRING,
0, "extproc.log",
NULL, NULL ) != OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3
( OCIExtractSetKey( envhp, errhp, (unsigned char *)"debugf_path",
OCI_EXTRACT_TYPE_STRING,
0, "", NULL, NULL ) != OCI_SUCCESS ) ||
( OCIExtractFromFile( envhp, errhp, 0,
//added (unsigned char *) to parameter 4
(unsigned char *)INI_FILE_NAME ) != OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3
( OCIExtractToBool( envhp, errhp, (unsigned char *)"debugf", 0,
&myCtx->debugf_flag ) != OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3 and 5
(OCIExtractToStr( envhp, errhp, (unsigned char *)"debugf_filename",
0,(unsigned char *)myCtx->debugf_filename,
sizeof(myCtx->debugf_filename ) )
!= OCI_SUCCESS ) ||
//added (unsigned char *) to parameter 3 and 5
( OCIExtractToStr( envhp, errhp, (unsigned char *)"debugf_path",
0, (unsigned char *)myCtx->debugf_path,
sizeof(myCtx->debugf_path ) )
!= OCI_SUCCESS ) ||
( OCIExtractTerm( envhp, errhp ) != OCI_SUCCESS ))
{
raise_application_error(myCtx, 20000, "%s", lastOciError(myCtx));
return NULL;
}
}
else
{
myCtx->ctx = ctx;
myCtx->envhp = envhp;
myCtx->svchp = svchp;
myCtx->errhp = errhp;
}
if ( OCIFileInit( myCtx->envhp, myCtx->errhp ) != OCI_SUCCESS )
{
raise_application_error(myCtx, 20000, "%s", lastOciError(myCtx));
return NULL;
}
return myCtx;
}
static void term( myCtxStruct * myCtx )
{
OCIFileTerm( myCtx->envhp, myCtx->errhp );
}
#define ERROR_OCI_ERROR 20001
#define ERROR_STR_TOO_SMALL 20002
#define ERROR_RAW_TOO_SMALL 20003
#define ERROR_CLOB_NULL 20004
#define ERROR_ARRAY_NULL 20005
#ifdef WIN_NT
_declspec (dllexport)
#endif
void pass_str
( OCIExtProcContext * ctx /* CONTEXT */,
char * p_ostr /* STRING */,
short * p_ostr_i /* INDICATOR short */,
int * p_ostr_ml /* MAXLEN int */
)
{
myCtxStruct*myCtx;
char * stringToAdd = " ta dah";
if ( (myCtx = init( ctx )) == NULL ) return;
_debugf( myCtx, "Enter Pass Str" );
/*
* Now, we will make the output string = upper(input string)
* if the output buffer is big enough and
*/
if ( *p_ostr_i == OCI_IND_NOTNULL )
{
//added unsigned int to *p_ostr_ml
if ((strlen(p_ostr) + strlen(stringToAdd) +1) <= unsigned int(*p_ostr_ml))
strcat( p_ostr, stringToAdd );
else
raise_application_error
( myCtx, ERROR_STR_TOO_SMALL, "String too small" );
}
else
{
//added unsigned int to *p_ostr_ml
if ( (strlen(stringToAdd) + 1) <= unsigned int(*p_ostr_ml) )
strcpy( p_ostr, stringToAdd );
else
raise_application_error
( myCtx, ERROR_STR_TOO_SMALL, "String too small" );
}
term(myCtx);
}
--------------------------------------the end----------------------------------