Author Archives: brandonio21

Setting a Windows Desktop Background in Rust

While working on wallers, I came across the need to set the desktop background for Windows machines. For Linux machines, the answer was simple: delegate to feh. For Windows, I needed to delegate to a Windows API call.

The Windows API call of interest is SystemParametersInfo, specifically with the action as SPI_SETDESKWALLPAPER (20 in decimal). For SPI_SETDESKWALLPAPER specifically, the second and fourth parameters are not used, so they can be set to 0. Thus, in order to change the desktop wallpaper, one would call

SystemParametersInfo(20, 0, "path/to/wallpaper.png", 0)

In particular, this function is found in user32.dll. Luckily, retep998 has created a Rust binding for it and it is available as a crate. With this knowledge, changing the desktop wallpaper becomes quite trivial. The only complication is that there are two forms of SystemParametersInfo : SystemParametersInfoA and SystemParametersInfoW. From what I’ve gathered, the former should be called on 32 bit systems while the latter is called on 64 bit systems. This works on my desktop machine; however, on my 64bit Surface Book, SystemParametersInfoW sets the wallpaper to black while SystemParametersInfoA works as intended.

The final bit of difficulty comes in that these functions accept a *mut c_void as a parameter rather than a string (making the function unsafe.. sad). Thus, conversion must also take place.

extern crate user32;

use std::ffi::CString;
use std::os::raw::c_void;

fn set_wallpaper(path: String, force_32bit: bool) -> Result<(), ()> {
 let wallpaper_func = if force_32bit || cfg!(target_pointer_width = "32") {
        user32::SystemParametersInfoA
    }
    else {
        user32::SystemParametersInfoW
    };

    // do work to get the pointer of our owned string
    let path_ptr = CString::new(path).unwrap();
    let path_ptr_c = path_ptr.into_raw();
    let result = unsafe {
        match path_ptr_c.is_null() {
            false => wallpaper_func(20, 0, path_ptr_c as *mut c_void, 0),
            true => 0
        }
    };

    // rust documentation says we must return the pointer this way
    unsafe {
        CString::from_raw(path_ptr_c)
    };

    match result {
        0 => Err( () )
        _ => Ok( () )
    }
}

As you notice, we also insert a way for the user to call SystemParamtersInfoA on a 64bit system, just in-case…. Of course, since this uses OS-specific libraries, it’s best to use macros to prevent this code from running on non-Windows machines.

#[cfg(windows)] extern crate user32;

#[cfg(windows)] use std::ffi::CString;
#[cfg(windows)] use std::os::raw::c_void;
#[cfg(not(windows))] use std::process::Command;

#[cfg(windows)]
fn set_wallpaper(path: String, force_32bit: bool) -> Result<(), ()> {
 let wallpaper_func = if force_32bit || cfg!(target_pointer_width = "32") {
        user32::SystemParametersInfoA
    }
    else {
        user32::SystemParametersInfoW
    };

    // do work to get the pointer of our owned string
    let path_ptr = CString::new(path).unwrap();
    let path_ptr_c = path_ptr.into_raw();
    let result = unsafe {
        match path_ptr_c.is_null() {
            false => wallpaper_func(20, 0, path_ptr_c as *mut c_void, 0),
            true => 0
        }
    };

    // rust documentation says we must return the pointer this way
    unsafe {
        CString::from_raw(path_ptr_c)
    };

    match result {
        0 => Err( () )
        _ => Ok( () )
    }
}

#[cfg(not(windows))]
fn set_wallpaper(path: String, _: bool) -> Result<(), ()> {
let result = Command::new("feh")
        .arg("--bg-fill")
        .arg(path)
        .status()?.success();

    match result {
        true => Ok( () ),
        false => Err( () )
    }
}

 

Moving Projects off of CodePlex

On March 31, Codeplex announced its plans to shut-down. Although this might be bad for competition in the hosted-repository space, I understand why Microsoft felt the need to shut-down its service.

