Posts Tagged ‘win32’

Programmatically registering and un-registering a COM DLL

Playing around with WebKit a bit and stumbled across the headaches of working with a COM DLL. WebKit uses COM interfaces, so an application using a WebKit DLL must register the DLL. COM DLL registration is system-wide (i.e. all calls will be directed to a single DLL file), and this creates a problem if an application wants to use a DLL of a different version or a custom one (assuming renaming all the interfaces is not a viable option).

A simple solution to this seems to be to register and unregister the DLL programmatically. By doing so, any existing (or non-existent) registration is ignored and the exact DLL file the application needs is registered and used. The registration is still system-wide, but if other applications follow the same policy there doesn’t seem to be a problem. For applications that don’t follow this policy, it seems their calls will go to the last DLL registered.

(This is just from my own investigation and I really don’t know a whole lot about COM stuff. If anything mentioned is incorrect, please correct me.)

How to register and unregister COM DLLs can be found here. This is a bit convoluted. I actually just followed the steps listed in the second post of this thread on the MSDN forums to write my code.

To register:

HMODULE webKitMod = ::LoadLibrary(L"WebKit.dll"); FARPROC regFunc = ::GetProcAddress(webKitMod, "DllRegisterServer"); if(regFunc != NULL) { HRESULT regRet = regFunc(); if(regRet != S_OK) { // report error } } else { // report error } ::FreeLibrary(webKitMod);

To UnRegister:

HMODULE webKitMod = ::LoadLibrary(L"WebKit.dll"); FARPROC unRegFunc = ::GetProcAddress(webKitMod, "DllUnregisterServer"); if(unRegFunc != NULL) { HRESULT unRegRet = unRegFunc(); if(unRegRet != S_OK) { // report error } } else { // report error } ::FreeLibrary(webKitMod);

How to open the containing folder

Opening a window showing the contents of a folder and highlighting a specific file in the folder can be a pretty useful action, especially for applications that show or list files to the user, as it allows for the user to quickly get to a file and manipulate or open it via. the operating system’s shell. The most popular implementations of this features are, perhaps, in web browsers; it’s referred to by a couple of different phrases, “Open Folder” in IE7, “Show Containing Folder” in Safari, “Open Containing Folder” in Firefox.

Anyways, I wanted this feature for something I’m working on in Windows and it’s actually pretty simple to implement, but it took me a while to find information on it. There’s no Win32 function, instead, explorer has to be run with a command-line switch. Switches and accompanying examples can be found here.

So the command to open a containing folder would be:

explorer /select, <file_path>

Here’s a bit of C# code (it should be simple to do this in almost any language):

System.Diagnostics.Process p1 = new System.Diagnostics.Process();
p1.StartInfo.FileName =
"explorer";
p1.StartInfo.Arguments =
"/select, \"" + <file_path> + "\"";
p1.StartInfo.WorkingDirectory =
Environment.GetEnvironmentVariable("SystemRoot");
p1.StartInfo.UseShellExecute =
true;
p1.Start();

Of course, replace <file_path> with the file you want highlighted.

Based on the info here, as well as the page linked to above, it seems this should work on all versions of Windows since Win 95.

Compiling Webkit on Win32

Compiling WebKit on Windows is far from trivial. In the process of getting it compiled and linked successfully, I took some notes which may help others struggling to getting it done.

First, a few precautions, don’t try to be smart. Follow the instructions on the site closely, this means:
  • Don’t try to compile w/ Visual C++ 2008, it won’t work. Use VC++ 2005.
  • For VC++ 2005 Express Ed., make sure you have SP1, compilation will fail w/o it.
  • For Vista, make sure you have the additional SP update.
  • Don’t try to compile outside of cygwin directory structure.
Next, you need to get the webkit source.
  • Get the source from a nightly build (make sure to download the source and not one of the platform specific builds). You can also get the files from the subversion repository, but I had issues with this and discovered missing files. It may have been fixed, but in general, I think just getting the nightly build is simpler.
  • The source is in a .bz2 file. So you’ll need a bzip2 decompressor.
  • The bz2 file decompresses to a .tar file (wtf?!), so you’ll also need a tar decompressor.
  • Next, download the WebKit Support Libraries.
  • Put the source under your cygwin/home/<username> directory (e.g. \cygwin\home\<username>\WebKit)
  • Finally, update, by running the update-webkit script.
Preparing for compilation.

Note: I tried to build from the cygwin shell, as demonstrated on webkit.org, but it failed. Failing in this way and then having to dig thru build logs is insane, so I diverted to building from within VC++ so I could easily spot and fix errors as they occurred.

Note: I failed to realize that I didn’t have SP1 for VC++ 2005 Express when I started this whole process and didn’t figure it out until I hit a few perplexing errors related to the linker failing because it detected different versions of the compiler were used for the 2 parts of compilation on a few object files (yea, it’s weird, I have no clue what was going on internally here). I found a few quick and dirty solutions to this problem by playing around with optimization settings, but eventually hit a brick wall, where no amount of fidgeting with optimization parameters resulted in compilation/linking going forward. Installing SP1 solved the issue and may have solved the earlier issues as well without me having to play with optimization settings.

So, onwards we go,
  • I found that after opening the WebKit solution, none of the projects were loaded. This is because the project files are looking for *.vsprops files in WebKit\WebKitLibraries\tools\vsprops, but the vsprops files are actually in WebKit\WebKitLibraries\win\tools\vsprops. The simplest solution is just copy over what’s in the /win directory into the parent directory (WebKit\WebKitLibraries). Looking at WebKit\WebKitLibraries now, I copied over the win/tools folder and the files in /win. Alternatively, you can edit the project files (they’re XML files, you can open them up in a text editor).
  • Set the 2 environment variables necessary for compiling within VC++,
    • WEBKITLIBRARIESDIR = cygwin\home\<user>\WebKit\WebKitLibraries
    • WEBKITOUTPUTDIR = cygwin\home\<user>\WebKit\WebKitBuild
  • Get the Platform SDK, specifically the Windows Server 2003 Ed. of the Platform SDK. I did not have luck getting it to work with an older or newer version of the SDK. I didn’t install the R2 update; I’m not sure what effect it would have.
  • Update VC++ directories:
    • Executables >> add cygwin/bin, windows/system32, Platform SDK/bin
    • Includes >> Platform SDK/include
    • Library Files >> Platform SDK/lib
  • Update VC++ directories for the WebKit Support Library:
    • Includes >> WebKit\WebKitSupportLibrary\WebKitSupportLibrary\win\include
    • Library Files >> WebKit\WebKitSupportLibrary\WebKitSupportLibrary\win\lib
  • Update VC++ directories for JavaScriptCore/icu:
    • Includes >> WebKit\JavaScriptCore\icu
  • Update VC++ directories for WebCoreSQLite3:
    • Includes >> WebKit\WebKitLibraries\WebCoreSQLite3
  • Update VC++ directories for JavaScriptCore:
    • Includes >> WebKit\JavaScriptCore
  • I had issues with the ARRAYSIZE macro not being defined, so in WebCore/config.h, add the following:

    #ifndef ARRAYSIZE
    #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
    #endif

    right before the line,

    #endif /* PLATFORM(WIN_OS) */

  • Get rid of cygwin’s link.exe in cygwin/bin (rename it or delete it). This causes a conflict with the real linker (VC++’s link.exe). It was likely caused by the order in which VC++ executable directories were set, so if you need it, you might be able to keep it.
  • For the QtMovieWin project, edit all source files (there’s only 2) so that windows.h is included first (this is due to macro above, which will now cause a macro redefinition by the preprocessor).
  • For QtMovieWin, also rearrange the order of include, so that #include <wtf/Vector.h> is included before config.h and windows.h to prevent some min/max macro nonsense.
  • For QtMovieWin, take off prompt error reporting for linking (ERRORREPORT:PROMPT) project (see Linker >> Advanced page in project setting).
  • Make a new config file by copying WebCore/config.h and making WebCore/config_qt.h. Remove the ARRAYSIZE macro defintions from config_qt.h. Update all files in QtMovieWin project so that config_qt.h is included instead of config.h.
  • Install the QuickTime SDK.
  • Update VC++ directories:
    • Includes >> QuickTime 7.3 SDK\CIncludes
    • Library Files >> QuickTime 7.3 SDK\Libraries
  • For QtMovieWin, make sure you linker inputs are set correctly for QuickTime libraries and VC++ can find them.
  • For QtMovieWin, add “CoreFoundation.lib” to linker inputs.
  • (This may have been an issue caused by not having SP1, you might not have to do this, try compiling first) Turn off whole program optimization for all projects.
  • (This may have been an issue caused by not having SP1, you might not have to do this, try compiling first) Turn off treat warnings as errors for JavaScriptCore, WebKit, WebKitGUID, WebCore, and WTF projects.
That should do it.

Some general advice, work from within VC++ and compile as you make changes; make it a touch-and-go operation. In this way you see errors as they occur and can more easily spot and fix issues. Don’t simply follow what I’ve written above as tutorial thinking it’s a sure-fire solution for compilation, things may very likely change with different iterations of the code.

Win32 annoyances and tips when working with files and processes

1. ShellExecuteEx and lpFile

SHELLEXECUTEINFOW setupxInfo;
memset(&setupxInfo, 0,
sizeof(SHELLEXECUTEINFO));
setupxInfo.cbSize =
sizeof(SHELLEXECUTEINFO);
setupxInfo.fMask = SEE_MASK_FLAG_NO_UI |
SEE_MASK_NOCLOSEPROCESS;
setupxInfo.lpVerb = L
"open";
setupxInfo.lpFile = L
"setupx.exe"; // can't be full path
setupxInfo.nShow = SW_SHOWNORMAL;
setupxInfo.lpDirectory = L
"setupdir";
setupxInfo.lpParameters = NULL;

ShellExecuteExW(&setupxInfo);

The code above is correct and it works, however if the lpFile member of the SHELLEXECUTEINFO struct is the full path to the executable file the ShellExecuteEx call fails. It seem you must specify the working directory for the child process (lpDirectory) and the filename only for the lpFile member.

2. SHFileOperation and pFrom

SHFILEOPSTRUCTW SHFileOp;
ZeroMemory(&SHFileOp,
sizeof(SHFILEOPSTRUCT));
SHFileOp.hwnd = NULL;
SHFileOp.wFunc = FO_DELETE;
SHFileOp.pFrom = killFolder;
// double-null-terminated string, can't end with path separator
SHFileOp.pTo = NULL;
SHFileOp.fFlags = FOF_SILENT |
FOF_NOCONFIRMATION |
FOF_NOERRORUI |
FOF_NOCONFIRMMKDIR;
SHFileOp.fAnyOperationsAborted = FALSE;
SHFileOp.lpszProgressTitle = NULL;
SHFileOp.hNameMappings = NULL;
SHFileOperationW(&SHFileOp);

In the above code, SHFileOp.pFrom (killFolder variable) is a double-null-terminated string to a folder I want to delete (I want to also recursively delete everything inside the folder, that’s why I’m using SHFileOperation). However, aside from the peculiar double null-termination, the path can’t end with the path separator char or the SHFileOperation call fails.

3. WaitForSingleObject

After calling ShellExecuteEx or CreateProcess you can do the following to wait for the child process to terminate:

WaitForSingleObject( setupxInfo.hProcess, INFINITE );

4. fclose

Don’t forget to close your file handles. I spent a few hours today trying to figure out why I couldn’t delete an exe, thinking it was an issue with the ShellExecuteEx function. Turns out I opened the file and forgot to close it.

btw, Process Explorer is very helpful and a great tool for programmers; it’s wonderful for detecting things like unclosed file handles.

The pains of making an installer + uninstaller

I’ve been working on an installer/uninstaller for firesync 2. The installer is fairly straightforward, although it is quite a bit of work to set registry keys and make shortcuts, but overall it’s all fairly standard stuff. The one unique aspect of the firesync installer is that it is multi-part and actually consists of 2 executables. The first executable, the setup.exe file that will be distributed, is a native code binary + a zip archive. This program checks that the .NET Framework 2.0 is installed and extracts the zip archive to a folder in the user’s temporary directory. The zip archive contains the firesync executable and data files, but also contains another setup executable setupx.exe (a .NET executable). This executable is the real setup, as it shows/prompts the user for setup information (installation directory, whether or not to make a desktop icon, etc.), copies over the necessary files, makes program shortcuts, and writes registry entries so that firesync appears in the Add/Remove Programs list.

The reason for the multi-part installation is that it is much easier to design an interface using the WinForms designer in Visual C# compared to MFC, wxWidgets, etc. However, a .NET executable alone won’t suffice because users without the framework will just be given a cryptic error message (I think you get dialog about mscoree.dll not being found) and they’re not actually told that they need to install the framework.

(fyi, the native code executable is being done with wxDevC++)

Now making an uninstaller should be simple, your just deleting files and removing a couple of registry entries (in general, I avoid the registry and for firesync I’m only using it for the entry in Add/Remove Programs). Also, I think it’s rare that a user will uninstall the .NET Framework then try to uninstall firesync, so it’s pretty safe to make the uninstaller a .NET executable. However, there’s one big issue (and it’s a major pain in the ass) that pops up when it comes to making an uninstaller, how do you delete the uninstaller executable itself? I was lucky enough to stumble across this old Q&A from Microsoft Systems Journal. It’s a very inter… well to be honest it’s a very boring read (how interesting can deleting a file be?), but it’s important stuff. Ultimately, I went with the simplest method, and just used a batch file (although I haven’t played around with setting thread priorities as mentioned in the article; I’m not crazy about doing it and I’m not sure if it’s even worth it).

Finally, some important issues I ran across that was not mentioned in the article:

  • The directory your attempting to remove may be in use by the OS or another application. Another loop in the batch file should take care of this.
  • You can’t have the batch file in the directory your trying to delete (you’ll never be able to delete it as it’ll be in use by cmd.exe, you’ll have to manually kill cmd.exe to delete the folder and free the CPU). This is simple to solve, I just put the batch file in the user’s temp directory.

firesync and the daylight savings time bug

Seems like I’m talking too much about firesync in this blog, but anyway, another rare, but serious bug popped up in firesync on this special day. However, unlike the previous bug (see previous blog entry), this one doesn’t seem to be unique to firesync; it’s the daylight savings time bug (note: this article is informative, but some parts are not written too clearly and can be difficult to understand).

The bug popped up when I noticed firesync seemed to be thinking that all of my files were out-of-sync. I looked at the file modification times of a few files on the two PCs and noticed that modification, creation, and access times were off by 1 hour. The reason for this, as I discovered, is that the filesystem of one system was FAT32 while the other was NTFS. FAT32 time stamps are equal to the local time (so, inherently, DST information is contained within the time stamp). NTFS time stamps are based on UTC time and the local time is calculated as an offset from the UTC time. The problem (i.e. the daylight savings bug) results from the fact that Microsoft chose a somewhat odd way of handling conversion from UTC-to-local time. Instead of storing DST information as part of a time stamp (which would enable a correct conversion), NTFS systems will add 1 hour to the UTC time (during the UTC-to-local time conversion process) if daylight savings is currently in effect!

So, the solution seems to be to do all time stamp comparisons based on UTC time. Ahh, more work for firesync 2.