Blaise 31UK.pdf

March 24, 2017 | Author: Leonard Tucker | Category: N/A
Share Embed Donate


Short Description

Download Blaise 31UK.pdf...

Description

BLAISE BLAISE PASCAL PASCAL MAGAZINE MAGAZINE 31/32 D E L P H I, L A Z A R U S, O X Y G E N E, S M A R T, A N D P A S C AL R E L A T E D L A N G U A G E S A N D R O I D, I O S, M A C, W I N D O W S &

L I N U X

TWO ISSUES IN ONE Book Review: Coding in Delphi By Jim Duff Book: Coding Delphi Author: Nick Hodges Designing an API: common mistakes By Alexander Alexeev Newest Leap developments Michael Van Canneyt 3D Printing By Bj Rao Kinect ?! By Michael Van Canneyt Smart Mobile Studio 2.0 By Primož Gabrijelčič Interview with David I: plans about updating buying etc A simple superscript text editor By David Dirkse Interview with Gwan Tan - better office Using GEO services in Delphi applications with TMS components By Bruno Fierens Correcting a bad API design: By Alexander Alexeev

maXbox

The maXbox Pure Code By Max Kleiner Interview with Ray Konopka Programming Bitmap Rotation By David Dirkse Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for Delphi framework By Jeroen Pluimers kbmFMX for XE5 (android) By Fikret Hasovic

5 / 6 2013 Publisher: Foundation for Supporting the Pascal Programming Language in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep) © Stichting Ondersteuning Programmeertaal Pascal

BLAISE BLAISE PASCAL PASCAL MAGAZINE MAGAZINE 31/32 D E L P H I, L A Z A R U S, O X Y G E N E, S M A R T, A N D P A S C AL R E L A T E D L A N G U A G E S A N D R O I D, I O S, M A C, W I N D O W S & L I N U X

CONTENTS

ISSN 1876-0589 Royal Library -Netherlands Koninklijke Bibliotheek Den Haag

Articles Editorial Book Review: Coding in Delphi By Jim Duff Book: Coding Delphi Author: Nick Hodges Designing an API: common mistakes By Alexander Alexeev Newest Leap developments Michael Van Canneyt 3D Printing By Bj Rao Kinect ?! By Michael Van Canneyt Smart Mobile Studio 2.0 By Primož Gabrijelčič Interview with David I: plans about updating buying etc A simple superscript text editor By David Dirkse Interview with Gwan Tan - better office Using GEO services in Delphi applications with TMS components By Bruno Fierens Correcting a bad API design: By Alexander Alexeev

Page 3 Page 4

Page 8 Page 21 Page 26 Page 33 Page 41 Page 48 Page 50 Page 52 Page 57 Page 65

maXbox

News and Press Releases email only to [email protected] Authors A B C D F G H J L K M N O P S

Alexander Alexeev Peter Bijlsma, Michaël Van Canneyt, Marco Cantù, David Dirkse, Daniele Teti Bruno Fierens Primož Gabrijelčič, Fikret Hasovic Cary Jensen Wagner R. Landgraf, Sergey Lyubeznyy Max Kleiner Kim Madsen, Felipe Monteiro de Cavalho Jeremy North, Tim Opsteeg, Inoussa Ouedraogo Howard Page-Clark, Jeroen Pluimers Rik Smit, Bob Swart,

Editors

The maXbox Pure Code Page Page 77 By Max Kleiner Interview with Ray Konopka Page 93 Programming Bitmap Rotation Page 98 By David Dirkse Introduction to Model, View and View Model (MVVM) and the Caliburn Micro for Delphi framework Page 102 By Jeroen Pluimers kbmFMX for XE5 (android) Page 113 By Fikret Hasovic

maXbox

Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit

Correctors Howard Page-Clark, James D. Duff

Trademarks All trademarks used are acknowledged as the property of their respective owners. Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions. If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant. Subscriptions ( 2014 prices ) 1: Printed version: subscription € 60.-- Incl. VAT 6% (including code, programs and printed magazine, 6 issues per year excluding postage). 2: Electronic - non printed subscription € 45.-Incl. VAT 21% (including code, programs and download magazine) Subscriptions can be taken out online at

www.blaisepascal.eu or by written order, or by sending an email to

Advertisers

[email protected]

Barnsten BetterOffice Components 4 Developers ITDevCon Lazarus the complete guide Learnto program maXbox Raize Software Smart Mobile Studio

Pag Page Page Pag Page Page Page Pag Page

20 25 120 39 19 24 77 92 40 / 46

Copyright notice All material published in Blaise Pascal is copyright © SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial purposes. Commercial use of program listings and code is prohibited without the written permission of the author.

2

Editor - in - chief Detlef D. Overbeek, Netherlands Tel:+31(0)30 890.66.44/Mobile +31(0)6 21.23.62.68

COMPONENTS DEVELOPERS

4

Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.

Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email. Subscriptions can be paid by sending the payment to: ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal or TakeTwo Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)

Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68

[email protected]

Nr 5 / 6 2013 BLAISE PASCAL MAGAZINE

From The Editor

F

irst of all a Happy New Year to you all. This is a season of dark days, so I especially like the light from our Christmas tree which adds to the warm ambience of those holiday opportunities to relaxing with your family and friends, times that are enhanced with little gifts and delicious meals... 2013 was supposed to have been the end of the world – and though it was not the worst of years in the political sense, yet world crises seem not to be over, even if some of them are lessening in severity…

This year we will see 3D printing take off, and of course Leap Motion as a human-computer interface used increasingly. For Leap Motion we are developing a version that will run on ARM cpus: so we will be able to port Leap Motion software to the Raspberry-Pi and set up some new tools. This year we plan to offer our new group of Components for Leap Motion on several platforms.

