• Solutions
    • FERC XBRL Reporting
    • FDTA Financial Reporting
    • SEC Compliance
    • Windows Clipboard Management
    • Legato Scripting
  • Products
    • GoFiler Suite
    • XBRLworks
    • SEC Exhibit Explorer
    • SEC Extractor
    • Clipboard Scout
    • Legato
  • Education
    • Training
    • SEC and EDGAR Compliance
    • Legato Developers
  • Blog
  • Support
  • Skip to blog entries
  • Skip to archive page
  • Skip to right sidebar

Friday, November 02. 2018

LDC #109: Getting a Handle on Handles

A term often used in programming that sometimes leaves new developers flummoxed is the “handle”. Handles are a convenient way to identify complex objects like windows, files, etc. In this blog, we’re going to get a “handle” on handles by explaining what they are, how Legato creates and manages them, and what you need to know to properly use them.


Introduction


Handles are an abstract way to track and access complex objects within a part of a program, server or operating system. There isn’t really any magic behind them. They are simply a number representing something managed by an API.


Within a specific context, a handle will be a unique number. Internally, the code that employs the handle to provide a service will have a table or management scheme to hold information regarding operations associated with an object. For example, if we open a file within an operating system, the file management system will manage internal information about the device location of the file, cluster information, access privileges, current file position, and so on. The handle provides a method for the file system to pick up the file data and satisfy requests concerning the file. If the handle is never closed or destroyed, the API will never release the resources. This is what is known as a ‘leak’, which used to be a real problem for operating systems that resulted from crashed programs or sloppy programming. Fortunately, operating system developers eventually added protections such that each process’ handles are tracked and automatically closed even if you as the programmer don’t close them. This works for the most part and significantly improves system stability. Still, even with this safeguard, you can still leak resources within your program, which can lead to issues both for your code as well as the system.


Handle Management


Diagram of handle relations


Within Legato, handles are managed or unmanaged. For managed handles, which are also the majority of handles used by most programs, there is a composite handle manager that tracks each handle and all of its resources. As shown above, an action such as Open, will request a specific resource from a object processor. If that succeeds, it will register a handle and return that value. The value is then used to identify the resource for further operations. Under the hood, most object processors will in turn request system resources and have their own handles to such resources.


By default, when a handle data type is declared, the script engine will automatically close the handle when the variable pool in which it was created is destroyed or released. In each subroutine, local variables have their own variable pool. Thus, when you exit a function, the pool is released, and any variables declared as handle are closed. Each running script engine also has a global variable pool. Similar to a subroutine exiting, when the script engine shuts down, all global handles are closed. Note that when a handle is passed as a function parameter, it is considered unmanaged and not closed when the called function exits. Finally, when a script is shutdown, the handle manager is closed and all resources are released.


The auto close function for variables declared as handle relies on the variable value being intact. If a handle is not closed and the variable is reused, perhaps to open another object, the resource will only be released at the termination of the script engine.


To manually close a handle, the CloseHandle function can be used (shown below). The variable holding the handle does not need to be cleared, although it is good programming practice to do so when variables are reused or global. 


Unmanaged handles are typically resources such as windows.


Unmanaged handle relations


For those handles, the script engine provides no assistance in management. In most cases, an unmanaged handle is not managed by the script engine but rather is expected to be managed by the operating system, other service, and or the script itself. In some cases it may be up to the programmer to exercise good technique when using them. For example, when a window is closed, the operating system will automatically discard all resources associated with a window and the handle will become invalid.


Can Managed Handles Leak?


Absolutely. Consider a variable declared as a global variable, perhaps a Basic File Object. If the variable is set to a file object handle and later reused to open or create another file, the first handle will be lost. It will of course be released when the script terminates, but until then that file may be locked. The same thing can happen as a local variable, but it is less likely as a matter of practice. On the other hand, if a handle variable is reset without closing the associated object, access to that object will be lost. Therefore, it’s always advisable to manage handles correctly, with or without Legato’s help.


