05260350.txt 26-May-00

Load VO-DLLs when PC starts


Hey,

is it possible to load the needed VO-DLLs and leave them in
  memory when the pc starts?
I hope the startup time for an VO app will be shorter.
Is this right and how to preload the dlls?

Calle

Calle,

This is not a very good idea. Nor is it likely to achieve
  what you desire.
For example, reboot your PC and then load something big -
  say, MS Word. Shut down Word and then restart it. Notice
  that the second time it starts it basically pops up onto
  your screen? That's because its memory image is still in
  VMM. The OS does not release the mappings until it has to.
  (The reason and mechanics of this a different between W98
  and NT but the effect is the same.) Now, just say you load
  in your VO dll's by running an app and leaving it in
  memory. Then you load MS Word and later you run an
  internet browser and then come back to another VO app.
  What do you think has happened? Well all your carefully
  loaded VO dll's have well and truly been kicked out of
  memory by now and still need to be reloaded. Hence, very
  little time saving.
Further, if the startup app you load actually does something
  to keep itself active (and hence as loaded as possible),
  it will chew out resources from other apps and slow
  everything else down.
I suggest you throw up a splash screen of something to keep
  users amused while the app loads. Either that or break up
  the app so that secondary dll's are loaded after the main
  screen is presented to the user.

Geoff


Hey,

thanks a lot to everybody jumping in.
I am now convinced that preloading the DLLs at system
  startup is not so good.
Splash screen or breaking up the app seems to be the best
  ways.

Thanks again.
Calle

Hi Calle,

> Splash screen or breaking up the app seems to be the best
  ways.
We break our application to 300++ of .dll, and load
  individual .dll when needed, using this approach have a
  few advantage:
Startup very fast, splash screen are not necessary.
Application can run on low end machine, some .dll can even

  run on Win95 486 with 8MB RAM.
Since the binary code are small, more free memory for data.
Each .dll are unrelated, we can do modular development, and
  doing unit test on individual .dll, instead of testing on
  a integrated .exe.
When a .dll are tested, we will remove it from repository,
  this keep our repository small, and lesser repository
  corruption problem.
Since repository are small, compile and linking on our
  166Mhz 32MB development machine are fast.
When update customer, we E-mail a sigle small .dll instead
  of large integrated .exe.
In the loader .exe, we put some diagnostic routine, when GPF
  or this sort of thing happen, we know which .dll to be
  check.
We can do "HRPCall.exe P320 2000.01.01 2000.01.02" for
  testing processing or put it to task schedular and run it
  at night.
Loader(.exe) can do a check sum test on .dll, to detect .dll
  corruption or virus infection.
Undate from WAN or LAN are fast, base on individaul .dll
  date-time-size, and download newer version of .dll only.
And a lot more... 

Regards,
Wong

Hi Wong,

that sounds very interesting.
Breaking the app into 300++ dlls, whow, that means every
  call to a function/methode residing in a DLL has to be
  prototyped and such things.
I haven't done such things till now, so I have to look into
  the books
.

Thanks
Calle

Hi Calle,

> Breaking the app into 300++ dlls, whow, that means every
  call to a function/methode residing in a DLL has to be
  prototyped and such things. >
No, we can define a standard/common interface between .exe
  and .dll, and let the .dll interface handle it, so .exe
  will no need to change even later on we create other .dll.
In our application, we got 300++
  Entry/Inqiry/Reporting/Processing, eah one we put them in
  a .dll, and use iMain as standard interface, please look
  at code below.

Regards,
Wong

ShowDate.dll
------------
e.g:
  HRPCall.exe ShowDate.dll 19990101


//ShowDate.prg 19-Feb-00 Wong
FUNCTION iMain(iArgC AS INT, acArgV AS ARRAY) AS INT PASCAL
  ShowDate(SToD(String2Psz(acArgV[3])))
RETURN (0)

STATIC FUNCTION ShowDate(dDate AS DATE) AS VOID PASCAL
  MessageBox(NULL,String2Psz(DToC(dDate)),;
    String2Psz("ShowDate"),0)
RETURN


ShowYear.dll
------------
e.g:
  HRPCall.exe ShowYear.dll 1999

//ShowYear.prg 19-Feb-00 Wong
FUNCTION iMain(iArgC AS INT, acArgV AS ARRAY) AS INT PASCAL
  ShowYear(Val(acArgV[3]))
RETURN (0)

STATIC FUNCTION ShowYear(wYear AS WORD) AS VOID PASCAL
  MessageBox(NULL,Str(wYear),String2Psz("ShowYear"),0)
RETURN


HRPCall.exe
-----------

