Tuesday, January 12, 2021

TeamViewer Overview

At the library where I work, we recently purchased a paid TeamViewer license. This allows us to conduct remote computer classes and provide socially distanced patron computer support during the pandemic. This blog post contains some of the things I have learned about TeamViewer during this process.

What is it?

TeamViewer is a remote access application which allows you to view the screen of another computer over the internet, as well as control the mouse cursor and keyboard. In this way, it is like Remote Desktop, but the similarity ends there. TeamViewer is a "zero config" service, which means it does not require the complicated network setup that Remote Desktop requires to be functional over the internet. To make this possible, all traffic is routed over port 80 through TeamViewer’s servers. The data is protected with end-to-end encryption, so nobody, not even the TeamViewer server admins, can eavesdrop on your session.

Also unlike Remote Desktop, it does not interact with the Windows login system. With Remote Desktop, you must supply the login credentials of a Windows user account to initiate a connection. But with TeamViewer, you directly see what is being displayed on the remote computer’s screen, as if you were sitting in front of it. For example, if you logout of the current Windows user account while using TeamViewer, you are shown the Windows login screen. Doing this whist using Remote Desktop would cause your connection to be terminated.

Instead of using IP addresses to connect to remote computers, TeamViewer assigns each computer a nine-digit, globally unique ID. This number never changes, even if you uninstall and reinstall TeamViewer (which makes me wonder if the ID is stored in the registry or if it is associated with your IP or MAC address). It also gives you a random, 6-character password, which the person connecting to your computer must know in order to connect. This password changes every time you open TeamViewer, which prevents someone who connected to you in the past from connecting again without your permission.

License

TeamViewer’s license states that you may use it for free so long as you are only using it for personal use. What is "personal use" exactly? If you’re using it to connect to a friend or family member’s home computer, that’s considered personal use. The moment you use it to connect to a computer at work or to a server, you are expected to purchase a license. If you don’t have a paid license and TeamViewer’s algorithms think that you are using it non-personal purposes, it will put a block on your computer’s ID, which prevents you from initiating and receiving connections. How it makes this determination, I don’t know. But if you think you have been wrongly accused, there are ways to submit a request to have them unblock you.

One thing that is restrictive about the paid plans is that it heavily limits how many computers can have active connections open at time. TeamViewer calls these "channels". A channel is created when a computer initiates a connection to one or more computers. For example, if Computer A remotes into Computer B, that’s considered one channel. A single channel can include multiple connections. For example, if Computer A remotes into Computers B, C, and D at the same time, that’s still a single channel.

The least expensive paid plan only allows a maximum of one channel. So, if you install TeamViewer on two computers, only one of those computers can initiate remote connections at a time (both computers can still receive incoming connections, it’s just that only one computer at a time can create outgoing connections). If another computer associated with your license has a channel open and you try to create a new channel by connecting to a remote computer, you will get an error message that blocks you from doing so.

The more expensive plans allow you to add more channels, and they come at a hefty price tag. We decided to purchase 1 additional channel, which would have costed us an additional $778/year. But because the library is a non-profit organization, we were able to obtain a 60% discount through TechSoup, which is a website that sells software at reduced prices to non-profits.

Variants of the software

There are three different variants of the TeamViewer software.

TeamViewer: Listed at the top of the download page on their website, this is the full-featured software application. With it, you can both connect to other computers and have other computers connect to you.

TeamViewer QuickSupport: This is a good choice for when you want to do a one-off computer support session with someone who is sitting at their computer. It allows other computers to connect to you, but does not give you the ability to connect to other computers. One nice thing about this application is that it does not actually install anything onto the computer—it’s just an EXE file the user downloads and runs. With a paid TeamViewer plan, you can customize the way the QuickSupport window looks, which is useful for displaying your business’s logo and branding.

TeamViewer Host: This is the best choice for when you need remote access to computers that are under your control (as opposed to the computers of random people on the internet). Like QuickSupport, it only allows incoming connections. But unlike QuickSupport, it installs software onto the computer, which automatically launches when Windows boots. If you’re installing this on a server, you’ll want to enable unattended access by assigning it a password that never changes.

Other features

Integrated voice/video chat: Talk with the person on the other end directly through TeamViewer without needing to maintain a second line of communication (e.g. phone call or VoIP call).  In my research, I have not been able to find any other remote access software product that has this capability. The audio quality is fine, and I’ve never had problems understanding people.

Clipboard syncing: TeamViewer supports seamless copy and paste between your local system and the remote computer. Not all remote access software supports this.

