The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,854 other subscribers

Archive for the ‘Software Development’ Category

Delphi interface generic function – Is there a work around? – Stack Overflow

Posted by jpluimers on 2021/06/30

The documentation simply states “no”, without explaining why.

Luckily, there is [WayBack] David Heffernan at [WayBack] Delphi interface generic function – Is there a work around? – Stack Overflow

Interfaces do not support generic parameterized methods, as the compiler says.

There is no workaround because it’s a fundamental limitation. Parameterized methods in classes are implemented by adding one method per instantiation to the class. That works for classes since they are concrete, but is not viable for interfaces. That’s because interfaces are a table of functions and the size of that table cannot vary depending on which generic method instantiations happen to be present elsewhere in the code. For similar reasons, generic methods cannot be virtual or dynamic.

In any case, it’s not possible. One option is to use a class instead. I agree that this is a bind.

If you want to do it in an interface, the best you can do is:

function CastAs(const IID: TGUID): IInterface;

But you’d have to call it like this:

MyIntf := ProxyIntf.CastAs(IMyIntf) as IMyIntf;

which feels somewhat foul.

Choose your poison!

In the background, for every distinct use of generic method parameters, the compiler generates an overloaded concrete version of the method. That is possible for classes and records, but not for interfaces.

In practice, I either go for the last option, or have the interface expose a record or class type that can have the required generic method (so the compiler can generate the overloads at run-time).

All options feel somewhat ugly, so I like the comment by [WayBack] Jason:

Wish I could “Program into Your Language, Not in it” programmers.stackexchange.com/questions/2777/…

Which leads to Code Complete

[WayBack] What are the key points of Code Complete? – Software Engineering Stack Exchange answer by [WayBack] limist:

Code Complete is about software craftsmanship; it is an advanced-beginner/intermediate-level book, written for the working programmer, but it would still be very useful to someone who’s been programming for at least a year.

Thus the key points of Code Complete (2nd ed.) are nicely summarized in its Chapter 34, Themes in Software Craftsmanship. As paraphrased from my notes:

  1. Conquer Complexity: reduce the cognitive load on your mind via discipline, conventions, and abstraction.
  2. Pick Your Process: be conscious of quality from start (requirements) to finish (deployment) and beyond (maintenance).
  3. Write Programs for People First, Computers Second: code readability is hugely important for comprehensibility, review-ability, error-rate, error-correction, modifiability, and the consequent development time and quality.
  4. Program into Your Language, Not in it: think of the What? and Why? before the How?
  5. Focus Your Attention with the Help of Conventions: conventions manage complexity by providing structure where it’s needed, so that the ultimate resource – your attention – can be effectively used.
  6. Program in Terms of the Problem Domain: work at the highest level of abstraction possible; top-level code should describe the problem being solved. Distinguish OS level, programming language level, low-level implementation structures, low-level problem domain terms, and finally, high-level problem-domain terms that would make total sense to the (non-coder) user.
  7. Watch for Falling Rocks: as programming merges art and science, good judgement is vital, including heeding warning signs.
  8. Iterate, Repeatedly, Again and Again: iterate requirements, design, estimates, code, code tuning.
  9. Thou Shalt Render Software and Religion Asunder: be eclectic and willing to experiment. Don’t be an inflexible zealot, it precludes curiosity and learning. Go beyond having just a hammer in your toolbox.

But the most important take-aways are in Chapter 33, Personal Character: once you consciously seek to improve as a coder, you can and will. The fastest way to do so is to take on the the attitudes of master coders(humility, curiosity, intellectual honesty, discipline, creativity), while also practicing their habits (many good habits are listed in the book, e.g. choosing good variable/value names).

Also, the book makes clear that the gap between average and excellent in software is immense; that fact alone should drive the conscientious coder to better himself.

That’s the short of it; the long version is in the book. :) I can also send you my not-so-long, not-so-short notes if you want more details. But the book is certainly money and time well-spent, even if the writing style is tiresome at times.

