TweetFollow Us on Twitter

Dec 01 REALbasic

Volume Number: 17 (2001)
Issue Number: 12
Column Tag: Declaring External Functions

by Joe Strout

Declaring External Functions in REALbasic

Harness the power of toolbox calls without resorting to C

Declaring Your Intentions

REALbasic’s extensive library floats on top of the sea of low-level functions that comprise the operating system like a cruise ship, protecting you from the dangers that lurk below. This is a two-sided coin; it frees you to concentrate on what you want to accomplish, rather than the details of how it’s done on this or that platform, but it also places limits on what you can do. When you hit one of those limits, and need to accomplish something not directly supported by the REALbasic library, then you’re going to have to get wet.

But even then, you have a choice. You could write a plug-in in C++, and link this plug-in into your REALbasic application [reference - Erik’s article on plugin dev]. That gives you full power to do pretty much anything, but it also requires quite a bit of learning, especially for developers not already familiar with C++. The other option is to use the Declare statement, which makes a function in REALbasic that links to a function in the operating system or a shared library. This is done entirely within REALbasic; no C++ is involved. To extend the ocean analogy a just bit further: writing a plug-in is like diving into the ocean untethered, while using a Declare is like hanging onto a life preserver on a rope — there are still restrictions on what you can do, but it’s also a lot easier to avoid drowning.

Though the Declare statement is used entirely within REALbasic, it’s still an advanced feature and a source of difficulty for many experienced REALbasic coders. This article will attempt to clarify and illustrate its use. After reading this article, you’ll be able to declare calls to classic MacOS, Carbon, or Win32 system routines, as well as to other shared libraries. This gives you the power to do a wide range of things not otherwise possible in REALbasic.

The Declare Statement: A Close Look

With no further ado, let’s jump right into an example. Listing 1 shows a REALbasic function that toggles the “modified” flag of a window. (Under Mac OS X, this sets or clears the little dot in the red Close widget in the title bar.) Since the functions involved are only available in MacOS 8.5 and higher, it also checks the system version before attempting to call them.

Listing 1: ToggleModified

ToggleModified

Toggle the “Modified” flag of the given REALbasic window.  This subroutine requires MacOS 8.5 or higher,
and does nothing under older versions of MacOS.  It will not compile as-is for 68k (these functions are 
not available to 68k apps).

Sub ToggleModified(win As Window)
   // Toggle the given window’s Modified flag
   Dim sysVersion, err As Integer

   #if TargetCarbon
   Declare Function IsWindowModified Lib “CarbonLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “CarbonLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #else
   Declare Function IsWindowModified Lib “WindowsLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “WindowsLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #endif

   // check the system version, since these functions are only available
   // in MacOS 8.5 and higher
   if System.Gestalt(“sysv”, sysVersion) and sysVersion >= 
            &h0850 then
      // system version is OK, so do the toggle
      err = SetWindowModified(win, not IsWindowModified(win))
    end if
  end if
end Sub

There are several things to notice in this example. First, if we want to compile for both “classic” Macintosh and for Carbon, we need two versions of each Declare — one linking against CarbonLib, and the other against the appropriate toolbox library (often InterfaceLib, but WindowsLib in this case). Next, built-in REALbasic functions generally do the right thing for any version of MacOS your code may be running on, but it can’t do so with a Declare; it’s your responsibility to make sure that the function you’re calling will be available. In this case, that means checking that the system version is at least 8.5. But once you’ve done all this, using the function you’ve declared is easy — just call it like any ordinary REALbasic method.

Let’s look at the Declare statements more closely. First, note that the declaration occurs within some other REALbasic method (subroutine or function). Just like a local variable, these declarations are local to where they’re declared; they will not be visible outside of this method. There is currently no way to make such a declaration global, though you could make a global wrapper function (as we’ve essentially done in this example).

The declaration always begins with the keyword “Declare” followed by either “Function” or “Sub”; REALbasic doesn’t actually care which you use, but by convention you use “Sub” when the C return type is void. Next is the name of the function, exactly as it appears in the external library. This is also the name used to invoke the function, unless you specify an “Alias” clause (which we’ll cover a bit later).

The next part of the declaration is the library name. This is the fragment name as it appears to the Code Fragment Manager, not the file name as it appears in the Finder; and unlike most words in REALbasic, this name is case sensitive. Finding this name is not hard, but we’ll come back to it a bit later.