//HRPCall.prg 19-Feb-00 Wong
FUNCTION Start() AS INT
  //Enable3DControls()
  LOCAL aoClass AS ARRAY, iSub AS INT, dwALen AS DWORD,;
    acArgV AS ARRAY, iArgC AS INT, oModule AS HRModule,;
    iExitCode AS INT
  aoClass:={HRError{},HRDyn{},HRModule{}}
  iSub:=1
  dwALen:=ALen(aoClass)
  DO WHILE iSub<=dwALen .AND. aoClass[iSub]:lInit()
    ++iSub
  ENDDO
  IF iSub==dwALen+1
    oModule:=HRModule{}
    BEGIN SEQUENCE
      acArgV:=acArgV()
      HRError{}:vAssert(At("HRPCALL.EXE",acArgV[1])!=0,;
        "Start up file must be HRPCALL.EXE",{acArgV[1]})
      iArgC:=INT(ALen(acArgV))
      HRError{}:vAssert(iArgC>=2,"DLL basename needed",{})
      oModule:oLoadLibrary(acArgV[2])
      BEGIN SEQUENCE
        acArgV[2]:=oModule:cGetModuleFileName()
        iExitCode:=oModule:iMain(iArgC,acArgV)
      END SEQUENCE
      oModule:vFreeLibrary()
    END SEQUENCE
  ENDIF
  DO WHILE --iSub>=1
    aoClass[iSub]:vAxit()

  ENDDO
  //If program hang, will not beep
  MemoWrit("C:\CON.DOS",_Chr(7))
RETURN (iExitCode)

FUNCTION acArgV() AS ARRAY PASCAL
  //acArgV.prg 15-Jan-00
  LOCAL cStr AS STRING
  LOCAL dwPos AS DWORD
  LOCAL acArgV AS ARRAY
  cStr:=Psz2String(GetCommandLine())
  cStr:=Upper(SubStr(AllTrim(cStr),2))
  dwPos:=At('"',cStr)
  acArgV:={Left(cStr,dwPos-1)}
  cStr:=AllTrim(SubStr(cStr,dwPos+1))
  HRError{}:vAssert(At('"',cStr)==0,;
    '"(double quote) does not allow',{cStr})
  DO WHILE !Empty(cStr)
    dwPos:=At(' ',cStr)
    IIf(dwPos==0,dwPos:=SLen(cStr)+1,NIL)
    AADD(acArgV,Left(cStr,dwPos-1))
    cStr:=AllTrim(SubStr(cStr,dwPos+1))
  ENDDO
RETURN (acArgV)

//HRError.prg 12-Feb-00
CLASS HRError INHERIT Error
DECLARE METHOD lInit
DECLARE METHOD vAxit
DECLARE METHOD lAlert
DECLARE METHOD vAssert

METHOD lInit() AS LOGIC PASCAL CLASS HRError
RETURN (TRUE)

METHOD vAxit() AS VOID PASCAL CLASS HRError
RETURN

METHOD lAlert(lValid AS LOGIC,cDescription AS STRING,;
  auArgs AS ARRAY) AS LOGIC PASCAL CLASS HRError
  EnforceType(lValid,LOGIC)
  EnforceType(cDescription,STRING)
  EnforceType(auArgs,ARRAY)
  IF !lValid
    SELF:Args:=auArgs
    SELF:Description:=cDescription
    SELF:CanDefault:=.T.
    Eval(ErrorBlock(),SELF)
  ENDIF
RETURN (lValid)

METHOD vAssert(lValid AS LOGIC,cDescription AS STRING,;
  auArgs AS ARRAY) AS VOID PASCAL CLASS HRError
  EnforceType(lValid,LOGIC)
  EnforceType(cDescription,STRING)
  EnforceType(auArgs,ARRAY)
  IF !lValid
    SELF:Args:=auArgs
    SELF:Description:=cDescription
    EVAL(ErrorBlock(),SELF)

  ENDIF
RETURN

//HRDyn.prg 19-Feb-00 Wong
STATIC GLOBAL dwRegisterAxit AS DWORD
STATIC GLOBAL dwDynInfoUsed AS DWORD

CLASS HRDyn
DECLARE METHOD lInit
DECLARE METHOD vAxit
DECLARE METHOD vCollectForced

METHOD lInit() AS LOGIC PASCAL CLASS HRDyn
  LOCAL lSuccess AS LOGIC
  IF ! InCollect()
    CollectForced()
  ENDIF
  lSuccess:=HRError{}:lAlert(DynInfoFree()>=16*1024*1024,;
    "DynInfoFree()>=16*1024*1024",{})
  dwRegisterAxit:=Memory(MEMORY_REGISTERAXIT)
  dwDynInfoUsed:=Memory(MEMORY_DYNINFOUSED)
RETURN (lSuccess)

