Morten Brendefu 28 Light Poster

Dear enlightened ones.
First of all. I am using Delphi 9.
My code compiles and run with no error at all, still I am confused about the structure window in the Delphi window.
It tells me that I have redeclared some identifiers.
The thing is that I have a few overloaded procedures of which uses different parameters only. The code in each procedure is however very similar, hence I use same local variables.

I think the structure window is wrong in its assumption, but I am not 100% certain.
All of these procedures are marked with overload-directive in unit declaration.

My logic say that maybe it is enough to declare the local variables in first procedure since they are Overloaded.
I tried commenting out the local var and variable where the Structure window tell me they are redeclared, and it seem to work alright.
what I don't seem to grasp is that I do not get the redeclared error in all the overloaded procedures only a few of them.

I would really appreciate some thoughts on this issue.
I think that even though the procedures are Overloaded, the correct approach is to have the declaration of local variables in place in every one of them.
Compiling does not cause problems and unit works flawlessly.
:-/ Is this a known issue with the Structure window?

PROCEDURE DynamicCreateIndex(VAR ArrParam:DynamicLongIntArray; VAR ArrParamIndex: DynamicIntegerArray);
  VAR
    x : INTEGER;
  BEGIN
    FOR x:=0 …
Morten Brendefu 28 Light Poster

Although not writing this procedure for you, I can give you a few hints.

First a tip :)
you are working with the "old style" of data storing and reading.
It could be better if you actually made a typed record of your needed information.
Afterwards, easier to read or write individual records from/to disk instead of like now, you have to read or write from beginning to end of file.

Anyway. The commands for reading from file is the same as reading from the Console.
You just use READLN in order to read a whole line in your textfile
your assign command is ok :-) Assign(currentfile, ............
Now consider this use of 'currentfile' :-)
READLN(currentfile,Name);
READLN(currentfile,Age);

As for putting information into an array, I would strongly suggest making this as a typed record due to much better filehandling abilities later.
The only advantage I can see about keeping it as a pure textfile is that you can open the file with notepad and look at and also edit stored data very easily.

Consider:
TYPE
persona = RECORD
Name : STRING;
Age : BYTE; // No person to my knowledge can be older than 255 years.
END; // you must however convert textinput into number. StrToInt() is your command for this.

VAR
Persons : ARRAY[0..100] OF Persona;
DataFile: FILE OF Persona;


Other useful commands to you …

Morten Brendefu 28 Light Poster

Hmm.. it looks like WinRar or a RAR file can not be added.
I am trying to add Zip file now :-)

Enjoy.

DelphiGuy commented: Thanks, some very useful code in that package +1
Morten Brendefu 28 Light Poster

I managed to dig up useful code for this problem.
I'll try to upload a file for this.
I made everything using Delphi 9.
Best regards.

Morten Brendefu 28 Light Poster

I'll just give you a few more commands to play with :-)

On a typed file, you just use the command FileSize(Type).
It gives you the exact number of records in the file.
:icon_exclaim: just remember that 0 is also a record.

Now for the coding:

VAR
  TheStart, TheEnd:INTEGER;
BEGIN
.... ... ..
  TheEnd:=FileSize(YourAssignedFileType);
  TheStart:=FileSize-14;
  DEC(TheStart); // We need to decrease this value by one because we count Zero too.
  Seek(YourAssignedFileType,TheStart);// Jumps to the correct location of data.
  REPEAT
    .... read the data
    INC(TheStart);
  UNTIL TheStart=TheEnd;
.. ... ....
END;

I am sure you figure the rest out.
If you have less than 14 records, you need to make some code to avoid error there too.

Best regards.
Morten, Norway.

Morten Brendefu 28 Light Poster

The link I provided gave insight in how the keyboard interfaces with computer also.
It gave me a nasty idea.
Make an electronic device that plug into your computer. Plug your keyboard into this device. connect cable from device into serial or usb port.
Make a program With Delphi or Turbo Pascal to access the keyboard buffer in device and change the data as you may find useful. release changed data into computers keyboard port.
Of course, you need to make this electronic device first, but no other software based keyloggers would get your key-inputs before you do with your special design combination of electronics and software.
If you get rich on this idea, please send me a postcard:twisted:
Best regards.
Morten, Norway.

Morten Brendefu 28 Light Poster

I would love to help you out if I only could.
My skills in assembly language is "only" useable on the old Commodore 64.
I would not know how and where to insert or change adresses of interrupts.
I just have many ideas, and only a few of them is within my reach so far :D
I could elaborate a little bit further on your problem. It may help, but I can not make code towards it except hinting about the possible structure and method of how to do it.

