Emulating WINKEY-L

I've long been addicted to the combination of Win XP's “Fast User Switching” feature and the <WinKey>-L hotkey combination as a quick way to lock my PC when I step away from it for a moment.  I like to tell Keith it's because I'm extremely security conscious, but it probably has more to do with having 4 kids running around my house that like to see what Dad's working on when he's away from his computer and (worse) see if they can help me out with my work a bit.

But at some point after getting hooked on <WinKey>-L, I switched from Dell to IBM laptops (I use a laptop exclusively - the only proper desktop I have is a dual Xeon server in the basement).  ThinkPads, unfortunately, don't have a Windows Key.

For self-preservation reasons, I still locked my workstation when I left it alone, but it really irked me that I had to invoke the following sequence of hotkeys to pull it off:

CTRL-SHIFT-ESC (starts taskmgr)
ALT-U (select Shutdown menu)
S (switch user)

It didn't take long conclude (i.e., instantly) that I had to find a work around.  I probably could have googled up a solution, but being not so bright, and knowing about the existence of the Windows LockWorkStation function from some previous escapade, I figured I could whip up a little program on my own and drop a shortcut to it on my desktop with an assigned hotkey in less time than it took to find a solution on the web.

So I cranked up VS.NET (2002 at the time), ran the New Project wizard, selected a Win32 Project, named it LockWorkStation, and hit go.  Then I dropped into the wizard-generated code, which had a lot of goo in it I didn't need, and stripped it down to this:

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPTSTR lpCmdLine, int nCmdShow )
{
    LockWorkStation();
}

I originally forgot to account for the fact that this function wasn't part of the original Win32 API, so my program failed to build.  But adding the following to stdafx.h before inclusion of windows.h fixed that problem:

#define _WIN32_WINNT 0x0500

A quick CTRL-SHIFT-B and I had myself a function LockWorkStation.exe.  Then I noticed the size of this one-line executable on disk: 167,936 bytes.  Yikes!

Of course, that was a debug build (the default build target) so I quickly toggled over to do a release build.  This yielded a more streamlined executable weighing in at 81,920 bytes.  Hmm.  Kind of hefty for a 1 line program that just jumped to an exported entrypoint in another DLL.

At this point, I started fiddling around with compiler and linker switches, but wasn't making any headway at all.  So I stopped what I was doing for a moment and dusted off an old assembly language Windows program I had laying around from years gone by.  I wanted to see just how small a 1 line Windows program I could make to understand what my goal was.  Here's what it looked like (comments stripped for brevity):

TITLE LockWorkStation

.386
.MODEL  FLAT, STDCALL

option casemap:none

ExitProcess     PROTO   WINAPI  :DWORD
LockWorkStation PROTO   WINAPI

.code

WinMain:
    invoke LockWorkStation
    xor eax, eax
    invoke ExitProcess, eax
END WinMain

Building this guy resulted in a functioning LockWorkStation executable measuring a svelt 2,048 bytes.  Now we're cookin' with gas!  Of course, it really ends up occupying 4096 bytes in memory (a page), but that's not my fault.

Being a stubborn person, and evidently having lots of free time on my hands that day, I went back to VS.NET determined to harrass it into producing an executable image of equivalent size.  My starting point was the 81,920 byte release build.

The first thing I did (not that I can tell you why it was the first thing I did) was to tell the linker to not include all the default libraries it normally includes.  This was done as follows (path to settings on the project settings dialog box):

Configuration Properties\Linker\Input
  Ignore All Default Libraries = Yes (/NODEFAULTLIB)

This change necessitated a tweak to my code so that the release build version of it defined its own WinMainCRTStartup function, which had previously been included in one of the default libraries.  My revamped app now looked like so:

#ifdef _DEBUG
int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        LPTSTR lpCmdLine, int nCmdShow)
#else
// Release build linker settings includes /NODEFAULTLIB.
//
int APIENTRY WinMainCRTStartup( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                                LPTSTR lpCmdLine, int nCmdShow )
#endif
{
    LockWorkStation();
    return(0);

}