File transfer: There are several ways TeamViewer allows you to copy files between computers. Note that the transfer speed is quite slow (seems to be capped around 1 Mbps), so it’s not great for large files.

  • File browser: This is similar to an FTP client in that it allows you to browse the remote computer’s entire file system (or, at least, the folders that the remote user has access to) and download any files you want. You can also upload files to any location of your choosing.
  • File box: Allows you to upload individual files to a drop box, which the person on the other end then downloads from the drop box.
  • Clipboard transfer: Just like you can "copy and paste" files in File Explorer to make copies of file, you can do the same with TeamViewer to transfer a file to the remote computer.

Multiple monitor support: If the computer you are connecting to has multiple monitors attached to it, TeamViewer allows you to switch between them with ease or display them all at once.

My Verdict

My two main criticisms are the channel limits and price. I feel that channel limits can be very restrictive when you are working in a team, and the software seems expensive compared to alternatives.

But on the flipside, you definitely get what you pay for. Think of TeamViewer as the iPhone of the remote desktop world. The service is reliable, and the software is very easy to use. It was the only remote access software I could find that had integrated voice chat, which is feature that we needed to have. The company is based in Germany, a country that is subject to strict European privacy laws such as the GDPR, which is reassuring from a security and privacy standpoint. Oh, and did I mention the user interface has a dark theme? ;-)

If you’re an IT technician that just needs remote access to a handful of machines, there are other less expensive solutions out there. But for everyone else, TeamViewer provides a reliable, user-friendly solution that non-computer professionals can use with relative ease.

Wednesday, May 27, 2020

8 Tips for using Excel as a drawing tool

Over the past several months, I have been playing a factory-building game called Satisfactory. The game involves a lot of number crunching for calculating the input and output rates of the machines. You also have to think about how to optimally arrange the machines and conveyor belts in physical space on the factory floor. Because of this, I've found it to be much easier to design my factories outside of the game so that my in-game time can be spent actually building them!