Personally, I had 3 projects hosted on Codeplex: HTML-IDEx, UpdateVB, and NotifierForVB. I have decided to archive the latter two in a versioned zip file. Since HTML-IDEx has had some fairly recent membership requests, I moved it to GitHub.

Making the choice to archive UpdateVB and NotifierForVB was a tough one. After all, they were both artifacts of my first early years of programming and were used extensively in the software I created during that time. However, they both had not received any updates in nearly 7 years. Since I was quite the inexperienced programmer at that time, I also expect these libraries to be riddled with small issues and bugs.

UpdateVB is a unique case, since I actually started working on its successor in 2014. Thus, the decision to archive it was purely for historical purposes. If you are looking to use UpdateVB, please use update.net instead.

NotifierForVB, on the other hand, is a library that’s about 30 lines of code and strongly tied to the Krypton Toolkit (which is apparently now open source). Due to its simplicity and lack of usefulness, I’ve decided to archive it for historical purposes without a successor.

HTML-IDEx is my only project hosted on Codeplex that had real community collaboration. Further, it is the only open-source project owned by BrandonSoft. Due to a few membership requests in 2017, its popularity, and its tie-in with BrandonSoft, I’ve decided to package the source code and move it to GitHub. If git is not your thing, a versioned zip archive will also be hosted here.

It was a tough decision to officially deprecate and decommission two of my older projects, but it was in the best interests of those who may have wanted to use the libraries (Definitely don’t use them!) and for me and my dwindling free-time.

Thank you to everyone who supported, used, and helped create UpdateVB, NotifierForVB, and HTML-IDEx. You can view the projects in the Archive section of the code page.

Custom FileExtension Path-Based Importers in Python

Python 3.1 introduced importlib, which can be used to modify the behavior of Python’s import system. In fact, standard Python imports have also been ported to use this new library.

As stated in the documentation, one of the reasons for this libraries existence is to allow programmer’s to write their own Importer modules. As one can imagine, this functionality is not widely used, however, because most people don’t have a desire to alter the functionality of standard Python importing.

However, there are definitely use cases. One blog post describes using the system to block certain modules from being imported. Further, Python has actually used the module to write an Importer that allows one to import modules from zip files. There is also a great Usenix article that describes a lot of the functionality covered in this post.

In this post, I’d like to describe how one can use pre-existing machinery, namely importlib.machinery.FileFinder to quickly write a path-based Importer module to handle custom imports.

First, some background. Importing in Python is actually pretty straight-forward (and pretty elegant). During each import statement, a list of known-importers is consulted. Each importer returns whether it can handle the module name provided. The first importer that can handle the module name is then used to load the module.

Naturally, then, each Importer has two components, a finder and a loader:

find_loader(fullname) indicates whether a module can be loaded based on its name. If the module can be loaded, the loader is returned. If not, None is returned.

load_module(fullname) loads the module, returning the actual module and also doing some other work, such as placing it in sys.modules. All work is described here.

The Importers are loaded from two lists,  sys.meta_path and  sys.path_hooks .  The former imports modules simply based on their names, while the latter imports modules based on their names within a certain path.

Using this knowledge, our goal is to then allow something like this to happen:

In the directory of our project, there is a JSON file,  records.json , which contains customer records indexed by their full name. We want to seamlessly import this file and use it as if it were a dictionary. If the file doesn’t exist, naturally, we’d like to throw an error.

import records
print(records['Jane Doe'])

This seems pretty simple knowing what we know about how the Python import system works:

  1. Since we are operating on the FileSystem, we need to know information about paths. Therefore, we’d like to create an Importer module that can be appended to  sys.path_hooks .
  2. Our find_loader implementation should take the module name (records, in this case), append the “.json” extension to it, and then check to see if it exists on the filesystem. If it does, it should return the loader described in (3)
  3. Our load_module implementation should take the module name, append “.json” to it, read the contents from the filesystem, and load the JSON using Python’s  json module.