As soon as any computer is turned on, the BIOS starts loading. The BIOS involves a keyboard handler that can be used from assembler.
I suppose it should be possible to examine the memory with some tool, and actually make a copy of this keyboard assembler code.
Then you may alter the code to your likings. write it to someplace suitable in memory and redirect interrupt to this code instead of letting interrupt point to BIOS code.

I think that is basically what a keyboard driver program should do, and could do.
I suppose the code itself is not big at all. less than 2-300 byte maybe.
The tiny size should make it possible to find a location for it that is compatible with windows, and also mask it so that windows does not rewrite that particular area of memory.

If I were to really make an effort into …

Morten Brendefu 28 Light Poster

My TypeDefinition is simple enough:

TYPE
  DynamicIntegerArray : ARRAY OF INTEGER;
  DynamicStringArray : ARRAY OF STRING;

When transferring an array as a parameter, I have to make a type first, and then it works.

Morten Brendefu 28 Light Poster

That link was indeed a nice one for my personal purposes.:*

However.
As it goes, the hook is inserted in some Windows API.
I am pretty sure that some viruses can access the keyboard directly via hardware.
All in all, Windows API does just that too, and if you ask a windows API for keyboard events, then this is controlled by this API first. In other words, your program will not be the first to read the keyboard.

Without actually knowing 100% sure, I think the method to use is best done via Assembler code and redirecting Keyboard scan interrupt to your own assembler routines for then to direct the flow of control back to original address of original interrupt.

Back in the old days, before I fell in love with Turbo Pascal, I wrote some simple ASM code on the commodore 64 that redirected interrupts.
A much simpler computer with easier memory handling, but the basics of computers has not changed. Even the most advanced and sophisticated computer today depends on the same basic stuff of interrupts for many actions.
With the layer of Windows on top, it is not easy to find the right information on how to be allowed to change such an interrupt after Windows have started up.

My best suggestion is to write an assembler program, A kind of Keyboard Driver, one that loads into computer memory BEFORE windows starts up.
Windows Keyboard API should …

Morten Brendefu 28 Light Poster

Dear knowledgeable ones.
Some time ago I made a few of my routines kind of universal, and I wonder if there is any way in Delphi (My version is Delphi9) that can be used to reuse this code in example below.
As you see, the two procedures are almost identical, it is just the typedeclaration that is different in the parameters passed to the procedures.

Maybe it is possible to use some kind of typecasting but my experience there is limited.

The code is not bad if only 2 similar blocks, I just see that it would be better with one block of code that handles sorting of maybe 10 different types of data..

Best regards, and thanks in advance for input on this issue.


Example of code:

PROCEDURE BinarySortIndex(VAR ArrParam,ArrParamIndex: DynamicIntegerArray; CONST HighBound:INTEGER); OVERLOAD;
    VAR
      x       : INTEGER;
      TempVar : INTEGER;
    BEGIN
      Lo:=0; Hi:=HighBound; Mid:=FindMid;
      TempVar := ArrParam[HighBound];
      REPEAT
        IF TempVar>ArrParam[ArrParamIndex[Mid]] THEN Lo:=Mid ELSE Hi:=Mid;
        Mid:=FindMid;
      UNTIL (Mid=Lo) OR (Mid=Hi);
      IF TempVar>ArrParam[ArrParamIndex[Mid]] THEN INC(Mid);// We always need a last check just in case.
      FOR x:=HighBound-1 DOWNTO Mid DO ArrParamIndex[x+1] := ArrParamIndex[x];// Shift the index.
      ArrParamIndex[Mid]:=HighBound;// Store the pointer to index at its sorted place
    END;

  PROCEDURE BinarySortIndex(VAR ArrParam:DynamicStringArray; VAR ArrParamIndex: DynamicIntegerArray; CONST HighBound:INTEGER); OVERLOAD;
    VAR
      x       : INTEGER;
      TempVar : STRING;
    BEGIN
      Lo:=0; Hi:=HighBound; Mid:=FindMid;
      TempVar := ArrParam[HighBound];
      REPEAT
        IF TempVar>ArrParam[ArrParamIndex[Mid]] THEN Lo:=Mid ELSE Hi:=Mid;
        Mid:=FindMid;
      UNTIL (Mid=Lo) OR (Mid=Hi);
      IF TempVar>ArrParam[ArrParamIndex[Mid]] THEN INC(Mid);// We always need a last check just …
Morten Brendefu 28 Light Poster