Next come the function parameters, and finally the return type. This is where things get interesting. The documentation or header files on which you’re basing your Declare probably specify the parameter and return types with C syntax, rather than REALbasic. It may say something like “char *” or “Str255” or something unpalatable. What to do?

The Right Type for the Job

Most of the work in creating a Declare statement is in finding the right conversions between the C types you see in the headers and documentation, and the REALbasic types that serve the same purpose. Fortunately you have this article, which contains the very handy Table 1. This shows you some of the most common C types, and the corresponding REALbasic types you should use in a Declare statement.

Table 1: C types and REALbasic
equivalents for Declare statements

C data type Declare type Variable Type
Boolean Boolean Boolean
long Integer, OSType, Color Integer, String, Color
int
UInt32
SInt32
void*
Ptr
(etc.)
OSType OSType (parameters only) String
ResType
DescType
short Short Integer
unsigned short
UInt16
SInt16
float Single Single
double Double Double
Str255 PString String
Str63
Str31
unsigned char *
char * CString (parameters only) String
WindowPtr WindowPtr Window
WindowRef (parameters only)
void* Ptr MemoryBlock
Ptr
(or pointer to data structure)

The first column of this table is the data type the function is expecting, as you might see it in the documentation or the C header file. The middle column shows the corresponding types you could use in the Declare statement. Finally, the last column shows the “natural” REALbasic types which most closely match the declared types; these are what you would pass (or get as a result) when calling the function.

REALbasic uses the type you specify in the Declare to figure out how much data to push onto the call stack when making the call; so the most important thing is to get the size right. If the function is expecting any 4-byte value, you can probably declare this as type Integer. If you declare it as Short instead, REALbasic (when compiling for 68k processors) will push only two bytes, the function will try to grab four, and you’ll almost certainly crash.

Many of the type choices are very straightforward; if the function wants a float, declare it as a Single and pass it a number. If the function is expecting a Pascal string, declare it as type PString, and when you invoke the function, give it any ordinary string. REALbasic will automatically convert its internal string representation into a Str255 when calling the function (but note: if the function expects a shorter string than that, it’s up to you to make sure your string isn’t too long before making the call). Or if the return type is a Pascal string, the returned value will be automatically converted into a normal REALbasic string.

With other types, it’s not so clear cut. Suppose the function expects a pointer. Should you declare this as type Integer, or type Ptr? The answer depends on the details. If it’s a function that allocates some data structure, returns you a pointer to it, and you simply pass it back to other functions without trying to peek at the data, then an Integer will do fine. REALbasic integers are four bytes, and a pointer is four bytes, so treating the pointer as if it were just a number will make life easiest for you. Most modern Apple toolbox calls, such as those in Carbon or QuickDraw 3D, are of this sort, since the underlying data structures are opaque.

If, however, the function is expecting you to allocate storage for some data before calling the function, then a bit more work is required. In this case, declare the parameter as type Ptr. Allocate the memory in the form of a REALbasic MemoryBlock, created with the NewMemoryBlock function (and be sure to make it the right size!). If the function is expecting the data structure to be initialized with some values, you can use the MemoryBlock methods to stuff appropriate values into it. Then, pass this MemoryBlock to the function. From the function’s point of view, it just gets a pointer to some already-allocated memory. It may stuff values into this area, which you can later read by using those MemoryBlock methods again.

Calling a Spade a Spade

Your Declare statement must name the shared library which implements the function. In the case of a Windows DLL, you simply use the file name. In the Mac world, we don’t rely on file names, which are too frequently changed to be reliable. So you must use the fragment name as seen by the Code Fragment Manager (CFM). Most of the MacOS toolbox is in the fragment “InterfaceLib” (or “CarbonLib” for Carbon/OS X apps). But suppose you’re calling some other library — for example, the “Quesa” high-level 3D graphics library. The CFM name of this library is “QuickDraw™ 3D” (because it’s a binary-compatible replacement for Apple’s QuickDraw 3D technology). If you don’t happen to have me around to tell you handy facts like this, how would you know?

If you have ResEdit handy, it can give you the answer. Drop the library onto ResEdit and open the “cfrg” (code fragment) resource. This gives you a list of code fragments — usually there’s only one — and the name you’re looking for is labeled “Member Name”. Copy that value, and paste it into your Declare statement as the library name.