Handle Values


So, what is the actual handle value? For Legato, it is the address in memory of the associated object that controls the resource. Since two items cannot generally occupy the same location in memory, each handle is unique to that specific script. In addition, each open handle is placed in the management table to resolve the associated object for use. If the handle is not in the table, the requesting function will fail. Since handle values are unique to a script, you cannot take a handle value from one script and pass it to another script. Managed handle values will always be larger than 0x00010000 and never zero.


Unmanaged handles use different schemes for identification which may vary between different operating system versions. For example, window handles, file handles, and GDI objects will all have a range of values. Over the years I have tried to find an article on the logic for Windows SDK handle values, but, alas, there does not seem to be a comprehensive document describing them.


There are two constants defined for handles: NULL_HANDLE and INVALID_HANDLE_VALUE. For Legato handles, NULL_HANDLE or 0x00000000 simply indicates a variable that is devoid of a handle. Since variables initialize as zero, they are automatically null. The second value, INVALID_HANDLE_VALUE or -1, is used by the operating system. Some handles, such as Windows file handles, can be zero and still be a valid reference to a file. In that case, you should check the handle against INVALID_HANDLE_VALUE to determine if it’s invalid.


Legato has provisions for pseudo handles, that is, handle values that can be used by certain functions to reference specific items that are always open. The most notable is the SDK definition APP_LOG_HANDLE. Using this with the AddMessage function drives output to the application log, which is always open.


Finally, do not save handle values and use them in a different script session since each managed handle value is unique to the script in which it is running. For certain unmanaged handles such as windows, the handle value is global to the system and can be shared. Still, it’s always a good idea to check a handle whose origin isn’t necessarily known to make sure it’s valid.


Multiple Purpose Functions


Many Legato function groups allow for the use of multiple handle types. A perfect example is the AddMessage function. It allows the use of Basic File Objects, Pool Objects, Log Objects, and consoles. This function will also operate without a handle by directing output to the default log. Many functions that act on Edit Objects will also work with Mapped Text Objects. Similarly, Data View functions will work with a Data View Object or a unmanaged window handle for a specific view or tab.


If the handle type is incorrect or stale, the error code 6 (ERROR_INVALID_HANDLE) will be part of the returned formatted error code.


Why a Data Type


While a handle is a number, there really is no point in performing any level of math or logical operation on a handle. So, while the underlying type for a handle in an unsigned integer, and you can perform math on a dword or int, a handle is a handle. Adding 1 to a variable declared as handle will result in a run-time error in Legato. So the handle data type protects the variable from incorrect uses that may have resulted from a brain hiccup.


Legato has special typing matching for the literal values 0 and -1 for setting a handle value. Any other integer will fail with a run-time type mismatch. As such, only API functions can return handle values. To make a handle from an integer or string, the MakeHandle function can be used:


handle = MakeHandle ( string value | int value );


This is useful when a handle is returned from a function as a value in a string array. For example, when running certain menu functions, the window handles for the MDI and edit views are returned as strings as part of the result. Handle values can also be printed to strings, which is again useful for storing and passing to non-Legato functions.


Finally, at some point applications supporting Legato will be 64-bit. When that occurs, handles will automatically change to a wider type. That’s something to consider for later compatibility.


Handle Types


There are two broad types of handles: Legato object handles and external handles. Legato objects include things like Mapped Text, File, SGML or ODBC object handles. We’ve used a great many of these in the past on this blog. External handles are things like window handles.


Legato handles are used to control access to predefined types of objects, such as the Basic File or Mapped Text Objects. These handles are managed by the script engine, which means that they function in defined ways. They are also closed and released by the script engine upon a function’s return or the script’s termination, so an explicit statement to close a Legato handle is not necessary. As I said above, other handles for Windows items or program-defined objects may be employed, but these are not automatically “cleaned up” by the script engine. It is up to the programmer to properly create, maintain, and release non-Legato objects and their handles.