Beyond Code Complete, I’d highly recommend The Pragmatic Programmer. It’s for intermediate-level programmers, nicely-written and a great mix of high, medium, and low-level advice.

There are more limitations on generics in Delphi

The documentation on limitations has not changed much since Delphi 2009.

Delphi 2009

[WayBack] Overview of Generics: Platform Requirements and Differences

Generics are supported by the Delphi for Win32 compiler.

Runtime type identification (RTTI)

In Win32, generics and methods do not have RTTI, but instantiated types do have RTTI. An instantiated type is the combination of a generic with a set of parameters.

Interface GUID

In Win32, an instantiated interface type does not have an interface GUID.

Parameterized method in interface

A parameterized method (method declared with type parameters) cannot be declared in an interface.

Instantiation timing

Instantiation is processed by the compiler. All instantiated objects are emitted into .obj files.

Dynamic instantiation

Dynamic instantiation at runtime is not supported.

Interface constraints

The Win32 interface is not a “light” interface. This means all type parameters with interface constraints always support the COM IUnknown methods _AddRef_Release and QueryInterface or inherit from TInterfacedObject. Record types cannot specify an interface constraint parameter.

Delphi 10.3 Rio

[WayBack] Overview of Generics – RAD Studio: Platform Requirements and Differences

Generics are supported by the Delphi compilers.

Run-time type identification

In Win32, generics and methods do not have run-time type information (RTTI), but instantiated types do have RTTI. An instantiated type is the combination of a generic with a set of parameters. The RTTI for methods of a class is a subset of the RTTI for that class as a whole. If a non-generic class has a generic method, that method will not be included in the RTTI generated for the class because generics are instantiated at compile time, not at run time.

Interface GUID

In Win32, an instantiated interface type does not have an interface GUID.

Parameterized method in interface

A parameterized method (method declared with type parameters) cannot be declared in an interface.

Instantiation timing

Generic types are instantiated at compile time and emitted into executables and relocatable files. Instance variables of a generic type are instantiated at run time for classes and at compile time for generic records. The RTTI for generic classes is only generated when the classes are instantiated. RTTI for instantiated classes follows just as for non-generic classes. If the generic class has a generic method, then the instantiated generic class will not have RTTI for that generic method.

Dynamic instantiation

Dynamic instantiation at run time is not supported.

Interface constraints

The Win32 interface is not a “light” interface. This means all type parameters with interface constraints always support the COM IUnknown methods _AddRef_Release, and QueryInterface or inherit from TInterfacedObject. Record types cannot specify an interface constraint parameter.

–jeroen

Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | Leave a Comment »

SQL server: getting database names and IDs

Posted by jpluimers on 2021/06/29

A few statements go get database names and IDs based on these functions or system tables:

Part of it has the assumption that a master database always exists.

-- gets current database name
select db_name() as name
;
name
--------------------------------------------------------------------------------------------------------------------------------
acc

(1 row affected)
-- gets current database ID
select db_id() as dbid
;
dbid
------
5

(1 row affected)
-- gets all database IDs and names
select dbid,name from sys.sysdatabases
;
dbid   name
------ --------------------------------------------------------------------------------------------------------------------------------
1      master
5      acc

(2 rows affected)
-- gets current database name by ID
select db_name(db_id()) as name
;
name
--------------------------------------------------------------------------------------------------------------------------------
acc

(1 row affected)
-- gets case corrected database name for sys.sysdatabases.name having a case insensitive collation sequence
select dbid,name from sys.sysdatabases 
where name='Master'
;
dbid   name
------ --------------------------------------------------------------------------------------------------------------------------------
1      master

(1 row affected)
-- gets case corrected database name for sys.sysdatabases.name having a case sensitive collation sequence
select dbid,name from sys.sysdatabases 
where name = 'Master' collate Latin1_General_100_CI_AI
;
dbid   name
------ --------------------------------------------------------------------------------------------------------------------------------
1      master

(1 row affected)