You can also use PEF Viewer, a free little utility from Apple. It also lists the code fragments it finds, and even lets you peek into the code, data, and loader information for each. (The last can be handy when Carbonizing — if the shared library you’re using imports any symbols from anywhere other than CarbonLib, you probably can’t use it in a Carbon app.) The CFM name of the library is given right at the top, next to the disclosure triangle.

If you don’t have either of these tools handy, you can use REALbasic itself. Just launch REALbasic, and drag the shared library file into the project window. It will appear there with its CFM name, rather than its file name. If you’re curious, what you have there is the alternative interface to importing functions from shared libraries — useful if you need a large number of functions from a single shared library, and you want them to be global rather than local. Since you’re using Declares rather than importing the library, you can just make note of the name, then delete the library from your project.

TBFinder to the Rescue

Writing Declares has always been a bit of an arcane art. Even armed with the knowledge in this article, you’ll find yourself wondering if it’s really worth the trouble when you just need a quick Mac toolbox call. Fortunately, there is a tool which can take much of the guess-work out of writing a toolbox-related Declare.

TBFinder is an open-source REALbasic project coordinated by Fabian Lidman. First, you point it to your copy of Universal Headers — i.e., Apple’s toolbox interface files, which you can download from Apple’s Development Kits web site. Then you can just find the toolbox call you need by searching or browsing, and it writes the Declare statement for you. There are some caveats, but in many cases it’s as simple as that.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 1. If you don’t know the name of the call you’re looking for, click “Browse” to get a nice hierarchical list of the functions defined in all the interface files, then just double-click the one you want. If you do know the name of the function you need, type it in the edit field; if you happen to know the header in which it’s declared, you can check the “Look first in:” checkbox and choose the appropriate header from the pop-up menu. Then click Search.


Figure 1. TBFinder search window.

The result, assuming the function actually exists, is a window like that shown in Figure 2.


Figure 2. TBFinder result window.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 2. The key bit is the second section, where the REALbasic syntax is shown. You can either use the Copy command to move this text to the clipboard, or drag the little text-clipping icon directly into REALbasic. The other icons in the window are actually buttons that do things when you click on them: the upper left icon by the header file name reveals that header file in the Finder; the text file icon by the original C declaration opens the header file (careful — Apple’s headers are set to open in Macintosh Programmer’s Workshop); and the globe at upper right does a web search for the function name in Apple’s documentation.

At the bottom of the window, you’ll notice a handy note telling you whether or not the function appears to be Carbon-compatible. If it is, you can use it in a Carbon application by simply changing the library name to “CarbonLib”. In the case of a classic application, TBFinder correctly identifies the library for you, using a big internal look-up table which I fervently hope was generated automatically by parsing the various system libraries.

TBFinder also supports one other feature of the Declare that I not previously mentioned: the “Inline68K” block. Many Mac toolbox calls on 68k machines are not shared library calls at all, but rather direct jumps to system traps. The C headers give the necessary machine language glue with the xWORDINLINE macros; TBFinder converts this to RB syntax, and even appends the extra glue needed in some cases by RB itself. (Be sure to get the latest version of TBFinder; version 2.0, included on the REALbasic CD, does not do this correctly.)

Finally, note that while TBFinder is invaluable, it isn’t infallible. In particular, it takes its best guess at the appropriate parameter conversions, but these often require human judgment (see Table 1 again). Always double-check the parameter types TBFinder has chosen, and consider whether there are other choices that make more sense for you.

Some Examples

Now that we’ve covered all the basics, let’s look at some real examples. We already saw one in Listing 1, used to toggle the “modified” flag on a window. Listing 2 shows another example, a call to the toolbox routine ObscureCursor, which hides the cursor until the mouse is moved. As this routine takes no parameters and returns no result, the declaration is very straightforward.

Listing 2: ObscureCursor

ObscureCursor
Hide the cursor until the mouse is moved (MacOS only).