I had decided to use Microsoft Excel to create these factory designs since I was already familiar with Microsoft Office's shape drawing and image editing tools. For the past ten months, I've been sharing many of my designs with the community via Reddit and Google Drive (I've built up a bit of a reputation for being the "floor plan guy"). I've become quite adept at using Excel for this purpose, so I thought I would share some of the things I've learned.

1. Snap to Grid

The "Snap to Grid" setting is crucial for helping to keep your shapes aligned with the worksheet grid lines.

To toggle it, go to: [ Page Layout tab > Arrange group > Align button > Snap to Grid]

2. Select Objects

The Select Objects tool allows you to select multiple objects by clicking and dragging to draw a selection box. Any object that is fully inside of the selection box will be selected. Objects that are only partially inside the box will not be selected. While this tool is activated, only objects can be selected--the cells inside of the actual spreadsheet cannot be selected.

To toggle it, go to: [ Home tab > Editing group > Find and Select button > Select Objects ]

3. Quick Access Toolbar

Located in the upper-left corner of the window, the Quick Access Toolbar serves as a holding place for your personal collection of favorite commands. Any button that's on the ribbon can be added to this toolbar. I've added buttons for "Snap to Grid" and "Select Objects" because I use these commands so much.

To customize the Quick Access Toolbar, click on the arrow on the right side of the toolbar and click "More Commands".

4. Grouping

Sometimes, you need to create multiple objects to achieve a specific goal. For example, to create the "clipboard" graphic pictured below, I needed:
  • An image for the "board" part of the clipboard
  • An image for the "clip" part of the clipboard
  • An image for the pencil
  • A textbox for the paper

To help manage complex collections of objects like this, you can group them together. Grouping allows multiple objects to be treated as a single object, allowing you to move and resize them as a whole. Even though they are grouped, you can still manipulate each individual "sub" object by clicking on them to select them.

To group a collection of objects, select them all by holding down the Shift key and clicking on each object (or by using the Select Objects tool, described above). Then, go to: [ Page Layout tab > Arrange group > Group button > Group ]. They can be ungrouped by selecting "Ungroup" from the same menu.

5. Nudging objects with the arrow keys

It can be difficult to precisely position an object using just the mouse. The arrow keys on the keyboard can be used to "nudge" an object in a particular direction. Tapping an arrow key will move the object one pixel at a time. Or, if Snap to Grid is enabled, the object will move one column/row at a time.

6. Zoom level issues

My experience has been that the sizes and positions of objects change slightly when you adjust the zoom level. To keep everything precise, I find it helpful to only move and resize objects at a specific zoom level. The zoom level that I work at is 100%.

It's also useful to note that the way Excel renders text can change when you adjust the zoom level. For example, you may have a textbox that wraps in a certain way at one zoom level, and then wraps differently at another zoom level. For example, the images below show the same textbox at two different zoom levels. Notice how the word wrapping changes starting with line 3.



To adjust the zoom level, use the slider in the bottom-right corner of the window or roll the mouse scroll wheel while holding Ctrl.

7. Paste as image

When an object, or group of objects, is copied to the clipboard, Excel will automatically generate a paste-able image when you paste into any image editing program, such as Photoshop or Paint. It even takes transparency into account! For example, if you copy a textbox that has a drop shadow, the drop shadow will render as semi-transparent pixels. This is useful for generating images out of the objects you've created in Excel (as I have done with my factory floor plans).

8. Image quality settings

By default, when you save an Excel file, Excel compresses all images to reduce the size of the file. For example, if you've inserted an image that's 800 pixels wide, and then resized it to be 400 pixels wide, Excel will down-sample the image to 400 pixels. The downside to this is that, if you later decide to make the image larger, it will look distorted because you've lost the original, 800 pixel version of the image.

To force Excel to preserve the original quality of all images, follow the steps below. These settings are applied only to the spreadsheet file you currently have open (they are not global). And if your spreadsheet already has images in it, you'll have to re-insert them to restore their original sizes.
  1. Go to [ File > Options > Advanced ].
  2. Scroll down to the "Image Size and Quality" section.
  3. Click the "Do not compress images in file" checkbox.
  4. Change "Default resolution" to "High fidelity".

Conclusion

I hope that my list of tips help you to "excel" at Excel! Happy spreadsheeting!

Sunday, April 12, 2020

Fixing Swing's JOptionPane class

I maintain a handful of client-side Java programs that use Java's older GUI API called Swing. One thing that I have found awkward about Swing is the way dialog boxes are handled.

Swing provides a class called JOptionPane for creating simple dialog boxes. It alleviates the need to manually code your own, which saves a lot of time. All you have to do it make a single method call and your dialog appears.

While the class works just fine, some of its methods have up to eight parameters. When a method has more than three or four parameters, it makes it hard for someone who is not already familiar with the API to tell what the code does at a glance.

For example, the code below has a total of eight parameters. Unless you are already familiar with this method's signature, you probably don't have a clue as to what half of these parameters do.
int choice = JOptionPane.showOptionDialog(
    this,
    "The password you have chosen may not be secure.\n\n" +
      "Please keep the following guidelines in mind:\n\n" +
      "1. Eight or more characters long.\n" +
      "2. Is not a word from the dictionary." +
      "3. Completely different from all your other passwords.\n\n" +
      "Would you like to create a better password or continue?",
    "Security warning",
    JOptionPane.YES_NO_OPTION,
    JOptionPane.WARNING_MESSAGE,
    null,
    new Object[]{"Pick a new password", "Continue"},
    "Pick a new password"
);
One thing you could do to make the code more readable is assign each parameter to a variable with a descriptive name. This allows a programmer who is not familiar with the method to see at a glance what each parameter is for.
Component parentWindow = this;
String text = 
    "The password you have chosen may not be secure.\n\n" +
    "Please keep the following guidelines in mind:\n\n" +
    "1. Eight or more characters long.\n" +
    "2. Is not a word from the dictionary." +
    "3. Completely different from all your other passwords.\n\n" +
    "Would you like to create a better password or continue?";
String title = "Security warning";
int buttons = JOptionPane.YES_NO_OPTION;
int messageType = JOptionPane.WARNING_MESSAGE
Icon icon = null; 
Object[] buttonLabels = new Object[]{"Pick a new password", "Continue"}
Object defaultButton = "Pick a new password";

int choice = JOptionPane.showOptionDialog(parentWindow, text, title, buttons, messageType, icon, buttonLabels, defaultButton);
However, there are still a some problems with this:
  1. Variable name conflicts: The variable names could be confused with other variable names in the same scope. For instance, the name "text" is fairly generic, so it's not unlikely that another variable a dozen or so lines down could have the same name.
  2. Parameters are ordered: It is very easy for the programmer to get the parameter order wrong without the compiler noticing. For example, if the "buttons" and "messageType" variables were swapped, the compiler wouldn't notice. The programmer likely wouldn't notice either because they are next to each other in the method signature.
  3. Null parameters: One of the parameters is null. The code would be more concise if we could just leave it out, but the JOptionPane class does not provide an appropriate method signature for that.
  4. Misleading code formatting (1): The way the message text string is formatted in the code makes it look like each line will end with a single line break. However, some lines end in two line breaks.
  5. Misleading code formatting (2): Each line of the message appears on its own line in the code. This makes it much easier to read! However, you may not have noticed that the fourth line is missing a line break. Because of the way the code is formatted, an error like this can easily be overlooked.
  6. Neglecting native system settings: The code may not be using the native system's preferred line break sequence. Appending "System.lineSeparator()" (or even a concisely-named "newline" variable) onto each line would be technically correct, but it would make the code less consice.
  7. Duplicate data: One of the button labels is duplicated.
  8. Ambiguous types: Defining button labels as Objects will lead to problems only detectable at runtime if an Object that JOptionPane does not accept is passed into the method (also, it's just weird!).
To resolve these issues, I designed a class that acts as a fluent wrapper around JOptionPane. Using this wrapper class, the above code would look like this:
int choice = DialogBuilder.warning()
    .parent(this)
    .title("Security warning")
    .text(
        "The password you have chosen may not be secure.",
        "",
        "Please keep the following guidelines in mind:",
        "",
        "1. Eight or more characters long.",
        "2. Is not a word from the dictionary.",
        "3. Completely different from all your other passwords.",
        "", 
        "Would you like to create a better password or continue?")
    .buttons(JOptionPane.YES_NO_OPTION, "*Pick a new password", "Continue")
.show();
The above problems are addressed as follows:
  1. Variable name conflicts: No variables are needed because the method name provides context. 
  2. Parameters are ordered: Each parameter is replaced by a method call. Methods can be called in any order.
  3. Null parameters: The class internally provides a default value for every optional parameter. The method call for this null parameter can be left out, making the code more concise.
  4. Misleading code formatting (1): The "text()" method takes a single vararg parameter, where each parameter is a line of text. If a line ends in two line breaks, an empty string is provided, making it clearer that an empty line will appear on the dialog box.
  5. Misleading code formatting (2): Line breaks are automatically added by the "text()" method.
  6. Neglecting native system settings: The "text()" method uses the native system's preferred line break sequence.
  7. Duplicate data: The default button is identified by an asterisk character, which is removed when the button label is passed into the JOptionPane class. This is not as obvious as I would like it to be, as it requires detailed reading of the Javadocs, but it is very concise.
  8. Ambiguous types: The button labels are defined as Strings in the "buttons()" method, not Objects. This means the compiler can prevent invalid objects from being passed in. 
The full class can be viewed here: https://github.com/mangstadt/emc-shopkeeper/blob/master/src/main/java/emcshop/gui/DialogBuilder.java

Thursday, March 26, 2020

Renewing my CompTIA A+ certification


CompTIA is an organization that administers certification exams for IT professionals. I took and passed their entry-level exam, CompTIA A+, three years ago. The exam tests your knowledge of broad IT-related topics, such as internal hardware, cabling, troubleshooting, operating systems, and much much (MUCH) more.

I wrote a handful of blog posts about some of these topics while I was studying for the exam if you want to get a feel for what they test you on. You can also get the exam objects from their website (the form asks your for your email, but you don't have to give a real one).

Having to pay over $400 to take a test (actually, it's two separate tests) that I might fail was stressful to me, so I'm glad that I passed it! The exam expires after three years, so since I took it three years ago, I needed to renew it. There are several ways they allow you to renew your certification. I chose to enroll in their CertMaster CE training course, which renews your certification instantly upon completion. It's all done online and costs $129.

The way it works is you read through the content it gives you, which is organized based on the exam objectives of the exam you are renewing. At the end of each section, it gives you a 20-30 question multiple choice quiz. When you complete the quiz, it shows you which questions you got wrong, but doesn't tell you the right answer. You can "reset the assessment", which resets the quiz and lets you take it again.

You can give it a date for when you want to complete the training by, and it will recommend to you how much content ("knowledge points") you have to study each day in order to finish by that date. As you progress through each section, you earn knowledge points. It tells you how many knowledge points are in each section so you can get a feel for how long a section is.

It also has various "achievements" that you can earn to help motivate you. For example, there's one for getting 100% on a quiz and one for getting twenty consecutive questions correct. My left-brain found some of them to be a little patronizing (for example, resetting a quiz gives you an achievement), but it was nice to have that extra bit of encouragement.

I found the user interface of this web app to be very clean and intuitive. It is also very responsive, which leads me to believe that it's heavily JavaScript-based and doing a lot of caching in the background. For example, going back and forth between the pages in a lesson is instantaneous, as is flipping through the questions in a quiz.

I also loved the graphics they provided for showing what the different cable connectors look like (VGA, DisplayPort, USB, etc). They all have a consistent aesthetic to them, which made the learning process that much easier. I would post one here as an example, but I don't want to violate any copyrights.

In order to renew your certification, you have to get a 100% on each quiz. Once you do that, your certification is automatically renewed. You literally don't have to do anything else. I got an email no more than a day or two after I completed all the quizzes saying that my certification was renewed. My new expiration date was based upon my current expiration date, not the date I completed the CertMaster training. I appreciate that because I completed the training several months early!

Tuesday, March 24, 2020

Workaround implemented for file timestamp issue

A few months ago, I wrote a blog post about a problem related to the date modified timestamp on a database file not updating immediately after the file was modified. This was causing a problem with an application I wrote that relied on this timestamp to update the data it displayed on the screen to the end user. It would take around ten seconds for the timestamp to update, which meant that the user had to wait that amount of time before the most recent information was displayed to them.

While I did discover a clue as to why this was happening to some files and not others, I wasn't able to fix the core issue itself--the delayed updating of the date modified timestamp. However, I implemented a workaround that not only solved the problem of the end user getting delayed updates, but also improved the integrity of the file (an Access database file).

The workaround involved creating a lock file whenever a user writes to the database. When the user is done writing to the database, the application deletes the lock file. The application monitors for this file deletion event and refreshes itself whenever a deletion is detected. There are no delays in the file system reporting when a file is deleted, which means the user's screen updates instantaneously whenever the database is updated.

I also use the lock file to improve the integrity of the database file. If the user tries to write to the database, but the application detects the presence of the lock file, it means another user is currently writing to the database. If this happens, the application waits until the lock file is deleted before creating its own lock file and executing its own writes. This prevents multiple users from writing to the database simultaneously and potentially corrupting the database.

There is a second kind of lock file which the application also makes use of. If the file is opened in Microsoft Access, Access creates its own lock file. My application monitors for the presence of this lock file. If the user tries to write to the database while the Access lock file exists, the application cancels the write operation and displays an error message. The error message informs the user that the database is open in Access and that it cannot write to the database while Access is open. This also helps to prevent file corruption.

Similarly to the lock file that the application creates when the user writes to the database, the application uses the Access lock file to detect changes to the database. When the Access lock file is deleted, it not only means that Access has been closed, but that changes could have been made to the database while it was open. So, when this lock file is deleted, the application refreshes itself and updates the on-screen information for the end user.

This workaround has been in production for 1-2 months and has been working quite well.

Wednesday, January 22, 2020

PHP, XML, and character encodings

The library where I work subscribes to an online service that keeps track of the library's ongoing public events. We link to it from our website so that patrons can discover the various programs the library has to offer.

I wrote a WordPress plugin that posts a listing of these events on the library's events page using the RSS feed the service provides. I just noticed today that a few of the event titles had empty, square boxes in them. When you see this, it usually means there is a character encoding problem, which means that it does not recognize a particular letter or symbol.


To make page loads more performant, my plugin caches the RSS file it downloads so that it does not have to query the event service every time someone loads the events page on the website. I opened the cached RSS file to see what might be causing the problem. The event in question had curly quotes (also called smart quotes) in its title. My experience has been that curly quotes frequently cause problems when people try to use them on websites, so I wasn't surprised to see this.


(table from computerhope.org)

You can't type a curly quote on the keyboard. At least, not directly. In my experience, they usually appear when someone copies and pastes something from Microsoft Word because Word will automatically insert them into your document as you're typing to make your document look more aesthetically pleasing.

It turned out that the problem was with the RSS data's character encoding. An RSS file is just XML, and every XML file has an "encoding" attribute at the top.

<?xml version="1.0" encoding="UTF-8" ?>

This tells the program that parses the XML what kind of character set the XML data uses so that all of its content will remain intact after being parsed. If this attribute does not reflect the character set that was actually used to create the XML file, the data may not be parsed correctly, and you may end up with "empty boxes" like the ones I was getting.

When I changed this encoding attribute to "UTF-8" (a widely used character encoding that supports many different languages), the empty boxes went away, and the curly quotes correctly appeared.


To prevent this from happening in the future, I modified my WordPress plugin to change the RSS feed's character encoding right before it is saved to the cache. Doing a simple str_replace function call seemed to do the trick. I thought I might have to use the mb_convert_encoding function to do a thorough conversion of the entire file, but this did not appear to be necessary.