Note that:

  • even though by default the SQL server collation sequence is case insensitive, it can make sense to do a case insensitive search, for example by using the upper function, specifying a collation, or casting to binary. I like upper the most, because  – though less efficient – it is a more neutral SQL idiom.
  • the most neutral case insensitive collation seems to be Latin1_General_100_CI_AI

Related:

  • [WayBack] SQL server ignore case in a where expression – Stack Overflow answered by Solomon Rutzky, summarised as:
    • Do not use upper as upper with lower does not always round-trip.
    • Do not use varbinary as it is not case insensitive.
    • Neither the = or like operators are case sensitive by default: both need a collate clause.
    • Find the collation of the column(s) involved; if it contains _CI, then you are done (it is already case insensitive); if it contains _CS, then replace that with _CI (case insensitive) and add that in a collate clause.
    • Collations are per predicate, so not per query, per table, per column nor per database. This means you have to specify them if you want to use a different one than the default.
  • [WayBack] What is Collation in Databases? | Database.Guide
    Latin1_General_100_CI_AI Latin1-General-100, case-insensitive, accent-insensitive, kanatype-insensitive, width-insensitive
  • [WayBack] Collation Info: Information about Collations and Encodings for SQL Server
  • [WayBack] SQL Instance Collation – Language Neutral Required:

    I recommend using Latin1_General_100_CI_AI. I recommend this because:

    1. If Latin1_General_CI_AI is supported, then there’s almost no chance thatLatin1_General_100_CI_AI (which is a far better choice) isn’t also supported. The version 100 collation has about 15,400 more sort weight definitions, plus 438 more uppercase/lowercase mappings. Not having those sort weights means that 15,400 more characters in the non-100 version equate to space, an empty string, and to each other. Not having those case mappings means that 438 more characters in the non-100 version return the character passed in (i.e. no change) for the UPPER() and LOWER() functions. There is no reason at all to want Latin1_General_CI_AI instead of Latin1_General_100_CI_AI. There might be a need if code was put into place to work around these deficiencies, and that code would behave incorrectly under the newer, better version of that collation. However, it’s highly unlikely that code was put into place to account for this, and extremely unlikely that if such code did exist, that it would error or doing things incorrectly due to the newer collation.
  • [WayBack] Differences Between the Various Binary Collations (Cultures, Versions, and BIN vs BIN2) – Sql Quantum Leap
  • [WayBack] How to do a case sensitive search in WHERE clause (I’m using SQL Server)? – Stack Overflow answered by Jonas Lincoln:

    By using collation or casting to binary, like this:

    SELECT *
    FROM Users
    WHERE   
        Username = @Username COLLATE SQL_Latin1_General_CP1_CS_AS
        AND Password = @Password COLLATE SQL_Latin1_General_CP1_CS_AS
        AND Username = @Username 
        AND Password = @Password 

    The duplication of username/password exists to give the engine the possibility of using indexes. The collation above is a Case Sensitive collation, change to the one you need if necessary.

    The second, casting to binary, could be done like this:

    SELECT *
    FROM Users
    WHERE   
        CAST(Username as varbinary(100)) = CAST(@Username as varbinary))
        AND CAST(Password as varbinary(100)) = CAST(@Password as varbinary(100))
        AND Username = @Username 
        AND Password = @Password 
  • [WayBack] sql – How to get Database name of sqlserver – Stack Overflow

–jeroen

Posted in Database Development, Development, Encoding, internatiolanization (i18n) and localization (l10), SQL Server | Leave a Comment »

Public traffic ticked vending machines can have good user experience.

Posted by jpluimers on 2021/06/29

At the bottom a few examples on on how not do do user experience.

Most of them are related to public traffic ticket vending machines, which seem to have a common pattern of having very low usability.

Good UX is possible

There is one example I know that has quite a good user experience, because taking usability into account aspart of the design was done at the start of the project.

This is contrary to most machines: they are built by engineers just taking into account their needs and challenges: build from existing parts, allowing for easier serviceability, aiming for ease of manufacturing.