Sub ObscureCursor()
   {
  // Declare it with a “Sys” prefix to avoid a name clash
   #if TargetCarbon
  Declare Sub SysObscureCursor Lib “CarbonLib” Alias 
            “ObscureCursor” ()
  #else
  Declare Sub SysObscureCursor Lib “InterfaceLib” Alias 
            “ObscureCursor” () Inline68K(“A856”)
  #endif
  
  // Call it!
  SysObscureCursor
end Sub

Note that in this example, I’ve chosen to name the REALbasic subroutine exactly like the toolbox routine. That means that when I declare the toolbox call within the subroutine, I’d have a name clash unless I use the “Alias” option. “Alias” allows me to declare the system routine as SysObscureCursor, avoiding thus avoiding the clash, and allowing all other users of this subroutine to simply call “ObscureCursor”.

Naturally, if you needed to use HideCursor/ShowCursor to hide the mouse even when it moves — say, because you’re making a game and have a custom cursor or no need for a cursor at all — then the declarations would be very similar. But let’s suppose you need to hide the cursor only within a rectangular region of the screen, perhaps because you’re doing some animation there, or simply because you want to demonstrate a Declare involving a MemoryBlock. The toolbox call for this is ShieldCursor. TBFinder gives the declaration as:

Declare Sub ShieldCursor Lib “InterfaceLib” (shieldRect as Ptr, offsetPt as Point) Inline68K(“A855”)

Apple’s documentation gives the first parameter as a Rect pointer, which TBFinder has rendered as type Ptr. From Table 1, you know that a MemoryBlock is needed for times like this. We use the MemoryBlock to manually construct a Rect structure, recalling that a Rect is just a set of four short integers representing top, left, bottom, and right

Dim m As MemoryBlock

m = NewMemoryBlock(8) // 8 bytes (four 2-byte ints)
m.Short(0) = r.top
m.Short(2) = r.left
m.Short(4) = r.top + r.height
m.Short(6) = r.left + r.width

We can then pass this as the first parameter to ShieldCursor. What about the second parameter, “offsetPt”? Again referring to Apple’s documentation, we find that this is the offset from the global coordinate system. In other words, it’s the top-left corner of the window containing the rectangle to which we’re referring. TBFinder has left this declared as “Point”, which is not a valid Declare parameter type. But since a Point is the same size as an Integer, we can fairly easily construct an integer that contains the information we need:

Dim point As Integer

// put pt.v into the high word, pt.h into the low word:
point = BitwiseAnd(self.top * 65536, &HFFFF0000)
point = BitwiseOr(point, BitwiseAnd(self.left, &H0000FFFF))

Then, we simply change the Declare statement so that the second parameter is of type Integer, and we’re ready to go. The full listing for the ShieldCursor function is shown in Listing 3.

Listing 3: ShieldCursor

ShieldCursor
Hide the cursor within the given rectangle control.

Sub ShieldCursor(r As RectControl)
     Declare Sub SysShieldCursor Lib “InterfaceLib” Alias “ShieldCursor” (shieldRect as Ptr, offsetPt 
     as Integer) Inline68K(“A855”)
  
  Dim m As MemoryBlock
  Dim point As Integer
  
  // construct the rect within which the cursor will be shielded
  m = NewMemoryBlock(8)  // 8 bytes (four 2-byte ints)
  m.Short(0) = r.top
  m.Short(2) = r.left
  m.Short(4) = r.top + r.height
  m.Short(6) = r.left + r.width
  
  // construct the point that converts global to local coordinates
  point = -self.top * 65536 - self.left   
                           // pt.v in the high word, pt.h in the low word
  
  // make the call
  SysShieldCursor m,point
End Sub

Let’s end with a Windows declaration. I know, this is a Mac programming magazine, but sometimes you need to deploy your app for that Other Platform — and the ability to do so easily is one of REALbasic’s strengths. However, with the Declare statement, you’re making a direct call to a toolbox or shared library routine; this is rarely portable. So the first thing you’ll need to do, is conditionalize your code so that Mac declares are included only in Mac builds, and Windows declares are included only in Windows builds. You can use either “#if TargetMacOS” or “#if TargetWin32” for this purpose.

Next, you’ll need information on the Windows API. Two good references are given at the end of this article.

Finally, you’ll need to construct the Declare statement by hand. TBFinder can’t help you here; but with this article on your shelf, you’re well-equipped for the task. So let’s dive into an example.

Windows applications can take command-line parameters, and these are often used to specify options or initial conditions for the application. REALbasic can get these one at a time via the OpenDocument event, but in many cases it’d be preferable to get the whole command line. A search on the MSDN Online Library quickly leads to the GetCommandLine function, declared in the documentation as:

LPTSTR GetCommandLine(VOID);

This function takes no parameters, making our lives easy in that respect. And the return type, it turns out, is a C string of either ASCII or UniCode characters. This is an important point: many Windows functions come in both ASCII and UniCode versions, as signified under “Requirements” with a note such as “Unicode: Implemented as Unicode and ANSI versions on all platforms.” When you see this, it means that the function name given is not the real function name — you need to append “A” for the ASCII version, or “W” for the UniCode version. So the actual function name, for our purposes, is GetCommandLineA.

The return type in this case is an ordinary C string. But, while REALbasic knows how to automatically convert its String type into a CString for use as a parameter, it can’t do the opposite conversion. So we need another approach. A C string is really just a pointer to some data, so we can declare the return type as “Ptr”, and use a MemoryBlock to get at the data. The result is shown in Listing 4.

Listing 4: GetCommandLine

GetCommandLine
Get the full Win32 command line, including executable name and parameters.

Function GetCommandLine()
  #If TargetWin32
  Dim m as MemoryBlock
  Declare Function GetCommandLineA Lib “KERNEL32.DLL” () as Ptr
  m = GetCommandLineA()
  Return m.cstring(0)   // get the C string to which m points
  #Else
  return “Command line available on Win32 only.”
  #EndIf
End Function

There is one other trick that can sometimes make Windows declares easier. If you can find a web site or other documentation with Declare statements for Visual Basic — such as the last reference given at the end of this article — then you can use those Declare statements in REALbasic almost verbatim. You’ll just need to make a few changes: the “ByVal” keyword can be omitted, and data types may have to be translated according to Table 1 (e.g., use “Integer” where VB says “Long”).

Conclusion

REALbasic tries to abstract away the details of the underlying operating system, so you don’t have to worry about whether you’re running on classic MacOS, OS X, or some flavor of Windows. But sometimes you need more control. You may need to talk directly with the OS, or you may need access to some other shared library (such as OpenGL, for example). In those cases, the Declare statement is invaluable.

The Declare statement allows you to do most of the same things you could do directly in C. There are some limitations; you can’t call a function that requires a callback, and you can’t directly invoke a function pointer (e.g., to call a mach-o entry point loaded via the CFBundle API). For those, you’ll still need to write a plug-in. But for most other needs, you can Declare your external calls, providing a neater, more integrated solution.

While Declare gives you nearly the power of C, it gives you nearly the complexity of C as well. Writing a Declare statement is error-prone, and if done incorrectly, can crash your program (and under Classic MacOS, the system as well). Deciphering the headers and documentation for the calls you’re making requires some understanding of how things work in C or Pascal. But, with the tips gained here and a bit of study and experimentation, there will soon be little you can’t do in REALbasic.

References

REALbasic Home Page
http://www.realsoftware.com/realbasic

TBFinder Home Page
http://homepage.mac.com/fabianl/development/

Apple’s Developer Documentation
http://developer.apple.com/techpubs/

Apple’s Software Development Kits
http://developer.apple.com/sdk/

Microsoft’s Official SDK Documentation
http://msdn.microsoft.com/library/

Unofficial Windows API Guide
http://www.vbapi.com/ref/index.html


Joe Strout is a neuroscientist-turned-software engineer currently working for REAL Software, Inc. You can reach him at joe@strout.net.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Is GO Gear the Pokemon GO map app you...
Now that we've settled into something of a Pokemon GO status quo, the number one desire of most players can best be summed out by modifying a quote from Rod Tidwell of Jerry Maguire: "Show me the Pokemon!" [Read more] | Read more »
Rodeo Stampede update: Mountains, new an...
The Savannah and Jungle were just the beginning in Rodeo Stampede. Get ready to head for the Mountains. I think I heard that in a beer ad once. [Read more] | Read more »
COSMOS RINGS (Games)
COSMOS RINGS 1.0.0 Device: iOS iPhone Category: Games Price: $5.99, Version: 1.0.0 (iTunes) Description: This game cannot be played without the Apple Watch.Released anniversary sale until August 31,2016 PST! A tragic tale of time's... | Read more »
How to get started selling on Mercari
As far as ecommerce has come over the last decade or so, there's still a tremendous opportunity to make it easier for people to buy and sell goods. That's especially true when it comes to shopping apps, which should only continue to increase in... | Read more »
Human Anatomy Atlas 2017 Edition - Compl...
Human Anatomy Atlas 2017 Edition - Complete 3D Human Body 1.0.24 Device: iOS iPhone Category: Medical Price: $24.99, Version: 1.0.24 (iTunes) Description: | Read more »
Heroes of Normandie (Games)
Heroes of Normandie 1.5 Device: iOS Universal Category: Games Price: $14.99, Version: 1.5 (iTunes) Description: The game does not support iPhone 4s and below | Read more »
Why you should never power up Pokemon in...
There's no question that candy is dandy in Pokemon GO. You need big quantities of it to evolve your Pokemon, and when combined with stardust, it can be used to power up your favorite pocket monsters as well, making them more formidable for the gym... | Read more »
Webzen launches 3D MMORPG MU Origin on i...
Mu Origin is featured time and time again at the very top of App Stores in China, and within the top five worldwide top-grossing charts on Google Play.Its popularity in Korea and China, featuring more than 120 registered players in China and 6... | Read more »
Severed (Games)
Severed 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: LAUNCH DISCOUNT ON NOW!! ENDS AUGUST 4! ==== Take control of a one-armed warrior named Sasha, wielding a living sword on her journey... | Read more »
CSR Racing 2: How to master Live Races
Getting better at racing the AI in CSR Racing 2 is a pretty straightforward process. You run enough races with a particular car to get its shift timing down, continually improve it by buying upgrades whenever you can, and add any fusion parts you... | Read more »

Price Scanner via MacPrices.net

Second-Quarter Tablet Shipments Fell 4.8% –...
The latest report from the global market research firm TrendForce finds that worldwide tablet shipments for this second quarter totaled 33.54 million units, representing a quarterly drop of 4.8% and... Read more
Global Smartphone Sales Volumes Mark Second S...
According to preliminary results from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, vendors shipped a total of 343.3 million smartphones worldwide in the second... Read more
Apple TVs on sale for $20-$40 off MSRP
Best Buy has 32GB and 64GB Apple TVs on sale for $20-$40 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only, in-store... Read more
Mac minis on sale for $50-$100 off MSRP
B&H Photo has Mac minis on sale for $50 off MSRP including free shipping plus NY sales tax only: - 1.4GHz Mac mini: $449 $50 off MSRP - 2.6GHz Mac mini: $649 $50 off MSRP - 2.8GHz Mac mini: $949... Read more
Clearance 2015 13-inch MacBook Airs available...
B&H Photo has clearance 2015 13″ MacBook Airs available for $300 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 1.6GHz/4GB/128GB MacBook Air (MJVE2LL/A): $799... Read more
Apple certified refurbished iPad mini 4s avai...
Apple has certified refurbished iPad mini 4s now available for up to $120 off the cost of new models. An Apple one-year warranty is included with each iPad, and shipping is free. The following models... Read more
Notebook Makers In No Rush To Adopt USB-C – R...
Digitimes’ Cage Chao and Joseph Tsai note that while the USB Type-C interface is enjoying growing popularity among smartphones and tablet makers, notebook and all-in-one (AIO) PC vendors (other than... Read more
iMacs on sale for up to $250 off MSRP
B&H Photo has 21″ and 27″ Apple iMacs on sale for up to $250 off MSRP including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2049 $250 off MSRP - 27″ 3.2GHz/1TB Fusion iMac 5K: $... Read more
12-inch 1.1GHz Retina MacBooks on sale for up...
Amazon has 2016 12″ 1.1GHz/256GB Retina MacBooks on sale for up to $100 off MSRP including free shipping: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100 off MSRP - 12″ 1.1GHz Silver Retina... Read more
Bare Bones Software Releases Free TextWrangle...
Bare Bones Software has announced the release and immediate availability of TextWrangler 5.5, a significant update to its powerful, free, general purpose text editor for Mac OS X. TextWrangler is a... Read more

Jobs Board

*Apple* Solutions Consultant - APPLE (United...
Job Summary As an Apple Solutions Consultant, you'll be the link between our future customers and our products. You'll showcase your entrepreneurial spirit as you Read more
Lead *Apple* Solutions Consultant - APPLE (...
Job Summary The Lead ASC is an Apple employee who serves as the Apple business manager and influencer across a number of Reseller locations. The Lead ASC's role Read more
*Apple* Retail - Multiple Positions, Charles...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions, Willow...
Job Description:SalesSpecialist - Retail Customer Service and SalesTransform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Evangelist - JAMF Software (United S...
The Apple Evangelist is responsible for building and cultivating strategic relationships with Apple 's small and mid-market business development field teams. This Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.