This change produced an executable that was 61,440 bytes.  Not a huge change, but on the right track at least.  The next change I made was to disable a Windows 98-specific optimization.  I have no idea what this optimization does, but I knew I didn't need it for a program that would only run on XP:

Configuration Properties\Linker\Optimization
  Optimize for Windows98 = No (/OPT:NOWIN98)

New image size: 51,200 bytes.  So far, I was batting a thousand - each change I'd made reduced the size of the image a bit.  But that didn't last long.  I then made a bunch of changes to the compiler's “optimization” settings:

Configuration Properties\C/C++\Optimization
  Optimization = Minimize Size (/O1)
  Global Optimization = Yes (/Og)
  Inline Function Expansion = Only __inline (/Ob1)
  Favor Size or Speed = Favor Small Code (/Os)
  Omit Frame Pointers = Yes (/Oy)
  Optimize for Windows Application = Yes (/GA)

New image size: 51,200 bytes.  No change.  Not what I'd expected.  But you gotta love a compiler switch called “oh be one”.  Leaving those changes in place, I moved onto the next configuration area, making another shotgun blast at settings that sounded even remotely like they might make my program smaller:

Configuration Properties\C/C++\Code Generation
  Enable String Pooling = Yes
  Enable C++ Exceptions = No
  Buffer Security Check = No
  Enable Function-Level Linking = Yes (/Gy)

New image size: 51,200 bytes.  Hrumph.

At this point, I realized that the project wizard had generated and included some resources for me that I surely didn't need for this program.  So I removed all the files from my solution that were included under the “Resources” folder (*.ico, *.rc).

New image size: 51,200 bytes.  Huh.

Then I hit the motherload:

Configuration Properties\C/C++\Advanced
  Compile As = Compile as C Code (/TC)

New image size: 2,048 bytes.  Ahhh.  Finally.  A proper VS.NET solution that generated the smallest Windows program apparantly possible.

Of course, I also realized that I had no clue which one, or combination, of the above changes actually caused the effect I finally achieved.  So I started up another default wizard-generated project and just made the /TC change.  That alone didn't work.  So I backed that out and made just one other change.  That didn't work either.  I played around a bit to see if I could find a smaller subset of the switches that still resulted in the 2,048 byte executable, but I never found my way back to a 2,048 byte image and eventually had to get back to paying work and gave up.  I'm pretty sure not all of the switches I ended up throwing need to be thrown, but I'm also pretty sure more than one needs to be thrown.  Somewhere in between the default settings and what I ended up is just the right set of switches that will result in a 2,048 byte version of my LockWorkStation program.  But I'd run out of patience looking for it.  Having solved my original problem, I decided it was time to move on.

So I dropped a shortcut to my LockWorkStation.exe on my desktop, configured it's hotkey as CTRL-SHIFT-L, and I was in business.  It's 3 keys instead of the 2 required by <WinKey>-L, but it beats the pants off the 6 key taskmgr hack (or letting my kids have open season on my laptop).

But it was fun dusting off my assembly language skills and running ml.exe for the first time in something like 6 years.  And only another 2 years to blog about it after being asked several times by colleagues what hotkey I was using to lock my ThinkPad.  :-)

A copy of the code, including a prebuilt version of the 2,048 byte exe, is here if you're interested.  Keep those workstations locked!


Posted Dec 09 2004, 02:43 PM by mike-woodring

Comments

Wes Haggard wrote re: Emulating WINKEY-L
on 12-09-2004 4:06 PM
Nice little experience, I tend to do things similar from time to time.

I hardly ever use WIN-L, most of the time I just use CTRL-ALT-DELETE, ENTER. I guess that is four keys but I believe they should all be on your IBM laptop. ;)
Stuart Dootson wrote re: Emulating WINKEY-L
on 12-09-2004 5:05 PM
That's interesting...I built this in VS2003 as a Win32 console application.