Dutch GVB did it differently: they hired Dutch design agency [WayBack] Fabrique to design and test the user experience before the whole machine went into production.

This resulted in a machine that combines easy usability, good servicing, and straightforward manufacturing process. In addition, an “extended” version that allows for non-electronic payment was designed and manufactured in the same go.

[WayBack] Fabrique

Fabrique is a strategic design agency, specialised in service design, app development, e-commerce and website design. Discover Fabrique!

(Note I am not affiliated, nor endorsed by Fabrique. I just think they did a very good job)

Here are some pictures of the designed and manufactured machines; the vertical stripes light up the place where the next user interaction takes place:

Read the rest of this entry »

Posted in Development, Software Development, Usability, User Experience (ux) | Leave a Comment »

Hopefully datendomina (@sys_adm_ama) has followed up with some cool vi tips…

Posted by jpluimers on 2021/06/28

I hope that datendomina (@sys_adm_ama) has followed up with some cool vi/vim tips.

Though I can do basic editing (far more than quit-without-saving) and know about he various mode, I still feel not proficient.

[WayBack] Jeroen Pluimers on Twitter: “LOL! Boy was I glad that after finding my way in Ed and sed on SunOS in the 1980s, I discovered vi. Still not proficient in it (and I probably never will). However, knowing some of the basics allowed me to visually edit any file on any Unix like system. That’s still gold to me.…”

It also made me discover [WayBack] ed(1) Conference (@ed1conf) | Twitter.

One important tip:

[WayBack] Kristian Köhntopp on Twitter: “vi movement Kommandos haben System. Erkenne und lerne das System. hjkl + prefix+hjkl, Marken, prefix+jump to mark und so weiter. Und bleibe von den verblödeten Plugins weg. Die braucht kein Mensch und machen vim nur langsam im Start und kompliziert.”

The original thread, which I hoped would get longer: [WayBack] Thread by @sys_adm_ama: “Ich lerne jetzt vi(m). Klingt beknackt, oder?se […]”

Ich lerne jetzt vi(m).

Klingt beknackt, oder? Aber ich hab überlegt: ich möchte effizienter werden, meinen Kram stressfreier bewältigen. Und ich finde, es bietet sich an da an Baustellen anzusetzen, die täglich relevant sind. Und vi(m) nutze ich in der Tat täglich.

1/

Aber auch wenn ich ihn nutze und über den »Hilfe, wie komm ich aus dem Editor wieder raus?!«-Witz nur sehr müde die Augen rolle gehe ich davon aus, dass ich nicht mal einen Bruchteil der Möglichkeiten ausschöpfe, die er bietet (1. Release 1976, älter als ich!). Das ist spannend.
Das ist jetzt meine Mini-Challenge, auf die ich jeden Tag eine Viertelstunde verwenden will: wenn ich eine Funktion brauche mich nicht mehr drum herum zu hacken, sondern recherchieren wie es richtig geht und das dann gefälligst auch verinnerlichen. Mal sehen, ob das so klappt

3/

In dem Zuge will ich auch wieder verstärkt (neo)mutt in Verbindung mit vim nutzen – das wäre ein wunderbares tägliches Training 😎 Mails schreiben muss man irgendwie immer.

neomutt bietet leider auch einen Eimer voll Funktionen, die ich noch nicht ordentlich nutze. Gnah.

4/

An euren Replies erkenne ich, dass das mit dem »sich die Kürzel merken« echt heikel zu sein scheint 🤔 Wie handhabt ihr das im Alltag? Einfach ein paar Basics wie :u und CTRL+r und gut ist? Ich bin neugierig. Oder nutzt ihr alle nano? (Ich glaub, dann muss ich entfolgen) 😂
Nur als kleinen Zwischenstand: nach dem Lesen eurer Antworten schließe ich, dass ich mich mit meinem Kenntnisstand nicht verstecken muss 😂 Da hat mir der virtuelle Schulterblick schon weiter geholfen.