As a point of interest, a managed handle’s type can be retrieved using the GetHandleType function:


dword = GetHandleType ( handle value );


The following table enumerates the handle types for the base engine. Each extension object will have its own types.



 Name Bitwise Description 
 Types    
  SOT_LOCATION_MASK 0xFF000000 Location Mask 
  SOT_LOCAL 0x00000000 Local Object (0x00------) 
      (other types defined in SDK) 
 Base Object Types    
  SOT_BASIC_FILE_OBJECT 0x00000000 Basic File Object 
  SOT_MAPPED_TEXT_OBJECT 0x00000001 Mapped Text Object 
  SOT_EDIT_OBJECT 0x00000002 Edit Object 
  SOT_POOL_OBJECT 0x00000003 Pool Object 
  SOT_LOG_OBJECT 0x00000004 Log Object 
  SOT_WORD_PARSE_OBJECT 0x00000005 Word Parse Object 
  SOT_CSV_OBJECT 0x00000006 CSV Object 
  SOT_MD5_DIGEST_OBJECT 0x00000007 MD5 Digest Object 
  SOT_FOLDER_ENUM_OBJECT 0x00000008 Find File Object 
  SOT_FTP_OBJECT 0x00000009 FTP Communications Class 
  SOT_HTTP_OBJECT 0x0000000A HTTP Communications Class 
  SOT_ZIP_OBJECT 0x0000000B Compression Object 
  SOT_DATA_SHEET_OBJECT 0x0000000C Data Sheet Object 
  SOT_CLIPBOARD_OBJECT 0x0000000D Clipboard Object (base) 
  SOT_CONSOLE_WINDOW 0x0000000E Console (Synthetic, is Window Handle) 
  SOT_DATA_OBJECT 0x0000000F Data Object 

 


It may be necessary or desirable to access handles for Windows objects or other components. These might include handles to dialog boxes, files, external libraries, and other items. These are accessible through various Legato functions.


Closing Handles


As mentioned above, managed handles will close automatically in certain circumstances such as when a local variable containing the handle is discarded at the end of the subroutine. To manually close a handle, though, the CloseHandle function can be used:


int = CloseHandle ( handle handle );


The function takes a valid, open handle and performs the necessary cleanup. In the case of a basic file or mapped text object, the file is released and becomes available for use by other processes, users, or elsewhere within the program.


Closing an unmanaged handle does not do anything. However, while the chances are slim, an unmanaged handle value could match a managed handle, so it is not advisable to close unmanaged handles with the CloseHandle function.


There is also a deprecated CloseFile function that maps to essentially the same logic.


Additional Handle Functions


Some objects support the GetName function, which returns the name of the object related to the handle:


string = GetName ( handle handle );


A handle can be tested as a valid managed handle by using the IsValidHandle function:


boolean = IsValidHandle ( handle handle );


There is also a similar function for window handles, the IsWindowHandleValid function:


boolean = IsWindowHandleValid ( handle hWindow );


Window Handles and Views


When dealing with the application desktop and edit windows, window handles are frequently used. Window handles are always unmanaged.


Let’s look at an example of using window handles to access the contents of an edit view:


handle          hTextView, hEdit;
string          s1;
int             ix, size;
int             rc;

rc = RunMenuFunction("FILE_NEW_HTML");
if (IsError(rc)) { AddMessage("Error %08X on open", rc); exit; }
s1 = GetMenuFunctionResponse();

hTextView = MakeHandle(GetParameter(s1, "CodeViewWindow"));
if (IsError(hTextView)) { AddMessage("Failed to get Code View handle"); exit; }

hEdit = GetEditObject(hTextView);
if (IsError(hTextView)) { AddMessage("Failed to get Edit Object handle"); exit; }

AddMessage("HTML Code Dump:");
size = GetLineCount(hEdit);
while (ix < size) {
  AddMessage("  %2d : %s", ix, ReadLine(hEdit, ix));
  ix++;
  }