METHOD vAxit() AS VOID PASCAL CLASS HRDyn
  LOCAL dwErrorCode
  IF !_DynCheck()
    dwErrorCode:=DynCheckError()
    HRError{}:lAlert(FALSE,;
      "DynCheckError()=="+AsString(DynCheckError()),{})
  ENDIF
  IF ! InCollect()
    CollectForced()
  ENDIF
  HRError{}:lAlert(;
    Memory(MEMORY_REGISTERAXIT)==dwRegisterAxit,;
    "Memory(MEMORY_REGISTERAXIT)==dwRegisterAxit",{})
  HRError{}:lAlert(;
    Memory(MEMORY_DYNINFOUSED)<=dwDynInfoUsed+1024,;
    "Memory(MEMORY_DYNINFOUSED)<=dwDynInfoUsed+1024",{})
RETURN

METHOD vCollectForced() AS VOID PASCAL CLASS HRDyn
  IF ! InCollect()
    CollectForced()
  ENDIF
  HRError{}:vAssert(DynInfoFree()>=4*1024*1024,;
    "DynInfoFree()>=4*1024*1024",{})
RETURN

//HRModule.prg 19-Feb-00 Wong
STATIC GLOBAL aoModule:={} AS ARRAY

CLASS HRModule
DECLARE METHOD lInit
DECLARE METHOD vAxit
DECLARE METHOD oLoadLibrary
DECLARE METHOD iMain
DECLARE METHOD vFreeLibrary
DECLARE METHOD cGetModuleFileName
PROTECT ptrModule AS PTR

PROTECT cModule AS STRING

METHOD lInit() AS LOGIC PASCAL CLASS HRModule
  AAdd(aoModule,SELF)
RETURN (TRUE)

METHOD vAxit() AS VOID PASCAL CLASS HRModule
  LOCAL iSub AS INT, oM AS HRModule
  FOR iSub:=1 UPTO ALen(aoModule)
    oM:=aoModule[iSub]
    HRError{}:lAlert(GetModuleHandle(;
      String2Psz(oM:cModule))==NULL_PTR,;
      oM:cModule+" still loaded")
    HRError{}:lAlert(oM:ptrModule==NULL_PTR,;
      "oM:ptrModule==NULL_PTR")
  NEXT
  aoModule:={}
RETURN

METHOD oLoadLibrary(cModule AS STRING) ;
  AS HRModule PASCAL CLASS HRModule
  SELF:cModule:=cModule
  //Changed to _VOLoadLibrary?
  SELF:ptrModule:=LoadLibrary(String2Psz(cModule))
  HRError{}:vAssert(SELF:ptrModule!=NULL_PTR,;
    "Unable to load DLL: "+cModule,{})
RETURN (SELF)

FUNCTION iMain(iArgC AS INT, acArgV AS ARRAY) AS INT PASCAL
RETURN (0)

METHOD iMain(iArgC AS INT, acArgV AS ARRAY);
  AS INT PASCAL CLASS HRModule
  LOCAL ptrMain AS iMain PTR, iExitCode AS INT
  ptrMain:=GetProcAddress(SELF:ptrModule,;
    String2Psz("iMain"))
  HRError{}:vAssert(ptrMain!=NULL_PTR,;
    "iMain(iArgC,acArgV) not found",{})
  iExitCode:=PCALL(ptrMain,iArgC,acArgV)
RETURN (iExitCode)

METHOD vFreeLibrary() AS VOID PASCAL CLASS HRModule
  LOCAL lSuccess AS LOGIC
  IF ! InCollect()
    CollectForced()
  ENDIF
  //For VO generated .dll, must use
  //_VOFreeLibrary to clear up DynMem,
  //FreeLibrary will cause 4660/5333, check for better way.
  lSuccess:=_VOFreeLibrary(SELF:ptrModule)
  HRError{}:vAssert(lSuccess,;
    "Unable to free "+SELF:cModule,{})
  HRError{}:vAssert(;
    GetModuleHandle(String2Psz(SELF:cModule))==NULL_PTR,;
    SELF:cModule+" still loaded",{})
  SELF:ptrModule:=NULL_PTR
RETURN

METHOD cGetModuleFileName() AS STRING PASCAL CLASS HRModule
  LOCAL DIM abBuffer[256] AS BYTE, dwLen AS DWORD,;

    cFileName AS STRING
  dwLen:=GetModuleFileName(SELF:ptrModule,@abBuffer,256)
  HRError{}:vAssert(dwLen!=0,"dwLen!=0",{})
  cFileName:=Mem2String(@abBuffer,dwLen)
RETURN (cFileName)

Wong,

I am impressed with your approach but do you think there
  might not be a little overkill in some of the many smaller
  dll's you must have? Could it not be a little more
  efficient to make up a few libraries of the smaller
  routines? It would reduce the dll complexity, increase
  operational speed (and obviously increase load size a
  little - but not much). Also you would be reducing the
  number of FAR calls to so many minor functions.

Geoff