First attempt was 22k (Release).
Then I told it to use the DLL C runtime (using /MD) rather than linking to the static lib and it dropped to 3,584 bytes on disk.

That should be small enough...Oh, and compiling with /TC yields the same size code.

I also tried building it as a straight Win32 app - same size when I turned on the /MD option.
TrackBack wrote : Tiny Tiny CodeElliott Back
on 12-09-2004 5:26 PM
: Tiny Tiny CodeElliott Back
Craig wrote re: Emulating WINKEY-L
on 12-09-2004 5:30 PM
Heh - I went through almost exactly the same exercise a few years ago. Of course, my program didn't actually do anything...I just wanted to see how small I could get it.
dominick wrote re: Emulating WINKEY-L
on 12-09-2004 10:23 PM
hi mike,

that bugged me too -

ctrl-alt-delete + enter will only work if you are a domain member...

i took the more pragmatic approach - i added a link to "rundll32.exe user32.dll,LockWorkStation" to my desktop and added a shortcut :)

btw - there is also a way to map an unused key (e.g. caps lock) to the winkey in registry...

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex:00,00,00,00,00,00,00,00,02,00,00,00,5b,e0,3a,00,00,00,00,00

dominick
John wrote re: Emulating WINKEY-L
on 12-10-2004 4:57 AM
You might want to take a look at SharpKeys (http://www.randyrants.com/sharpkeys/). It's a handy little GUI app for remapping keys via the registry key mentioned by Dominick. Rather indispensable with an IBM laptop...
Mike wrote re: Emulating WINKEY-L
on 12-10-2004 6:08 AM
Re Dominick's rundll trick. That's a good one.

You've also reinforced my long-held belief that the surest way to discover a quick, elegant solution to a problem I'm having is to tell someone that I spent the better part of half a day rolling my own solution :-)

-Mike
Alan wrote re: Emulating WINKEY-L
on 12-20-2004 12:16 PM
ctrl-esc followed by l, s
4 keystrokes, no mouse, and alas no excuse to code. For those of us still using pre winkey keyboards. Although I have to admit page up and down could use a context sensitive way to remap to scroll up and down.
Thanks for the story it was heartening that anyone cares about elegance anymore.

ajh
Fernando Tubio wrote re: Emulating WINKEY-L
on 12-24-2004 7:07 AM
The problem with the 'rundll trick' is that LockWorkstation doesn't have the appropiate signature to be used by rundll32.exe and will corrupt the stack. This is discussed in Raymond Chen's blog:

http://weblogs.asp.net/oldnewthing/archive/2004/01/15/58973.aspx


Malcolm wrote re: Emulating WINKEY-L
on 12-30-2004 6:49 AM
I also have a ThinkPad, and found the lack of a 'WinKey' to be a major PITA. However, you may find that your ThinkPad has an IBM utility installed in <Program Files>\ThinkPad\Utilities, called TpKmapAp.exe. It allows you to customize the keyboard, including the emulation of a 'WinKey'. Since I never use the right alt key, I assigned that to the 'WinKey', and haven't looked back since.
John Bates wrote re: Emulating WINKEY-L
on 03-25-2005 3:16 AM
I also went through similar hurdles a while ago. The trick to get a small executable that only uses Win32 APIs is to not use the bloated CRT by defining an entry point function like "void mainStartup(void) {...}". Also remember is to call ExitProcess otherwise it won't die properly.
Adam Prato wrote re: Emulating WINKEY-L
on 05-23-2005 10:44 AM
That seems like an awful lot of trouble...

- right click your desktop, create new shortcut
- right click the shortcut, properties
- Under general, label it "Lock Workstation"
- Under shortcut, the target is "C:\WINDOWS\system32\rundll32.exe user32.dll LockWorkStation"
- Shortcut Key: ctrl-alt-l