There are many places on internet for great help regarding many things.
Proper implementation of really fast routines for sorting data however is not common.
I am not claiming these routines to be the fastest around, but their ease of implementation and use may make them welcome.

As a thank you to this site for great help regarding some of my questions, I am now posting my own developed routines for making a sorted index of either Integers or Strings.
A binary sort routine is basically like what I have written. Coincidence may make the routines look like something previously developed by other people. This is however my own work, hence why I now choose to make them freely available through this site.


The implementation of this is easy.
All you need to do is :

  1. add the unit to the USES clause in your form/unit.
  2. Instead of creating a fixed array of Integer or a fixed array of Strings, you must make a dynamic array with either of the predefined types listed in this unit.

If you already have a list of 10.000 records of strings that you want sorted, then:
DynamicCreateIndex(StringArray,IndexArray);
Within not long at all you have an index of your data in rizing order.

Same with Integers:
DynamicCreateIndex(IntegerArray,IndexArray);


If you are entering new data one by one, then you do not need to sort everything every time.

Morten Brendefu 28 Light Poster

Some times the answer is so obvious that it is kind of unreal
within the code I can right click and choose Fold.
It can fold(Shrink/minimize) one or more procedures/methods.

I still have not found the ability to keep them folded though. That is my next goal :-)

Morten Brendefu 28 Light Poster

In my Delphi9, I have the ability to "Shrink" complete procedures and functions so that only the first line is visible.
There is a small + or - at the beginning of first line of a routine, and when pressed, the code is 'minimized' or 'maximized'.
I find this to be a great way of making code easy to browse through, only opening the routine I need to work with at the moment.

However. I seem to have a problem with this, because when I start writing a new procedure between some of those already minimized, my editor at some point always expand all code below where I am working.

I have tried looking through options for my editor, but I can not find anything of use there.
I simply would like a way to keep the procedures and functions minimized until I choose to open them.

Another thing. I wonder if there is an ability to automatically minimize all procedures and functions in the editor. so that I dont have to click all of them..

Thank you for any help regarding this issue.

Morten Brendefu 28 Light Poster

in pascal, you have to predefine all your variable types, including array lengths, so you should already know the length.

For most programs we need to write, this may be true.
There are however many cases where we need to find the length of an array.

An array can be defined as open.

VAR
  Example:ARRAY OF STRING;
  Example2:ARRAY OF ARRAY OF INTEGER;

These arrays are resizeable in runtime :-)

SetLength(Example,10);
SetLength(Example2,10,10);

A multidimensional array is nice to use this way. Each sub array can be defined separately.

SetLength(Example2,5);
SetLength(Example2[0],10);
SetLength(Example2[1],6);
SetLength(Example2[2],90);
SetLength(Example2[3],12);
SetLength(Example2[4],19);

Instead of creating an array that is holding all the possible records our program can possibly need, we can design the program to only use as much memory as is actually needed on demand.

In these cases with SetLength, it makes sense to use the command HIGH(Example2[4]) in order to read out its set length.
Since SetLength always start an array at [0], the command LOW will always return Zero.

PROCEDURE SomeParameters(CONST Params:ARRAY OF INTEGER);
  VAR
    x:BYTE;
  BEGIN
//    HIGH(Params) will return the number of integers passed in the Params array.
    FOR x:=0 TO HIGH(Params) DO BEGIN
      // Code for whatever you would like to do with the parameters.
    END;
  END;

And again... When an array is passed as parameters, to my knowledge it always start with [0], so LOW(Params) will always return Zero.

IF you however declare an array from scratch in VAR section of program.

VAR
  Example : ARRAY[7..14];
BEGIN …
Morten Brendefu 28 Light Poster

Any radio button created belongs to the actual form it was created on.
As long as the form is in the uses clause on other forms, then you can access its components and states.

If you have a form named SETUP and a form named CODE,
Then:
CODE.RadioButton1 will access properties on this radiobutton from Setup section/form.

OR, From CODE form:
SETUP.RadioButton5 will access this SETUP RadioButton.

I hope this is understandable :-)
Best regards.

Morten Brendefu 28 Light Poster

To pass parameters as a variant record works to a certain degree.
The procedure VarType(Variant) returns Double on TDate and TTime though.
In a way, it does make sense because TDate and TTime is actually a Double.
It is just strange that the variant holds VarDate as an identifier, but can not identify it :-)

Similarily I have problems when passing a STRING as a parameter. VarType(Name) does not recognize that Name is a variable of type STRING. SHORTSTRING however is no problem.

