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