May Eighteenth 2007 From: Bob Zale, President PowerBASIC, Inc. PowerBASIC Gazette #52 ====================== Subject: Use LoadLibrary API to avoid "MISSING EXPORT" Errors ============================================================= Have you visited our web site lately? Lots of great new information for PowerBASIC Programmers. We've redesigned the Download Section, so it's easy to find what you need. See Quick-Start tutorials, and much more! And the forums? Now more than 254,000 messages! More than a quarter-million messages from programmers just like you, on virtually every topic! Visit us right now at www.powerbasic.com -- and be sure to leave a comment in the PowerForums! So, what about "LoadLibrary"? Can it help you avoid "MISSING EXPORT" errors? Yes, it can! Just read on to get all the inside information. But first, would you indulge me? Would you help us spread the word about PowerBASIC? You know, you're the world's best PowerBASIC advertisement. And every time we add a PowerBASIC customer, it helps us all. Every new user is an investment in the future... with more users, we'll create better products, deliver them sooner, and keep the pricing right for you. How about it? Will you tell a friend about PowerBASIC? I hope so. We'll make it easy for you to "Share the Power"! {smile} Anyway, here's the plan... You give us a name (or a few names), and we'll send just one short, informative e-mail under your name and ours. We won't follow up. We'll never use their e-mail again, unless they choose to sign up. So, Share the Power! Just GoTo... http://www.powerbasic.com/bin/ps.exe You'll see the text of the message and add a contact name! The message is harmless, yet informative. And just think of the compilers we could build for you if everyone referred just one new PowerBASIC user! Subject: Use LoadLibrary API to avoid "MISSING EXPORT" Errors ============================================================= by Lance Edmonds Implicit linking ---------------- By default, implicit (Load-time) linking is used for every API Sub/Function declared in WIN32API.INC and the associated INC files supplied with PowerBASIC. Implicit linking is used whenever a DECLARE statement includes a LIB clause, and the named Sub/Function is referenced or used in the application. When an application is initially loaded, and before it starts being executed, Windows loads all of the DLLs that the application requires, as specified in the applications Import Table. However, if any of the DLLs are not present, Windows produces a missing DLL error, and the application is terminated before it is even started. This is the primary purpose of the LIB clause in the DECLARE statement. Also, Windows will match up the actual Sub/Function names in the Import Table with those in the Export Table of the associated DLL. The name specified in the Import Table is derived from the ALIAS clause of the DECLARE statement, or if an alias is not specified, the capitalized Sub/Function name is used. Windows requires that every single entry in the applications Import Table must be successfully matched to an exported Sub/Function in the named DLL. If not, the application is immediately terminated, and the user presented with a "Missing Export" error message. While implicit linking is by far the easiest way to call API functions, it presents a problem when an application calls API functions that are only present in some versions of Windows. Restricting the choice of platforms ----------------------------------- For applications using features specific to Win2000 and WinXP, the #OPTION VERSION5 metastatement can be used to limit the application to use on just those platforms. An attempt to run it on earlier platforms will produce a "new version of Windows required" error, rather than a cryptic "export" error message. However, sometimes applications are required to run on all versions of Windows, and could be required to take advantage of features on newer versions of Windows, but only if such features are available. Explicit Linking joins the fray ------------------------------- Since a Missing Export error is generated by Windows before the application even starts running, it is not possible to write a program, using implicit linking, which programmatically checks the Windows version to decide what it can call and what it must avoid. Therefore, to get the application to start running on a platform that may have either a missing DLL or a missing export function, we must abandon implicit linking of platform-specific Subs/Functions, and use explicit (run-time) linking instead. LIB-less DECLARE statements --------------------------- The first step in achieving explicit linking is to remove the LIB and ALIAS clauses from the DECLARE statement of the target API Subs and Functions. Next, we replace all direct calls to those Subs and Functions with a small piece of code that dynamically checks the availability of the API before calling it. To achieve the first part, editing the WIN32API.INC file may seem the easiest route, but that comes with a small cost -- any changes made to that file will be lost if you install an updated version of the file in the future. As an aside, .INC files are updated quite regularly, and the latest version can always be downloaded from http://www.powerbasic.com/files/pub/pbwin/win32api.zip The best solution is to copy the DECLARE statement from the include file, and paste it directly into your application code. Then the LIB clause can be removed. Finally, to avoid conflicts with the DECLARE statement in the INC file, the function name is modified slightly to make it unique. This can be done since this new DECLARE statement acts purely as a template for the code we'll be discussing below. For example, looking at the GetDiskFreeSpaceEx() API (which only became available in the OSR2 release of Windows 95), the original DECLARE looks like this: DECLARE FUNCTION GetDiskFreeSpaceEx LIB "KERNEL32.DLL" ALIAS _ "GetDiskFreeSpaceExA" (lpPathName AS ASCIIZ, _ lpFreeBytesAvailableToCaller AS QUAD, _ lpTotalNumberOfBytes AS QUAD, _ lpTotalNumberOfFreeBytes AS QUAD) AS LONG Using the above guidelines, our modified template DECLARE will look like this: DECLARE FUNCTION t_GetDiskFreeSpaceEx (lpPathName AS ASCIIZ, _ lpFreeBytesAvailableToCaller AS QUAD, _ lpTotalNumberOfBytes AS QUAD, _ lpTotalNumberOfFreeBytes AS QUAD) AS LONG We used the prefix "t_" here to indicate that it is intended as a template function declaration only. Dynamically checking the API exists ----------------------------------- Since we removed the LIB clause from the new DECLARE statement, we have to instruct Windows to try to open the DLL that we know may contain the target API. In the above case, this is KERNEL32.DLL, and we load it like this: LOCAL hLib AS DWORD hLib = LoadLibrary("KERNEL32.DLL") IF ISFALSE hLib THEN MSGBOX "KERNEL32.DLL cannot be found or loaded!" ELSE ' We have a handle to the DLL! END IF At this point, we will know if the DLL was loaded successfully or not. Naturally, if the DLL could not be loaded, we warn the user and bail out. A real application should handle this situation with more finesse, but this is suitable for our concept explanation. The next step is to query whether the name of the Sub/Function is present in the DLL. For this, we use the precise name given in the ALIAS clause, thus: LOCAL pFunc AS DWORD ' Capitalization is very important! pFunc = GetProcAddress(hLib, "GetDiskFreeSpaceExA") IF ISFALSE pFunc MSGBOX "KERNEL32.DLL does not contain ""GetDiskFreeSpaceExA""!" ELSE ' We have the address of the target Sub/Function END IF At this point, assuming the target Sub/Function is present (GetProcAddress returns a pointer to the Sub/Function, or zero if the function is not in the DLL), thus: LOCAL szPath AS ASCIIZ * %MAX_PATH, FreeBytesTo App&& LOCAL TotalBytes&&, TotalFreeBytes&&, Result& szPath = "C:\" CALL DWORD pFunc USING t_GetDiskFreeSpaceEx(szPath, _ FreeBytesToApp&&, TotalBytes&&, _ TotalFreeBytes&&) TO Result& And there we have it, one API function whose availability has been dynamically determined. Now that a pointer to the API has been obtained, it can be called again and again without going through the whole process again. However, this also means that we should clean up after ourselves, so as soon as the API is no longer required (or we terminate our application), we should release the library, thus: CALL FreeLibrary(hLib) A practical example ------------------- Of course, there are a few things that can be done to stream line this approach in a regular application where the API may need to be called multiple times. For example, loading the DLL and getting the address of the API could be wrapped into a Function that does the LoadLibrary and GetProcAddress calls only once for the duration of the application, thus reducing overhead. Another approach could be to create a general purpose function to deal with LoadLibrary and GetProcAddress procedures. For example: '------------------------------------------------------------------------- ' Explicit (Dynamic run-time) linking example for 32-bit PB/DLL and PB/CC ' By Lance C Edmonds, 2001. This code is released into the public domain. '------------------------------------------------------------------------- #COMPILE EXE #DIM ALL #INCLUDE "WIN32API.INC" '------------------------------------------------------------------------- ' The original DECLARE looks like this: ' DECLARE FUNCTION GetDiskFreeSpaceEx LIB "KERNEL32.DLL" ALIAS _ ' "GetDiskFreeSpaceExA" (lpPathName AS ASCIIZ, _ ' lpFreeBytesAvailableToCaller AS QUAD, _ ' lpTotalNumberOfBytes AS QUAD, lpTotalNumberOfFreeBytes AS QUAD) AS LONG ' Specify a modified "template DECLARE" for the CALL..DWORD statement: DECLARE FUNCTION t_GetDiskFreeSpaceEx (lpPathName AS ASCIIZ, _ lpFreeBytesAvailableToCaller AS QUAD, lpTotalNumberOfBytes AS QUAD, _ lpTotalNumberOfFreeBytes AS QUAD) AS LONG ' Declare our helper function DECLARE FUNCTION GetApiAddress(sDll AS ASCIIZ, _ sAPI AS ASCIIZ, BYVAL hLib AS LONG) AS DWORD '------------------------------------------------------------------------- FUNCTION PBMAIN LOCAL x AS LONG LOCAL pFunc AS DWORD LOCAL hLib AS DWORD LOCAL szPath AS ASCIIZ * %MAX_PATH LOCAL FreeBytesToApp AS QUAD LOCAL TotalBytes AS QUAD LOCAL TotalFreeBytes AS QUAD LOCAL Result AS LONG LOCAL Msg AS STRING LOCAL TmpStr AS STRING ' Load the library and get the address pFunc = GetApiAddress("KERNEL32.DLL", "GetDiskFreeSpaceExA", hLib) ' The API is available, lets use it IF pFunc THEN ' Loop though all drive letters FOR x = 1 TO 26 ' Call the API address directly, using our modified declare szPath = CHR$(x + 64) & ":" CALL DWORD pFunc USING t_GetDiskFreeSpaceEx(szPath, _ FreeBytesToApp, TotalBytes, TotalFreeBytes) TO Result ' If Result is non-zero, the API call produced useful results IF Result THEN TmpStr = SPACE$(20) RSET tmpStr = FORMAT$(FreeBytesToApp, "#,") Msg = Msg + TmpStr & " bytes available on Drive " _ & szPath & $CRLF END IF NEXT ' Display our results $IF %DEF(%PB_CC32) STDOUT Msg WAITKEY$ $ELSE MSGBOX Msg $ENDIF ELSE ' Display our results $IF %DEF(%PB_CC32) STDOUT "The API is not supported on this platform" WAITKEY$ $ELSE MSGBOX "The API is not supported on this platform" $ENDIF END IF ' At the app end, release the library, and clear pFunc & hLib ' Also the syntax: pFunc = GetApiAddress(BYVAL 0&, BYVAL 0&, hLib) pFunc = GetApiAddress("", "", hLib) END FUNCTION '------------------------------------------------------------------------- FUNCTION GetApiAddress(sDll AS ASCIIZ, sAPI AS ASCIIZ, _ BYVAL hLib AS LONG) AS DWORD ' If hLib is zero then DLL is loaded, otherwise the existing ' handle is used ' If sDLL is passed BYVAL 0& or simply "" (and hLib is non-zero), ' then the DLL is unloaded IF ISFALSE hLib THEN ' No library handle passed, so we'll try to load the DLL hLib = LoadLibrary(sDLL) ' Hmmm, the DLL is missing IF ISFALSE hLib THEN EXIT FUNCTION ELSEIF VARPTR(sDLL) = 0 OR LEN(sDLL) = 0 THEN ' Free the DLL CALL FreeLibrary(hLib) hLib = 0 EXIT FUNCTION END IF IF sAPI <> "" THEN FUNCTION = GetProcAddress(hLib, sAPI) END FUNCTION =================================================================== Time for a "Hard Break" {smile} =============================== Since I have your attention, can I take take a small commercial break? Just in case you haven't yet upgraded both compilers, now is the time! How can you join in the fun without the new versions? Get them now, while you can take advantage of Upgrade Pricing! PowerBASIC 8 for Windows is priced at $199, but the upgrade from PBWin 7 is just $99. PowerBASIC Console Compiler 4 is priced at $169, and the upgrade from PBCC 3 is just $89. PowerBASIC Forms, our visual designer, is $99, but the upgrade from version 1.0 is just $39. With prices like these, how can you go wrong? We don't believe in BloatWare, and we don't believe in $2,000 compilers. We just "Compile Without Compromise". You'll get Graphics... Lines, Arcs, Fills, Ellipses, Polygons, and more. Proportional text in any font, any size, any style, any color. Custom scaling systems, even with fractional floating point coordinates. Copy and stretch bitmaps, even to a printer page. Perhaps most important -- we'll extract Device-Independent Bitmaps you can manipulate yourself for incredible power and speed. Then there's USB printing. All the power of screen graphics transferred to the printer. Any font, any size, style, or color. Graphic primitives, Bitmaps, and more. Printer selection and setup dialogs. Everything you need to print your latest work of art, or your latest business form. And it's just as easy as the old LPRINT. You'll get graphics that are truly extensible. Using the Device Context (DC) from PowerBASIC, you can call the Windows API, your own custom code, even third-party code for special graphic functions. And with the new GRAPHIC GET/SET BITS, you can even manipulate the bitmap directly... each pixel is stored in succession as a long integer color value. You'll get new variable types. Just for that "special situation". BIT and SIGNED BIT variables in TYPES and UNIONS. Each may be from 1 to 31 bits wide, packed one after another. Perfect for bit coding, or even translation from C code. For "Thread Local Storage", you'll get the new THREADED variables... each thread gets a unique copy of your data. And FIELD variables? They work with the new FIELD statement, just like you had in DOS. A FIELD is a string, mapped to a specific section of a file buffer, or another string. They're allocated at run-time, so they can be changed as needed. Much like a TYPE that can be altered under program control. Best of all, a new internal design makes them fast, fast, fast! Pointers are great, but sometimes the syntax is daunting, particularly for very simple tasks. So we implemented Dynamic Pointers, using the original PEEK/POKE as a model: xx# = PEEK(DOUBLE,addr&) This retrieves the double precision value stored at addr&, assigning it to xx#. Very straightforward and it works for all data types. And unlike the old PEEK and POKE, it's just as fast as a pointer variable! Speaking of simplification, DIM, LOCAL, STATIC, GLOBAL, and RESET can use a list of variables, like: STATIC abc, bcd, xyz AS STRING. ARRAY ASSIGN lets you assign values to every element of an array in a single statement! More tools for error processing? RESUME lets you retry the statement which failed, while RESUME NEXT just skips to the statement which follows it. And, for a great debugging tool, ERL returns the line number which precedes the error. For PB/CC, you'll get new console functions: define a screeen buffer larger than the screen, then treat the console as a moveable view port. Move the console window on the desktop, get its size, even set the focus. END is back for DOS compatibility, while KEY and KEY$() assign strings to the function keys. Even INPUT is back with multiple variables. Just think how easy DOS conversion will be. PB/WINDOWS gets some specialties, too. Dialogs, and their controls, now have a PIXEL option -- just in case you want more precision than dialog units, while DIALOG SET CLIENT lets you resize to a specific client area size, regardless of styles. There's even a new GRAPHIC control for all your special designs. Both compilers offer even more... Multiple ALIAS declarations. DESKTOP functions to get all the sizes, #COMPILER to specify which compiler should be used. JOIN$, PARSE, and PARSE$ offer binary options for better, faster packing. LEFT$, MID$, and RIGHT$ support negative lengths to return LEN(x$)-n bytes. Then there's BGR and an ENVIRON statement, too. FILEATTR enumerates file numbers currently in use, while HI, LO, MAK, and BITS are greatly expanded. We even have NUL$, and SHELL with a HANDLES option. The PowerBASIC IDE (for both compilers) had a serious makeover. You'll really like the results. First, the editor has BLOCK INDENT/OUTDENT and BLOCK COMMENT/UNCOMMENT! Then add more undo buffer space, bookmarks in the editor, and source file templates for that tiresome boilerplate code. There's faster debug load/unload, and file save with tab compression for even smaller source files. The debugger and editor are unified, so you can set breakpoints in advance, even in include files. How many times have you wondered "Is there an array bounds error somewhere?" or "Where's that pointer error?" Well, here's how you find it! Just set the debugger "BREAK ON ERROR"! Your program runs normally, in the debugger, but halts as soon as any error is generated -- even if you have no error handlers installed. The new watch windows are moveable and resizable -- and all dialogs remember the last used size and location. Help files for add-ons are supported, and there's a direct link to Win32 API help, too. With all this, debugging might now be fun! More information? Sure, it's very simple. Just click to go to each or all of the PowerBASIC product pages... PB/CC 4.0: http://www.powerbasic.com/products/pbcc/ PB/WINDOWS 8.0: http://www.powerbasic.com/products/pbdll32/ PowerBASIC FORMS: http://www.powerbasic.com/products/pbforms/ PowerTREE: http://www.powerbasic.com/products/powertree/ PowerSHIRT 1.0: http://www.powerbasic.com/products/pbshirt/ SQL Tools: http://www.powerbasic.com/products/sqltools/ Console Tools: http://www.powerbasic.com/products/contools/ Graphics Tools: http://www.powerbasic.com/products/graftool/ PB/WIN 8 is attractively priced at $199, while PB/CC 4 is just $169. Upgrades from versions 7 and 3 are just $99 and $89 respectively. PowerBASIC Forms is priced at $99, while the upgrade to version 1.5 is $39. You can order by replying to this email. You can call us today at (800)780-7707 or (941)408-8700, place an e/order on our secure web site https://www.powerbasic.com/shop/ or even mail it in. But no matter what method you choose, please do it today with confidence. Every product PowerBASIC ships for physical delivery is offered with a money-back guarantee for a full 30 days from the transaction date. Best regards, Bob Zale, President PowerBASIC Inc. p.s. Don't forget PowerSHIRT version 1.0! The new PowerBASIC T-Shirt! It's a high quality, black T-Shirt, emblazoned with "PowerBASIC.COM", and a personal motto "I Compile Without Compromise". You couldn't be more stylish! The new PowerSHIRT 1.0 is available in sizes M/L/XL/XXL/XXXL, and priced at just $19.95. http://www.powerbasic.com/products/pbshirt/ =================================================================== PowerBASIC Price List ------------------------------------------------------------------- PB/CC Console Compiler 4.0 - Full Product $169.00 PB/CC Console Compiler 4.0 - Upgrade from vs 3 89.00 PB/CC Console Compiler 4.0 - Upgrade from vs 1-2 119.00 Add Printed Documentation 39.00 ------------------------------------------------------------------- PowerBASIC for Windows 8.0 (GUI) - Full Product $199.00 PowerBASIC for Windows 8.0 - Upgrade from ver 7 99.00 PowerBASIC for Windows 8.0 - Upgrade from prior versions 129.00 Add Printed Documentation 39.00 PowerBASIC FORMS Visual Designer fro PB/Win 99.00 ------------------------------------------------------------------- PowerSHIRT 1.0 T-Shirt (M/L/XL/XXL/XXXL) $19.95 ------------------------------------------------------------------- PowerBASIC for DOS 3.5 - Full Product $99.00 PowerBASIC for DOS 3.5 - Upgrade from prior versions 49.00 Add Printed Documentation (2 book set) 29.00 ------------------------------------------------------------------- PowerTree BTree Manager for DOS and Windows $99.00 PB/Vision for DOS 20.00 PB/Xtra III for DOS and Windows 49.00 ------------------------------------------------------------------- Console Tools Standard: 49.95 Console Tools Professional: 99.95 SQL Tools Standard Version: 99.95 SQL Tools Professional Version: 199.95 Graphics Tools Standard ver 2: 69.95 Graphics Tools Professional ver 2: 139.95 ------------------------------------------------------------------- Shipping/Handling costs: Software & Each Any Software 1 or 2 books Addl Book Email $6 N/A N/A UPS Ground/Mail US $10 $10 $8 Express 2-day US $16 $16 $8 Express 1-day US $28 $28 $8 Air Mail Canada/Mex $10 $16 $8 Air Mail Intl $16 $28 $12 DHL Express Intl $34 $44 $14 PowerSHIRT T-Shirts are shipped by postal mail for one shipping fee, regardless of quantity. North America: $6 Elsewhere: $12 ------------------------------------------------------------------- Order online at https://www.powerbasic.com/shop/ or just send an email with all pertinent information to sales@powerbasic.com We'll take it from there! ------------------------------------------------------------------- Most PowerBASIC products (those without printed books) can now be delivered by electronic mail. No wait for a package to arrive... No high shipping costs... For just $6 per order, no matter how many products, we'll deliver directly to your computer. If you're outside the U.S., savings might be greater. You won't pay taxes or duties to a freight company or postal service, because they aren't involved in the delivery. Check your tax code to be sure, but some countries charge no tax at all on transactions of this type. It could just be your lucky day! ==================================================================== Is your PowerBASIC Gazette Electronic Edition subscription coming to you at home or work? If you don't want to miss a single issue, why not subscribe from both email addresses? Send your subscription request to email@powerbasic.com and please include your name and all email addresses you'd like to add as well as your Zip or Postal Code. If you know someone else who would enjoy this newsletter please forward a copy to them so they can subscribe. ==================================================================== All contents Copyright (c) 2007 PowerBASIC Inc All Rights Reserved. PowerBASIC, PB/CC, PB/DLL, PowerBASIC Forms, and PowerTREE are trademarks of PowerBASIC Inc. Other names are trademarks or registered trademarks of their owners. ==================================================================== PowerBASIC Gazette - Electronic Edition Volume 1 - Issue 52 PowerBASIC, Inc. (800) 780-7707 Sales 2100 S. Tamiami Trail (941) 408-8700 Voice Venice, FL 34293 (941) 408-8820 Fax Visit us on the World Wide Web at www.powerbasic.com Email PowerBASIC Sales at sales@powerbasic.com This newsletter is only sent to email addresses in our subscription list. If you have received this newsletter by mistake or no longer wish to receive it, please send a simple unsubscribe request to support@powerbasic.com with your name and zip/postal code. This newsletter is best viewed with a fixed-width font. ====================================================================