I wrote a chapter about the history of computing for our Learning Pascal book which has turned out to be a lengthy story: about 70 pages. Naturally I had to The immense horns Mr Snowden placed on us made us find out something about the founding father of realize that our governments do not play by the rules. Pascal and write a few lines about him. Ordinary mortals can do little beyond despairing at the In Cologne recently I met Max Kleiner and we invasion of privacy and taking what steps we can to discussed the origins of Pascal, and he told me combat it. Politicians must battle this erosion of individual's rights, and I think in the long run the battle Professor Wirth was still alive, and so I thought it will be won. would be interesting to interview the author of Simply because it has to be. Pascal: Professor Dr. Niklaus Wirth. I think the sheer quantity of garbage the intelligence He will be 80 years old next month. community is accumulating will collapse upon the So we will visit him shortly to do an interview. perpetrators. It will take some time to reach critical mass; Mr. Wirth has agreed to this and you will be able to but in the world of programming we already have a read the outcome of our conversation in the next solution for this: simply remove it by deletion (Garbage issue. In his honour we will start a new project: PEP – Collection)! PASCAL EDUCATION PROGRAM. We will launch So many complain about deception. But who knows what PEP for the first time on 15th February, since that is the truth is? We all lie now and then, because we do not his birthday. In line with this educational theme we want to damage our relationship to someone; and I think it plan in future issues to reserve about 4 pages for in the end humans do not always want a black-and-white Pascal beginners, explaining the fundamentals of difference between Yes and No, we also prefer a gray zone. programming, and creating basic examples especially We find this even in scientific theory (although that is denied for those new to programming. as well). Young people (12 to 18 years of age) and students String theory is a very good example of this: it should be impossible for an entity to exist at two distinct will be offered a free Blaise magazine subscription so they can receive information without needing to pay. places at the same time. And when you cast your mind back over history, and look at longer epochs, you will see Details will be available at our website. that there is a pendulum effect: things move back and Many of you always prefer Delphi's help as it was in forth. Sometimes there is a slight improvement before the Delphi 5, 6 and 7. Here is some good news: we are cycle starts all over again… shortly releasing The Helper Component. The universe as a whole seems to work on this principle. This is a component that presents help in the way we Some claim we will find people who can write programs think help should be organized. We plan to announce that will protect us from any intrusion or decryption. the first trial version in the next issue: meantime you Don’t believe them… can help us by letting us know your gripes and wishes for an improved IDE help system. You would do better to spend your money on the newly developing future like the adventure of the Leap Motion or even 3D Printing, and be creative (as you probably have always been) in writing new programs with new code. People outside the programming world may not understand this need to innovate, or the many new possibilities in our Pascal world offers. No Operating System is excluded any more. Pascal is not only doing good it is getting back where it belongs: at the top of the range of available languages, because of its enormous potential in education, for solving problems, in facilitating Rapid Application Development, in bringing the “write-once-run-anywhere” dream closer for cross-platform programmers. As the user group that publishes this Magazine we will always try to help you advance to the next step.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

There are many plans for 2014: The next Pascon conference to follow the great success of last summer's event; The new Leap component – Experimenting with Kinect Many articles about Android development using Pascal, and new opportunities for that OS… It’s going to be a very busy year. Very Exciting!

COMPONENTS DEVELOPERS

4

3

Book Review: Coding in Delphi By Jim Duff Developers at all levels will be able to use the information contained within this book. It doesn't matter if you're relatively new to Delphi or programming or you've got many years of experience. If there is one common trait I've found among software developers, it is their desire to learn. Learning a new technique or figuring out how to apply that technique to one's unique situation is part of the excitement of being a software developer." Now, coming back down to the details of what's in the book, Nick's next chapter, being the Introduction, is yet another flag that waves your desire to read the book. Once more, I provide some partly quoted sentences that illustrate the book's high value for developers. “..noticed that a lot of Delphi developers are “behind”.

That is, they are either using an older version of Delphi, or they aren't using or aren't even aware of all the features in the newer versions of Delphi that they are using." "For instance, two powerful language features were added in Delphi 2009: generics and anonymous methods. Both are features that enable the development of really cool code and frameworks" The content of the book contains the following chapters: Book: Coding Delphi Author: Nick Hodges There are a couple of ways to preview what a technical software book is all about before one goes ahead to use it. The first quick act is to look at the list of contents and then flick through some of the chapters to preview the wording, code samples and pictorial views. The next step is to analyse the knowledge and validity of the author to make sure that the contents and sample code are going to give you the correct, accurate and productive assistance for your development design and coding. As a reviewer, the above second step is an important one in order to provide the magazine readers with a brief and positive (or negative - not in this case) assessment of the book. So, here we go with a summary of the first two write ups, namely Forward, by Allen Bauer, Embarcadero's Chief Scientist, and Introduction by author Nick Hodges. In Allen's Forward chapter, he provides a write up of Nick's background beginning with "Nick holds the honor of producing one of, if not the first non-Borland built component. In order to learn about Delphi component building, he built TSmiley." Looks like that must have worked OK as Nick became a member of Borland-Embarcadero. And near the end of Allen's summary, he made the following link between the positive aspects of the book and how such value would provide a beneficial effect for developers. 4

COMPONENTS DEVELOPERS

4

Forward Introduction Acknowledgements Frameworks used in Coding in Delphi 1 Exceptions and Exception Handling 2 Using Interfaces 3 Understanding Generics 4 Understanding Anonymous Methods 5 Delphi Collections 6 Enumerators in Delphi 7 IEnumerable 8 Run-time Type Information 9 Attributes 10 Using TVirtualInterface 11 Introduction to Dependency Injection 12 A Deeper Look at Dependency Injection 13 Unit Testing 14 Testing with an Isolation Framework Appendix A: Resources Appendix B: My Delphi Story The list of chapters shows that the book is aimed at experienced Delphi developers rather than being a beginner's guide. Having already mentioned the Forward and Introduction items above, one more item to mention in support of the qualified author is the final Appendix - My Delphi Story. This is the third part that once more gives the reader the confidence in going ahead to take in the technical aspects of the book.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Book Review: Coding in Delphi (Continuation 1)

Now, we are ready to go, and a major influence of this book is having this subject matter as the first main chapter: 1. Exceptions and Exception Handling Introduction Structured Exception Handling How Not to Use Exceptions Don't Eat Exceptions Don't Generally Trap Exceptions Don't Go Looking For Exceptions Don't Use the Exception Handling System as a Generic Signaling System How to Use Exceptions Properly Use Exceptions to Allow Your Code to Flow Without the Interruption of Error Handling Code Application Writers Should Trap Exceptions Trap only specific exceptions Component Writers and Library Code Writers Raise Exceptions Raise Your Own Custom Exceptions Let Your Users See Exception Messages Feel Free To Provide Good Exception Messages Provide Two Versions Of A Library Routine Conclusion

