Greetings. A basic question.
I have this abl callign that class:
DEFINE VARIABLE clFinger AS CLASS FingerPrint NO-UNDO. DO ON ERROR UNDO, LEAVE ON ENDKEY UNDO, LEAVE ON STOP UNDO, LEAVE ON QUIT UNDO, LEAVE: clFinger = NEW FingerPrint (INPUT-OUTPUT dataset dsBlobTemplates, OUTPUT resultadoProceso ) . WAIT-FOR System.Windows.Forms.Application:Run ( clFinger). IF resultadoProceso <> "" THEN DO: MESSAGE "se presentó el siguiente error:" SKIP resultadoProceso VIEW-AS ALERT-BOX ERROR. END. ELSE DO: FIND FIRST ttblobTemplates. COPY-LOB FROM ttblobTemplates.TemplateField TO FILE "c:\t\t1.txt". END. END.
Class:
// output parameters DEFINE TEMP-TABLE ttblobTemplates NO-UNDO FIELD TemplateField AS BLOB FIELD TemplateLenght AS INTEGER. // DEFINE DATASET dsTT1 FOR ttblobTemplates. DEFINE VARIABLE Resultado AS CHARACTER NO-UNDO. CONSTRUCTOR PUBLIC FingerPrint ( INPUT-OUTPUT DATASET dsTT1, OUTPUT resultado AS CHARACTER): SUPER(). InitializeComponent(). THIS-OBJECT:ComponentsCollection:ADD(THIS-OBJECT:components). CATCH e AS Progress.Lang.Error: UNDO, THROW e. END CATCH. END CONSTRUCTOR. @VisualDesigner. METHOD PRIVATE VOID btnStart_Click( INPUT sender AS System.Object, INPUT e AS System.EventArgs ): DEFINE VARIABLE ufs_res AS UFS_STATUS NO-UNDO. DEFINE VARIABLE EnrollQuality AS INTEGER NO-UNDO. DEFINE VARIABLE myChar AS CHARACTER NO-UNDO. DEFINE VARIABLE j AS INTEGER NO-UNDO. ASSIGN Scanner = ? scannerManager = NEW UFScannerManager(THIS-OBJECT) ufs_res = scannerManager:Init(). // * * ** * some initialisation code here... Scanner:ClearCaptureImageBuffer(). ufs_res = Scanner:CaptureSingleImage(). ASSIGN Template = NEW "System.Byte[]" (max_template_size) ufs_res = Scanner:Extract(Template,TemplateSize, EnrollQuality). CREATE ttblobTemplates. ASSIGN ttblobTemplates.TemplateField = ByteArrayToMemptr(template) // from knowledgebase ttblobTemplates.TemplateLenght = TemplateSize. COPY-LOB FROM OBJECT ttblobTemplates.TemplateField TO FILE "e:\t\fingerprint.dat" NO-CONVERT. RELEASE ttblobTemplates. END. ELSE ASSIGN resultado = msgError(ufs_res:value__). ufs_res = scannerManager:Uninit() . THIS-OBJECT:close(). RETURN. END METHOD.
There are two huge problems, I can't get on to...
1) the class indeed creates de fingerprintfile.dat
2) back on ABL, the TT is empty, and
2.1) The "resultado" parameter gets empty, no matter any value I stored in the class..
I wonder, where in the road I missed all about OO?
Either make a method that returns the table as an OUTPUT parameter (which will make a copy of it unless you use the BIND/BY-REFERENCE feature - look up passing table parameters), or provide methods in the class to return the data that you're looking for.
This is expected. It's an Application:Run(...). You are very likely closing the form either using the Red X or calling THIS-OBJECT:Close(), so the temp-table might be cleaned up because the form is disposed.
Also, the resultado variable you pass to the constructor and the one you assign inside btnStart_Click are two different instances.
Re the last answer. Not sure what the Application:Run() has to do with this. But yes - you called the constructor with an output parameter and the constructor did not populate it. The constructor is just like a method - it has to do whatever you want it to do before it returns. But since you do have the temp-table has a class member (labelling it "output parameter" makes no sense - it is just a class member), it should indeed be populated once the button is clicked and so would still be available once the Application:Run() is over (yes - from hitting the X or whatever).
Sorry!!!! I missed the edit when pasting... the application:run() is on the same class....
Isn't the temp-table a copy when it was passed to the constructor? I would assume the copy is scoped to the form. The form is non-modal and will be disposed when Application:Run() ends, so I think the copy should go away too. If the form is not disposed after Application;Run() then that's a memory leak.
I always thought that, for this case, you need to call a method that outputs the temp-table before the form is disposed.
On pursue of a solution, I want to conver this form to a class I can call from ABL to do it's stuff, this would be the resulting class:
BLOCK-LEVEL ON ERROR UNDO, THROW. USING Suprema.UFS_STATUS FROM ASSEMBLY. USING Suprema.UFScanner FROM ASSEMBLY. USING Suprema.UFScannerManager FROM ASSEMBLY. CLASS Procs.enroll.Act.FingerPrint: DEFINE PRIVATE VARIABLE scannerManager AS Suprema.UFScannerManager NO-UNDO. DEFINE PRIVATE VARIABLE Scanner AS Suprema.UFScanner NO-UNDO. DEFINE PRIVATE VARIABLE max_template_size AS INTEGER NO-UNDO INITIAL 512. DEFINE PRIVATE VARIABLE Template AS "System.Byte[]" NO-UNDO. DEFINE PRIVATE VARIABLE TemplateSize AS INTEGER NO-UNDO. DEFINE VARIABLE ufs_res AS UFS_STATUS NO-UNDO. DEFINE VARIABLE EnrollQuality AS INTEGER NO-UNDO. DEFINE TEMP-TABLE ttblobTemplates NO-UNDO FIELD TemplateField AS BLOB FIELD TemplateLenght AS INTEGER. DEFINE DATASET dsTT1 FOR ttblobTemplates. DEFINE VARIABLE Resultado AS CHARACTER NO-UNDO. CONSTRUCTOR PUBLIC FingerPrint ( INPUT-OUTPUT DATASET dsTT1, OUTPUT resultado AS CHARACTER): SUPER(). ASSIGN Scanner = ? scannerManager = NEW UFScannerManager(THIS-OBJECT) ufs_res = scannerManager:Init() resultado = "OK". IF STRING(ufs_res) = STRING(UFS_STATUS:OK) THEN ASSIGN Scanner = scannerManager:Scanners[0] Scanner:Timeout = 5000 Scanner:TemplateSize = max_template_size Scanner:DetectCore = FALSE Scanner:nTemplateType = 2001. ELSE DO: ASSIGN resultado = msgError(ufs_res:value__). ufs_res = scannerManager:Uninit() . RETURN. END. Scanner:ClearCaptureImageBuffer(). ufs_res = Scanner:CaptureSingleImage(). IF STRING(ufs_res) <> STRING(UFS_STATUS:OK) THEN DO: ASSIGN Resultado = "No se pudo leer ". ufs_res = scannerManager:Uninit() . RETURN. END. ASSIGN Template = NEW "System.Byte[]" (max_template_size) ufs_res = Scanner:Extract(Template,TemplateSize, EnrollQuality). IF STRING(UFS_STATUS:OK) <> "OK" THEN ASSIGN resultado = "Lectura incorrecta". IF EnrollQuality < 40 THEN ASSIGN resultado = "Repita la lectura de la huella". ELSE DO: CREATE ttblobTemplates. ASSIGN ttblobTemplates.TemplateField = ByteArrayToMemptr(template) ttblobTemplates.TemplateLenght = TemplateSize. COPY-LOB FROM OBJECT ttblobTemplates.TemplateField TO FILE "e:\t\fingerprint.dat" NO-CONVERT. RELEASE ttblobTemplates. END. ufs_res = scannerManager:Uninit() . THIS-OBJECT:close(). RETURN. CATCH e AS Progress.Lang.Error: UNDO, THROW e. END CATCH. END CONSTRUCTOR. /*------------------------------------------------------------------------------ Purpose: msgError. Genera un mensje de error, de acuerdo a los codigos enviados por ufs_res:value__ Notes: ------------------------------------------------------------------------------*/ METHOD PUBLIC CHARACTER msgError( INPUT ndx AS INTEGER): DEFINE VARIABLE Cadena AS CHARACTER NO-UNDO. CASE ndx: WHEN 0 THEN cadena = "Success". WHEN -1 THEN cadena = "(1) General error". WHEN -101 THEN cadena = "(101) System has no license". WHEN -102 THEN cadena = "(102) License is not match". WHEN -103 THEN cadena = "(103) License is expired". WHEN -111 THEN cadena = "(111) This function is not supported". WHEN -112 THEN cadena = "(112) Input parameters are invalid". WHEN -201 THEN cadena = "(201) Module is already initialized". WHEN -202 THEN cadena = "(202) Module is not initialized". WHEN -203 THEN cadena = "(203) Device number is exceed". WHEN -204 THEN cadena = "(204) Error on loading scanner library". WHEN -211 THEN cadena = "(211) Capturing is started USING UFS_CaptureSingleImage OR UFS_StartCapturing". WHEN -212 THEN cadena = "(212) Capturing is timeout or aborted". WHEN -301 THEN cadena = "(301) Input image is not good". WHEN -302 THEN cadena = "(302) Extraction is failed". WHEN -351 THEN cadena = "(351) Core is not detected". WHEN -352 THEN cadena = "(352) Move finger to left". WHEN -353 THEN cadena = "(353) Move finger to left-top". WHEN -354 THEN cadena = "(354) Move finger to top". WHEN -355 THEN cadena = "(355) Move finger to right-top". WHEN -356 THEN cadena = "(356) Move finger to right". WHEN -357 THEN cadena = "(357) Move finger to right-bottom". WHEN -358 THEN cadena = "(358) Move finger to bottom". WHEN -359 THEN cadena = "(359) Move finger to left-bottom". END CASE. RETURN Cadena. END METHOD. METHOD PUBLIC STATIC MEMPTR ByteArrayToMemptr (poBytes AS "System.Byte[]":U): DEFINE VARIABLE myMemptr AS MEMPTR NO-UNDO . DEFINE VARIABLE oIntPointer AS System.IntPtr NO-UNDO . SET-SIZE (myMemptr) = poBytes:Length . oIntPointer = NEW System.IntPtr (GET-POINTER-VALUE (myMemptr)). System.Runtime.InteropServices.Marshal:Copy (poBytes, 0, oIntPointer, poBytes:Length). RETURN myMemptr . FINALLY: DELETE OBJECT oIntPointer. END FINALLY. END METHOD . DESTRUCTOR PUBLIC FingerPrint ( ): DEFINE VARIABLE ufs_res AS UFS_STATUS NO-UNDO. ufs_res = scannerManager:Uninit() . END DESTRUCTOR. END CLASS.
The problem came on:
scannerManager = NEW UFScannerManager(THIS-OBJECT),
it fires a (12905) error on the THIS-OBJECT, as I will have no visuals of the process no more...
and on THIS-OBJECT:close().. I bet there should be no CLOSE()
TIA
Jorge
Per documentation of suprema.dll, the instatiation should like this (this is the C# doc)
UFS_STATUS ufs_res; UFScannerManager ScannerManager; int nScannerNumber; // Create an instance of ScannerManager ScannerManager = new UFScannerManager(this); // Initialize scanner module ufs_res = ScannerManager.Init(); // Always check status return codes after running SDK functions // Meaning of status return code can be retrieved using UFScanner.GetErrorString() // In the tutorial, we omit error check codes // Check number of scanners nScannerNumber = ScannerManager.Scanners.Count;
Re the post by jquerijero on Wed, Feb 22 2017 12:26: I would like to clear up this common misconception. Being disposed and being deleted are 2 different things. When the form is closed, Disposed is called on it, but the object still exists as long as there is a reference to it. The purpose of being disposed is really to clean up "unmanaged" resources (i.e., not managed by the .NET runtime) in a timely manner, such as file handles. So unless the class explicitly clears the temp-table, the temp-table and its contents will still be there when the form is closed, given that the object reference clFinger, is still in scope.
So, changing the visual to a simple class to be called from ABL, will let me pass the temp-table back? Still not able to run, as can't compile because the : NEW UFScannerManager(THIS-OBJECT) , that is present..
Before when the form inherited from FORM, there were no error. now I supose it whould inherit from other class, but can't figure wich... any clue?
The UFScanerManager relies on communicating with a Form. This is not uncommon to WinForms applications communicating with devices to be able to synchronize events from background threads into the UI thread. .
See my remark on this subject from another thread: It does not need to be THIS-OBJECT. If you plan to use the UFScannerManager from a simple class, you can use the application's main menu form or any other form. Just pass the reference of that form to the Constructor of your simple class, so that you can pass it along to the constructor of the UFScannerManager class.
Thanks Mike...
Just to learn more... My main menu is an ABL UI design, from top to bottom.... in the middle there is no form realized... so you think I should pop a simple form to show "processing..." or something like that? In any case, how would this be achieved?
Because, if i revert to the version where a full form made the process.. I'll be back ar square 1, where I can't pass the temp-table back to the calling procedure....
One step further in the solution:
HOW TO ACCESS THE CONTENT OF A TEMP-TABLE, FILLED IN A CLASS, OUTSIDE THE CLASS?
Either make a method that returns the table as an OUTPUT parameter (which will make a copy of it unless you use the BIND/BY-REFERENCE feature - look up passing table parameters), or provide methods in the class to return the data that you're looking for.
Another option would be to pass a TT or Dataset handle through a method and move the data that way.
But, (having a real hard time trying to imagine what's hapening at runtime) what should be steps with this kind of procedure...
Just got a strike of ligth.. I'll explain so newbies like me, don't strugle with old mental patterns for days like me....
I was trying to use old sequential flow of instructions, wanted the class do all the process on instatiation...
the "provide methods" on Ms Laura's post, as simple and natural for her to say it, created a breaktru in my mind. Finally I got it, after several years trying to accomodate the OO paradigm in my head. and countless readings, finally make sense...
Thanks!.
Think of class as if it is a procedure (.p) called persistently.
Just an extra question.....
As this class needs visual representation, for what comented Mike Fechner, how should I do this task at hand?
define a class, which in several methods which in turn, just initialize the scanner (scannerManager = NEW UFScannerManager(THIS-OBJECT) and pops a dialog.. no.. that would leave this-object with no suitable properties to be hooked by the device...
How valid (solid) is the first object (class) to instantiate another object wich in turn has visuals and do the fingerprint reader stuff?
I can't imagine if this is a solid approach
Of course, if you encapsulate the TT in the class and provide methods on the class to do all the work on the TT, then you don't need to pass the TT in or out. This is really the OO thing to do, i.e., hide the implementation details so that nothing else needs to know how things are done, just that certain services are available.