Load VO-DLLs when PC starts.doc 19-Feb-00

Hi Geoff,

I'm not really understand your message, either one of us
  must have bad English. 
Maybe that is better to let me explain our case, somebody
  may can treat it as a case study.
For our this DOS to Windows convertion project, we had
  complete restructure/simplify/documentation/enhancement
  under DOS(Clipper) version, now is at the last stage, code
  porting.
This application contain 5 modules, each of them contain
  about hundard of sub-modules of
  Entry/Inqiry/Roporting/Processing, total up about 300++
  sub-modules.
Partial sub-modules of two modules(Time Attendance,
  Utilities) are listed below.
Time Attendance
  Masterfile
    E302 Time Card Allocation
    E304 Shifts Applicable
  Transactions
    E332 Group Change-Of-Shift
    E336 Overtime Application
  Inquiry
    I360 CICO Inquiry
    I350 Time Record Inquiry
  Processing
    P352 Attendance Processing
    P354 OT Reason Processing
  Report
    R386 Time Card Allocation
    R390 Group Change-Of-Shift
Utilities
  File
    P966 Pack Dbf & Recreate Index
    P968 Delete Unwanted Record
  Report
    R986 User Profile Report
    R988 Security Option Report

Each module contain in an .exe(Attendan.exe, Utility.exe...)
We put source code for each sub-module in seperated
  directory, e.g.
HRSource\Module\P966
HRSource\Module\P968
Sometime user ask for modification, after applied changes,
  we will email P966.exe/P968.exe to them, after user
  confirm this is what they want, then we send Utility.exe.
Now if Windows version of P966/P968 had completed, we will
  send P966.dll/P968.dll instead of P966.exe/P968.exe, in
  the Utility.exe it check for existance of
  P966.dll/P968.dll.
IF FILE("P966.dll")
  RUN ("HRPCall.exe P966.dll")
ELSE
  P966()
ENDIF
So in this way we don't need to send DOS version of
  Utility.exe again(Utility.exe are quite large).
I hope you understand what I said, can you further explain
  your message?

Regards,
Wong

Wong,

Actually, Chapter 17 and pages 244-245, in particular, of
  the Programmer's Guide can explain it best. But there are
  some good guidelines for the evolution of new dlls. What I
  was trying to say was that execution speed and processing
  overhead will always be lower with a local function than
  one found in a dll. So, if you have lots of calls to
  little functions in separate dll's, the overhead can be
  high.
Of course if you are not experiencing any problems or slow
  execution then there is nothing to worry about. But your
  demo code seemed to indicate that many of the dll's may in
  fact contain only a few lines of code. In which case some
  efficiency is to be gained consolidating them into grouped
  libraries. There is also overhead in the dll itself. 10
  functions as dll's will be larger than one dll with 10
  functions. As I said, it may not be an issue but it may
  also be worth considering.
Geoff

Hi Geoff,

>  But your demo code seemed to indicate that many of the
  dll's may in fact contain only a few lines of code. >
I think you may refer to generic function/class library. Due
  to NG are not for long code, actually the code I posted
  are extract/simplified from our actual function/class
  library, acArgV.prg are from Misc.dll, HRError class from
  HRError.dll. Take HRError class as example, where are the
  code HRError:uHandler() of
  ERRORBLOCK({|oErr|HRError{}:uHandler(oErr)})?
HRError:lInit() and HRError:vAxit() should also contain some
  code, and some more .prg for internal supporting routine
  in HRError.dll. am I right?  ;-)

Anyway, thank for your input. :-)

Regards,
Wong

Calle,

The one technique that gets you around the prototyping issue
  is to break the app into class related DLLs. In other
  words, you place seperate or related classes into seperate
  DLLs, then dynamically load those DLLs when you need them.
  You can at the start of some generic part of an app like
  in the Accounts Payables section have a peice of code
  which detects whether or not the user has traversed this
  particular section of the app and if not, then dynamically
  load the classes from the DLLs, necessary to run that
  portion of the application.
I have a white paper on these issues at the voregistry
  website. The paper is titled "Team development using CA-
  Visual Objects". Go to http://www.tksoftware.com and click
  on the VO Registry menu option then look for the "Other VO
  related stuff" hyperlink.
This paper documents the technique that we used at Rent
  Roll, Inc. when I was the director of development a while
  back. The technique is still valid and works great. The
  needed source code to implement the technique is in the
  paper as well.
We use the same technique in our Active Web Toolkit ActiveX
  component to allow for the external extension of our
  product. This is the extension technique used by
  KnowVo.com to implement the high speed search engine for
  the VO knowledge base.
Please have a look at the paper. I think it will help.

George

    Source: geocities.com/n_s_wong/vo/ng

               ( geocities.com/n_s_wong/vo)                   ( geocities.com/n_s_wong)