This provides not only the 'how' together with sample code, but the reasoning behind having it as a fundamental method of coding. It reminds one of the basic input-process-output of computer processing; if these steps don't work, then the hardware/software doesn't work properly and/or stops working - full stop. Part of his final comments in the Conclusion provides the 'why': "It's quite easy to fall into the trap of using exception handling improperly. ... Use exceptions wisely, and you'll be able to product robust, clean code." 2. Using Interfaces Introduction Decoupling What are Interfaces? Interfaces Everywhere A Simple Example Implementing an Interface Some Further Things to Note Interface Inheritance Other Things to Think About About TInterfacedObject How to Actually Use Interfaces Why Should You Be Using Interfaces?

If I could teach a new programmer one thing it would be this: Program to an interface, not an implementation.

This sample quote that soon follows the above one, is provided to further confirm the reasonings provided throughout the book, in line with the sample code and instructions. "All through this book, I'll talk a lot about decoupling your code and why that is really good. But a definition here is probably a good idea. Code can be said to be decoupled when your classes are designed in such a way that they don't depend on the concrete implementations of other classes. Two classes are loosely coupled when they know as little about each other as possible, the dependencies between them are as thin as possible, and the communication lines between them are as simple as possible." A code sample and part of the associated instructions that provide associated reasonings are now given as the final quoted sample in this review. type IName = interface ['{671FDA43-BD31-417C-9F9D-83BA5156D5AD}'] function FirstName: string; function LastName: string; end;

" Note that the declaration of the interface has a Globally Unique Identifier (GUID) right after the initial declaration. This GUID is used by the compiler to identify this interface. You can use an interface without the GUID, but much of the RTL and most frameworks that take advantage of interfaces will require a GUID be present. (You can generate a GUID any time you want in the IDE by typing CTRL+SHIFT+G)” The remaining chapters follow in a logical sequence, each with code samples plus the 'how' and 'why' to use them.

Coding in Delphi

So, finally getting into the technical documentation, an introductory chapter Frameworks used in Coding in Delphi provides "...a number of open source frameworks to facilitate the coding techniques that are discussed herein. I refer to them here at the beginning of the book to enable you to retrieve the code and get them set up before we begin."

This chapter has the only photo of the author, so it just goes to show how important he believes the following advice is.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

COMPONENTS DEVELOPERS

4

5

Book Review: Coding in Delphi (Continuation 2) Summary Hopefully, the selected quotations from the book have provided the reader with positive views of the author's ability to explain the 'how' and 'why' to advance your coding capabilities. Nick has also given a good background of his own and Delphi's history, from the early days to the present, to show how they and we readers can advance our development capabilities as the technical environment is accelerating in this day and age. The 'book' reviewed has been the pdf version and there are several links in Appendix A such as Unit Testing, Projects, Good Stuff etc. The following link is also provide at the start of the book: "Find out what other people are saying about the book by clicking on this link to search for this hashtag on Twitter: https://twitter.com/search?q=#codingindelphi

Reviewed by Jim Duff ADUG Melbourne Member The following lines are taken out the book. Forward I first met Nick during a Delphi 1 pre-launch “ boot-camp” that was held at the brand new Borland campus in Scotts Valley, California. We had invited a cadre of developers, authors and long-term luminaries to get some in-depth training and presentations directly from the development team and product management. Enthusiastic and exuberant are adjectives that don’t fully characterize my first impressions of him. He was never afraid of asking questions and absorbed the information quickly.

Nick held the position of Product Manager for a while then managed a portion of the Delphi R&D team. Nick was never afraid to challenge assumptions and actively stand up for the Delphi developer community. Even though Nick’s position didn’t require him to write code, he didn’t let that stop him. He was always looking for ways to keep his programming skills as sharp as possible. As new features were developed, Nick was always right there to give them a workout. To this day there is a large amount of code written by Nick that remains a key portion of the overall regression and unit-testing process. Nick discovered that some of the best ways to learn about new code is to test that new code. It is not without irony that this process requires code to be written. It stands to reason that Nick would end up here; writing a book with a single focus on the code. That is the engine that drives all the other revolutionary features of Delphi. Without the code, there would be no “Visual” in the Visual Component Library (VCL). In fact, Delphi has always been about getting the developer to their code as quickly as possible. The VCL and the newer FireMonkey component frameworks make the use and construction of UI, database, connection and others as simple as possible. Its ultimate goal is to allow the developer to focus on their task, which is to produce an application that solves a specific problem. It is the code that is written in between those UI, database and connection components that make up the application. Developers at all levels will be able to use the information contained within this book. It doesn’t matter if you’re relatively new to Delphi or programming or you’ve got many years of experience. If there is one common trait I’ve found among software developers, it is their desire to learn. Learning a new technique or figuring out how to apply that technique to one’s unique situation is part of the excitement of being a software developer. This is right up there with the thrill experienced when some thought, idea or concept comes to life within the computer. When a developer sees their running application, rarely do they think about the few moments they spent arranging some controls on the User Interface. They feel a sense of pride about that one difficult and thorny problem they solved through the clever use of code. At the end of the day, to a developer, it is really all about the code.

I cannot talk about Nick without also discussing TSmiley. Inquisitive and curious, Nick embraced Delphi without reservation. To that end, Nick wasn’t satisfied with what Delphi did, but was just as keen on learning about how it did it. To that end, Nick holds the honor of producing one of, if not the first non-Borland built component. In order to learn about Delphi component building, he built TSmiley. In this one simple component all the core aspects of using Delphi’s Pascal language to extend and enhance the VCL framework were demonstrated. You see, Delphi component building is all about the code. Allen Bauer I had the pleasure of working closely with Nick during his time at Borland and then Embarcadero.

6

COMPONENTS DEVELOPERS

4

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Book Review: Coding in Delphi (Continuation 3)

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

