|
|
 |
 |
September 1st, 2009
I ran into a problem with ListView_GetHeader returning NULL for a ListView that had a header during WM_INITDIALOG. The issue ended up being that if the ListView was initially created hidden, I wasn’t able to get the header window. However, when the ListView control was created with WS_VISIBLE, it returned the header as expected. What I think happens is that the ListView control in Windows doesn’t actually create the header window until the first time it becomes visible, that’s why calls to ListView_GetHeader returned NULL when I thought they shouldn’t have.
So during WM_INITDIALOG, before the dialog is shown, I set each ListView to visible and then back to whatever state it was before. That way it forces the ListView to create the header window internally.
Style = GetWindowLong(ListViewWnd, GWL_STYLE);
ShowWindow(ListViewWnd, SH_SHOW);
if ((Style & WS_VISIBLE) == 0)
ShowWindow(ListViewWnd, SW_HIDE);
HeaderWnd = ListView_GetHeader(ListViewWnd);
August 23rd, 2009
If setting up a Virtual PC image with Windows 95 on it isn’t difficult enough, I had the audacity to try and run a application built using Visual Studio 2008 on Windows 95.
To those who are trying to install Windows 95 on Virtual PC 2007, it appears the Windows 95 is no longer supported on Virtual PC. What I had to do is install DOS on the image, copy all the Windows 95 setup files onto the virtual hard disk image (via mounting it in Windows 7), and then running the setup. It won’t work if you try and mount the Windows 95 ISO in Virtual PC because the CD-ROM isn’t recognized when Windows 95 setup restarts.
Virtual PC Guy’s WebLog: Installing Windows 95 under Virtual PC
Virtual PC still has its problems with Windows 95, sometimes it will come up and say “While initializing device IOS: Windows protection error. You need to restart your computer.” That happens if you have hardware virtualization turned on.
Thread: Win 95 install problem, in Virtual PC
I was disappointed to see that Microsoft removed Windows 95 from MSDN. There also isn’t 98/ME/NT4. It has to do with Microsoft settling a lawsuit with Symantec. Suprisingly even Visual C++ 6.0 isn’t on MSDN, but I don’t know if that is for the same reason or not. I had to grab Windows 95 from Mininova.
To get an application to run in Windows 95 you have to make sure that it doesn’t link to any functions that are not available to Windows 95. You can do this by setting the _WIN32_WINNT preprocessor definition variable to 0×0400 (/D “_WIN32_WINNT=0×0400″). That should expose which functions and structures are not available in Windows 95.
If the application you are using uses Unicode function calls you have to implement the Microsoft Layer for Unicode on Windows 95/98/ME Systems. Unicode wasn’t a part of Windows until Windows NT. I had to link with unicows.lib and set unicows.dll as a delay loaded dll in order for it to work.
If that wasn’t difficult enough, whenever you compile your application in Visual Studio 2008 it modifies the PE’s NT Optional Header and sets the MajorSubsystemVersion and MinorSubsystemVersion. These two properties tell Windows whether or not it will be able to support the application. So if Visual Studio 2008, sets it to 0×0500, then when you try and launch the application in Windows 95 (which is 0×0400), it will throw an error that says, “File expects a newer version of Windows – Upgrade your Windows”. To get around this, I found an AWESOME product called LegacyExtender, which includes a utility EditVersion.exe, that modifies the PE’s SubsystemVersion properties. You can setup a post-build operation to call “EditVersion.exe $(TargetPath) 4.0″. LegacyExtender also includes a .lib which links in function wrappers for the most common functions that Windows 95 doesn’t have (IsDebuggerPresent, InitializeCriticalSectionAndSpinCount, etc).
LegacyExtender
PE Disassembly Viewer – NikPEViewer
MSFN Thread: Visual Studio 2008 and Windows 9x
Targeting Windows 95 was a lot of fun, just to see if I could do it. It wasn’t an easy journey. It really makes you search out the bare bone functions that will work in Windows to get a particular job done.
August 22nd, 2009
It is not easy to get icons working on shell context menus. Each version of Windows has its own way of putting icons on context menus. I came across this great article about the proper way of setting an icon on a shell context menu.
nanoant: Themed menu’s icons, a complete Vista and XP solution (updated)
The code is based off TortoiseSVN’s context menu. The only problem with it is the fact that it causes Windows to display two icon columns on the menu in the Classic theme. This occurs because legacy context menu extensions will use the MENUITEMINFO’s hbmpChecked and hbmpUnchecked properties to set the bitmap icon. TortoiseSVN uses the hbmpItem property which is a newer way of setting the bitmap. The way to get around having to icons is to set the style of the menu via SetMenuInfo so that the menu has the MNS_CHECKORBMP style flag set. The Aero theme actually sets this style on all menus automatically which is why the issue can’t be seen in Aero.
Below is are screenshots of the context menu with two icon columns (left) and one icon column (right).
 Two icon columns |
 One icon column |