The function runs a menu command, FILE_NEW_HTML, which will create Page View and Code View windows on success. The GetMenuFunctionResponse function returns the response from the action. An example of the returned response string on success:


ClientWindow: 0x0191136C; FirstWindow: 0x01CA0C4E;
              PageViewWindow: 0x01CA0C4E; CodeViewWindow: 0x01740DB6


Here is an example of a function returning handle values as part of a string. Next we need to translate the string values into a specific handle:


hTextView = MakeHandle(GetParameter(s1, "CodeViewWindow"));


This compound statement will take the response, locate the “CodeViewWindow” properties, and translate its value into the window handle. We can then access data within the window using the GetEditObject function:


handle = GetEditObject ( handle hwTarget | int index | string name );


Now we have a handle to an Edit Object. We can then just dump out the window.


HTML Code Dump:
   0 : <HTML>
   1 : <HEAD>
   2 :      <TITLE></TITLE>
   3 : </HEAD>
   4 : <BODY STYLE="font: 10pt Times New Roman, Times, Serif">
   5 : 
   6 : <P STYLE="margin: 0"> </P>
   7 : 
   8 : </BODY>
   9 : </HTML>

In the example, there are two handles used: an unmanaged window handle and a managed Edit Object. In addition, the returned response has string versions of three other window handles.


Conclusion


I always like to understand what is going on “under the hood”. I hope this blog helps to uncover some of the mystery behind handles. While writing this blog it gave me an opportunity to perform a little stress testing and also resulted in a few ideas about improving handle management. As a result, the next release of Legato will contain two new handle related functions: EnumerateHandles and GetHandleCount. These should help programmers inspect open handles, improve performance, and track down certain resource leaks. At any rate, understanding handles and how Legato manages them is important to any programmer working in an environment with multiple objects and windows to control and manipulate. Getting a “handle” on the handles is vital in making sure your scripts run smoothly.


 


Scott Theis is the President of Novaworks and the principal developer of the Legato scripting language. He has extensive expertise with EDGAR, HTML, XBRL, and other programming languages.

Additional Resources

Novaworks’ Legato Resources

Legato Script Developers LinkedIn Group

Primer: An Introduction to Legato 



Posted by
Scott Theis
in Development at 15:49
Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)
No comments
The author does not allow comments to this entry

Quicksearch

Categories

  • XML Accounting
  • XML AICPA News
  • XML FASB News
  • XML GASB News
  • XML IASB News
  • XML Development
  • XML Events
  • XML FERC
  • XML eForms News
  • XML FERC Filing Help
  • XML Filing Technology
  • XML Information Technology
  • XML Investor Education
  • XML MSRB
  • XML EMMA News
  • XML FDTA
  • XML MSRB Filing Help
  • XML Novaworks News
  • XML GoFiler Online Updates
  • XML GoFiler Updates
  • XML XBRLworks Updates
  • XML SEC
  • XML Corporation Finance
  • XML DERA
  • XML EDGAR News
  • XML Investment Management
  • XML SEC Filing Help
  • XML XBRL
  • XML Data Quality Committee
  • XML GRIP Taxonomy
  • XML IFRS Taxonomy
  • XML US GAAP Taxonomy

Calendar

Back May '25 Forward
Mo Tu We Th Fr Sa Su
Sunday, May 18. 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Feeds

  • XML
Sign Up Now
Get SEC news articles and blog posts delivered monthly to your inbox!
Based on the s9y Bulletproof template framework

Compliance

  • FERC
  • EDGAR
  • EMMA

Software

  • GoFiler Suite
  • SEC Exhibit Explorer
  • SEC Extractor
  • XBRLworks
  • Legato Scripting

Company

  • About Novaworks
  • News
  • Site Map
  • Support

Follow Us:

  • LinkedIn
  • YouTube
  • RSS
  • Newsletter
  • © 2024 Novaworks, LLC
  • Privacy
  • Terms of Use
  • Trademarks and Patents
  • Contact Us