There seem to be a few caveeats with parameters as variant because it does not necessarily recognize the actual type given in the array of parameters.
...
I moved on to trying out ARRAY OF CONST as a parameter.
This seem to work better.
Strings are recognized. Double is recognized as Extended. but then again. I think this is where I will continue my development. I need strings to be recognized, but a double to be extended, that is just a minor triviality that can be solved easily in my routines as I know which types to be expected in the array of parameters.

Many thanks for pointing me in the somewhat right direction :-)

pritaeas commented: Thanks for the update. +14
Morten Brendefu 28 Light Poster

Hmmm. What language I use :-)
I would like to say that it is Turbo Pascal. I use Delphi 9 though.
I don't really get the difference between Turbo Pascal and Delphi.
I think the main difference is all the premade stuff with forms and components written for windows.
I just program Turbo Pascal with Delphi 9 and its components and forms :-)

I suppose I can use a variant record as an array of parameters. Not sure how to automatically find the variable I am dealing with afterwards though.
The whole point is that I want to pass some parameters with only 1 line of code.
If needing to add a lot of different code in order to preset an array before then passing the array as a parameter, then the whole point of making procedure is gone.

Maybe you have an example of how to use a variant record for such a task ?

Morten Brendefu 28 Light Poster

I am trying to write a few very dynamic procedures and functions and wonder about transferring parameters to a procedure as an Array.
This is actually easy enough done.... just that I would like to pass something like

VAR 
  a,b,c             : INTEGER;
  Name, Text1,Text2 : STRING;
  todaysDate        : TDate;

PROCEDURE HandleValues(Const Values:ARRAY OF ???)
  BEGIN
    // Code to handle fetching the values.
  END;


BEGIN
  HandleValues([a,b,c,Name,Text1,TodaysDate]);
  HandleValues([Name,Text1,Text2,a,c]);
END.

The whole point is that I do not know in which order the parameters occur in the array.
I do not know what type comes where.
If it only was a few possibilities, I would use OVERLOAD, but since my HandleValues procedure some times only get one parameter and other times maybe 30-50 parameters, Overload simply can not be used because the possible variations would be similar to those in lotto.

In my procedure HandleValues I need to be able to distinguish which type the parameter is as well. This so that it can be dealt with accordingly.

Any ideas of how to solve this?
Many thanks in advance.

Morten Brendefu 28 Light Poster

Hmmmm.. First of all.
Delphi and its Time routines does not handle more than 24 hours with TimeToStr.
At least I do not think so. I must admit that I have not tested this :-)

Anyway. The fractional part, FRAC(DateTime) holds the time only.
The Integer part, holds the date only.

I think you should try to write your own TimeToStr function.
Something like

FUNCTION FullTimeToStr(StartDT,EndDT:TDateTime):STRING;
  VAR
    sTemp            : STRING;
    Days,Hours,iTemp : INTEGER;
    TimeStart,TimeEnd: TTime;
  BEGIN
    TimeStart:= StartDT;
    TimeEnd  := EndDT;
    IF TimeStart<0 THEN TimeStart:=TimeStart*-1; // Makes negative number positive.
    IF TimeEnd<0 THEN TimeEnd:=TimeEnd*-1; // Makes negative number positive.
    sTemp    := TimeToStr(TimeStart-TimeEnd);
    iTemp    := StrToInt(Copy(sTemp,1,2));
    Days     := INT(EndDT)-INT(StartDT);
    IF Days <0 Then Days := Days *-1; // Makes negative number positive.
    Hours:=Days*24+iTemp;
    Result:=IntToStr(Hours)+COPY(sTemp,3,5);
  END;

I am a little bit unsure of the way I use INT on a value that is a double. Might get an error on that one, but the code should at least give you some ideas.
Good luck.

Morten Brendefu 28 Light Poster

A friend of mine seem to have found a kind of solution to my problem.
If my unit is compiled with the

{$DebugInfo Off}

Compiler directive, then if the raise exception part of code references one of the parameters in the call, then the exception is 'raised' or shown in main part of program by delphi when responding to the error.
It seems like it target the line after the actual line causing the triggered exception, but fair enough, and close enough for me :-)

I simply put the directive right after my units Uses clause, and problem solved.

Morten Brendefu 28 Light Poster

I suppose that would work, but that also mean that I have to use an exception handler in every part of my main program where errors might occur.
It would lead to a lot of extra code and make it more difficult to read.

It is a solution, but I want it to be handled outside of main program :-)
Thanks for the suggestion anyway.

Morten Brendefu 28 Light Poster

It works, but Binary sort approach is just sooooo much faster.
This code is good enough though.
It is easy to understand and easy to implement.
Not so with a Binary sort approach or even the more complicated but ultimate speedy approach of Red/Black tree.