/5
vi(m), weil ich Admin bin und dieses Tool auf jedem System und ohne X-Geraffels üblicherweise vorfinde (wenn auch bei neueren Installationen dieser absurde „visual mode“ der Default ist 😳). Auch unter (Open)Solaris, IRIX, was weiß ich.

/6

Eben hab ich das Buch von @MasteringVim aus der Packstation gezogen (extrem vielversprechend!) – und klar, ich werde berichten 😎 Ich bin sehr gespannt.

/7ed

–jeroen

Read the rest of this entry »

Posted in *nix, *nix-tools, Development, ed, Power User, Scripting, sed, sed script, Software Development, vi/vim | Leave a Comment »

I don’t want to learn your garbage query language · Erik Bernhardsson

Posted by jpluimers on 2021/06/24

Interesting read:

This is a bit of a rant but I really don’t like software that invents its own query language. There’s a trillion different ORMs out there. Another trillion databases with their own query language. Another trillion SaaS products where the only way to query is to learn some random query DSL they made up.

Source: [WayBackI don’t want to learn your garbage query language · Erik Bernhardsson

Related:

Via: [WayBack] Die wichtigste Funktion in jedem ORM ist der Notausgang. Also, wie man literales SQL durch das Ding durch piped, ohne daß der ORM rein pullert. – Kristian Köhntopp – Google+

–jeroen

Posted in Database Development, Development, Software Development | Leave a Comment »

DCOM calls from thread pool threads: CoInitialize/CoUnitialize location and expensiveness?

Posted by jpluimers on 2021/06/24

Interesting takeaway from [WayBack] DCOM calls from thread pool threads

call CoInitialize* at the start, and call CoUninitialize before returning. Expensive, but necessary

Related:

–jeroen

Posted in .NET, C, C++, COM/DCOM/COM+, Delphi, Development, Software Development, Windows Development | Leave a Comment »

“No mapping for the Unicode character exists in the target multi-byte code page”

Posted by jpluimers on 2021/06/24

Usually when I see this error [Wayback] “No mapping for the Unicode character exists in the target multi-byte code page” – Google Search, it is in legacy code that uses string buffers where decoding or decompressing data into.

This is almost always wrong no matter what kind of data you use, as it will depend in your string encoding.

I have seen it happen especially in these cases:

  • base64 decoding from string to string (solution: decode from a string stream into a binary stream, then post-process from there)
  • zip or zlib decompress from binary stream to string stream, then reading the string stream (solution: decompress from binary stream to binary stream, then post-process from there)

Most cases I encountered were in Delphi and C code, but surprisingly I also bumped into C# exhibiting this behaviour.

I’m not alone, just see these examples from the above Google search:

–jeroen

Posted in .NET, base64, C, C#, C++, Delphi, Development, Encoding, Software Development, Unicode | Leave a Comment »

delphi – Is it possible to define {$IFDEF} for more than one directive at once? – Stack Overflow

Posted by jpluimers on 2021/06/24

[WayBack] delphi – Is it possible to define {$IFDEF} for more than one directive at once? – Stack Overflow:

–jeroen

Posted in Delphi, Delphi 6, Development, Software Development | Leave a Comment »

Some notes on dumping IceCast ICY streams to get meta-data

Posted by jpluimers on 2021/06/23

Some links I want to investigate further.

This is difference in the header meta data of an icecast URL, than in the stream data.

The header meta-data you get from this cURL command (via [WayBack] linux – Catch Metadata from Icecast-audio-stream – Super User):

# curl -H "Icy-MetaData: 1" -v "http://icecast.omroep.nl:80/radio2-bb-mp3" > /dev/null
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0*   Trying 145.58.53.154...
* TCP_NODELAY set
* Connected to icecast.omroep.nl (145.58.53.154) port 80 (#0)
> GET /radio2-bb-mp3 HTTP/1.1
> Host: icecast.omroep.nl
> User-Agent: curl/7.54.0
> Accept: */*
> Icy-MetaData: 1
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: audio/mpeg
< Date: Mon, 01 Jul 2019 06:46:33 GMT
< icy-br:192
< ice-audio-info: samplerate=48000;channels=2;bitrate=192
< icy-br:192
< icy-genre:Mixed
< icy-metadata:1
< icy-name:NPO Radio2
< icy-pub:0
< icy-url:http://www.radio2.nl
< Server: Icecast 2.4.0-kh10
< Cache-Control: no-cache, no-store
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: Origin, Accept, X-Requested-With, Content-Type
< Access-Control-Allow-Methods: GET, OPTIONS, HEAD
< Connection: Close
< Expires: Mon, 26 Jul 1997 05:00:00 GMT
< icy-metaint:16000
< 

The content meta-data you can for instance get with node.js in a call like [WayBack] https://colon.roderickgadellaa.com:8001/get/id3/?url=http%3A%2F%2Ficecast.omroep.nl%3A80%2Fradio2-bb-mp3:

{"ok":true,"code":200,"error":false,"data":{"headers":{"content-type":"audio/mpeg","date":"Sat, 29 Jun 2019 17:07:03 GMT","icy-br":"192, 192","ice-audio-info":"samplerate=48000;channels=2;bitrate=192","icy-genre":"Mixed","icy-metadata":"1","icy-name":"NPO Radio2","icy-pub":"0","icy-url":"http://www.radio2.nl","server":"Icecast 2.4.0-kh10","cache-control":"no-cache, no-store","access-control-allow-origin":"*","access-control-allow-headers":"Origin, Accept, X-Requested-With, Content-Type","access-control-allow-methods":"GET, OPTIONS, HEAD","connection":"Close","expires":"Mon, 26 Jul 1997 05:00:00 GMT","icy-metaint":"16000"},"metadata":{"StreamTitle":"AUDIOLINK-AUDIO-FTP-SERVER - +EJ+  Ons Uuropener"},"timestamp":1561828023103,"cacheValidUntil":1561828038103},"request":{"protocol":null,"slashes":null,"auth":null,"host":null,"port":null,"hostname":null,"hash":null,"search":"?url=http%3A%2F%2Ficecast.omroep.nl%3A80%2Fradio2-bb-mp3","query":{"url":"http://icecast.omroep.nl:80/radio2-bb-mp3"},"pathname":"/get/id3/","path":"/get/id3/?url=http%3A%2F%2Ficecast.omroep.nl%3A80%2Fradio2-bb-mp3","href":"/get/id3/?url=http%3A%2F%2Ficecast.omroep.nl%3A80%2Fradio2-bb-mp3"}}

This was at a time where the Dutch NPO Radio 2 had trouble with their ID3 service as the first part stopped refreshing for days: "metadata":{"StreamTitle":"AUDIOLINK-AUDIO-FTP-SERVER - +EJ+ Ons Uuropener"}

Some links that will help me eventually dump this from the command-line:

–jeroen

Posted in Development, IceCast, Media Streaming, Software Development | Leave a Comment »

Disable Filtered Gap – Scooter Forums

Posted by jpluimers on 2021/06/23

There is a short thread mentioning [WayBack] Disable Filtered Gap – Scooter Forums (Issues concerning Text Compare sessions.)

It is the feature that when you select “Diffs” or “Same” on the toolbar on menu in a “Text Compare” (or similar, like XML compare), you see only the differences (or equalities when you selected “Same”).

There also will be small plus signs (+) in the gutter with the hover text “Expand Filtered Gap” on it to expand that gap and show the equalities (or differences when you selected “Same”).

There are two problems that basically make this feature very hard to use, which both stem from the need to view context around a difference in order to understand the proper meaning of that difference:

  • you cannot expand or collaps the “Filtered Gap” by keyboard
  • the “Number of context lines” in the “Text editing” part of the “Options” is not adhered to

Too bad as this could have been such a useful feature.

–jeroen

Posted in Beyond Compare, Development, Power User, Software Development | Leave a Comment »