OUT OF PRIVATE HANDLES

i'm not very good on c++ and i got to develop a dll for our lotus domino server.
the problem i have is, when the dll is called about 1000 times the http-task of the domino server crashes whith following error message on the server console:
"MemAlloc: OUT OF PRIVATE HANDLES! -- pid 000004F4 Handles used so far 16953, Maximum handles = 10495"

here's the code of my dll
i think that my mistake is, that i use goto's when an error appears and i don't free the memory.

------------------------------------------------------------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>

// Lotus C API for Domino and Notes Header files.
#include <global.h>
#include <editods.h>
#include <osmem.h>
#include <schgtw.h>
#include <textlist.h>
#include <lapiplat.h>

// Header file.
#include "busytime2.h"
   

/************************************************************************
    DLL Main
*************************************************************************/
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved){
    switch (ul_reason_for_call){
            case DLL_PROCESS_ATTACH:
            case DLL_THREAD_ATTACH:
            case DLL_THREAD_DETACH:
            case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}


/*************************************************************************
      Initializing Notes
*************************************************************************/
BUSYTIME_DLL_API int Start_Notes (char *exepath, char *inipath) {            
      STATUS sError;
      char *argv[2];
      //Umschlüsseln der Parameter
      argv[0] = exepath;
      argv[1] = inipath;
      //Notesinitialisierung
    sError = NotesInitExtended(2, argv);
      // Errorhandling
    if (sError != NOERROR) {
        LAPI_RETURN(2);
      } else {
        LAPI_RETURN(0);
      }
}


/*************************************************************************
      Wrapper function to exit from Domino and Notes.
*************************************************************************/


BUSYTIME_DLL_API void Stop_Notes (void) {
    NotesTerm();
}


/*************************************************************************
      Write Busytimes to File
*************************************************************************/
BUSYTIME_DLL_API int BusyTimeToFile (char *szUser, char *startTime, char *endTime, char *fname) {
      //Declarations
      char                  *theSchedList;
      int                        retcode=0;
      int                        laenge=0;
      int                        i;
      int                        anzuser=0;
      int                        anzschedules=0;
      char                  szMyUser[256]="";
      char                  *tmpUser=szUser;
      char                  *tmpStartTime;
      char                  *tmpEndTime;
      int                        zaehler=0;
      //Filepointers
      FILE                  *fz;
      //
      VOID                  *list_ptr;
      WORD                  list_size;
      DWORD                  retdwSize;
      //Notestypes
      STATUS                  sError = NOERROR;                  //typedef WORD STATUS;
      UNID                  apptUnid;                              //#define UNID UNIVERSALNOTEID
      HCNTNR                  schListCntnr = NULLHANDLE;      //typedef HANDLE HCNTNR; [ void LNPUBLIC SchContainer_Free(HCNTNR  hCntnr); ]
      HCNTNROBJ            schedObj;                              //typedef DWORD HCNTNROBJ;
      HCNTNROBJ            nextSchedObj;
      HCNTNROBJ            hMoreObj = NULLCNTNROBJ;
      TIMEDATE_PAIR      interval;                              //typedef struct {TIMEDATE Lower;TIMEDATE Upper;} TIMEDATE_PAIR;
      TIMEDATE_PAIR      pInterval;
      SCHEDULE            *nextSchedule;                        //typedef struct {DWORD reserved[8]; DBID dbReplicaID; TIMEDATE_PAIR Interval; DWORD dwErrGateway; STATUS error; WORD wReserved; WORD wOwnerNameSize; } SCHEDULE;
      SCHEDULE            *pSchedule;
      HANDLE                  list_handle;                        //typedef unsigned int HANDLE; [ OSMemAlloc / OSMemFree ]
      HANDLE                  rethSchedList;

      //try to open file
      if ((fz=fopen(fname,"w"))==NULL) {
            retcode=1;
            goto done;
      } else {
            sError = ListAllocate (0, 0, FALSE, &list_handle, &list_ptr, &list_size);
            if (sError != NOERROR)
                  goto NError;
            i=0;
            while (*tmpUser!='\0') {
                  if (i>=256) {
                        retcode=2;
                        goto done;
                  }
                  if(*tmpUser!=',') {
                        szMyUser[i++]=*tmpUser;
                  } else {
                        szMyUser[i++]='\0';
                        anzuser++;
                        sError = ListAddEntry(list_handle, FALSE, &list_size, (WORD) anzuser, szMyUser, (WORD) strlen(szMyUser));
                        if (sError != NOERROR)
                              goto NError;
                        i=0;
                  }
                  tmpUser++;
            }
            //add last user
            szMyUser[i]='\0';
            anzuser++;
            sError = ListAddEntry(list_handle, FALSE, &list_size, (WORD) anzuser, szMyUser, (WORD) strlen(szMyUser));
            if (sError != NOERROR)
                  goto NError;

            //Convert Start and Endtime to Notesdatetime
            tmpStartTime = startTime;
            tmpEndTime       = endTime;
            sError = ConvertTextToTIMEDATE(NULL, NULL, &tmpStartTime, (WORD) strlen(startTime), &interval.Lower);
            if (sError != NOERROR)
                  goto NError;
            sError = ConvertTextToTIMEDATE(NULL, NULL, &tmpEndTime, (WORD) strlen(endTime), &interval.Upper);
            if (sError != NOERROR)
                  goto NError;
            //Get Schedules
            OSUnlockObject(list_handle);
            list_ptr = OSLockObject(list_handle);
            sError = SchRetrieve(&apptUnid, NULL, SCHRQST_FORCEREMOTE|SCHRQST_EACHPERSON,&interval,(LIST *)list_ptr, &schListCntnr, NULL, NULL, NULL);  
            if (sError != NOERROR)
                  goto NError;
            
            // going through the schedules
            while (sError == NOERROR) {
                  zaehler++;
                  //beginn
                  if (anzschedules==0) {
                        // first schedule
                        sError = SchContainer_GetFirstSchedule(schListCntnr, &schedObj, &pSchedule);          
                        if (sError != NOERROR)
                              goto NError;
                        memcpy (szMyUser,pSchedule + 1 ,pSchedule->wOwnerNameSize);
                  }else {
                        // next schedule
                        sError = SchContainer_GetNextSchedule(schListCntnr, schedObj, &nextSchedObj, &nextSchedule);
                        if (sError != NOERROR)
                              goto NError;
                        schedObj=nextSchedObj;
                        memcpy (szMyUser,nextSchedule + 1 ,nextSchedule->wOwnerNameSize);
                  }
                  
                  //* extract the list
                  sError = Schedule_ExtractSchedList(      schListCntnr, schedObj, &pInterval, (unsigned long *) &retdwSize, &rethSchedList, &hMoreObj);
                  if (sError == NOERROR) {
                        anzschedules++;
                        fprintf(fz,"#\n");
                        fprintf(fz,"%s\n",szMyUser);
                        theSchedList=(char *)OSLockObject(rethSchedList);
                        // ... und in ein Stringarray hinzufügen
                        DumpTextSchedEntryListToFile(theSchedList,fz);
                        OSUnlockObject(rethSchedList);  
                  } else if (sError==1002 && zaehler<anzuser) {
                        sError=NO_ERROR;
                  }else {
                        goto NError;
                  }
            }

            //* Free all handles
            
            //OSUnlockObject(theSchedList);
            //OSMemFree(theSchedList);

            OSUnlockObject(list_handle);
            OSMemFree(list_handle);

            OSUnlockObject(rethSchedList);
            OSMemFree(rethSchedList);
      
            Schedule_Free(schListCntnr,schedObj);
            SchContainer_Free(schListCntnr);
      }
      
NError:
      retcode=0;
      if (sError!=NO_ERROR && sError!=1006 && sError!=1002) {
            retcode=2;
      } else if (anzuser!=anzschedules) {
            retcode=1;
      }
done:
      fclose(fz);
      LAPI_RETURN(retcode);
}

/************************************************************************
    FUNCTION:     DumpTextSchedEntryListToFile
*************************************************************************/
void LNPUBLIC DumpTextSchedEntryListToFile(char *pSchedList, FILE *fz) {
      //Declaration
      int                        num=0;
      int                        i=0;
      char                  szLowerTD[MAXALPHATIMEDATE+1];
      char                  szUpperTD[MAXALPHATIMEDATE+1];
      //Notesdatatypes
      SCHED_LIST            SchedList;            //typedef struct { DWORD NumEntries; WORD  wApplicationID; WORD Spare; } SCHED_LIST;
      SCHED_ENTRY            SchedEntry;            //typedef struct { UNID Unid; TIMEDATE_PAIR Interval; BYTE Attr; BYTE UserAttr; BYTE spare[2];} SCHED_ENTRY;

      
      memcpy( (char*)&SchedList, pSchedList, sizeof(SCHED_LIST) );
      pSchedList += sizeof(SCHED_LIST);
      num = SchedList.NumEntries;
      //fprintf(fz,"next\n");
      for (i=0;i<num;i++) {
            memcpy((char *)&SchedEntry, pSchedList, sizeof(SCHED_ENTRY) );
            //Check if busy
            if (SCHED_ATTR_AVAILABLE(SchedEntry.Attr)==FALSE) {
                  GetTDString( &(SchedEntry).Interval.Lower, szLowerTD );
                  GetTDString( &(SchedEntry).Interval.Upper, szUpperTD );
                  fprintf(fz,"%s-%s\n",szLowerTD,szUpperTD);
            }
            pSchedList += sizeof(SCHED_ENTRY);
      }

}

/************************************************************************
    FUNCTION:   GetTDString
*************************************************************************/
void LNPUBLIC GetTDString( TIMEDATE * ptdModified, char * szTimedate )
{
    WORD  wLen;
    ConvertTIMEDATEToText( NULL, NULL, ptdModified, szTimedate, MAXALPHATIMEDATE, &wLen );
    szTimedate[wLen] = '\0';
    return;
}
------------------------------------------------------------------------------------------------------------------
LVL 3
ragerinoAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

AxterCommented:
You should avoid using goto.  It will give you much more problems then it solves.


 if ((fz=fopen(fname,"w"))==NULL) {
          retcode=1;
          goto done;
     }

The above section of the code tries to open a file, and thens jumps to done if it fails.
Done tries to close this same file that could not be open.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
SteHCommented:
Can you show how you typically call the DLL? And can some of the initialisation be done in the call of
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
taking care of the ul_reason_for_call parameter?

          case DLL_PROCESS_ATTACH: // a new process (application) calls DLL
          case DLL_THREAD_ATTACH:   // a new thread (of a known process) calls DLL
          case DLL_THREAD_DETACH:  
          case DLL_PROCESS_DETACH:

0
AxterCommented:
The main problem I see, is that after you successfully open the file, you then jump to NError.
NError does not close the file before exiting, which means you end up with a bunch of open files, and you run out of file handles.
0
Cloud Class® Course: Microsoft Windows 7 Basic

This introductory course to Windows 7 environment will teach you about working with the Windows operating system. You will learn about basic functions including start menu; the desktop; managing files, folders, and libraries.

AxterCommented:
Disregard my last comment.
I didn't see that NError flows down to done.

0
SteHCommented:
if ((fz=fopen(fname,"w"))==NULL) {
          retcode=1;
          goto done;
     }
//snip
done:
     fclose(fz);
     LAPI_RETURN(retcode);
}
could be replaced with