Morten Brendefu 28 Light Poster

The TDateTime is a Double Type.
The fractional part handles the time only.

VAR
  td,t :TDateTime;
BEGIN
  td=Now; // Both Time and Date in one double.
  t:=FRAC(td); // Variable t now holds Only the Time portion.
  IF t<0 THEN t:=t*-1 // If t is negative then make it positive.
  // The time is never a negative number, but the date can be. 
END.

This tiny example should give you the input needed.

your existing code work fine, but you must take away the date information and make sure negative numbers is made positive.

Best regards.

Morten Brendefu 28 Light Poster

Maybe I am missing something, but it is easy enough to have Delphi to write to a text file.
Put a comma or semicolon between all columns on each row.
Add a #13 (Carrier return) at end of row.
Then start next row.

Excel can easily import a comma or semicolon delimited textfile, and everything is ok.

It does not actually make an Excel file, but it is readable and importable by Excel.
Best regards.

Morten Brendefu 28 Light Poster
VAR
  a : ARRAY OF INTEGER;
  x : INTEGER;
BEGIN
  SetLength(a,3); // This dynamically set the size of array to 3.
  a[0]:=1;
  a[1]:=2;
  a[2]:=3; // Now the given values are set. Now to resize.
  SetLength(a,6);// This resizes the array.
  x:=3;
  REPEAT
    a[x*2-1]:=a[x-1];
    a[x*2-2]:=a[x-1];
    DEC(x);
  UNTIL x=0;
END.

This code should do the trick in your array without the need of a temporary array for storage.
If you do not actually know the size of the array, then you can use the command HIGH

VAR
  a : ARRAY OF INTEGER;
  x : INTEGER;
  aSize : INTEGER;
BEGIN
  SetLength(a,3); // This dynamically set the size of array to 3.
  a[0]:=1;
  a[1]:=2;
  a[2]:=3; // Now the given values are set. Now to resize.
  aSize:=HIGH(a);
  SetLength(a,aSize*2);// This resizes the array to the double size.
  x:=aSize; // We must start at bottom of existing data in array.
  REPEAT
    a[x*2-1]:=a[x-1];// The data is then copied from middle and up of new array
    a[x*2-2]:=a[x-1];// to the bottom and up of new array. This line writes copy 2 of data.
    DEC(x);
  UNTIL x=0;
END.

I did not actually test this code, but I think everything is as it should be.
Best regards.

Morten Brendefu 28 Light Poster

Dear enlighted ones.

I happen to try to finsh a kind of a function library that my Main program can utilize.
As of now, Everything work great... but. I would like to implement some kind of error handling.
The way I do this also work, but the error is generated so that the procedure detecting the error is listed when Delphi breaks execution.
Is there some way of implementing something so that the Break shows the line in main program instead?
It is important to me to make descriptive error messages that can be used to solve problem.

//From Main program
PROCEDURE TestForError;
  BEGIN
    Example(6);
    Example(8);
    Example(10);
    Example(15); // I would like Delphi to break here, and show a nice red line.
    Example(7);
    Example(1);
  END;

//From Library Unit
PROCEDURE Example(Max10:BYTE);
  VAR
    ErrorMessage:STRING;
  BEGIN
    IF Max10>10 THEN BEGIN
      ErrorMessage='An error occurred. Parameter can not hold larger values than 10';
      RAISE Exception.Create(ErrorMessage) at @Example;
    END;
  END;

I thought I could use something like this, but I need some kind of more information.
This way of raising an exception does everything I would like, except it show my LibraryUnit and procedure there as the cause of the error. This is of course correct enough as it is there the error is detected. I would just like to redirect the error so that the line calling the procedure get the "fault", hence Delphi breaks, showing this line of code instead of showing my procedure in library unit.

Morten Brendefu 28 Light Poster

Thank you for the answer about memory management by the way :-)
Best regards.
Morten.

Morten Brendefu 28 Light Poster

Simply because I want very easy access.
The whole point is to extend a listbox, combobox, checklistbox or similar with a number of extra fields not visible to the user of software.
I know I can use different type of ready made boxes or grids too, but I dislike the constant conversion between text and numbers too :D

If there was an easy way to add several integers to every line in a listbox, then I would use it, but I do not see any easy implementation of this.
I would like the ability to sort the listbox and have extra numbers to follow.
But also be able to choose a column of extra numbers and do sorting there.
I just demand too much out of a simple box :-)

Morten Brendefu 28 Light Poster

