• API data transfer. What is API

    You have probably seen the term "API". The operating system, web browser, and app updates often announce new APIs for developers. But what is an API?

    Application programming interface

    The term API is an acronym and it stands for "Application Programming Interface".

    API is like a menu in a restaurant. The menu contains a list of dishes that you can order, as well as a description of each dish. When you specify which menu items you want, the restaurant kitchen does the job and provides you with ready meals. You don't know exactly how the restaurant prepares this food, and you don't need to.

    Likewise, the API provides many operations that developers can use, as well as a description of what they do. The developer does not need to know how, for example, an operating system is created and the Save As dialog box is displayed. They just need to know that it's available for use in the app.

    It's not a perfect metaphor as developers may have to provide their own API data to get results, so maybe it's more like a fancy restaurant where you can provide some of your own ingredients that the kitchen will work with.

    APIs allow developers to save time by taking advantage of platform injection to get important work done. This helps reduce the amount of code you develop and also helps create consistency across applications on the same platform. APIs can control access to hardware and software resources.

    APIs make life easier for developers

    Let's say you want to develop an application for the iPhone. The Apple iOS operating system provides a large number of APIs, just like any other operating system, to make this easier for you.

    For example, if you want to embed a web browser to display one or more web pages, you don't need to code your own web browser from scratch just for your application. You
    You can use the WKWebView API to embed a WebKit (Safari) web browser into your application.

    If you want to take photos or videos from the iPhone's camera, you don't need to write your own camera interface. You can use the Camera API to embed the iPhone camera in your app. If the API didn't exist, application developers would have to write their own camera software and interpret the camera hardware inputs. But the developers of Apple's operating system have done all the hard work, so developers can simply use the camera API to embed the camera and then continue writing their application. And when Apple improves the camera API, all applications that use it will automatically take advantage of this improvement.

    This applies to all platforms. For example, do you want to create a dialog box in Windows? There is an API for this. Want to support fingerprint authentication on Android? There is an API for this, so you don't have to test every fingerprint sensor from every Android manufacturer. Developers don't have to reinvent the wheel over and over again.

    APIs control access to resources

    APIs are also used to control access to hardware devices and software features that an application might not have permission to use. This is why APIs often play a big role in security.

    For example, if you have ever visited a website and saw a message in your browser that the website is requesting your exact location, that website is trying to use the geolocation API in your web browser. Web browsers provide APIs to make it easy for web developers to access your location - they can simply ask "where are you?" and the browser does the hard work of accessing GPS or nearby Wi-Fi networks to find your physical location. location.

    However, browsers also provide this information via an API because access to it can be controlled. When a website wants to access your exact location, the only way to get it is through the Location API. And, when a website tries to use it, you - the user - can choose to allow or deny the request. Access to hardware resources, such as a GPS sensor, is only possible through an API, so the browser can control access to the hardware and limit applications.

    The same principle is used for modern mobile operating systems such as iOS and Android, where mobile applications have permissions that can be implemented by API access control. For example, if a developer tries to access the camera through the camera API, you can deny the permission request and the app will not be able to access your device's camera.

    File systems that use permissions, as they do on Windows, Mac, and Linux, have the same permissions that are applied by the file system API. A typical application does not have direct access to a raw physical hard drive. Instead, the application must access the files through an API.

    APIs are used to communicate between services

    APIs are also used for other reasons. For example, if you've ever seen a Google Maps object embedded in a website, that website uses the Google Maps API to embed that map. Google provides APIs like these for web developers, who can then use the APIs to assemble complex objects right on their website. If such APIs do not exist, developers may have to create their own maps and provide their own map data in order to host a small interactive map on a website.

    And because it's an API, Google can control access to Google Maps on third-party websites, ensuring they use it in a consistent way, rather than trying to randomly embed a frame that shows the Google Maps website, for example.

    This applies to many different online services. There are APIs for requesting a translation of text from Google Translate, or displaying Facebook comments or tweets from Twitter on a website.

    The OAuth standard also defines a number of APIs that allow you to sign in to a site through another service, such as using your Facebook, Google, or Twitter accounts to sign in to a new website without creating a new user account just for that site. APIs are standard contracts that define how developers interact with a service and the kind of product that developers should expect to receive.

    If you have read this article, then you will have a better understanding of what an API is. Ultimately, you don't need to know what an API is unless you're a developer. But, if you see that a software platform or service has added new APIs for different hardware or services, it should be easier for developers to use such features.

    Windows API - a set of operating system functions

    The abbreviation API seems to many novice programmers to be very mysterious and even frightening. In fact, the Application Programming Interface (API) is just some ready-made set of functions that application developers can use. In general, this concept is equivalent to what used to be more often called a library of subroutines. However, API is usually understood as a special category of such libraries.

    During the development of almost any fairly complex application (MyApplication), a set of specific internal functions is formed for the end user, which is used to implement this particular program, which is called the MyApplication API. However, it often turns out that these functions can be effectively used to create other applications, including other programmers. In this case, the authors, based on the strategy of promoting their product, must decide whether they open access to this set for external users or not? If the answer is yes, in the description of the software package, the phrase appears as a positive characteristic: “The package includes an open set of API functions” (but sometimes for additional money).

    Thus, most often, an API refers to a set of functions that are part of one application, but at the same time available for use in other programs. For example, Excel, in addition to the interface for the end user, has a set of Excel API functions that can be used, in particular, when creating applications using VB.

    Accordingly, the Windows API is a set of functions that is part of the operating system itself and at the same time available to any other application, including those written using VB. In this regard, the analogy with the BIOS/DOS system interrupt set, which is actually a DOS API, is quite justified.

    The difference lies in the fact that the composition of the Windows API functions, on the one hand, is much wider compared to DOS, on the other hand, it does not include many of the tools for direct management of computer resources that were available to programmers in the previous OS. In addition, access to the Windows API is performed using ordinary procedural calls, and DOS functions are called through a special machine instruction of the processor, which is called Interrupt ("interrupt").

    Why you need Win API for VB programmers

    Despite the fact that VB has a huge variety of functions, in the process of more or less serious development, it turns out that their capabilities are often not enough to solve the necessary tasks. At the same time, novice programmers often begin to complain about the shortcomings of VB and think about changing tools, not suspecting that their computer has a huge set of tools and you just need to know how to use them.

    When getting acquainted with the Win API, it turns out that many built-in VB functions are nothing more than a call to the corresponding system procedures, but only implemented in the form of the syntax of this language. With this in mind, the need to use the API is determined by the following options:

    1. API functions that are fully implemented as built-in VB functions. Nevertheless, sometimes in this case it is useful to switch to using the API, since this can sometimes significantly improve performance (in particular, due to the absence of unnecessary conversions of the passed parameters).
    2. Built-in VB functions only implement a special case of the corresponding API function. This is a fairly common option. For example, the CreateDirectory API is more powerful than the built-in VB MkDir statement.
    3. A huge number of API functions have no analogues at all in the current version of the VB language. For example, you can't delete a directory using VB - you need to use the DeleteDirectory function to do this.

    It should also be emphasized that some API functions (their share in the Win API is very small) cannot be called from VB programs due to a number of language restrictions, for example, due to the lack of the ability to work with memory addresses. But in some cases, non-trivial programming techniques can help (in particular, in the case of the same addresses).

    The author's personal point of view is that instead of expanding from version to version of the built-in functions of VB, one should give a good description of the most popular API functions. At the same time, I would like to advise developers not to wait for a new version of the tool with advanced features, but to carefully study the composition of the existing Win API - it is likely that the features you need could be implemented already in the VB 1.0 version of the 1991 release.

    How to learn Win API

    This is not such an easy question, given that the number of Win32 API functions is estimated at around 10,000 (no one knows the exact number, not even Microsoft).

    VB (versions 4-6) includes a file with a description of Win API declarations - WIN32API.TXT (more on its use later). But, firstly, it can be used to obtain information about the purpose of a particular function and its parameters only by the mnemonic names used, and secondly, the list of functions in this file is far from complete. At one time (seven years ago) VB 3.0 had special help files describing the functions of the Win16 API. However, already in v.4.0 this useful information with a convenient interface has disappeared.

    Comprehensive information about the Win32 API can be found in the Platform Software Development Kit help, which is included on the MSDN Library CDs included with VB 5.0 and 6.0 Enterprise Edition and Office 2000 Developer Edition. However, it is not easy to find the necessary information there and understand it. Not to mention the fact that all descriptions there are given in relation to the C language.

    The world-renowned guide for learning API programming in the VB environment is the books of the famous American expert Daniel Appleman. His series Dan Appleman's Visual Basic Programmer's Guide to the Windows API (for Win16, Win32, in relation to different versions of VB) has consistently been a bestseller for VB programmers since 1993. Dan Appleman's VB 5.0 Programmer's Guide to the Win32 API, released in 1997, was brought to the author from the USA by a friend who found it in the first bookstore in a small provincial town.

    This book is over 1,500 pages long and includes general VB API programming techniques, as well as over 900 functions. The accompanying CD-ROM contains the full text of the book and all programming examples, as well as a few additional chapters not included in the printed version. In 1999, Dan Appleman released a new book, Dan Appleman's Win32 API Puzzle Book and Tutorial for Visual Basic Programmers, which includes information on another 7600 functions (though not as extensive).

    Win API and Dynamic Link Library (DLL)

    The Win API set is implemented as dynamic DLLs. Further, we will actually talk about the technology of using DLLs in the VB environment using the example of libraries that are part of the Win API. However, when talking about DLLs, there are a few important things to note.

    In this case, by DLL we mean the traditional variant of binary dynamic libraries, which provide direct access of applications to the necessary procedures - subroutines or functions (much like it happens when calling procedures inside a VB project). Such libraries can be created using different tools: VC ++, Delphi, Fortran, except for VB (we'll see what will appear in version 7.0) - the latter can only create ActiveX DLLs, which are accessed through the OLE Automation interface.

    Typically, dynamic link library files have a .DLL extension, but this is not necessary at all (for Win16, the .EXE extension was often used); External device drivers are identified with .DRV.

    As we have already noted, it is quite difficult to determine the exact number of Windows APIs and the files that contain them, but they are all located in the system directory. In this regard, it is better to single out the composition of the libraries included in the core of the operating system, and the main libraries with key additional functions.

    And now a few tips.

    Tip 1: Make sure your DL ad is formatted correctly L-procedures

    The very call to DLL procedures in the program looks exactly the same as to "regular" Visual Basic procedures, for example:

    Call DllName([list of arguments])

    However, to use external DLL functions (including the Win API), they must be declared in the program using the Declare statement, which looks like this:

    Declare Sub LibProcedureName _ “LibraryName” _ [([ArgumentList])]

    Declare Function FunctionName _ Lib "LibraryName" _ [([ArgumentList])]

    Here, optional elements of the operator are given in square brackets, expression variables are in italics, the rest of the words are keywords. The help system has a pretty good description of the syntax of the operator, so for now we'll just make a few points.

    External function declarations must be placed in the General Declarations section of the module. If you place it in a form module, then you must specify the Private keyword (this declaration will only be available inside this module) - this is the restriction for all procedures of the form module.

    The Win32 API set is implemented only as functions (the Win16 API had a lot of Sub routines). For the most part, these are functions of the Long type, which most often return the operation completion code.

    The Declare statement appeared in MS Basic back in the days of DOS, and it was also used to declare the internal procedures of the project. In Visual Basic, this is not required, since the declaration of internal procedures is automatically their Sub or Function declaration. Compared to Basic/DOS, it is obligatory to specify in the new description the name of the library file where the required procedure is located. The Wip API libraries are located in the Windows system directory, so it is enough to give only the file name. If you are accessing a DLL that is located in an arbitrary location, you need to write down the full path to this file.

    The description of the Declare statement usually takes up quite a lot of space and does not fit on one line in the code window. Therefore, we recommend that you follow some specific line-breaking scheme when writing applications, for example:

    Declare Function GetTempPath _ Lib "kernel32" Alias ​​"GetTempPathA" _ (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long

    In this case, all the main elements of the description are separated into different lines and therefore are well read.

    Tip 2: Be especially careful when working with DLL functions

    Using the Win API and various DLL functions significantly expands the functionality of VB and often improves the performance of programs. However, the payoff for this is the risk of reducing the reliability of the application, especially in the process of debugging it.

    One of the most important advantages of the VB environment is the reliability of the programming process: functioning under the control of the interpreter, the program code cannot theoretically disrupt the operation of Windows and VB itself. The programmer may not be very attentive to the correctness of passing parameters to the called functions - such errors will be easily detected by the interpreter itself either in the process of translating the code or during its execution. In the most unpleasant case, the processing mode will simply be interrupted, and with an indication of where and why the error occurred.

    Directly using Windows API functions or other DLLs removes such control over data transfer and code execution outside of the VB environment. Therefore, an error in accessing external functions can lead to the inoperability of both VB and the operating system. This is especially true at the stage of program development, when the presence of errors is quite natural. Thus, by applying the wider possibilities of the functions of the base layer of the system, the programmer takes responsibility for the correctness of their application.

    The problem is exacerbated by the fact that different programming languages ​​use different ways of passing parameters between procedures. (More specifically, different passing methods are used by default, as many languages ​​may support multiple methods.) The Win APIs are implemented in C/C++ and use C/C++ parameter passing conventions, which are different from what VB is used to.

    In this regard, it should be noted that the appearance of analogs of API functions built into VB is justified precisely by the adaptation of the latter to the VB syntax and the implementation of the corresponding data exchange control mechanism. Also note that at the stage of experimental debugging of the application, when creating an executable module, it is better to use the P-code compilation option instead of Native Code (machine code). In the first case, the program will run under the control of an interpreter - slower than machine code, but more reliable in terms of possible erroneous effects on the operating system and providing a more convenient mode for detecting possible errors.

    Tip 3: Dan Appleman's Ten Tips for Reliable API Programming in VB

    Using an API function requires more careful programming, using some of the more unfamiliar methods of calling procedures (compared to VB). We will continue to address these issues in the following. And now here is a summary of Dan Appleman's advice on this topic (their first version appeared back in 1993), with some of our additions and comments.

    1. Remember ByVal. The most common mistake made when accessing API and DLL functions is the incorrect use of the ByVal keyword: they either forget to set it, or, conversely, set it when it is not needed.

    These examples show the effect of the ByVal statement on parameter passing

    Parameter type With ByVal Without ByVal
    Integer A 16-bit integer is pushed onto the stack. The 32-bit address of a 16-bit integer is pushed onto the stack.
    Long A 32-bit integer is pushed onto the stack. The 32-bit address of a 32-bit integer is pushed onto the stack.
    String The string is converted to the format used in C (data and a terminating null byte). The 32-bit address of the newline is pushed onto the stack The VB descriptor of the string is pushed onto the stack. (Such handles are never used by the Windows API itself and are only recognized in DLLs implemented specifically for VB.)

    It should be recalled here that the transfer of parameters in any programming system, including VB, is performed in two main ways: by reference (ByRef) or by value (ByVal). In the first case, the address of the variable is passed (this option is used in VB by default), in the second - its value. The fundamental difference lies in the fact that with the help of a reference, the changed value of the passed parameter is returned to the calling program.

    To understand this, conduct an experiment using the following programs:

    Dim v As Integer v = 2 Call MyProc(v) MsgBox “v = “ & v Sub MyProc (v As Integer) v = v + 1 End Sub

    When you run this example, you will receive a message with the value of the variable equal to 3. The fact is that in this case, the address of the variable v, physically created in the calling program, is passed to the MyProc subroutine. Now change the description of the procedure to

    Sub MyProc (ByVal v As Integer)

    As a result, when you run the test, you will get v = 2, because only the initial value of the variable is passed to the procedure - the result of operations performed on it is not returned to the calling program. You can also change the pass-by-value mode using the Call statement as follows:

    Sub MyProc (v As Integer) ... Call MyProc((v)) ‘ (v) - the parentheses indicate the pass-by-value _ mode.

    However, when referring to internal VB procedures, the use of the ByVal keyword in the Call statement is prohibited - parentheses are used instead. This has its own explanation.

    In the classical case (C, Fortran, Pascal), the difference between the ByRef and ByVal modes depends on what exactly is placed on the data exchange stack - the address of the variable or its value. Basic historically uses a variant of ByVal software emulation - the address is always on the stack, but only when passing by value is a temporary variable created for this. To distinguish between these two options (classic and Basic), different ways of describing the ByVal mode are used. Note that emulation of the ByVal mode in VB provides higher program reliability: by confusing the form of the call, the programmer risks only that the corrected value of the variable will be returned (or not returned) to the calling program. In the "classic" version, however, such confusion can lead to a fatal error when executing the procedure (for example, when a variable value equal to, say, zero is used instead of a memory address).

    DLL functions are implemented according to "classical" principles and therefore require a mandatory description of how data is exchanged with each of the arguments. This is the purpose of function declarations through the Declare description (more precisely, the list of passed arguments). The most common way to pass parameters to a Windows API or DLL function is to use the ByVal keyword. Moreover, it can be set both in the Declare statement, and directly when calling the function.

    The consequences of incorrect parameter passing are easy to predict. If you receive an obviously invalid address, you will receive a GPF (General Protection Fault) message. If the function receives a value that matches a valid address, then the API function will crawl into someone else's area (for example, into the Windows kernel) with all the ensuing catastrophic consequences.

    2. Check the type of the passed parameters. Equally important is the correct number and type of parameters passed. The arguments declared in the Declare must match the expected parameters in the API function. The most common case of error in passing parameters is related to the difference between NULL and a zero-length string - remember that they are not the same thing.

    3. Check the return type.

    VB is pretty tolerant of function return type mismatches, since numeric values ​​are usually returned via registers rather than the stack. The following rules will help determine the correct value returned by an API function:

    • A DLL function that does not return a value (analogous to void in 'C') must be declared as a VB Sub.
    • an API function that returns an integer value (Integer or Long) can be defined as either a Sub or a Function that returns a value of the appropriate type.
    • none of the API functions return floating point numbers, but some DLLs may well return such a data type.

    4. Use the "As Any" construction with great care. Many Windows API functions have the ability to accept parameters of various types and use the As Any construct (the interpretation of the type is performed depending on the value of other parameters passed).

    A good solution in this case may be to use several function aliases (Alias) with the creation of two or more declarations for the same function, and each of the declarations specifies parameters of a certain type.

    5. Don't forget to initialize strings. There are many functions in the Win API that return information by loading data into string buffers passed as a parameter. In your program, you can seem to do everything right: do not forget about ByVal, correctly pass parameters to the function. But Windows cannot check how large the size of the memory allocated for the string is. The row must be large enough to accommodate all the data that can be placed in it. It is the responsibility of the VB programmer to reserve a buffer of the correct size.

    Note that on 32-bit Windows, strings are converted from Unicode (double-byte encoding) to ANSI (single-byte) and vice versa, taking into account the national settings of the system. Therefore, to reserve buffers, it is sometimes more convenient to use byte arrays instead of string variables. (More on this will be discussed below.)

    More often than not, the Win API functions allow you to define the maximum block size yourself. In particular, sometimes this requires calling another API function that will "prompt" the size of the block. For example, GetWindowTextLength allows you to determine the size of the string required to accommodate the window title returned by the GetWindowText function. In this case, Windows ensures that you do not go out of bounds.

    6. Be sure to use Option Explicit.

    7. Carefully check the values ​​of parameters and return values. VB has good type checking capabilities. This means that when you try to pass an invalid parameter to a VB function, the worst thing that can happen is that you get an error message from VB. But this mechanism, unfortunately, does not work when accessing the Windows API functions.

    Windows 9x has an improved parameter checking system for most API functions. Therefore, the presence of an error in the data usually does not cause a fatal error, but it is not so easy to determine what caused it.

    Here we can advise you to use several ways to debug this type of error:

    • use step-by-step debugging or the Debug.Print command to inspect each suspicious API function call. Check the results of these calls to make sure everything is OK and the function exited gracefully;
    • use a Windows debugger like CodeView and a debug version of Windows (provided in the Windows SDK). These tools can detect a parameter error and at least determine which API function is causing the error;
    • use additional third-party tools to check the types of parameters and the validity of their values. Such tools can not only find parameter errors, but even point to the line of VB code where the error occurred.

    In addition, it is necessary to check the result of the API function execution.

    8. Remember that integers in VB and in Windows are not the same thing. First of all, you should keep in mind that the term "Integer" in VB refers to a 16-bit number, in the Win 32 documentation - a 32-bit number. Secondly, integers (Integer and Long) in VB are signed values ​​(that is, one bit is used as a sign, the rest are used as the mantissa of a number), in Windows only non-negative numbers are used. This circumstance must be kept in mind when you form the passed parameter using arithmetic operations (for example, calculate the address by summing some base and offset). The standard VB arithmetic functions are not suitable for this. How to be in this case, we will talk separately.

    9. Pay close attention to function names. Unlike Win16, the names of all Win32 API functions are sensitive to the exact use of lowercase and uppercase letters (this was not the case in Win16). If you use a lowercase instead of an uppercase letter somewhere, or vice versa, then the desired function will not be found. Also, watch out for the correct use of the A or W suffix in functions that take string parameters. (For more on this, see below.)

    10. Save your work often. Errors related to incorrect use of the DLL and Win API can lead to a crash in the VB environment, and possibly the entire operating system. You should make sure that the code you write before the test run is saved. The simplest is to set the project modules to auto-record before running the project in VB.

    After reading the previous tip, you might think that using the Win API functions is a risky business. To some extent this is true, but only in comparison with the safe programming provided by VB itself. But with their skillful application and knowledge of possible pitfalls, this risk is minimal. In addition, it is often simply impossible to completely abandon the use of the Win API - they will still be required for any serious development.

    In addition, earlier we mentioned the "pitfalls" for a wide class of DLL. In the case of the Win API, everything is much simpler, since the form of calling these functions is clearly unified here. In doing so, the following key points should be kept in mind:

    1. Win32 API functions are exactly functions, that is, procedures of type Function (there were many Sub routines in the Win16 API). These are all functions of type Long, so their descriptions are written as follows: Declare Function name ... As Long ‘ the function type _ is explicitly defined

      Declare Function name& ‘ function type _ is defined with suffix

      Calling the API function looks like this:

    Result& = ApiName& ([ ArgumentList]
    1. Most often, the return value of a function is the exit code of the operation. Moreover, a non-zero value in this case means a normal completion, zero - an error. You can usually (but not always) check the nature of the error by calling the GetLastError function. The description of this function is as follows: Declare Function GetLastError& Lib "kernel32" ()

      ATTENTION! When working in VB, it's best to use the LastDLLError property of the Err object to get the value of the refined error code, because VB sometimes resets the GetLastError function between calling the API and continuing with program execution.

      You can interpret the code returned by GelLastError using the constants written in the API32.TXT file, with names beginning with the ERROR_ suffix.

      The most typical errors have the following codes:

      • ERROR_INVALID_HANDLE = 6& - invalid handle
      • ERROR_CALL_NOT_IMPLEMENTED = 120& - call in Windows 9x a function available only for Windows NT
      • ERROR_INVALID_PARAMETER = 87& - invalid parameter value

      However, many functions return the value of some requested parameter (for example, OpenFile returns the value of a file descriptor). In such cases, the error is defined by some other special Return& value, most commonly 0 or -1.

    2. Win32 APIs use strictly fixed ways to pass the simplest data types. a) ByVal ... As Long

      Long variables handle at least 80% of the argument passing. Note that the argument Always is followed by the keyword ByVal, which, among other things, means that a one-way data transfer is performed - from the VB program to the API function.

      B) ByVal ... As String

      This type of data transfer is also quite common, and with an argument also Always ByVal is applied. When an API function is called, the address of the string is written onto the stack, so in this case, two-way data exchange is possible. There are several dangers to be aware of when working with strings.

      The first is that memory is reserved for a string in the calling program, so if the API function will fill strings, then before calling it, you need to create a string of the required size. For example, the GetWindowsDirectory function returns the path to the Windows directory, which by definition must be no longer than 144 characters. Accordingly, the call to this function should look something like this:

      WinPath$ = Space$(144) ‘reserve string in _ 144 characters Result& = GetWindowsDirectory& (WinTath$, 144) _ ‘buffer fill’ Result& - actual number of characters in directory name _ WinPath$ = Left$(WinPath, Result&)

      The second problem is that when the API function is called, the source string is converted into some internal representation of it, and vice versa when the function exits. If at the time of Win16 this operation consisted only in adding a null byte at the end of the string, then with the advent of Win32, the transformation of the two-byte encoding Unicode to ANSI and vice versa was added to this. (This was discussed in detail in the article "Features of Working with String Variables in VB", ComputerPress 10'99 and 01'2000). For now, just note that using the ByVal ... As String construct, you can only exchange strings with character data.

      C) ... As Any

      This means that some memory buffer address will be pushed onto the stack, the contents of which will be interpreted by the API function, for example, depending on the value of other arguments. However, As Any can only be used in a Declare statement - a specific variable must be defined as an argument when a specific call to the function is made.

      D) ... As UserDefinedType

      Such a construction is also often used when it is necessary to exchange data (generally in both directions) using some structure. This construct is actually some kind of concrete implementation of the As Any transfer form, it's just that in this case the function is set to a fixed structure.

      The form of the data structure is defined by a particular API function, and it is the programmer's responsibility to correctly describe and reserve it in the calling program. Such a design Always used without words ByVal, that is, in this case, passing by reference is performed - the address of the variable is written to the stack.

    An example of calling an API function

    Let's illustrate the above with an example of using two useful file functions - lopen and lread , which are described as follows:

    Declare Function lread Lib “kernel32” _ Alias ​​“_lread” (_ ByVal lpFileName As String, _ ByVal wReadWrite As Long) As Long Declare Function lread Lib “kernel32” _ Alias ​​“_lread” (_ ByVal hFile As Long, lpBuffer As Any, _ ByVal wBytes As Long) As Long

    In VB, their counterparts - in this case, exact ones - are the Open and Get operators (for Binary mode). Let's immediately pay attention to the use of the Alias ​​keyword in the function declaration - this is just the case when you cannot do without it. Real function names in the library begin with an underscore (typical C style), which is not allowed in VB.

    The file open operation might look like this:

    Const INVALID_HANDLE_VALUE = -1 ' invalid _ handle value lpFileName$ = “D:\calc.bas” ' file name wReadWrite& = 2 ' read-write mode hFile& = lopen(lpFileName$, wReadWrite&) _ ' define file handle If hFile& = INVALID_HANDLE_VALUE Then _ ' file opening error ' Specifying the error code CodeError& = Err.LastDllError 'CodeError& = GetLastError _ ' this construct does not work End If

    Here you need to pay attention to two points:

    • as the value of the function, we get the value of the file descriptor. An error corresponds to a value of -1;
    • just in this case, the call to the GetLastError function does not work - to get the refined error value, we turned to the Err object (we talked about the possibility of such a situation above).

    You can then read the contents of the file, but this assumes that the programmer must have some understanding of its structure (just as it happens when working with arbitrary binary files). In this case, the call to the lread function might look like this:

    Dim MyVar As Single wBytes = lread (hFile&, MyVar, Len(MyVar) ' reading a real number, 4 bytes ' wBytes is the number of data actually read, ' -1 is an error... Type MyStruct x As Single i As Integer End Type Dim MyVar As MyStruct wBytes = lread (hFile&, MyVar, Len(MyVar)) ' read data structure, 6 bytes

    Note again that the second argument to the function is passed by reference, the rest by value.

    Dim MyVar As String MyVar = Space$(10) 'reserve variable for 10 characters wBytes = lread (hFile&, ByVal MyVar, Len(MyVar)) ' read character string, 10 characters

    Here you can see an important difference from the earlier example - a string variable is necessarily accompanied by the ByVal keyword.

    Reading the contents of a file in an array (for simplicity, we will use a one-dimensional byte array) is performed as follows:

    Dim MyArray(1 To 10) As Byte wBytes = lread (hFile&, MyArray(1), _ Len(MyArray(1))* 10) ‘ reading 10 array elements

    By specifying the first element of the array as an argument, we pass the address of the beginning of the memory area reserved for the array. Obviously, any fragment of the array can be filled in this way:

    WBytes = lread (hFile&, MyArray(4), _ Len(MyArray(1))* 5) ' read array elements from 4th to 8th

    Tip 5: Use Alias ​​for Transmissions and parameters As Any

    Here, based on the previous example, we will reveal the essence of the fourth tip of Dan Appleman.

    When working with the lread function, you should remember that when accessing it using a string variable, you must use the ByVal keyword (otherwise the message about an illegal operation cannot be avoided). To be safe, you can make an additional special description of the same function to work only with string variables:

    Declare Function lreadString Lib "kernel32" _ Alias ​​"_lread" (_ ByVal hFile As Long, ByVal lpBuffer As String, _ ByVal wBytes As Long) As Long

    When working with this description, you no longer need to specify ByVal when accessing:

    WBytes = lreadString (hFile&, MyVarString, _ Len(MyVarString)) '

    It would seem that the syntax of the Declare operator allows you to make such a special declaration for an array:

    Declare Function lreadString Lib “kernel32” Alias ​​“_lread” (_ ByVal hFile As Long, lpBuffer() As Byte, _ ByVal wBytes As Long) As Long

    However, the appeal

    WBytes = lreadArray(hFile&, MyArray(), 10)

    inevitably leads to a fatal program error.

    This is a continuation of the conversation about the peculiarities of processing string variables in Visual Basic: VB uses a two-byte Unicode encoding, Win API uses a one-byte ANSI encoding (moreover, with the format adopted in C, with a null byte at the end). Accordingly, when using string variables as an argument, the conversion from Unicode to ANSI is always automatically performed when calling an API function (more precisely, a DLL function) and reverse conversion when returning.

    The takeaway from this is simple: String variables can be used to exchange character data, but they cannot be used to exchange arbitrary binary information (as was the case with 16-bit versions of VB). In the latter case, it is better to use a one-dimensional byte array.

    As you know, the String type can be used to describe a custom structure. In this regard, the following must be remembered:

    • It is strictly forbidden to use the following construction to access the Win API: Type MyStruct x As Single s As String ‘ variable length string End Type

      In the case of a string of variable length, a string descriptor is passed as part of the structure, with all the ensuing consequences in the form of a program execution error.

    • You can use a fixed length string as a structure element: Type MyStruct x As Single s As String*8 ‘ fixed length string End Type

    In this case, the corresponding encoding conversion is performed.

    And the last note: you cannot use an array of string variables (both fixed and variable length) when accessing an API function in any case. Otherwise, the emergence of an "illegal operation" will be guaranteed.

    It is likely that you will have a situation where you need to write your own DLL functions. The need for this will inevitably arise if you use the technology of mixed programming - the use of two or more programming languages ​​to implement one application.

    In this regard, we note that mixed programming is quite common for the implementation of a fairly complex application. Indeed, each language (more precisely, a programming system based on a language) has its own strengths and weaknesses, so it is quite logical to use the advantages of different tools for solving different problems. For example, VB - for creating a user interface, C - for efficient access to system resources, Fortran - for the implementation of numerical algorithms.

    The author's opinion is as follows: any serious programming requires the developer to own at least two tools. Of course, in today's conditions of a clear division of labor, it is very difficult to be an excellent expert even in two systems, so the “main and auxiliary languages” scheme is more logical. The idea here is that even a smattering of knowledge of the "auxiliary" language (writing fairly simple procedures) can greatly improve the efficiency of the "main" language. Note that knowledge of VB, at least as an auxiliary one, is today an almost mandatory requirement for a professional programmer. By the way, in the days of DOS for any programmer, including Basic, it was highly desirable to know the basics of Assembler.

    One way or another, but even in the conditions of group work, when each programmer is engaged in his own specific business, all project participants should have an idea about the features of the procedural interface in different languages. And to know that many programming systems (including VB), in addition to the default interface, allow you to use other, extended methods for calling procedures, which make it possible to adapt the interface to another language.

    When studying the interprocedural interface, you should pay attention to the following possible "pitfalls":

    • Different languages ​​may use different conventions for writing identifiers. For example, it is common to use an underscore at the beginning of a procedure name, which is not allowed in VB. This problem is easily solved by using the Alias ​​keyword in the Declare statement (see Tip 2-3 for example).
    • A different sequence of writing passed arguments to the stack can be used. For example, in the days of DOS (to be honest - I don’t know how it looks now in a Windows environment), C wrote arguments from the end of the list, other languages ​​​​(Fortran, Pascal, Basic) - from the beginning.
    • By default, different principles for passing parameters are used - by reference or by value.
    • Various principles for storing string variables. For example, in C (as well as in Fortran and Pascal) the length of a string is determined by a null byte at the end of it, while in Basic the length is written explicitly in the string descriptor. Of course, you need to keep in mind the possibility of using different character encodings.
    • When transferring multidimensional arrays, it should be remembered that there are various options for converting multidimensional structures into one-dimensional ones (starting from the first index or from the last, in relation to two-dimensional arrays - “by rows” or “by columns”).

    With this in mind, the following recommendations can be made:

    • Use the simplest, proven ways to pass arguments to DLL functions. The standards adopted for the Win API are quite suitable as a model.
    • Never pass arrays of string variables.
    • Be very careful about passing simple string variables and multidimensional arrays.
    • Be sure to check the functionality of the mechanism for passing arguments to and from the called procedure in a special way. Write a custom test to check the data transfer. Separately check that each argument is passed correctly. For example, if you have a procedure with several arguments, first check the correctness of passing each parameter for a variant with one argument, and only then - for the entire list.

    But what if the DLL function is already written, for example, in Fortran, but its input interface does not fit very well into the VB standards above? There are two pieces of advice here. First: write a test DLL function and use it to try and find the right call from the VB program by trial and error. Second: write an adapter procedure in the same Fortran that would provide a simple interface between VB and a DLL function with the transformation of simple data structures into complex ones (for example, converting a multidimensional byte array to a string array).

    So: use DLL functions. But stay vigilant...

    ComputerPress 9 "2000

    Let's start with the basics: what is an API? The abbreviation stands for Application Programming Interface, or an interface for programming applications. The name seems to speak for itself, but it is better to consider a more detailed explanation.

    As already mentioned, the API is, first of all, an interface. An interface that allows developers to use ready-made blocks to build an application. In the case of developing mobile applications, a library for working with a "smart home" can act as an API - all the nuances are implemented in the library and you only refer to this API in your code.

    In the case of web applications, the API can return data in a format other than standard HTML, which makes it convenient to use when writing your own applications. Third-party public APIs most often return data in one of two formats: XML or JSON. In case you decide to make an API for your application, remember that JSON is much more concise and easier to read than XML, and services that provide access to data in XML format are phasing out the latter.

    API in web applications by examples

    An application - for example, Github - has its own API that other developers can use. How they will use it depends on the possibilities that the API provides and how well the imagination of the developers works. The Github API allows, for example, to get information about the user, his avatar, readers, repositories, and many other useful and interesting information.

    Similarly, you can send a request in any language, including Ruby. The response to the request will be something like this:

    ( "login" : "Freika" , "id" : 3738638, "avatar_url" : "https://avatars.githubusercontent.com/u/3738638?v=3", "gravatar_id" : "" , "url" : "https://api.github.com/users/Freika", "html_url" : "https://github.com/Freika" , "followers_url" : "https://api.github.com/users/Freika/followers", "following_url" : "https://api.github.com/users/Freika/following(/other_user)", "gists_url" : "https://api.github.com/users/Freika/gists(/gist_id)", "starred_url" : "https://api.github.com/users/Freika/starred(/owner)(/repo)", "subscriptions_url" : "https://api.github.com/users/Freika/subscriptions", "organizations_url" : "https://api.github.com/users/Freika/orgs", "repos_url" : "https://api.github.com/users/Freika/repos", "events_url" : "https://api.github.com/users/Freika/events(/privacy)", "received_events_url" : "https://api.github.com/users/Freika/received_events", "type" : "User" , "site_admin" : false , "name" : "Evgeniy" , "company" : "" , "blog" : "http://frey.su/" , "location" : " Barnaul" , "email" : "" , "hireable" : true , "bio" : null, "public_repos" : 39, "public_gists" : 13, "followers" : 15, "following" : 21, "created_at" : "2013-03-01T13:48:52Z" , "updated_at" : "2014-12-15T13:55:03Z" )

    As you can see from the block above, the response contains a login, an avatar, a link to the profile on the site and in the API, user status, the number of public repositories, and other useful and interesting information.

    One API is not enough

    Creating a full-fledged API for your application is only half the battle. How are you going to access the API? How will your users access it?

    The first thing that comes to mind is the usual series of HTTP requests in order to get the desired information, and this is the wrong answer. The most obvious way in this case is not the most convenient and simple. It would be much more reasonable to create a special library for working with the interface, which will describe all the necessary ways to receive and send information using the API.

    Once again, we will use Github to give an example: to work with the API of this excellent service (and its interface provides extensive possibilities), several libraries have been created in various languages, for example, the Octokit gem. In the documentation for such libraries (and the gem given as an example), any interested developer will be able to find all the necessary ways to receive information from Github and send it back through the service API.

    Thus, if you are creating your own API, think about creating libraries for working with it in the most common languages ​​as well. And be prepared that at a certain level of demand for your application, someone else can create their own library to work with your API. This is fine.

    useful links

    In subsequent articles, we will talk about how to correctly create an API, ensure its security and restrict access to some of the information.

    Sooner or later, any programmer comes across such a concept as API. However, when such a meeting occurs, not everyone knows what it is, why it is needed and how to use it. And in this article I am going to fill this gap in the knowledge of some of you, and also give an example from my practice.

    API (application programming interface) - This application programming interface. In simpler terms, this is a set of various functions, constants, classes, query formats that can be used in other programs.

    It can be considered that API is a certain object, the implementation of which we do not know, however, we can use it. For example, a computer is an object whose implementation is known to very few people, however, almost everyone can use it by performing some actions: watching a video, surfing the Internet, printing text, and so on. Few people know how it all works, but almost everyone can do it.

    An example API is Windows API, OpenGL API, Direct3D API and so on.

    For example, not so long ago I also came across directly with API. I signed up for a mailing list service SmartResponder.ru"and started a mailing list, to which people began to subscribe. The task was as follows: within a day after subscribing, a person can purchase my paid video course at a discount. Since all information about subscribers is stored on the server" SmartResponder.ru", then normal access (for example, through DB) I didn’t have access to this data, but it was necessary to implement it. Good, u" SmartResponder.ru"have your own API, which I used.

    I found in them API query format to retrieve the subscription date as a result. Coming through cURL I sent the corresponding request and received the desired subscription date for a particular e-mail addresses. Next, standard processing and output of the result.

    APIs can be both fun and frustrating at the same time. On the one hand, by interacting with other applications, you can greatly increase the reach of the audience and the "wow effect" of your application. On the other hand, this includes reading tons of documentation, learning about authentication strategies, and parsing uninformative (or even missing) error messages.

    First of all, if you still don't fully understand what an API (Application Programming Interface) is, read Skillcrush's explanation and then the first part of this article to catch up.

    "API" is an incredibly broad concept - every time your application "talks" to another application, it is through some kind of API. Components within your own application, like different parts of Rails, also communicate with each other via an API. They are more or less independent sub-applications that pass on the data they each need to perform their own specific tasks. In the app world, everything is an API!

    When you create applications with more dynamic front-end functionality (both single-page Javascript applications and simple applications with separate AJAX calls), they will communicate with the Rails backend through your own API, which is really just an extra line or three of code. , which tells your controllers how to serve JSON or XML instead of HTML.

    In this tutorial, you will learn how to create your own API. In subsequent lessons, we will cover how to interact with the APIs of other applications. The lessons should be a good springboard for learning about this topic, but are unlikely to be able to fully cover all cases. A big part of working with APIs is being able to read their documentation and figure out what they want from you.

    Points for reflection

    Review the questions and see if you know the answers. Check yourself again after completing the task.

    • How Rails understands what type of file you are expecting in response when you send an HTTP request.
    • What is the purpose of the #respond_to method?
    • How do you return a user object (User) while specifying attributes that you don't want included in that object (i.e. you can't just return User.first)?
    • Name 2 steps behind the scenes of the #to_json method.
    • How do I tell a controller action to only render an error message?
    • How to create your own error message?
    • Why can't you use session-based controller authentication methods if you want to allow programmatic connection to your API?
    • What is "Service Oriented Architecture"?

    API Basics

    Your Rails application is actually an API already, although you might not think of it as an API. The web browser your users launch is also a program, so it actually sends an API request to your Rails application when the user opens a new page. We tend to think this way because rendering of HTML templates is such a common task that we just hardcode this functionality into our server programs as a standard response type, and consider everything else to be something unusual.

    However, often you want to make a request that doesn't require you to go through all the headaches of using a browser. You may not care about the structure of the page (HTML), but in return you want clean data. Let's say you want to get a list of all users. You can request something like http://yourapplication.com/users , which will likely trigger the #index action and render a list of all the app's users.

    But why bother with all this extra information when all you want is a list of users? The simplest option would be to send a request to the same URL, specifying the expectation of a JSON or XML response in return. If you set up your Rails controller properly, you'll get back a simple JSON array object containing all users. Wonderful!

    The same principle applies when you communicate with an external API. Let's say you want to get a user's recent "tweets" from Twitter. All you need to do is tell your Rails application how to interact with the Twitter API (i.e. authenticate yourself), submit the request, and process the set of "tweets" that will be returned.

    Creating an API

    You may want to make your Rails application a pure back-end API for front-end web pages, or you may simply want to learn how to send JSON when the front-end requests it. This section will not cover how to create full fledged RESTful APIs with authentication functionality. This is a smooth introduction to treating your application as an API.

    Basics

    If you want your Rails application to return JSON instead of HTML, you'll need to tell your controller to do so. The great thing is that the same controller action can return different types depending on whether your user is making a normal browser request or accessing an API via the command line. This determines what type of request was made based on the extension of the requested file, such as example.xml or example.json .

    You can check what Rails "thinks" about the type of file you're expecting by checking the server log:

    Started GET "/posts/new" for 127.0.0.1 at 2013-12-02 15:21:08 -0800 Processing by PostsController#new as HTML

    The first line tells you what URL was requested, and the second tells you where it was directed and how Rails handles it. If you were to use the .json extension it would look like this:

    Started GET "/posts.json" for 127.0.0.1 at 2013-12-04 12:02:01 -0800 Processing by PostsController#index as JSON

    If you have a test application running, try requesting different URLs. If your controller doesn't know how to handle them, then you might get an error, but you should still be able to see what Rails understands by your requests.

    Rendering JSON or XML

    When you decide you want to respond with JSON or XML, you will need to tell your controller to render JSON or XML instead of HTML. One way to do this is to use the #respond_to method:

    Class UsersController< ApplicationController def index @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render xml: @users } format.json { render json: @users } end end end

    In this case, #respond_to passes a format object to the block that you can attach the appropriate render call to. If you do nothing, html will be rendered using the standard Rails template (app/views/index.html.erb in this example).

    The #render function is smart enough to understand how to render a wide variety of formats. When you pass it the key:json , it will call #to_json on the value, in this example @users . This will convert your Ruby object(s) into JSON strings, which will be passed to the requesting application.

    This is how you get your API. Of course, creating an API can be a bit more complicated if you want to do some fancy stuff, but it's all about the basics.

    Specifying return attributes

    Let's say you want to make sure you don't return a user's email address along with a User object. In this case, you'll want to change the attributes that will be returned, modifying what the #to_json method does.

    Previously, you would have simply overridden the #to_json method with your version, but now you don't need to - in fact, you override the #as_json method instead. The #as_json method is used in the #to_json method, so modifying it implicitly changes the result of #to_json , but in a rather specific way.

    #to_json does 2 things: runs #as_json and gets a hash of the attributes to be rendered to JSON. It then renders to JSON using ActiveSupport::json.encode . So by modifying #as_json , you are more specific about the part of the #to_json method that you actually want to change.

    In our case, we do this by modifying #as_json in our model to only return the attributes we need:

    # app/models/user.rb class User< ActiveRecord::Base # Вариант 1: Полное переопределение метода #as_json def as_json(options={}) { :name =>self.name ) # DO NOT include the email field end # Option 2: Use the standard method #as_json def as_json(options=()) super(only: [:name]) end end

    Then, in our controller, all we need to do is render the JSON as normal (the example below will always return JSON, whether an HTML request was sent or not):

    # app/controllers/users_controller.rb class UsersController< ApplicationController def index render json: User.all end end

    Note that you don't need to call #to_json yourself when you use #render - it will do it for you.

    Sometimes Heroku may require additional steps to properly display your error pages. Look. You may need to first remove static pages from the app/public directory.

    Securing from the outside

    Let's say you want to allow access to the API only if the user is logged in. Your existing controller authentication already does the job - just make sure you have the correct #before_action set (e.g. before_action:require_login). You may want functionality where both logged in and not logged in users can view the page, but each needs to see different data. You don't want non-logged-in users to be able to make API requests to retrieve sensitive data. Likewise, you don't want to allow unauthorized users to visit certain HTML pages.

    If you want to process requests from a non-browser application (for example, from the command line), you cannot rely on browser cookies for authentication. This is why most APIs use native tokens as part of the authentication process. We'll talk a little more about tokens in the next tutorial.

    Next steps

    You now have the skills to use your Rails application to serve not just HTML, but any other format. If you want to go further and let other developers build things using your platform (for example, so they can make programmatic requests instead of authenticating as a user), you'll need to make your API system much more secure. We won't cover it all here, but check out the following:

    • The article Building Awesome Rails APIs describes many of the best approaches for moving from a toy app to industry standard APIs.

    Service Oriented Architecture

    It's time to introduce an architectural approach called Service-Oriented Architecture (SOA). The basic idea is that your application will consist of many services, such as a payment system, user registration, a recommendation module, etc. Instead of building it all inside one main application, you break the subsystems into completely independent pieces that interact with each other using internal APIs.

    This is good for many reasons. By ensuring that every piece of your application doesn't care how the other parts work and only knows how to request data through their API, you can make significant changes to the service code and the rest of the application will work as before. You can completely replace one service with another, and as long as it communicates using the same API methods, it will go very smoothly. You can use external APIs as part of your application (such as payment processors) instead of writing your own. You can create a PHP application that interacts with a Python application that interacts with a Rails application, and everything will work, because they communicate with each other using the API.

    It's generally a good idea to try to keep each part of your application as independent as possible. The concept of SOA encourages you to think in terms of exactly what methods you want to expose to other parts of your application, and it will make your code better along the way. In addition, by assuming that each major component of your application is independent, you can also isolate problems much more easily and handle errors more intelligently.

    Using a service-oriented architecture for an entire application is like breaking down a giant, complex Ruby script into nifty classes and methods, only on a larger scale.

    One of the most famous cases of transition to a service-oriented architecture is Amazon.com. One day in 2002, Jeff Bezos bluntly stated that all work teams must move to SOA or be fired. Notorious blog post a Google employee, intended for internal purposes but accidentally exposed to the public, talked about the power of Amazon using SOA. This is a great read, so be sure to rate it, but the main theses of Bezos's letter are taken out in the following quotes from the post:

    1) All commands now provide their data and functionality through service interfaces.

    2) Teams must communicate with each other through these interfaces.

    3) Other forms of inter-process communication are prohibited: no direct links, no direct reading of data from another command, no shared memory models, no "backdoors" and the like. The only allowed way to communicate is to access the service interface over the network.

    4) It doesn't matter what technology they use. HTTP, Corba, Pubsub, native protocols - it doesn't matter. Bezos doesn't care.

    5) All service interfaces, without exception, must be initially designed with the ability to be controlled from the outside. That is, the team must plan and design to be able to provide an interface to developers outside the company. No exceptions.

    6) Anyone ignoring these requirements will be fired.

    SOA is serious business. Sure, there are a lot of problems that come up when using it - check out this post on Amazon's "lessons learned" - but it has an incredible amount of benefits.

    You probably won't worry too much about SOA while you're building "toy" applications for yourself, but this question will definitely come up when you start working for an IT company, so getting to know it is good practice.

    Your aim

    1. Read Section 7 of the Rails Guide to Controllers to learn about JSON and XML rendering.
    2. They're not required viewing (because they go a bit further than we're currently prepared for), but if you're interested, take a look at the Railscasts in the Additional Resources section at the bottom of the tutorial to learn more about the benefits of the API.

    Conclusion

    We will work more closely with your application as an API during the Javascript course. In this course, you will create several full-stack applications that use AJAX calls for a better user experience, which actually involves rendering XML or JSON data instead of a full HTML page. Then you'll create several single-page Javascript applications that rely on the API provided by your Rails application to get all the data they need from the database, and otherwise run on the client side (in the browser).

    The best way to deal with an API is to create and interact with it, which is what we will focus on in our projects.