I submitted a patch to TortoiseSVN to fix it today.
One issue I came across while superclassing a button in Windows has to do with the fact that when the button processes the BM_SETSTATE message it does internal painting without calling WM_PAINT. This caused problems for me because I was doing custom painting and in certain cases, the button would paint its default look. I tried just returning true during the BM_SETSTATE message, instead of letting it go to the original procedure, but that caused the button clicks not to work. So I found this Dr. Dobbs article that shows that you have to set some flags (BST_PUSHED, ..) in the window’s extra memory.
Dr. Dobbs – Writing Windows Custom Controls
Update 9/5/2009:
The issue with BM_SETSTATE doing internal drawing without calling WM_PAINT is only present in older versions of the commons control library. If you attempt to set the button window’s extra memory values like that with a newer version of the common controls library, then it will crash your application. So if it does crash, check to see that you aren’t including a manifest which specifies a higher version of the common controls library.
April 16th, 2009
I’ve been checking for the end of a string the following ways…
while (*String != '\0')
or
while (*String != L'\0')
But have found out that it is better to use 0 instead of ‘\0′ when looking for the end of a string. Comparing to 0 works with both ascii and unicode strings, whereas comparing to ‘\0′ only works for ascii strings. For unicode strings you would have to do L’\0′.
while (*String != 0)
January 19th, 2009
Here’s a great resource for doing some common bit-wise tasks.
Bit Twiddling Hacks (http://graphics.stanford.edu/~seander/bithacks.html)
May 24th, 2008
The best way to refer to the size of an array is by using the sizeof keyword. For a long time I have been using the declared #define variable to refer to an array’s size throughout my code. Let’s take a look at the difference.
Referring to the size of Temp by the #define variable MAX_PATH.
#define MAX_PATH 260
char Temp[MAX_PATH];
String_CopyLength(Temp, "Hello World", MAX_PATH);
Referring to the size of Temp using sizeof.
#define MAX_PATH 260
char Temp[MAX_PATH];
String_CopyLength(Temp, "Hello World", sizeof(Temp));
Both pieces of code do the exact same thing. One calls String_CopyLength with MAX_PATH as the maximum buffer size and the other uses the compiler keyword sizeof. Now, the sizeof keyword is not “NEW”. The whole reason behind me using sizeof instead of referencing the #define variable is maintainability. If in the future you decide to change the declared size of the array, you only have to do it in one place, at the top where it is declared.
If you reference MAX_PATH throughout code instead of using sizeof(Temp) and you decide to change the declared size of Temp to say MAX_TEMP, you then have to change all references of MAX_PATH when referring to the size of the Temp array to say MAX_TEMP instead. What it is bad is if you forget to change one of the references of MAX_PATH to MAX_TEMP. It could cause a buffer overflow if MAX_TEMP is less than MAX_PATH.
Now instead of using a #define variable we might just insert the size of the array as it is declared.
char Temp[32];
String_CopyLength(Temp, "Hello World", 32);
So in the example above, if we want to change the size of Temp from 32 to 64 characters, then we’d have to change every reference where the number 32 is mentioned as the size of Temp. If instead we use sizeof(Temp), we only have to change the size of the array once in its declaration.
Update 8/24/09:
As I figured out in the comments below it would be better to create a macro called:
#define String_Size(e) (sizeof(e)/(sizeof(*(e)))
And then call:
String_CopyLength(Temp, "Hello World", String_Size(Temp));
That way it works with Unicode and ASCII strings.
March 3rd, 2008
There are many code examples, that don’t open a composite document properly but instead throw an error like “Cannot open DocFile”. Well, that is not good enough for me to be able to open it only “sometimes”. If Windows Explorer can edit the properties of a file in the file’s Summary tab, then why can’t I? I mean, it is not like this is new technology (NT)… it has been part of Windows, since Windows 95. The trick is has to do with how you attempt to open it. You have to open it using StgOpenStorageEx asking first for a IStorage interface and if that fails you have to attempt it again only this time asking it for an IPropertySetStorage interface. Take a look.
IStorage *Storage;
IPropertySetStorage *PropertySetStorage;
int32 Flags;
GUID *FormatId;
Flags = STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
FormatId = &FMTID_SummaryInformation;
Result = StgOpenStorageEx(Filename, Flags, STGFMT_ANY, 0, NULL, NULL, &IID_IStorage, (void**)&Storage);
if (SUCCEEDED(Result))
{
if (FAILED(IStorage_QueryInterface(Storage, &IID_IPropertySetStorage, (void**)&PropertySetStorage)))
return FALSE;
}
else
{
if (Result == E_NOINTERFACE)
{
Result = StgOpenStorageEx(Filename, Flags, STGFMT_ANY, 0, NULL, NULL, &IID_IPropertySetStorage,
(void**)&PropertySetStorage);
}
if (FAILED(Result))
{
if (Verbose == TRUE)
{
if (Result == STG_E_FILENOTFOUND)
Debug_Print("Error: File not found [%s]\n", Filename);
if (Result == STG_E_FILEALREADYEXISTS)
Debug_Print("Error: File is not a compound file [%s]\n", Filename);
}
return FALSE;
}
}
if (FAILED(IPropertySetStorage_Open(PropertySetStorage, FormatId, Flags, &PropertyStorage)))
{
if (FAILED(IPropertySetStorage_Create(PropertySetStorage, FormatId, NULL, PROPSETFLAG_DEFAULT,
Flags | STGM_CREATE, &PropertyStorage)))
return FALSE;
}
This way you are able to edit the extended file properties of any file, just like Explorer.
|