The whole point of having this one Global variable is exactly that. It have to be global so that a lot of procedures and functions can access it.
I am using this in combination with SQL and Interbase. If using only a local setup for this, it will lead to a lot of extra cpucycles and code for loading data into the local variables on demand.
At least I try to restrict my program to use One global array like this instead of a bunch of arrays :-)

I have found many examples on internet where an array is redefined to a bigger size, just no information what so ever about freeing memory afterwards.
In a way I just suppose this is not needed. I just hope there are no caveeats with this approach :D

Regards.
M.

Morten Brendefu 28 Light Poster

I have tried searching internet a little bit and found some useful information about the command

SetLength

I just wonder about a few things as I would like an array to "constantly" be redefined in size. When using it in one part of my program, it will be defined in one way. When using it in another part, it will be defined in another way.
Ultimately, in my program, this may be done thousands of times.
The reason why I want to use one variable for this is simply the enormous memory needed and lack of ability to make reuseable code if defining many different arrays.

I just wonder if it is "ok" to define the length of an array like this many times.
Should I somehow free the allocated memory before re-defining the length of the "new" array?

Any information about this issue would be greatly appreciated.

Morten Brendefu 28 Light Poster

As a last comment, I would like to add that above link gave me the input needed in order to solve my problem successfully.

Instead of trying to make SQL queries involving a column name that may not exist and by that create an error of which creates the need of exception handling, I now simply list the tables and column names and search via Delphi.
If the column is not found, then I can add it via SQL and that will not create errors and the need of exception handling.
All done with Delphi/TurboPascal logic.

Indeed a very useful link as it gives ideas of what other things we can do too.

pritaeas commented: Thanks for sharing +13
Morten Brendefu 28 Light Poster

Amazing.
I have not tested this yet, but now I will.
I feel 100% confident that this can do the trick. It will be some extra delphi coding, but this is after all a delphi thread :D

Thank you so much for this link.
Best regards,
Morten

Morten Brendefu 28 Light Poster

Dear knowledgeable ones
A new problem have arisen.
I have now an SQL database file with about 100 tables in it.
I would like to automatically go through all of these tables and check if the column "Owner" exist.
If this field does not exist, then it should be created and hold a value of type Integer.
This value must also be set to "1" for all existing rows in the table, but not as a "default" value.
Whenever I try to work this functionality I always get an error if the column of the table does not exist.

I try doing this from Delphi via Firebird SQL version 1.5, but I find no functionality for checking for column ID without causing an exception.

What I am after could be programmatically described as:

FOR x:=1 TO TotalCountOfTablesInDataBase DO BEGIN
WITH Table x DO BEGIN
IF EXIST(Column("Owner"))=FALSE THEN BEGIN
CREATE Column("Owner", INTEGER);
SELECT ALL ROWS;
SET Column("Owner")=1;
END;
END;
END;

I don't need error exception handling, but can this functionality be made without?
Delphi uses InterBase components and the SQL part installed is Firebird version 1.5

Many thanks in advance.

Morten Brendefu 28 Light Poster

The DLL in question is part of the Microsoft Windows OS.
I have managed to find a little bit of information regarding the DLL, but not so much when searching for it. Searching for the functions however provide results.

I have found out that StringParameters does not work directly, but when I make:
TYPE
tbArr : ARRAY OF BYTE
and use this in the parameters ie FUNCTION Test(sString:tbArr):LONGWORD;

and then use the method

PROCEDURE DoSomething;
  VAR
    tekst:ARRAY[1..1024] OF BYTE;
    returnvalue:LONGWORD;
  BEGIN
    returnvalue:=Test(@tekst[low(tekst)]);
  END;

Then I mostly get the results I am after, but with the added disadvantage of having to convert the String into its array representation and back again

Maybe there is a simpler way :-)

Morten Brendefu 28 Light Poster

Dear knowledgeable ones.

I started on a project to be able to use a smart card and a smart card reader in order to limit access to a program.

I had some sample code written in Visual Basic, and wanted to translate this to Delphi instead, something I think I have done pretty well.

At the moment, I get my program up and running. It compiles with no error... BUT...

Whenever I try to access external functions I have problems with the parameters.
I wonder if somebody maybe have a good explanation on how to be able to use the functions in the "SCardSyn.DLL"

It seems like Delphi and Visual Basic handles transfer of parameters quite differently.

My code for a few functions is as follows:

{Establish a context to resource manager
                                    Parameters:
                                      dwScope         = Scope (see Scopes)
                                      pvReserved1     = Reserved for further use
                                      pvReserved2     = Reserved for further use
                                      phContext       = Pointer to Context}
  FUNCTION SCardEstablishContext(dwScope, pvReserved1, pvReserved2:LONGWORD; phContext:PCHAR):LONGWORD; stdcall; external 'WINSCARD';