As you might notice, steps 1 and 2 are not necessarily JSON specific. In fact, they’re filesystem specific. Luckily, steps 1 and 2 have already been written and provided by Python in the form of importlib.machinery.FileFinder. We can then utilize this to write our JSON Importer.

FileFinder also has a nice helper function, FileFinder.path_hook, which allows us to specify a series of (loader, extensions) pairs. The function then returns a callable that is suitable to be inserted into  sys.path_hooks . We then only need to write the loader. The loader, by definition, is a callable that returns a Loader, which has a load_module(fullname) method. In our implementation, we are going to utilize a class’ constructor as this callable (as suggested in PEP302). We write our loader:

import json
import sys

class JsonLoader(object):
    def __init__(self, name, path):
        self.path = path

    def load_module(self, fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]

        with open(self.path, 'r') as f:
            module = json.load(f.read())

        sys.modules[fullname] = module
        return module

Now we can use the already existing machinery to add this loader into our import system:

from importlib.machinery import FileFinder

json_hook = FileFinder.path_hook( (JsonImporter, ['.json']) )
sys.path_hooks.insert(0, json_hook)

# Need to invalidate the path hook's cache and force reload
sys.path_importer_cache.clear()

import records
print(records['Jane Doe'])

And voila! We have added our new JSON importing functionality. The most important part of the above codeblock is  sys.path_importer_cache.clear() . When your code begins running, all paths checked for imports in  sys.path have already had their hook’s cached. Therefore, in order to ensure that the newly added JSON hook is processed, we need to ensure that the cached list of Importers contains the JSON hook, so we simply invalidate the cache.

The great thing about this code is that FileFinder’s path_hook handles all of the Filesystem operations for you. It automatically traverses directories if directories are part of the import statement and automatically verifies extensions. All you have to do is worry about the loading logic!

Of course, no specific-solution is a good solution. It’s also possible to generalize what we’ve done.

from importlib.machinery import FileFinder
import json
import sys

class ExtensionImporter(object):
    def __init__(self, extension_list):
        self.extensions = extension_list

    def find_loader(self, name, path):
        self.path = path
        return self

    def load_module(self, fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]
        
        return None