Now, it would be nicer to have a key on the stinkpad that emulates the winkey. Thats what I'm searching for next...
Bjorn wrote re: Emulating WINKEY-L
on 06-18-2005 2:57 AM
I would love to see the a code snippet that can also log in/activate a different user. A 'very fast user switching app", by providing a username/password.
Jonathan Rue wrote re: Emulating WINKEY-L
on 06-19-2005 9:24 AM
This is interesting, because I was on another site where someone wanted to run multiple instances of an application (itunes I think). Because the code doesn't allow that in many applications. One must create multiple user accounts in XP to run multiple instances (to get around that).

I found a program out there that does super fast user switching bypassing the "user accounts screen", but I can't remember the name. Also supposidly there is something similar to it built in to microsoft's power toys for xp called fast.exe. But I think you might have to have SP2 installed to use it.

You could also use the mouse by clicking on start, and logoff, switch user, but that is cumbersome and slow.
Jonathan Rue wrote re: Emulating WINKEY-L
on 06-19-2005 10:32 AM
I did some googling, the name of the program is called DUST, and it does direct user switching by remapping the winkey - L key to fit the function of the alt-tab. So basically you just use alt tab to directly switch into user accounts, or log off. In the original posters case I'm sure you might be able to use it with just one user, or create two users. At any rate it is faster if not as fast as the winkey - L hotkey.

There are some limitations mathmatically with the program if you are running like 20 users at once and switching.

Also I found out you can pres ctrl-alt-del, then click on the user tab, find the user, right click with the mouse and then connect to another user. Sounds like more work but actually I find this method connects you faster (to other users) than winkey - L. And if you recorded a script or used a macro, BINGO there is your hot key solution.

It would be interesting if someone would develop a program like multiapp (a MAC program that lets you run multiple application instances without having to switch between users), for the PC...or a better program than
DUST.
Waruwaru wrote re: Emulating WINKEY-L
on 07-23-2005 9:27 AM
Looks like you can cut the size down even further by setting

Configuration Properties/Linker/Debugging
Generate Debug Info = No
Debuggable Assembly = No Debuggable Attribute Emitted
Neil wrote re: Emulating WINKEY-L
on 08-13-2005 4:11 AM
I don't know about .NET but for VC6 I use the /ENTRY: linker switch instead of having to figure out what the default entry point is.

/NOENTRY is also useful when creating DLLs just to contain icons and other resources.
Andy wrote Thanks Malcolm
on 10-21-2005 4:52 AM
I have been using a IBM laptop for about 6 months and have been tearing my har out about the missing windows key.

Mapping it to AltGr will make my life so much easier

thanks.
Jason Lester wrote re: Emulating WINKEY-L
on 02-07-2007 3:48 PM
I created a shortcut to:
%windir%\system32\rundll32.exe user32,LockWorkStation
and then assigned F11 to it.. That's 1 key press..
Jason
Scott wrote Emulating WINKEY-L - THANKS, MALCOLM!!
on 02-26-2007 6:01 PM
I read through all of this and found Malcolm's posting (12/30/2004-6:40am) to be the best and easiest (especially for a not-so-technical guy like myself...). Twenty seconds and done and working (+ two minutes to document it in a word doc so that I can remember it next time somebody wants to know how I did it!!).

Thanks, Malcolm.
Cephalexin 500mg. wrote Cephalexin in dogs.
on 07-29-2008 2:27 PM

Cephalexin cold symptoms. Cephalexin. Cephalexin dosages for dogs. Cephalexin phatyngitis.

Tadalafil and proscar propecia success. wrote Generic propecia.
on 07-29-2008 9:45 PM

Propecia. Propecia sexual side effe. Propecia side effects.

Adderall xr. wrote Adderall without a prescription.
on 08-07-2008 12:19 AM

Adderall idiopathic edema. Adderall erowid. Adderall xr. Adderall.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?