That actually works, where as my direct translation of Visual Basic code
*** Visual Basic code START***
' Establish a context to resource manager
' Parameters:
' dwScope = Scope (see Scopes)
' pvReserved1 = Reserved for further use
' pvReserved2 = Reserved for further use
' phContext = Pointer to Context
'
Public Declare Function SCardEstablishContext Lib "WINSCARD" _
(ByVal dwScope As Long, _
ByVal pvReserved1 As Long, …

Morten Brendefu 28 Light Poster

I thought I could show you all my code for parts of this :-)

First I create 256000 random names with this procedure:

procedure TForm1.MakeMaxClick(Sender: TObject);
  VAR
    x :INTEGER;
    y :BYTE;
    z :CHAR;
    sr:abc6;
begin
  Randomize;
  ListBox1.Visible:=FALSE;
  ListBox1.Items.BeginUpdate;
  FOR x:=1 TO Maxarray DO BEGIN
    sr:='';
    FOR y:=1 to 6 DO BEGIN
      z:=CHAR(Random(26)+65);
      sr:=sr+z;
    END;
    ListBox1.Items.Add(sr);
  END;
  ListBox1.Items.EndUpdate;
  ListBox1.Visible:=TRUE;
end;

The code need 14 seconds on my computer in order to finish generating all the random words.
It then instantly show ALL data and I can start scrolling down this unsorted list.

Next step is:

procedure TForm1.Button5Click(Sender: TObject);
begin
  ListBox1.Visible := false;
  ListBox1.Items.BeginUpdate;
  ListBox1.Sorted :=True;
  ListBox1.Items.EndUpdate;
  ListBox1.Visible := True;
  ShowMessage('Finished');
end;

It displays the message "Finished" after approx 11 seconds but still the listbox is not visible.
After a total of 4 minutes(after clicking ok) the listbox is displayed.

My own code for sorting:

PROCEDURE QrebuildIndex;
  VAR
    wTop,wMiddle,wBottom,wPosition : INTEGER;
    bModifier    : BYTE;
    bNeedSearches: ARRAY[1..24] OF INTEGER; // 16 comparisons will find any and all data no matter where in an index of 65536 items. Morten Brendefur 2011.

  PROCEDURE BuildNeedSearches(antall:BYTE); // Builds index of numbers where we have to extend searches by one extra run.
    VAR
      x:BYTE;
    BEGIN
      FOR x:=1 TO antall DO BEGIN
        bNeedSearches[x]:=1;
        bNeedSearches[x]:=(bNeedSearches[x] SHL x);
      END;
      bModifier:=1;
    END;

  PROCEDURE FindPointOfInsert;
    VAR                       
      x:BYTE;

    PROCEDURE Doublecheck; // Some values of top and bottom variables lead to a result that is misplaced by one in a few cases.
      BEGIN
        IF sAbc6[wIndex[wMiddle]] < sAbc6[wPosition] THEN INC(wMiddle); // …
Morten Brendefu 28 Light Poster

I did an experiment regarding sorting of 256.000 random "names" using TListBox component.
A friend of mine say that TListBox is faster than my code at sorting, so I wanted to test this out of curiosity.

Method of testing.
When I have created 256 thousand random textstrings, each 6 characters, I add these into a listbox that is invisible, hence no updating whilst adding.
Then I Press a button in order to do only one thing:
TListBox.Sorted:=TRUE
It takes about 5 seconds, and the control is returned to my form.
Does this mean that 256000 purely random strings have been sorted in that time?
My friend claim so.

Now for the big thing.
If it is sorted, why when I press another button in order to view the TListBox does this operation halt the computer for almost 3 minutes before the component shows the data?
I would think that this is because it is actually sorting the data now.

My routines that is a derivate of a binary approach is not by far fast, but it does the job of hiding the TListBox, copying the unsorted data from a TListBox into an Array, Then sorting this data through an index, deleting the data in the TListBox, adding all the data from my now sorted array, making TListBox visible, and all data can be viewed in the correct order instantly as the TListBox.visible:=TRUE.
The complete work takes almost …

Morten Brendefu 28 Light Poster

I am trying to draw a few items on a canvas using Canvas.TextOut mainly.
My problem is that when I grab the form holding the canvas and then move the form outside
of the screen and back again, all text and drawings have vanished, as if they have been erased.

Is this standard behaviour of canvas? or?

I am trying to make a kind of setup for writing to the canvas before I then send this canvas to the printer for printing.