f ((fz=fopen(fname,"w"))==NULL) {
          retcode=1;
          LAPI_RETURN(retcode);
     }
//snip
0
SteHCommented:
But NError does not call the OSMemFree () and that can be the problem. Since the things to free depends on where you branch to NError I think you can equally save put the code for returning from the function several times in that function. And at each place you close and free what is open.

Else you need to adopt exception handling: When some error occurs cleanup is still done. But this will be a major rewrite, so perhaps not a real option.
0
grg99Commented:
One way to ensure things get cleaned up is to have a policy of returning ONLY from the very bottom of the routine, and have cleanup code just before the return.   Or to use structured exception handling, which amounts to much the same thing.

0
ragerinoAuthor Commented:
is there a tool which watches the busytime2.dll and tells me how much memory is still locked after it finished ????
0
waysideCommented:
Make sure you initialize all your handles to NULL:

HANDLE               list_handle = NULL;

Add code to close the handles after the done: label, and check them first:

done:
          //* Free all handles
         
          if (list_handle) {
            OSUnlockObject(list_handle);
            OSMemFree(list_handle);
          }

          if (rethSchedList) {
             OSUnlockObject(rethSchedList);
             OSMemFree(rethSchedList);
          }

etc.

You could still be leaking from within the while loop, it's hard to tell.
0
SteHCommented:
Since the memory is usually not allocated in the frame of the DLL but in the frame of the calling application it has to be done within all those applications.

Have a look at
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/_core_detecting_memory_leaks.asp
for several ways of debugging memory allocations. Difficulty is that memory, GDI resource and kernel objects are all handled differently and thus need different approaches for debugging.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
C++

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.