What you will find are ways to make your code much cleaner, much more powerful, and way easier to maintain. This book is all about the cool, new code you can write with Delphi. It won’t matter whether you are building a VCL or an FM application. I’ve titled it “Coding in Delphi” because I want to make it a book that shows you simple examples of how to use powerful features – that, is about the code. These language features are indeed advanced features – they are new relative to, say, the case statement – and thus many of you are beginners to them. By the end of this book, you won’t be. The approach I’m taking for this book is to try to distill things down to the very basics. The examples will be very simple but the explanations deeper. I believe that if you can understand the basic idea in a simple example, it is but a small leap to using those ideas in more complex code. There is no point in giving complex examples when you can get the main thrust using fundamental implementations that illustrate advanced ideas. Between the code in this book and in the samples online (https://bitbucket.org/NickHodges/codinginde lphi²) you can learn all the precepts and then begin applying them to your own code. In other words, nothing fancy here, just the basics – it is then up to you to use these ideas in your own code. This book is not done – it’s instead a living document. Since it is self-published on a platform that makes iteration very easy, I plan on having frequent releases to fix typos (which will, I’m sure, sneak through despite my best efforts), improve examples and descriptions, and keep up with technology changes. Owners of the PDF should get notifications of new versions automatically. If you are reading a paper version of this book, I’m sorry I can’t deliver fixes to your hard-copy – perhaps some day that will be possible. The book will be done when you guys say it is done. Maybe, it will never be done because Delphi keeps growing and expanding. I guess we’ll see. As a result, I’m totally open to feedback – please feel free to contact me at [email protected] with suggestions corrections, and improvements. Please join the Google Plus group for the book.³ I may even add whole new chapters. Thanks for your purchase – this book was a labor of love, so every sale is icing on the cake. Nick Hodges Gilbertsville, PA

²https://bitbucket.org/NickHodges

Coding in Delphi

Introduction Over the years, I’ve spoken in front of a lot of Delphi developers. The one thing that I notice is that there are a lot more of you Delphi guys than the average Delphi guy thinks. There are Delphi folks everywhere. Also, I have noticed that a lot of Delphi developers are “behind”. That is, they are either using an older version of Delphi, or they aren’t using or aren’t even aware of all the features in the newer versions of Delphi that they are using. Something I like to do when I’m in front of folks is ask a few questions about what people are doing. I’m always saddened that the response to questions like “Who is doing unit testing?” or “Who is taking advantage of Generics?” is pretty meager. This is particularly true for the language features and the run-time library. It’s quite easy to move forward with an older code base, utilizing the new features in the IDE and adding new things to your app using the new high level frameworks and components that come in the newer versions. For example, you might have been developing an application since Delphi 3, moving forward through various versions. Along the way, you may have added some DataSnap functionality, started using the Code Insight features in the IDE, and when you moved to XE2, you start poking around with FireMonkey. But it’s fairly easy to ignore the new language features that come along with those new versions. For instance, two powerful language features were added in Delphi 2009: generics and anonymous methods. Both are features that enable the development of really cool code and frameworks. But if you didn’t understand or feel the need for them, then it was pretty easy to simply not use them. You can still do all kinds of great stuff in Delphi without them, but with them, well, you can write some really beautiful, testable, and amazing code. For instance, a relatively new framework that exists only because of these new language features is the Spring Framework for Delphi, or Spring4D, as I’ll refer to it in this book. Spring4D is a feature rich framework that provides a number of interesting services, including a wide variety of collections that leverage generics, an Inversion of Control container, encryption support, and much more. I view Spring4Dsolid as much a part of the Delphi RTL as SysUtils is. Using Spring4D in your code will make many, many things easier and cleaner. But many Delphi developers don’t know this yet. If the above is familiar, this book is for you: The Delphi developer that hasn’t quite yet made the leap over into the cool and amazing things that you can do with the latest versions of the Delphi language. This book is all about introducing these new language features and some of the intriguing things you can do with them. It will take a look at these new language features, and then expand into some of the open source frameworks that have sprung up (no pun intended) as a result. It will then show you techniques that you can use to write SOLID¹, clean, testable code. You won’t find much of anything about the IDE or the higher level frameworks here. Screen shots will be few but code examples many. You won’t find anything about how to build better user interfaces or fancy components.

COMPONENTS DEVELOPERS

4

7

Designing an API: common mistakes By Alexander Alexeev starter

expert

When you want to create a public component for use by several potential applications, you can register it as a COM object. (Component Object Model (COM) is a binary-

interface standard for software components introduced by Microsoft in 1993) Because COM was designed to simplify exactly this task, creating a publicly offered component as a COM object is an obvious way to do it. However there are drawbacks to development using COM. Firstly COM technology exists only on Windows, and secondly it has a steep learning curve - which means that you need to learn many things before you can begin to develop your public component. Also if you are only developing a simple component COM may be overkill. Consequently a developer may avoid the COM route, choosing to implement his component as a simple DLL. When you create a new DLL you need to decide: - what functions you want it to export; - what arguments the functions will have; - how you want to transfer data, and other issues. Taken together, we call this the API of your DLL (it is

also termed a protocol or contract). The API (Application Programming Interface) is a set of rules (the contract), which you as developer of the DLL and the user (as caller of your DLL) agree to follow in order to understand each other and to interact successfully. All DLL services are provided only under this contract.

You do not have to do anything beyond distributing a .tlb file (which can also be embedded in the .dll itself). If the 'foreign' language can work with COM, then it can import information from the TLB and generate appropriate header files for itself. A TLB file is a binary file which has been created and edited by a TLB editor (or generated by an IDE). It is also possible to "compile" a TLB file from a text description - an IDL file (.idl or .ridl). If you are smart enough, you can "borrow" this feature from COM. Your documentation is a textual description of the DLL API written by the DLL developer for other developers who will use the DLL (i.e. it is one-sided). Of course, you can also document internal details for yourself; but that is not the concern of this article. So, you should provide documentation which at least contains a formal description of the API, listing of all the functions, methods, interfaces, and data types, along with explanations of "how" and "why" (the so-called Reference). Additionally, the documentation may include an informal description of the code-development process (a guide, a how-to, etc.). In the simplest cases, the documentation is written directly in the header files as comments, but more often it is provided as a separate file (or files) in chm, html, or pdf format.

Unwritten rules (This section is based on http://blogs.msdn.com/b/oldnewthing/archive/ 2006/03/20/555511.aspx ) There are some basic ground rules that apply to all programming, which are so obvious that most documentation and books do not bother explaining them (because these rules should have been internalized by practitioners of the art to the point where they don't need to be expressed). A driver planning what route to take wouldn't even e assume you are developing a public DLL. consider taking a shortcut through somebody's backyard or So you will have a .dll file, you will have the going the wrong way down a one-way street. header files (at least *.h and *.pas), and you will In the same way that an experienced chess player doesn't have documentation. even consider illegal options when deciding his next move, The header files (or headers) form a set of source files containing structure and function declarations used in the an experienced programmer doesn't even consider violating the following basic rules without explicit API for your DLL. permission in the documentation which allows Typically they contain no implementation. contravening the rule: The headers should be available in several languages. • Everything not defined is undefined. As a rule, this means the language used to create the DLL This may be a tautology, but it is a useful one. (in our case - Delphi), C (as standard) and perhaps additional Many of the rules below are just special cases languages (such as Basic, etc.). of this rule. All these header files are equivalent to each other, • All parameters must be valid. representing only translation of the API from one language The contract for a function applies only when the caller to another. adheres to the conditions, and one of the conditions is The more languages you include the better. that the parameters are actually what they claim to be. If you don't provide header files for a particular language, This is a special case of the "everything not defined is then developers using that language will be unable to use undefined" rule. your DLL, (unless they are able to translate your header files o Pointers are not nil unless explicitly from a language you provide (Delphi or C) into their language). permitted otherwise. This means that failing to offer headers for a particular o Pointers actually point to what they language is usually a big enough obstacle that developers purport to point to. in that language will not use your DLL, although it is not If a function accepts a pointer to a an absolute block to such usage. From this perspective CRITICAL_SECTION, then you must pass a pointer COM looks more attractive (the API description is stored in which points to a valid CRITICAL_SECTION. type libraries in the universal TLB format). This article describes typical mistakes, features and pitfalls developers encounter as they develop a public DLL API. In general, this article serves as a kind of check list for you. You can compare your code with this list and find out how good it is, and if it contains any of the mistakes typically found in such DLLs.

W

8

COMPONENTS DEVELOPERS

4

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 1) o

o

o

o

Pointers must be properly aligned. Pointer alignment is a fundamental architectural requirement, yet something many people overlook, having been pampered by a processor architecture that is very forgiving of alignment errors. The caller has the right to use the memory being pointed to. This means no pointers to memory that has been freed or to memory that the caller does not have control over. All buffers are as big as the declared (or implied) size. If you pass a pointer to a buffer and say that it is ten bytes in length, then the buffer really needs to be ten bytes in length (or more). Handles refer to valid objects that have not been destroyed. If a function wants a window handle, then you must pass a valid window handle.



All parameters are stable. o You cannot change a parameter while the function call is in progress. o If you pass a pointer, the pointed-to memory will not be modified by another thread for the duration of the call. o You cannot free the pointed-to memory either.



The correct number of parameters is passed with the correct calling convention. This is another special case of the "everything not defined is undefined" rule. o Thank goodness, modern compilers refuse to pass the wrong number of parameters, though you would be surprised how many people manage to sneak the wrong number of parameters past the compiler anyway, usually by devious casting. o When invoking a method on an object, the Self parameter is the object. Again, this is something modern compilers handle automatically, though people using COM from C (and yes they exist) have to pass the Self parameter manually, and occasionally they mess up.

·• Function parameter lifetime: o The called function can use the parameters during the execution of the function. o The called function cannot use the parameters once the function has returned. Of course, if the caller and the callee have agreed on a means of extending the lifetime, then those agreed rules apply. § The lifetime of a parameter that is a pointer to a COM object can be extended bythe use of the IUnknown.AddRef method. § Many functions are passed parameters with the express intent that they be used after the function returns. It is then the caller's responsibility to ensure that the lifetime of the parameter is at least as long as the function needs it. For example, if you register a callback function, then the callback function needs to be valid until you deregister the callback function.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE





Input buffers: o A function is permitted to read from the full extent of the buffer provided by the caller, even if the entire buffer is not required to determine the result. Output buffers: o An output buffer cannot overlap an input buffer or another output buffer. o A function is permitted to write to the full extent of the buffer provided by the caller, even if not all of the buffer is required to hold the result. o If a function needs only part of a buffer to hold the result of a function call, the contents of the unused portion of the buffer are undefined. o If a function fails and the documentation does not specify the buffer contents on failure, then the contents of the output buffer are undefined. This is a special case of the "everything not defined is undefined" rule. o Note that COM imposes its own rules on output buffers. COM requires that all output buffers be in a marshallable state even on failure. For objects that require nontrivial marshalling (interface pointers and BSTR/WideStrings being the most common examples), this means that the output pointer must be nil on failure.

(Remember, every statement here is a basic ground rule, not an absolute inescapable fact. Assume every sentence here is prefaced with "In the absence of indications to the contrary". If the caller and callee have agreed on an exception to the rule, then that exception applies.) Error: Providing no dedicated initialize and finalize functions The first mistake you can make as an API developer is not to provide standalone functions to initialize and finalize your DLL, but instead use the DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH notifications from the DllMain callback-function. DllMain is a special function in the DLL, called automatically by the system in response to certain events. Among those events are DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH - these are notifications about the loading and unloading of your DLL. If you are using Delphi, then the initialization section and the finalization section of Pascal units in the DLL are executed in response to DLL_PROCESS_ATTACH and DLL_PROCESS_DETACH, which the system sends to the DllMain function of your DLL. Of course, you do not see this process, it is happening under the hood of the RTL (language support library). You just see that when the DLL is loaded, all units are initialized, and when it is unloaded, they are finalized.

COMPONENTS DEVELOPERS

4

9

Designing an API: common mistakes (Continuation 2) What is the problem here? DllMain is no ordinary callback-function. It is invoked by a very specific point ( http://blogs.msdn.com/b/oleglv/archive/2003 /10/24/56141.aspx ). There are very few actions that you can take in DllMain with guaranteed safety. For example, loading libraries, starting threads, thread synchronization, COM calls, even calls to other libraries (except kernel32) – performing any of these actions inside DllMain may lead to blocking (deadlock/hang/freeze). When you realize that developers usually do not consider whether the code they put in the initialization and finalization sections of their units is truly admissible there, you also realize that relying on such behaviour (automatic initialization of units from DllMain) is not the best design solution. That's why your DLL must export the two functions like Init and Done, which will contain the actions that you otherwise would have inserted in the initialization and finalization sections. Whoever loads your DLL should immediately import and call the Init function. Later he should also call Done just before unloading your DLL. Error: Using ancient techniques for memory management and error handling In any API, there are two very important aspects: how you pass data of arbitrary (dynamic) size, and how you report any errors from API function calls. A typical WinAPI function contains this logic: The caller must call a function with lpData = nil and cbSize = 0, whereupon the function returns an ERROR_INSUFFICIENT_BUFFER error, and cbSize will contain the amount of memory in bytes required to store all the data. The caller can then allocate sufficient memory and call the function again, passing to lpData a pointer to a block of data, and passing the size of the block to cbSize. This approach is complex in itself (calling the function twice instead of once), and imagine how it would work for frequently changing data. What happens if the data increases in size between your two calls? Then the second call will return an ERROR_INSUFFICIENT_BUFFER error again, and again you will need to re-allocate more memory and repeat the call. That is - to reliably call the function - you have to write a loop. Why do most of the WinAPI functions use such a terribly complicated method? Answer: history. When these functions were first designed there were no modern de-facto standards; moreover the Windows developers sacrificed convenience for the sake of microoptimizations http://blogs.msdn.com/b/oldnewthing/ archive/2004/08/23/218837.aspx Similarly, a typical WinAPI function reports that it ...returns True on success and False on failure. The cause of the error can be obtained by calling GetLastError.

10

COMPONENTS DEVELOPERS

4

and some of the functions even report: ...in case of success, the function returns the size of the data, or 0 if there is no data. In the case of error, the function returns 0. To determine the exact cause, you should call GetLastError, which returns ERROR_SUCCESS for a successful call, or an error code. What a nightmare! Once again, we encounter two function calls rather than one, not to mention the complexity of the extensibility (to use our own error codes) and the inability to receive more context data about the error. Accordingly, many people when designing their own API see how it is done in the operating system and imagine they should do the same. "Well, if that is how Microsoft does it, then we will copy them because (perhaps) it is correct and necessary to do it that way." However, they fail to realize that today's WinAPI was created a long, long time ago. A huge number of functions began their life in 16-bit code. These functions were designed for requirements that simply do not exist today. There is no need to use such ancient and uncomfortable approaches. Instead use a modern approach. Here (in descending order of preference) is how you can transfer varyingly sized data without sacrificing the convenience of a call: • [Special case, only for strings] BSTR/WideString. • The interface with the lpData and cbSize properties. • A DLL can export a function for freeing memory which the DLL itself allocated. • You can use system memory management (most often: HeapAlloc/HeapFree). Here is a list for error handling (in descending order of preference): • COM style: HRESULT + ICreateErrorInfo. Delphi may additionally take advantage of the "magic" safecall call model. • The function returns HRESULT. • Functions return a sign of success/failure, the error code is obtained from GetLastError. • Similar to the previous item, but implementing your own error code function. Moreover, there is no need to use the naked functions because today we have interfaces. Interfaces offer solid advantages: • Automatic memory management - no problems with the allocation / release. • Delphi gives you a safecall calling convention and virtual methods - for customizing the compiler "magic". • Simplified versioning, because an interface can be uniquely identified by a GUID (aka IID). • Data is grouped with methods for handling this data. • Performance does not suffer (a lot).

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 3) Error: Using language-specific data types or other language-specific constructs Obviously if you create an API to be used from various programs each written in a different language, then you cannot use structures that exist only in your language. For example, string (as well as AnsiString, UnicodeString, ShortString, String[number]), array of (dynamic and open arrays), TObject (i.e. any objects), TForm (and components), etc. If you use a data type (or language construct) that has no counterpart in other languages, then code in this very different language simply will not be able to use your API. It would have to emulate the language constructs of your language. So, what can be used in an API? The following: • integer types (Integer, Cardinal, Int64, UInt64, NativeInt, NativeUInt, Byte, Word, etc. - with the exception of Currency); • real types (Single and Double - except Extended, Real, Real48 and Comp); • static arrays (array[number .. number] of) of the acceptable types; • set, enumerated and subrange-types (with some reservations - see below; it is preferable to replace them with integer types); • character types (AnsiChar and WideChar, but not Char); • strings (only in the form of WideString; strings as PWideChar - allowed, but not recommended; PAnsiChar is valid only for ASCII-strings; PChar strictly prohibited; ANSI-string is prohibited); • Boolean type (BOOL, but not Boolean; ByteBool, WordBool and LongBool are acceptable, but not recommended); • interfaces which use and operate with acceptable types only; • records with fields of acceptable types; • pointers to data of acceptable types; • untyped pointers; • data types from the Winapi.Windows.pas unit (or a similar unit for non-Windows platforms); • data types from other system headers (they are located in the \Source\ RTL\Win of your Delphi; replace "Win" path with OSX, iOS, etc. - for other platforms). Error: Using a shared memory manager and/or packages Any shared memory manager (such as ShareMem, FastShareMem, SimpleShareMem, etc.) is a language-specific facility which does not exist in other languages. So (as in the previous section) you should never use any of them in your API. The same applies to run-time packages (.bpl packages). This package concept exists only in Delphi (and C++ Builder).

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

What is the purpose of a shared memory manager? In a sense, a shared memory manager is a "hack". It is a quick and dirty way to solve the problem of exchanging dynamic data between modules. Never use a shared memory manager at the beginning of a new DLL project. A shared memory manager is a means of backward compatibility, but does not fit with new code. If you need to exchange objects (including exceptions), strings or other language-specific constructs you have to use BPL-packages, not DLLs. If you have used a DLL, then you must not use Delphispecific types and, therefore, must not use the shared memory manager. Hence the comment at the beginning of the earlier section about the forbidden use of UnicodeStrings (and so on) in DLLs. You can easily transfer data of varying size if you follow the guidance above; and you already know you should not use Delphi-specific types in a DLL. Therefore, there is no need to use a shared memory manager at all (either alone or using run-time packages). Error: Failing to protect each exported function with a try/except block The explanation above should have made it clear that you cannot pass any objects between modules. An exception is also an object (in Delphi). Adding two plus two, we realize that you cannot throw an exception in one module and then catch it in another. The other module has no idea how to work with an object that was created in a different programming language. This means, of course, that you have to think carefully how you will report errors (as mentioned above). Here I am talking only about the particular case of an exception. Because an exception cannot (that is: should not) leave the module, you must implement this rule religiously: put a try/except block to catch all exceptions around the code of each exported function. Note: • The function can be exported explicitly (“exports function-name”) or implicitly (e.g. as a callbackfunction or other function which returns a pointer to the calling code). Both options must be wrapped in a try/except block. • "Catch all exceptions," of course, does not mean that you wrap the function in an empty try/except block. You must not turn off all exceptions. You have to catch them (yes, catch them all) and transform rather than suppress them. You must convert each exception to something prescribed by your protocol (perhaps an error code, an HRESULT, or whatever). Note also that if you use the method recommended above (interfaces with safecall) then this issue is automatically covered for you. The safecall calling convention assures you that every method you write will be wrapped by a hidden try-except block through compiler "magic"; and no exception escapes your module.

COMPONENTS DEVELOPERS

4

11

Designing an API: common mistakes (Continuation 4) Error: Using ANSI-encoding(s) Support for Unicode appeared in the Windows API in 1996 (in Windows NT 4), and in 2000 Unicode support came to the client OS (Windows 2000). Although Windows 95 did contain Unicode functions, there was no full support. The mobile OS market was Unicode only from the start (Windows CE, 1996) through the PocketPC, Windows Mobile, and up to Windows Phone - all these systems support exclusively Unicode, but not ANSI. That is, for more than 13 years Unicode has been the default choice in all Windows versions. For more than 13 years the ANSI API Windows functions have been nothing more than stubs that do nothing beyond converting the string encoding before invoking Unicodevariants of themselves. Support for Unicode in Delphi has been present since Delphi 3 - as part of the COM support (that is from 1997). Although until 2008 (Delphi 2009), the entire language support library (RTL) and the component library (VCL) worked in ANSI. However, in spite of the wide open opportunity to use Unicode when constructing their own APIs most Delphi developers even since 1997 (over 16 years), have not hesitated to use the "familiar types" - that is, at best a PChar (equivalent to PAnsiChar on the systems of that time), and at worst – a string with the shared memory manager. Of course, those who were smart used PAnsiChar, and PWideChar (since 2000). But WideString was hardly used - despite its undeniable advantages: there are no problems with the exchange of strings between modules, auto-conversion to string and back again, built-in support for Unicode, built-in pointer length. Why the avoidance of WideString? Probably because PWideChar is sufficient for easy transfer of data inside the called function, and returning data from called functions was required much less frequently. To sum up: always use Unicode in your API for strings - even if you are using Delphi 7 and work with ANSI-strings inside: it does not matter. In 2013, the API must be Unicode. It is not 1995.

Gotcha: Enumerated types An enumerated type is a convenient way to declare casetypes (rather than using integers). What is the problem here? Look at this code: type TTest = (T1, T2, T3); var T: TTest;

Question: What is the size of the variable T in bytes? This is an important question because size affects the position of the fields in records, when passing arguments to functions, and in much other code. The answer is that in general you do not know the size. It depends on the compiler and its settings. By default, it is 1 byte for Delphi. However, another compiler and/or setting may result in 4 bytes. Here is another question: since this type occupies 1 byte in Delphi, it can hold up to 255 values. But what if your enumerated type has more than 255 values? Answer: then the variable will occupy 2 bytes. Do you see where this is leading? Suppose you have used 50 values in version 1 of your DLL, so the fields of this type occupied 1 byte. In version 2 you have extended the possible values up to 300 - and the field now occupies 2 bytes. It would not matter if we used this type only inside our own code. But since you are sharing it with other code, such a change in the size of the data will be a complete surprise to the other code. Overwriting (data corruption) and Access Violations are the result. Note: in fact the problem arises even with far less than 300 elements. It is sufficient that the type has a 300-th element: type TTest = (T1, T2, T3, T4 = 300); var T: TTest; begin WriteLn(SizeOf(T)); // shows 2

Well, what about the ANSI-adapters (stubs) to Unicodefunctions: are they necessary? No. Remember why they are You can solve this problem in two ways: there in the WinAPI: as a means of backward compatibility 1. You can place the compiler directive {$Z4} for legacy code which is not familiar with Unicode. (or {$MINENUMSIZE 4}) at the beginning of each This era ended in 2000 with the release of Windows 2000. header file. There is no need to create a means of This will cause the compiler to make every enumerated backward compatibility for something which type 4 bytes in size, even if you do not need so much. never existed in the first place. No code was using your 2013 API functions with ANSI – 2. You can use LongWord (or Cardinal) and a so there is no need to add ANSI-adapters to your API. set of numeric constants (const T1 = 0). Note that if you are using the recommendations in the preceding paragraphs you have already covered this issue. By now, you should be using WideString strings (aka BSTR) to pass string data between modules; or, in extreme cases, PWideChar. You should not use PChar and PAnsiChar.

12

COMPONENTS DEVELOPERS

4

It is possible that the second method is preferable because it clearly answers the question: What are the numerical values of the type's elements?

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

Designing an API: common mistakes (Continuation 5) Gotcha: Records The uncertainty about the size of enumerated types also applies to records: type TTestRec = record A: Byte; B: Int64; end;

What is the size of this record? 9? 10? 12? 16? The amount of unused space between fields (filler bytes) also depends on the compiler and its settings. Overall, I would recommend using interfaces instead of records when possible. If you need to use a record: either insert the directive {$A8} ({$ALIGN 8}) to the beginning of each header file, or use the keyword packed for the record. The latter may be preferable - because the alignment rules in Delphi might be different from the alignment rules in another language (for instance consider the case of problems similar to this bug: http://qc.embarcadero.com/wc/ qcmain.aspx? d = 75838 ).

For this purpose, a callback function is provided with a socalled user-argument: either a pointer or an integer (such as Native(U)Int, but not (U)Int), which are not used by the API itself and transparently passed directly to the callbackfunction. Or (in rare cases), it can be any value that uniquely identifies the callback function call. For example, the system function SetTimer has idEvent, while EnumWindows function has lpData. These parameters are not used by the functions and are simply passed directly to the callback-function unchanged. That is why we can use these parameters to pass arbitrary data. If you do not implement user-parameters in your API, then the calling code cannot associate a callback-function with the data. We will look at this issue in more detail in the next article. Gotcha: Mixing manual and automatic control of an entity's lifetime In general, you should try to use automatic control of a lifetime. You have less chance to screw up, because the compiler emits code to keep track of your objects and there is less (fallible) human work to do. But in any case, there will always be places where you want manual control of a lifetime. The junction of these two control mechanisms is what can cause problems.

Gotcha: Sets We can ask the same question about sets: how many bytes do they occupy? Look at this code: Here, however, everything is more complicated, type because a set can take up to 32 bytes, ISomething = interface and there is no compiler directive to control the size of sets. procedure DoSomething; end; Overall, the set is a syntactic convenience for dealing with flags. So instead of sets you can use an integer type (again: TSomething = class(TComponent, ISomething) LongWord or Cardinal) and a set of numeric constants, procedure DoSomething; combining them with OR for inclusion in the "set" and end; checking their presence in the "set" with AND. Error: Failing to provide user-arguments in callback-functions A callback-function is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. For example, if you want to find all the windows on your desktop, you can use EnumWindows: function MyEnumFunc (Wnd: HWND; lpData: LPARAM): Bool; stdcall; begin / / This is called once for each window in the system end; procedure TForm1.Button1Click (Sender: TObject); begin EnumWindows (@MyEnumFunc, 0); end;

Since the callback function normally performs the same task as the code that sets it, it turns out that the two pieces of code are working with the same data. Consequently, the data from the code setting the callback must somehow be passed to the callback function.

Nr 5 / 2013 BLAISE PASCAL MAGAZINE

var Comp: TSomething; function GetSomething: ISomething; begin Result := Comp; end; begin Comp := TSomething.Create(nil); try GetSomething.DoSomething; finally FreeAndNil(Comp); end; end;

As you know, TComponent does not use automatic reference counting and you must control its lifetime manually. The problem in the above code is in the line GetSomething.DoSomething. A temporary (hidden) variable of the interface type is created (for storing the result of the GetSomething call), which is cleared in the last line (at “end;”) - after the object has been released.

COMPONENTS DEVELOPERS

4

13

Designing an API: common mistakes (Continuation 6) Of course, this will never invoke the destructor for TComponent (TComponent uses manual control and does not respond to reference counting), but cleaning up is still necessary. Clean reference counting means calling the _Release method – a method of an already deleted object. Which will lead to an Access Violation. Note: Access Violations are not always raised for such errors – due to the “caching” behaviour of the memory manager. Which makes such errors especially dangerous. A similar problem can be seen in this code: begin Lib := LoadLibrary(...); Win32Check(Lib 0); try Func := GetProcAddress(Lib, ...); Intf := Func(...); // ... some action with Intf Intf := nil; finally FreeLibrary(Lib); end; end;

Between the LoadLibrary and FreeLibrary calls there may be temporary variables created that hold references to interfaces from the DLL. Therefore, even if we have cleared all clearly visible references before unloading DLL, hidden variables will be released after unloading DLL and thus will call already unloaded code (hello, another Access Violation). Of course, we (the developers) do not have an eagle eye to find all the places where the compiler wants to create hidden variables, so that we can convert such hidden variables into explicit variables and explicitly free them. Let me remind you that the solution would be to use the fact that all temporary hidden variables are released at the time of exit from the procedure. Therefore, we must clearly distinguish between code that works with manual and automatic control: procedure DoDangerousStuff(Comp: TComp); begin // ... some action with the Comp, including the use of types with automatic control end; begin Comp := TSomething.Create(nil); try DoDangerousStuff(Comp); finally FreeAndNil(Comp); end; end; procedure DoDangerousStuff(Lib: HMODULE); begin // ... some action with Lib, including the use of types with automatic control end; begin Lib := LoadLibrary(...); Win32Check(Lib 0); try DoDangerousStuff(Lib); finally FreeLibrary(Lib); end; end;

14

COMPONENTS DEVELOPERS

Gotcha: Identifying interfaces Interfaces are different from other data types. They have two levels of identification. On the programming language level an interface is identified by an identifier name (such as IUnknown, IApplication etc.) and is no different in this aspect from any other data type in Delphi. Two interfaces with the same declaration but with different type identifiers are considered to be different data types by the compiler. On the other hand, the interfaces may also be identified not only at the level of programming language, but at run-time (by the machine code) – via meta-information: the interface's GUID (IID). Two completely different declarations, but with the same IID will be considered to be identical by run-time machine code. Gotcha: The immutability of interfaces Once you have published an interface ("published" means you publicly release a version of the DLL with this interface definition), you cannot change it (either its IID or its structure) because the interface is used by third party code. Changing the interface declaration will break the other (third party's) code. Instead of changing the interface, you need to create a new interface with a new GUID. You should create a new independent interface (preferably and usually) or inherit from your old interface (allowed). Gotcha: Expanding interfaces Look at this code: type IColorInfo = interface {ABC} function GetBackgroundColor: TColorRef; safecall; ... end; IGraphicImage = interface {XYZ} ... function GetColorInfo: IColorInfo; safecall; end;

Suppose you want to add a new method to interface IColorInfo: type IColorInfo = interface {DEF} // = MinimumCount)

and (Value
View more...

Comments

Copyright ©2017 KUPDF Inc.
SUPPORT KUPDF