I tested Canvas.Refresh, but this does not deal with the problem.

Do I need to dynamically assign labels of text for everything on the canvas for this to work?

Thanks in advance.
-Morten-

Morten Brendefu 28 Light Poster

Project -> Options -> Application -> Enable Runtime Themes - uncheck this

Amazing how the biggest problems some times have the easiest solutions.
Thank you so much.

Morten Brendefu 28 Light Poster

Such a simple solution :-)

I tested it and it works.

Thank you so much.
You are a gem.

Morten Brendefu 28 Light Poster

I am designing a lot of TCheckBox at runtime, and uses mathematical formulas for placing these in columns and rows afterwards on a form.

I assign an on click event that is exactly the same on all of them, and everything work,
except... Color.

My OnClick event is made like this:

procedure TForm1.CheckBoxClicked(sender: TObject) ;
 begin
    if Sender is TCheckBox then
    begin
      WITH TCheckBox(Sender) DO BEGIN
        IF Color=clBtnFace THEN BEGIN
          Caption:='TESTER 1';
          TCheckBox(Sender).     Color:=clGreen;
          TCheckBox(Sender).Font.Color:=clGreen;
          Checked:=TRUE;
        END
        ELSE IF Color=clGreen THEN BEGIN
          Caption:='TESTER 2';
          Color:=clRed;
          Font.Color:=Color;
          Checked:=TRUE;
        END
        ELSE IF Color=clRed   THEN BEGIN
          Caption:='TESTER 3';
          Color:=clBtnFace;
          Font.Color:=Color;
          Checked:=FALSE;
        END ELSE Color:=clBtnFace;
        Repaint;
        Update;
      END;
    end;
 end;

My aim is to change the color one way or another on either the background or the actual text/font, but I have so far been unable to change this.
I get checkboxes that I can click on. Each click does cycle through the different captions. The color property is set correctly, otherwise this cycling would not work.

My form however when I run the program...
Always a green check when it is checked. Always Black text.
I have tried making all Parent properties False, but still I only get Black text and no color.

I would really appreciate some explanation and a solution to this problem.
I use Delphi 9 and Delphi 5, and experience the same on two different computers.
On Delphi 9, I cant even change Font color when I place CheckBoxes …

Morten Brendefu 28 Light Poster
Tlistname = class(TForm)
  procedure Button1Click(Sender: TObject);
  procedure Button2Click(Sender: TObject);
public
  procedure much_reuseable_code(AChoice: Integer);
end;

procedure Tlistname.Button1Click(Sender: TObject);
begin
  much_reuseable_code(1);
end;

procedure Tlistname.Button2Click(Sender: TObject);
begin
  much_reuseable_code(2);
end;

procedure Tlistname.much_reuseable_code(AChoice: Integer);
begin
end;

The PUBLIC declaration in beginning did the trick.
It also work with PRIVATE declaration :-)
Thanks a lot. you are a gem :-)

Morten Brendefu 28 Light Poster

I have a form with Delphi 5.
To simplify it, It has got two buttons and a list box.
I am accessing a database to list either first names or last names in the list box.
This is what the buttons are for. the choice of first name or last name.

The events created in Delphi is:

procedure Tlistname.Button1Click(Sender: TObject);
and
procedure Tlistname.Button2Click(Sender: TObject);

and it is the code within these two procedures that I try to simplify with the above method.
The problem is that I then do not get access to many of the things on the form like IBQuery and similar.

I Think I need to pass the (Sender: TObject) declaration somehow, but I seem to be unable to do that.


Maybe the question can be simplified into:::
How do I make a Procedure or a Function that is outside a form
but still has access to the form and its values?
and also, how do I make parameter declarations for this?

Morten Brendefu 28 Light Poster

A "normal" Delphi snippet:

Procedure help_me1(Sender: TObject);
  BEGIN
{100 lines of code}
  END;

Procedure help_me2(Sender: TObject);
  BEGIN
{100 lines of copy and paste code}
  END;

How can I make this so that I end up with ONE procedure with 100 lines of code?

Procedure much_reuseable_code(choice:BYTE);
  BEGIN
{100 lines of code}
  END;

Procedure help_me1(Sender: TObject);
  BEGIN
    much_reuseable_code(1);
  END;

Procedure help_me2(Sender: TObject);
  BEGIN
    much_reuseable_code(2);
  END;

--In old days this was the way to do it, but the Sender: TObject makes it much harder.
I would really appreciate any help in regards to this.
Preferably more than one solution if there are several ways of bypassing the problem.

Thanks in advance.
M.