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
               (
geocities.com/n_s_wong/vo)                   (
geocities.com/n_s_wong)