Subject: Problems with a DLL
From: [email-masked] (D. Allan Shoup, Computer Clerk, Lawrence, KS)

7 Jan 1998 21:13:33 -0800
Message-ID: <[email-masked]>
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
To: [email-masked]

I have been having problems calling C functions in a DLL from a 4GL GUI.  
When I link the DLL using Fergus Henderson's method (from the cygnus web 
page), the 4GL program can not even locate the functions in the DLL (even 
they show up using Win NT's Quick View).  I was wondering if this was 
because of how the functions are referenced through the use of the 
_impure_ptr.  If so how should these functions be referenced.  When I 
compile and link an executable (with gnu gcc), it correctly finds the 
functions and returns the correct values.

I have also tried a different makefile (shown below).  This results in 
the 4GL program being able to successfully find and return the correct 
result for the return_5() procedure, but when it calls the 
ais_current_fiscal_year() procedure, I get an "Access Violation 
Exception."  Any help would be greatly appreciated.  Thanks in advance.

*********** Makefile *****************

FILENAME = libu3gl

LIBPATH=/gnuwin32/b18/H-i386-cygwin32/i386-cygwin32/lib

C_SRC = \
	return_5.c \
	ais_current_fiscal_year.c

C_OBJS = \
	return_5.o \
	ais_current_fiscal_year.o 

# The `sed' commands below are to convert DOS-style `C:\foo\bar'
# pathnames into Unix-style `//c/foo/bar' pathnames.
CYGWIN32_LIBS = $(shell echo					\
	-L`dirname \`gcc -print-file-name=libgcc.a |		\
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \
	-L`dirname \`gcc -print-file-name=libcygwin.a |	\
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \
	-L`dirname \`gcc -print-file-name=libkernel32.a | \
	sed -e 's@^\\\\([A-Za-z]\\\\):@//\\\\1@g' -e 's@\\\\\\\\@/@g' \` ` \
	-lgcc -lcygwin -lkernel32 -lgcc)

all: $(FILENAME).dll

$(C_OBJS): $(C_SRC)
	gcc -c $(C_SRC) 

$(FILENAME).exp $(FILENAME).a: $(FILENAME).def 
	dlltool --def $(FILENAME).def --output-exp $(FILENAME).exp 	\
		--output-lib $(FILENAME).a --dllname $(FILENAME).dll

$(FILENAME).dll:$(C_OBJS) $(FILENAME).exp dll_fixup.o dll_init.o
	ld -o $(FILENAME).dll $(FILENAME).exp $(C_OBJS)		\
		dll_init.o dll_fixup.o	\
		$(CYGWIN32_LIBS)	\
		/OPING/ingres/lib/ingres.lib				\
		/OPING/ingres/lib/esqlc.lib				\
		$(LIBPATH)/libcygwin.a -e _dll_entry@12

dll_fixup.o: dll_fixup.c
	gcc -c dll_fixup.c

dll_init.o: dll_init.c
	gcc -c dll_init.c

# The following rule is just there to convince gcc
# to keep otherwise unused intermediate targets around.
dont_throw_away: dll_fixup.o dll_init.o

**************************** dll_fixup.c *****************

asm(".section .idata$3\n" ".long 0,0,0,0, 0,0,0,0");

********************* dll_init.c **********************

#include <windows.h>
#include <stdio.h>

extern struct _reent *_impure_ptr, 

*__imp_reent_data;

int WINAPI dll_main(HANDLE a, DWORD reason, void *q)
{ 

switch (reason){
case DLL_PROCESS_ATTACH: break;
case DLL_PROCESS_DETACH: break;
case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
};

_impure_ptr=__imp_reent_data;

return 1;
}

main(){}

********************** libu3gl.def *************

EXPORTS
ais_current_fiscal_year
return_5

********************* ais_current_fiscal_year.c **********

/*------------------------------------------------------------------------
- ---*/
/*                                                                        
   */
/* Arguments:   NONE                                                      
   */
/*                                                                        
   */
/* Returns:     char * (varchar in 4GL)                                   
   */
/*                                                                        
   */
/* Tables Used: NONE                                                      
   */
/*                                                                        
   */
/*========================================================================
===*/

/*************************************************************************
****
**  Enable POSIX Compatibility mode.                                      
  **
**************************************************************************
***/

#define _POSIX_SOURCE

/*************************************************************************
****
**  Standard Include Files.                                               
  **
**************************************************************************
***/

#include <stdio.h>
#include <string.h>
#include <time.h>

/*************************************************************************
****
**  Other Include Files.                                                  
  **
**************************************************************************
***/

#include "ais.h"

/*************************************************************************
****
**  Function Header.                                                      
  **
**************************************************************************
***/

char *ais_current_fiscal_year (void)

{

/*************************************************************************
****
**  Add RCS stuff.                                                        
  **
**************************************************************************
***/

   static char 

               rcsid[]      = "$Header: /home/ais/aisdev/source/libais/ais
_current_fiscal_year.c,v 1.1 1996/12/20 16:34:49 aisdev Exp $",
               rcs_what[]   = "$What$";

/*************************************************************************
****
**  Declarations and Initializations.                                     
  **
**************************************************************************
***/

   struct tm *local_time;

   time_t secs_now;
   
   static char fiscal_year[5];
   
   int year;

/*************************************************************************
****
**  Get the current time and apply local time-zone shifts.                
  **
**************************************************************************
***/

   secs_now = time (NULL);
   local_time = localtime (&secs_now);

/*************************************************************************
****
**  Now set year correcting for fiscal_year.                              
  **
**  Fiscal years start in October (10).                                   
  **
**  The tm structure starts months (tm_mon) at 0 (for January) which 
means  **
**  October is 9 and I used the greater than sign, so I used 8 for        
  **
**  after September.                                                      
  **
**  1900 was needed because tm_year is the number of years since 1900.    
  **
**************************************************************************
***/

   year = 1900 + (int) local_time -> tm_year + ((local_time -> tm_mon > 
8) ? 1 : 0);

/*************************************************************************
****
**  Now convert year to character.                                        
  **
**************************************************************************
***/

   (void) sprintf (fiscal_year, "%4.04d", year);

/*************************************************************************
****
**  Return the string.                                                    
  **
**************************************************************************
***/

   return (fiscal_year);

} /*  ais_current_fiscal_year  */

****************************** ais.h **********************************

#ifndef AIS_H
#define AIS_H

/*************************************************************************
*****
**  Include files                                                         
   **
**************************************************************************
****/

#include <stdio.h>

/*
**  KLUDGE Until we get the DG upgraded to Open Ingres
*/

#if INGVER <= 64
#include "eqdef.h"
#endif

/*************************************************************************
*****
**  Declare constants                                                     
   **
**************************************************************************
****/

#define AISSQL_NO_MORE_DATA            100
#define AISSQL_NOT_FOUND               100
#define AISSQL_DUPLICATE_INSERT_KEY -30210
#define AIS_HQ_REGION_CODE "hq"
#define AIS_MAX_SQL_COLUMNS 300
#define AIS_MAX_OPEN_FILES 250	/* Max files to be opened at 1 time */
/*#define DEBUG*/

/*************************************************************************
*****
**  Declare Macros                                                        
   **
**************************************************************************
****/

#define AIS_DEBUG_PRINTF (void) printf

/*************************************************************************
*****
**  Declare typedefs                                                      
   **
**************************************************************************
****/

typedef int Aisdb_Funit;	/*  */

typedef enum
{
   AIS_INVALID_CC_TYPE,		/* Invalid -- Used for Error detection */
   AIS_HQ_CC_TYPE,		/* Headquarters Cost Center */
   AIS_REGION_CC_TYPE,		/* Region */
   AIS_AREA_CC_TYPE,		/* Area */
   AIS_DISTRICT_CC_TYPE,	/* District */
   AIS_SUBDISTRICT_CC_TYPE,	/* Subdistrict */
   AIS_RESEARCH_CC_TYPE		/* Research */
} Ais_Cost_Center_Types;

typedef struct
{
   char region_code[3];		/* Region Code */
   char region_name[41];	/* Name of Region */
   char server_address[21];	/* IP address of region's server */
   char server_hostname[61];	/* IP Hostname of Region's server */
} Ais_Region_Code_Object;

typedef struct
{
   char cost_center[5];		/* Cost Center */
   char cost_center_name[81];	/* Name */
   Ais_Cost_Center_Types cost_center_type; /* Cost Center Type */
   char area_code[3];		/* Area to which the CC belongs to */
   char region_code[3];		/* Region to which CC belongs to */
   Ais_Region_Code_Object *region_info;
   char district_parent[5];	/* If type is Subdistrict, this is the **
				** parent CC, otherwise blank          */
   int is_local;		/* True if local Cost Center */
   int is_inactive;		/* True if inactive Cost Center */
   char server_address[21];	/* Internet Address of server (for CC) */
} Ais_Cost_Center_Object;

typedef enum
{
   AIS_INVALID_QUEUE_ENTRY_STATUS,
   AIS_QUEUED_QUEUE_ENTRY_STATUS,
   AIS_AISD_GENERATED_QUEUE_ENTRY_STATUS,
   AIS_PROCESSING_QUEUE_ENTRY_STATUS
} Ais_Transfer_Queue_Status_Object;

typedef struct
{
   int  transfer_id;		/* Unique id -- key */
   char requestor[9];		/* Login name of person making the request */
   char process_name[31];	/* Transfer Process to initiate */
   char where_clause1[81];	/* Part 1 of the where clause */
   char where_clause2[81];	/* Part 2 of the where clause */
   Ais_Transfer_Queue_Status_Object status;	/* Current status  */
   char date_modified[27];	/* Date inserted into the queue */
   char date_transferred[27];	/* Date transferred -- data should 	**
				** contain this date.			*/
} Ais_Transfer_Queue_Object;

typedef struct
{
   int     process_id;		/* Unique key */
   char    process_name[31];	/* Name of process */
   int foreground_mode_flag;	/* Ignored */
   int manual_load_flag;	/* If True, User Intervention is required */
   int     email_notification_id; /* Email Title ID to send mail to */
   int     auto_transfer_interval; /* Ignored */
   char    date_last_transferred[26]; /* Date */
   char    description[41];	/* Description of Process */
} Ais_Process_Type_Object;

typedef struct
{
   int  log_id;			/* Unique Key 				     */
   int  process_id;		/* Id of process initiated 		     */
   char requestor_userid[9];	/* Id of person submitting request	     */
   char request_date[27];	/* Date person submitted request 	     */
   char executor_userid[9];	/* Id of person executing request 	     */
   char execution_date[27];	/* Date request was processed 		     */
   char submitting_ais_server[81];	/* Hostname of submitting AIS Server */
   char log_message[41];	/* Message to Log */
} Ais_Process_Exec_Log_Object;

typedef struct
{
   char server_address[21];
} Ais_Server_Info;

typedef struct
{
   Ais_Server_Info server[200];
   int             num_servers;
   int             max_servers;
} Ais_Server_Info_Array;

typedef enum
{
   AIS_UNKNOWN_CONTROL_FILE,
   AIS_DBLOAD_CONTROL_FILE,
   AIS_DBLOG_CONTROL_FILE
} Ais_Control_File_Type;

/*************************************************************************
*****
**  Declare function prototypes                                           
   **
**************************************************************************
****/

/*------------------------------------------------------------------------
- ---**
**  Function:     ais_open_database
**  Description:  Opens a connection to an ingres Database.
**  Returns:      -1 on error; Ingres session ID on success
**------------------------------------------------------------------------
- ---*/

Aisdb_Funit ais_open_database (	/* Return -1 on error or Ingres session 
id */
   const int log_error,	/* True: Log error to terminal */
   const char *const db,	/* Ingres database to connect to */
   const char *const mode,	/* "r": nolock; "rw": shared locks */
   const char *const options,   /* Ingres connect options ie -uaisdev */
   int *status_code		/* Return code - 0: Connect successful 
				** If NULL, not set.			*/
			      );

/*------------------------------------------------------------------------
- ---**
**  Function:     ais_close_database
**  Description:  Closes a session
**  Returns:      Returns 0 on success, non-zero on error
**  Restrictions: No verifications is done on the funit to determine if it
**                was previously opened by ais_open_database.
**------------------------------------------------------------------------
- ---*/

int ais_close_database (	/* Return 0 on success, non-zero on error */
			const Aisdb_Funit funit /* Session ID to close */
		       );

/*------------------------------------------------------------------------
- --**
**  Function:     ais_close_remaining_dbsessions
**  Description:  Closes all Ingres database sessions except for the
**		  one specified in the argument.
**  Returns:      void
**  Restrictions: This only closes sessions that were opened by 
**                ais_open_database.
**------------------------------------------------------------------------
- --*/

void ais_close_remaining_dbsessions (
   const Aisdb_Funit dbunit
   );

/*------------------------------------------------------------------------
- --**
**  Function:     ais_set_default_dba
**  Description:  Override the default_dba attribute.  Normally, 
**		  ais_get_default_db uses the id 'ais' to find the default
**		  database name.  This function allows the caller to 
**		  set this to some other id so that ais_get_default_db
**		  can be used on databases not owned by AIS.
**		  In AIS, we store the default database in a file called
**		  .aisdb in the DBA's home directory.  On some occasions,
**		  we test the software on database(s) owned by aisdev...
**  Returns:      void
**------------------------------------------------------------------------
- --*/

void ais_set_default_dba (
   const char *dba
   );

/*------------------------------------------------------------------------
- --**
**  Function:     ais_get_dbname
**  Description:  Determine the name of the database.  This is	
**		  is accomplished by reading the .aisdb file.  If the dba
**		  of the database to be opened is not 'ais', then call
**		  ais_set_default_dba.  (The .aisdb file is expected to be 
**		  found in the dba's home directory.)
**  NOTE:	  This function will add the vnode name to the returned
**		  database name.  (It looks to see if the localhost
**		  is the same as hostname contained in the file.)
**  Args:	  char *  (At this time, code NULL)
**  Returns:      char *
**  Restrictions: The character pointer returned is from a static buffer
**		  which will be overwritten on the next call.
**------------------------------------------------------------------------
- --*/

char *ais_get_dbname (const char *const key);

/*------------------------------------------------------------------------
- ---**
**  Function:     ais_sql_strerror
**  Description:  Returns a pointer to a static character string 
containing 
**                the error message
**  Returns:      error message
**  Restrictions: Even though the function accepts an error code, it is 
not
**                used at the present time.  This is an Ingres 
restriction.
**                Ingres only allows the retrieval of the last error.
**                It is hoped that in the future, this restriction will be
**                removed.  Therefore, give the ingres error code as the
**                argument so the correct error message can be retrieved
**                if and when this happens.
**------------------------------------------------------------------------
- ---*/

char *ais_sql_strerror (	/* Return error message */
   const int sql_errno		/* Ingres sql error code -- NOT USED  */
		       );

/*------------------------------------------------------------------------
- ---**
**  Function:     ais_sql_errors
**  Description:  handle serious (fatal) ingres errors
**  Returns:      Returns 0 on success, non-zero on error
**  Restrictions: This module may not return if the error is serious 
enough
**                If the should_retry flag is set, then the caller should
**                retry the query they were doing; the error probably has
**                something to do with the log file.
**------------------------------------------------------------------------
- ---*/

int ais_handle_sql_error (	/* If true, error has handled */
   const int sql_errno,		/* Ingres error to test */
   int *should_retry	/* Caller should retry query */
			     );

/*------------------------------------------------------------------------
- ---**
**  Function:     ais_get_default
**  Description:  Get an installation default either for the user 
environment
**                or the ingres table
**  Returns:      NULL on error; ptr to character variable on success
**  NOTE:         memory is allocated for each default value returned 
**                via calloc(3).  Therefore it is up to the caller to 
release
**                the memory when the default value is no longer needed.
**------------------------------------------------------------------------
- ---*/

char *ais_get_default (		/* NULL on error; otherwise ptr to default */
   const int log_error,	/*  True: Log error to terminal */
   const int check_user_env, /* Use environment variables if possible */
   const char *const keyword	/* Keyword to retrieve the default value of 
*/
		      );

/*------------------------------------------------------------------------
- ---**
**  Function:	ais_get_userid
**  Args:	void
**  Returns:	char *			-- username of current user
**  Errors:	NOT Reported to user	-- signalled by returned value of NULL
**------------------------------------------------------------------------
- ---*/

char *const ais_get_userid (void);

Ais_Cost_Center_Object ais_get_cost_center_info (int log_error,
						 char *cost_center,
						 int *error);
Ais_Region_Code_Object ais_get_region_code_info (int log_error,
						 char *region_code,
						 int *error);
Ais_Region_Code_Object ais_is_region_ip (int log_error,
					 char *server_address,
					 int *error);

Ais_Transfer_Queue_Object ais_get_transfer_queue_entry (int log_error,
							int *error);

Ais_Process_Type_Object ais_get_process_type_info (int log_error,
						   char *process_name,
						   int *error);

Ais_Process_Exec_Log_Object ais_create_log_entry (int log_error,
			     const char * const process_name,
			     char *log_entry_string,
			     Ais_Transfer_Queue_Object *queue_entry,
			     Ais_Process_Type_Object process_type_entry,
						  int *error);
int ais_update_log_entry (
			  int log_error,
			  int okay_to_commit,
			  Ais_Process_Exec_Log_Object log_value
			  );
int ais_update_log_entry_sprintf (
				  int log_error,
				  int okay_to_commit,
				  Ais_Process_Exec_Log_Object *log_entry,
				  char *fmt, ...);

char *ais_get_database_time (void);
char *ais_get_database_dba (void);
void ais_generic_signal_handler (int raised_signal);

void ais_delete_transfer_queue_entry (int log_error,
				      Ais_Transfer_Queue_Object queue_entry,
				      int *error);

int ais_valid_db_column_name (int log_error, char *table_name,
				  char *column_name, char *datatype,
				  int *error);
int ais_print_ais_router_header (FILE *funit, int ascii_file,
				 const char * const destination_host,
				 const char * const destination_dir_keyword,
				 const char * const destination_fname);
int ais_print_aisd_header (FILE *funit,
			   Ais_Control_File_Type control_file_type,
			   Ais_Transfer_Queue_Object transfer_queue,
			   Ais_Process_Exec_Log_Object ais_log_entry);

int ais_exec_sql (int log_error, int okay_to_commit, char *sql);
Ais_Server_Info_Array *ais_get_all_servers (int log_error, int *error);
const char *const ais_escape_sql_char_data (const char * const sql_data);
char *ais_current_fiscal_year (void);

int ais_generate_number (char *table_name, char *column_name,
			 void *key_to_generate);
int ais_do_external_help (char *topic, char *errmsg, int errlen);

void ais_get_printer_list (char *list, int list_size,
			     int *num_printers,
			     int *default_printer_assigned);
#ifdef IISQLDA_TYPE

IISQLDA *ais_allocate_sqlda (const unsigned int number_of_columns);
int ais_populate_sqlda (IISQLDA *sqlda);
void ais_deallocate_sqlda (IISQLDA *sqlda);
#endif
#endif

********************* eqdef.h ***************

void	IIFRInternal();
int	IIFRaeAlerterEvent();
int	IIFRafActFld();
void	IIFRgotofld();
void	IIFRgpcontrol();
void	IIFRgpsetio();
int	IIFRitIsTimeout();
int	IIFRreResEntry();
void	IIFRsaSetAttrio();
void	IIFRsqDescribe();
void	IIFRsqExecute();
int	IIFRtoact();
int	IIG4acArrayClear();
int	IIG4bpByrefParam();
int	IIG4ccCallComp();
int	IIG4chkobj();
int	IIG4drDelRow();
int	IIG4fdFillDscr();
int	IIG4gaGetAttr();
int	IIG4ggGetGlobal();
int	IIG4grGetRow();
int	IIG4i4Inq4GL();
int	IIG4icInitCall();
int	IIG4irInsRow();
int	IIG4rrRemRow();
int	IIG4rvRetVal();
int	IIG4s4Set4GL();
int	IIG4saSetAttr();
int	IIG4seSendEvent();
int	IIG4sgSetGlobal();
int	IIG4srSetRow();
int	IIG4udUseDscr();
int	IIG4vpValParam();
void	IILQcnConName();
void	IILQesEvStat();
void *	IILQdbl();
void *	IILQint();
void	IILQisInqSqlio();
void	IILQldh_LoDataHandler();
long	IILQled_LoEndData();
void	IILQlgd_LoGetData();
void	IILQlpd_LoPutData();
void	IILQpriProcInit();
int	IILQprsProcStatus();
void	IILQprvProcValio();
void	IILQshSetHandler();
void	IILQsidSessID();
void	IILQssSetSqlio();
int	IITBcaClmAct();
void	IITBceColEnd();
int	IIactcomm();
int	IIactscrl();
int	IIaddform();
void	IIbreak();
int	IIchkfrm();
int	IIclrflds();
int	IIclrscr();
void	IIcsClose();
void	IIcsDaGet();
void	IIcsDelete();
void	IIcsERetrieve();
void	IIcsERplace();
void	IIcsGetio();
void	IIcsOpen();
void	IIcsParGet();
void	IIcsQuery();
void	IIcsRdO();
void	IIcsReplace();
int	IIcsRetrieve();
int	IIdispfrm();
void	IIendforms();
int	IIendfrm();
int	IIendmu();
void	IIendnest();
void	IIeqiqio();
void	IIeqstio();
int	IIerrtest();
void	IIexDefine();
int	IIexExec();
void	IIexit();
int	IIfldclear();
void	IIflush();
int	IIfnames();
int	IIforminit();
int	IIforms();
char	IIfrshelp();
int	IIfsetio();
int	IIfsinqio();
int	IIfssetio();
int	IIgetdomio();
int	IIgetfldio();
int	IIgetoper();
int	IIgtqryio();
char	IIhelpfile();
void	IIingopen();
int	IIinitmu();
int	IIinqset();
int	IIiqfsio();
int	IIiqset();
int	IImessage();
int	IImuonly();
int	IInestmu();
int	IInexec();
int	IInextget();
int	IInfrskact();
int	IInmuact();
int	IIparret();
int	IIparset();
void	IIprmptio();
void	IIprnscr();
void	IIputctrl();
void	IIputdomio();
int	IIputfldio();
int	IIputoper();
int	IIredisp();
int	IIrescol();
int	IIresfld();
int	IIresmu();
int	IIresnext();
void	IIretinit();
int	IIretval();
int	IIrf_param();
int	IIrunform();
int	IIrunmu();
int	IIrunnest();
void	IIsexec();
int	IIsf_param();
int	IIsleep();
void	IIsqConnect();
void	IIsqDaIn();
void	IIsqDescribe();
void	IIsqDisconnect();
void	IIsqExImmed();
void	IIsqExStmt();
void	IIsqFlush();
void	IIsqGdInit();
void	IIsqInit();
void	IIsqlcdInit();
void	IIsqMods();
void	IIsqParms();
void	IIsqPrepare();
void	IIsqPrint();
void	IIsqStop();
void	IIsqTPC();
void	IIsqUser();
void	IIsqlcdInit();
int	IIstfsio();
void	IIsyncup();
int	IItbact();
int	IItbinit();
int	IItbsetio();
int	IItbsmode();
int	IItclrcol();
int	IItclrrow();
int	IItcogetio();
int	IItcolval();
int	IItcoputio();
int	IItdata();
int	IItdatend();
int	IItdelrow();
int	IItfill();
int	IIthidecol();
int	IItinsert();
int	IItrc_param();
int	IItsc_param();
int	IItscroll();
int	IItunend();
char	IItunload();
int	IItupget();
int	IItvalrow();
int	IItvalval();
void	IIutsys();
int	IIvalfld();
void	IIvarstrio();
void	IIwritio();
void	IIxact();

********************  return_5.c *********************

return_5()
{
  return(5);
}

----------------------------
| Allan Shoup              |
| US Geological Survey     |
| [email-masked]          |
----------------------------

-
For help on using this list (especially unsubscribing), send a message to
"[email-masked]" with one line of text: "help".