class JsonImporter(ExtensionImporter):
    def __init__(self):
        super(JsonImporter, self).__init__(['.json'])

    def load_module(self, fullname):
        premodule = super(JsonImporter, self).load_module(fullname)
        if premodule is None:
             with open(self.path, 'r') as f:
                module = json.load(f)
                sys.modules[fullname] = module
                return module
            raise ImportError('Couldn't open path')

extension_importers = [JsonImporter()]
hook_list = []
for importer in extension_importers:
    hook_list.append( (importer.find_loader, importer.extensions) )

sys.path_hooks.insert(0, FileFinder.path_hook(*hook_list))
sys.path_importer_cache.clear()

import records
print(records['Jane Doe'])

Now there’s no need to worry about any filesystem details. If we want a new importer based on a file extension, we simply extend the ExtensionImporter class, create a load_module method, and pop it into the extension_importers list.

And thus, a complete solution to creating custom file-extension based path-importers has been created. Two lessons I’ve learned while writing this post:

  1. Don’t forget to call  sys.path_importer_cache.clear()
  2. For some reason, appending the finder function to the end of  sys.path_hooks doesn’t seem to work. Inserting it at the beginning, however, does.

Simple RPC With Thrift

A key aspect of building a server-based (cloud-based in today’s lingo) service is communication. The (often times remote) client needs to communicate with the server. Further, sometimes other processes on the server also need to communicate with each other. There are several ways to accomplish this, one of which is with RPC.

Many blooming programmers and hackathoners today will jump straight to “Why not just create a simple REST API using JSON serialization?”. On the surface, there are many good things about this. I’m sure you’ve heard them all:

  1. REST is simple and stateless. The semantics of a REST API are widely used and therefore easy to use once you’ve learned them. With a REST API on your server, it’s super easy to manipulate data and debug operations using tools like Postman.
  2. REST encourages readability. I’m all about readability everywhere in computing. Code should be readable, text should be readable, interfaces should be readable. It only makes sense, then, that an API should be readable as well. REST encourages this. It’s very easy to tell what GET api/users will do.
  3. REST encourages readable serializations. Since the API endpoints are readable, it’s only natural to make the response readable as well. Today, most APIs accomplish this by serialiazing data in the easy-to-read JSON format. This way, data is easy to read and easy to manipulate.

This is all well and good. Hackathoners and newcomers should not feel discouraged from using REST/JSON to create an interface to their cloud application. There’s one problem that I’m sure you’ve noticed, however.

JSON is heavy. When you’ve implemented a distributed, load-balanced, fully cached, and 100% optimized service, the largest bottleneck is transmission time from the server to the client, especially if the response object is large. In fact, on all of the teams I’ve worked on at various companies (except one), complaints about transmission time for huge serialized objects were extremely common.

Also, REST is cumbersome. I need more than two hands to count how many times I’ve coded up, from scratch, an interface layer that handles REST-style requests and serves the response in JSON. At one point I thought it a good idea to create a C# tool which actually generates a PHP REST interface when given the dataschema. Why did I have to do this? Surely, someone else has already done it!

Enter Facebook’s Thrift, which is open source and Apache licensed. For me, Thrift’s biggest features are how it solves the problems I’ve mentioned above. In order to use Thrift, you design a schema to represent both your objects and your service. The Thrift compiler then uses your schema to generate a client and server for you, meaning that you no longer have to handle the communication or serialization problems.

As a contrived example, say that I wanted to make a simple service to get my server uptime. I first design a Thrift schema:

service UptimeService {
    i32 getUptimeInDays();
}

Then, I compile the schema using Thrift

thrift --gen py uptime.thrift

I am going to use Python for my client and server, so I use the --gen py flag. Thrift has many supported languages, however.

I can then use the generated Python libraries to write my server implementation:

import UptimeService
import subprocess

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

class UptimeHandler:
    def getUptimeInDays():
        return int(subprocess.check_output('uptime').split(' ')[3])

if __name__ == '__main__':
    handler = UptimeHandler()
    processor = UptimeService.Processor(handler)
    transport = TSocket.TServerSocket(port=9090)
    tfactory = TTransport.TBufferedTransportFactory()
    pfactory = TBinaryProtocol.TBinaryProtocolFactory()

    server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
    server.serve()

And I also use the generated Python libraries to write my client implementation:

import UptimeService

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

def main():
    transport = TSocket.TSocket('localhost', 9090)
    transport = TTransport.TBufferedTransport(transport)
    protocol = TBinaryProtocol.TBinaryProtocol(transport)
    client = UptimeService.Client(protocol)

    transport.open()
    print("Server uptime: {} days".format(client.getUptimeInDays()))
    
    transport.close()

if __name__ == '__main__':
    main()

Of course, this client implementation only works when run from the server itself. If I wanted to get the uptime remotely, I would just change ‘localhost’ to my domain name.

And that’s it! All of the networking details and serialization is handled by Thrift. Of course, a big portion of this post went on about how JSON is too big and too heavy. If one compares performance of various Thrift-like libraries, they will notice that Thrift is definitely not the fastest. Instead, libraries like Avro, Protobuf, and CapnProto are much faster and also more compact.

However, a major pain point for me across the years has been implementing the actual interface layer. Writing client code to summon an HTTP Request and read the response from the server gets old after a while. This is something that Thrift handles for you. As you can see from the example above, Thrift handles all serialization, deserialization, and message-passing for you. All you have to worry about is defining a schema — Thrift gives the rest for free!