Code Gem

Code Gem

Knowledge was arriving in dribs and drabs

Code Gem RSS Feed
 
 
 
 

Turn off the Information Bar in IE

Start from the Internet Explorer in XP SP2, Microsoft added a new feature called Information Bar under the name of security. The Information Bar is used to block some actions that were considered “risky” for security, including

  • Active X Install Prompts
  • Non-user-initiated Download Prompts
  • Pop-up Windows
  • ActiveX Control Blocked Errors
  • Local Machine Zone Lockdown

For any HTML file that contains javascript code it will show this Information Bar. Given the fact that most of modern webpage contains more or less javascript code, you may find that it is extremely annoying.

There are several options to turn this thing off, through the IE settings, or registry. But as a web page author we need a way to bypass the feature for ALL users that browsing our pages.  Adding the following code to the very top (before the <HTML> tag) of the HTML document should help:

<!-- saved from url=(0013)about:internet -->

[reference]

Share/Save/Bookmark

HEX Speaks

While debugging applications, we have to face raw memory in many cases, and knowing some magic number can be a great help to find a clue of the problem.

For example:

  • 0xBAADF00D means uninitialized allocated heap memory.
  • 0xABCDBBBA, 0xABCDBBBB, 0xABCDAAAA, 0xDBCAAAAA … in page heap block structure

There are some more to find at

Share/Save/Bookmark

2 Windows Commands for Setting up File Association

Recently I learnt the following 2 commands which can be used for querying and setting file association in Win32 platform. Quite useful when we are trying to something through batch script. We can call the commands instead of doing the registry dirty work.

ASSOC

Displays or modifies file extension associations

ASSOC [.ext[=[fileType]]]

  .ext      Specifies the file extension to associate the file type with
  fileType  Specifies the file type to associate with the file extension

Type ASSOC without parameters to display the current file associations.
If ASSOC is invoked with just a file extension, it displays the current
file association for that file extension.  Specify nothing for the file
type and the command will delete the association for the file extension.

 

FILETYPE

Displays or modifies file types used in file extension associations

FTYPE [fileType[=[openCommandString]]]

  fileType  Specifies the file type to examine or change
  openCommandString Specifies the open command to use when launching files
                    of this type.

Type FTYPE without parameters to display the current file types that
have open command strings defined.  FTYPE is invoked with just a file
type, it displays the current open command string for that file type.
Specify nothing for the open command string and the FTYPE command will
delete the open command string for the file type.  Within an open
command string %0 or %1 are substituted with the file name being
launched through the assocation.  %* gets all the parameters and %2
gets the 1st parameter, %3 the second, etc.  %~n gets all the remaining
parameters starting with the nth parameter, where n may be between 2 and 9,
inclusive.  For example:

    ASSOC .pl=PerlScript
    FTYPE PerlScript=perl.exe %1 %*

would allow you to invoke a Perl script as follows:

    script.pl 1 2 3

If you want to eliminate the need to type the extensions, then do the
following:

    set PATHEXT=.pl;%PATHEXT%

and the script could be invoked as follows:

    script 1 2 3

Share/Save/Bookmark

An Easy Way to Visualize Customize Data Structure in Visual Studio

Start from Visual Studio 2005, STL containers can be shown in the Watch windows, and we can expand the tree-like control to navigator through the whole data structure to exam every element of the container. This brings a great benefit of debugging code, and it is one of the major reason why I prefer STL containers.

While working with legacy code there are a lot of customized data structures which doesn’t implemented well enough for the debugger to figure out its data structure automatically. Thus it cannot be shown in the Watch window other than a plain pointer address, even with symbol set up correctly. This really becomes an debug obstacle.

Once a while I believe that in order to get over the problem we have to implement an extension, like it is described in the following articles:

Well there turned to be another way easier approach. Under <VisualStudio Install Location>\Common7\Packages\Debugger there is a file named autoexp.dat. Open it with any text editor we could see a bunch of interesting stuff. It looks like that all those find STL/ATL Watch stuff is defined in this file!

The header of the file described how to add your own definition for the customize data structure. There is a great article also covered a good amount of details. See the following link:

Now armed with the new trick, my debugging life becomes easier!

Share/Save/Bookmark

GENERIC_READ? FILE_GENERIC_READ?

WTF the difference is?!

Today I got a task to refine an application to make it check the file access at more detailed level. I ran into the problem of the difference of GENERIC_READ and FILE_GENERIC_READ. In MSDN the definition of GENERIC_READ is:

GENERIC_READ

FILE_READ_ATTRIBUTES
FILE_READ_DATA
FILE_READ_EA
STANDARD_RIGHTS_READ
SYNCHRONIZE

But in WinNT.h it is:

#define GENERIC_READ                     (0x80000000L)

And there is another definition on WinNT.h:

#define FILE_GENERIC_READ         (STANDARD_RIGHTS_READ     |\
                                   FILE_READ_DATA           |\
                                   FILE_READ_ATTRIBUTES     |\
                                   FILE_READ_EA             |\
                                   SYNCHRONIZE)

Which one should I use?!! Damn, this is a typical case of unfriendly API, and misguiding document!

Anyway actually I can use either. The FILE_GENERIC_READ is specific to the file kernel object, while the GENERIC_READ can apply to all kinds of kernel objects.

There are a series of great article in CodeProject which explained the topic in very much detail:

The Windows Access Control Model

Share/Save/Bookmark

Improved SyntaxHighlighter A Little For This Blog

I am using SyntaxHighlighter to make my code snippet looks pretty in this blog. It a very cool code highlight engine: 100% JavaScript based, no server side code needed, can deal with various languages, and have very nice features like collapse code by default to make the post looks clean for the first glance.

But it bothers be as the latest version of SyntaxHighlighter lack something I want:

  • Code highlight for assembly language.
  • Collapse code again after expand it.

So today I took sometime to go over the JavaScript code and tweak it somehow to support my needs. As you can see in other posts, now assembly codes are highlighted correctly, and collapse/expand swith is supported:

PUBLIC  ??_7ClassHasVTable@@6B@             ; ClassHasVTable::`vftable'   

;   COMDAT ??_7ClassHasVTable@@6B@
CONST   SEGMENT
??_7ClassHasVTable@@6B@ DD FLAT:?foo@ClassHasVTable@@UAEXXZ ; ClassHasVTable::`vftable'
CONST   ENDS   

??0ClassHasVTable@@QAE@XZ PROC              ; ClassHasVTable::ClassHasVTable, COMDAT
; _this$ = ecx
    push    ebp
    mov ebp, esp
    push    ecx
    mov DWORD PTR _this$[ebp], ecx
    mov eax, DWORD PTR _this$[ebp]
    mov DWORD PTR [eax], OFFSET ??_7ClassHasVTable@@6B@
    mov eax, DWORD PTR _this$[ebp]
    mov esp, ebp
    pop ebp
    ret 0
??0ClassHasVTable@@QAE@XZ ENDP              ; ClassHasVTable::ClassHasVTable  

Enjoy!

Share/Save/Bookmark

What did we pay for C++ Runtime Polymorphism: Part II

It seems that there is some misunderstanding about my previous post. The purpose was never for measuring C++ performance but for better understanding of how polymorphism is implemented in C++, and make people aware that those extra power is not come for free. This is why I choose trivial cases and disable compiler optimization. Trivial cases for make the time penalty observable, disabling compiler optimization for ensure that assembly code is generated from C++ source code directly so it could be easier for analysis. These time penalty actually may not be a problem in real life as a considerable part of the overhead will be eliminated by compiler optimization.

Let’s back to our topic. This time we are going to discuss the overhead of indirect function call by comparing it with compile time polymorphism.

Compile Time Polymorphism

If we think about scenarios that we leverage polymorphism, a large part of it doesn’t need runtime binding actually. We can pretty much tell what the exact type is even if the object is referenced by base type. Take a look at the code snippet as below:

struct IBase
{
    virtual void go() const = 0;
};

class Derived : public IBase
{
public:
    virtual void go() const
    {
        doSomething();
        doSomethingElse();
    }

protected:
    virtual void doSomething() const
    {
        // Do something specific for class Derived
    }

    virtual void doSomethingElse() const
    {
        // Do something else specific for class Derived
    }
};

This is actually a very typical pattern that you could find everywhere. But if we take a second look, when we use the class in the following way:

const IBase& b = Derived();
b.go()

Apparently Derived::go() will be invoked at this time because of the indirect call. Inside Derived::go() it calls doSomething()and doSomethingElse(). You can easily figure out that it will need to go through indirect function call process again for these function calls. Can we save some overhead in this case? The answer is positive: we can use the compile time polymorphism.

Runtime polymorphism is all about figure out type information at run time and call the right function based on the real object type. The assumption here is that you do not have a way to determine what exact type of a object at compile time. We do not need bother to call a function indirectly if we can put the type information somewhere in the base class. Yes, template can do the dirty work.

Take a look at the new implementation below:

template <class T>
class IBaseImpl : public IBase
{
public:
    virtual void go() const
    {
        doSomething();
        doSomethingElse();
    }

protected:
    void doSomething() const
    {
        return static_cast<const T*>(this)->doSomething();
    }

    void doSomethingElse() const
    {
        return static_cast<const T*>(this)->doSomethingElse();
    }
};

class DerivedT : public IBaseImpl<DerivedT>
{
    friend IBaseImpl<DerivedT>;

protected:
    void doSomething() const
    {
        // Do something specific for class DerivedT
    }

    void doSomethingElse() const
    {
        // Do something else specific for class DerivedT
    }
};

OK, now let’s take a look at the code. There are couple of things you might notice:

  • First of all a template class, IBaseImpl is created. The template argument carries type of derived class. So it could down-cast it self to the sub-type
  • The sub-class now derived from IBaseImpl<DerivedT>. It passes itself to its base class so it could do the down-casting.
  • virtual functions like doSomething() and doSomethingElse() are not virtual any more. By down-casting the type we know exactly what function call. So there is no vtable query and indirect function call needed.

This pattern is used widely to reduce overhead of virtual function calls. You can find a lot of examples from ATL/WTL. The technique is mentioned for the first time in C++ Report, Feb. 1995 by James O. Coplien with a name “Curiously Recurring Template Patterns”. With the pattern we could

  • Save indirection calls at run time.
  • type cast is at compile time, so no runtime overhead out there.
  • static methods can be overridden in this way. Remember that you can never define a static virtual function.

Actually by doing this you only can gain a pseudo polymorphism (or static polymorphism, simulated dynamic binding, ATL style inheritance, upside down inheritance, whatever). I am calling it pseudo because it only have part of the magic that runtime polymorphism (virtual function) has:

  • go() have to be virtual otherwise we are not able to reference to the object by IBase. But we can use C++ extension __declspec(novtable) to eliminate the overhead of initializing vptr of base classes somehow.
  • Classes derived from class DerivedT cannot gain different behavior by override doSomething() and doSomethingElse().
  • Template classes have a code size deficit. If we have 10 different classes try to implement interface IBase, we’ll get 10 extra middle-level classes IBaseImpl<T> under the hood.

So the pattern definitely cannot replace virtual functions at all. We have to think really carefully and use the pattern only in necessary places.

Compile-time Polymorphism vs. Runtime Polymorphism

Now let’s keep distance with theory and see some numbers about the differences about two type of polymorphisms. Use the poor man’s profiler I have testing code as below. Note that I have added __declspec(novtable) to avoid creating and initialize vtable/vptr unnecessarily for classes that never have instance created.

#define NO_VTABLE __declspec(novtable)

struct NO_VTABLE IBase
{
    virtual void go() const = 0;
};

class Derived : public IBase
{
public:
    virtual void go() const
    {
        doSomething();
        doSomethingElse();
    }

protected:
    virtual void doSomething() const
    {
        // Do something specific for class Derived
    }

    virtual void doSomethingElse() const
    {
        // Do something else specific for class Derived
    }
};

template <class T>
class NO_VTABLE IBaseImpl : public IBase
{
public:
    virtual void go() const
    {
        doSomething();
        doSomethingElse();
    }

protected:
    void doSomething() const
    {
        return static_cast<const T*>(this)->doSomething();
    }

    void doSomethingElse() const
    {
        return static_cast<const T*>(this)->doSomethingElse();
    }
};

class DerivedT : public IBaseImpl<DerivedT>
{
    friend IBaseImpl<DerivedT>;
protected:
    void doSomething() const
    {
        // Do something specific for class DerivedT
    }

    void doSomethingElse() const
    {
        // Do something else specific for class DerivedT
    }
};

template <typename T>
void test_polymorphism(unsigned int N)
{
    const IBase& b = T();

    Timer timer;
    for (unsigned int i = 0; i < N; ++i)
        b.go();
}

int main()
{
    const unsigned int N = 1048576000;

    for(unsigned int i = 1000; i <= N; i *= 2)
    {
        printf ("--------- N:%d ----------\n", i);
        test_polymorphism<Derived>(i);
        test_polymorphism<DerivedT>(i);
    }

    return 0;
}

And the testing result looks like below in my machine:

 image

From the chart we can see the conclusions are:

  • The overhead of runtime polymorphism can be twice as expensive as compile time polymorphism
  • Optimization works :-)

Share/Save/Bookmark

What did we pay for C++ Runtime Polymorphism: Part I

One of the most useful and interesting features in C++ is virtual function. With the feature we are allowed to postpone determining the behavior of an object till run time. This gives us great flexibilities as we now are able to think of an object in terms of what category of types it belongs to, and the virtual functions will be evaluated respecting the object’s actual type while referring to it by category.

Under the hood C++ implement this by constructing a data structure called ‘virtual table’ at compile time, and adds an pointer called ‘virtual table pointer’ (aka. vptr) to each instances of the class. For each virtual function call, at run time we will first need to find proper virtual function table through vptr, and query the table with an offset which is filled by compiler. A call then can be made after the entry is found in the virtual table.

So what do we have to pay for this magic?

  • a vptr in each instance – class size gets bigger.
  • vptr need to be initialized in ctor of the class – this requires extra time.
  • virtual function call is indirect – a query to vtable has to be made before the call.

Now let’s see how expensive the pay is. As long as class size is not a major problem in a modern computer, time is all what we concern about. In this post we are going to take a look at the second bullet, that is, to see how virtual function impacts performance of ctor/dtor of a class. We will discuss about the performance impact of indirect function call in next post and see if we have any alternative approach to improve it.

First of all let’s make a poor man’s profiler, which looks like below. We are going to use the class to do couple of experiments. I am using Visual C++ 9.0 SP1 to do the test. All code is compiled in release mode but have optimization turned off because the compiler optimization will screw our analysis result, and analyze the effect itself is another huge story.

class Timer
{
public:
    Timer() : m_start(clock()) {}
    ~Timer()
    {
        printf("duration: %d\n", clock() - m_start);
    }

private:
    clock_t m_start;
};

My testing environment is 32bit Vista with SP1 on Intel Core(TM)2 Quad CPU 2.4GHz, 4.00GB RAM

Performance Impact: Constructor and Destructor

In order to test the performance of constructor and destructor, we need to implement a small helper function like below. The function basically just create and destroy instances of a certain type of class for many times to make the time difference obvious.

template <typename t>
void test_ctor_dtor(unsigned int N)
{
    Timer timer;
    for (unsigned int i = 0 ; i < N; ++i)
    {
        T c;
    }
}

Experiment 1: Flat Inheritance Hierarchy Case

We have to initialize the vptr in the constructor of a class. Extra time have to be spent to do the the work. In this experiment we have two classes, one has virtual table and one does not.  We will construct and destroy each of these classes to exam the time difference. Code shown as below:

struct ClassHasVTable
{
    virtual void foo() {}
};

struct ClassDontHaveVTable
{
    void foo() {};
};

int main()
{
    const unsigned int N = 1000000000;

    for(unsigned int i = 1000; i <= N; i *= 2)
    {
        printf ("------- N = %d ---------\n", i);
        test_ctor_dtor<ClassHasVTable>(i);
        test_ctor_dtor<ClassDontHaveVTable>(i);
    }
}

So if we take a look at the generated assembly code, The construct of ClassHasVTable would be like below. You can see clearly how the vptr is initialized. ClassDontHaveVTable doesn’t have a constructor at all. (For the reason, see Stanley B. Lippman’s book: Inside C++ Object Model).

PUBLIC	??_7ClassHasVTable@@6B@				; ClassHasVTable::`vftable'

;	COMDAT ??_7ClassHasVTable@@6B@
CONST	SEGMENT
??_7ClassHasVTable@@6B@ DD FLAT:?foo@ClassHasVTable@@UAEXXZ ; ClassHasVTable::`vftable'
CONST	ENDS

??0ClassHasVTable@@QAE@XZ PROC				; ClassHasVTable::ClassHasVTable, COMDAT
; _this$ = ecx
	push	ebp
	mov	ebp, esp
	push	ecx
	mov	DWORD PTR _this$[ebp], ecx
	mov	eax, DWORD PTR _this$[ebp]
	mov	DWORD PTR [eax], OFFSET ??_7ClassHasVTable@@6B@
	mov	eax, DWORD PTR _this$[ebp]
	mov	esp, ebp
	pop	ebp
	ret	0
??0ClassHasVTable@@QAE@XZ ENDP				; ClassHasVTable::ClassHasVTable

Calling test_ctor_dtor() with different N and I got the testing result as below. For this trivial case, ctor/dtor of a class has vptr is almost twice as slow as class that doesn’t have vptr. In my machine construct and destroy ~0.5 billion objects will cause about 1 second time loss.

image

Experiment 2: Object Construction and Destruction – Multiple Inheritance Hierarchy

Take a look at the class hierarchy below.

struct ClassHaveVTable
{
    virtual void foo() {}
};

struct SubClassHaveVTable : public ClassHaveVTable {};

struct SubSubClassHaveVTable : public SubClassHaveVTable {};

struct SubSubSubClassHaveVTable : public SubSubClassHaveVTable {};

int main()
{
    const unsigned int N = 1000000000;

    for(unsigned int i = 1000; i <= N; i *= 2)
    {
        printf ("------- N = %d ---------\n", i);
        test_ctor_dtor<ClassHaveVTable>(i);
        test_ctor_dtor<SubClassHaveVTable>(i);
        test_ctor_dtor<SubSubClassHaveVTable>(i);
        test_ctor_dtor<SubSubSubClassHaveVTable>(i);
    }
}

Since SubSubSubClassHaveVTable does nothing more than its ancient ClassHaveVTable, we would expect that creating a SubSubSubClassHaveVTable instance should not more expensive than creating a ClassHaveVTable instance. But wait! If we look back to see how an object is constructed, it would be pretty similar like human growing from baby to adult. That is, we have to start with initializing a ClassHaveVTable instance, including initialize its vptr. Then initialize SubClassHaveVTable specific class members as well as its vptr. After that we do the same thing for SubSubClassHaveVTable and SubSubSubClassHaveVTable is the last one we have to initialize. So if we counted it correctly the vptr is set 4 times when initialize a SubSubSubClassHaveVTable instance. The deeper class inheritance hierarchy is, the more extra work have to be done.

We can verify this by taking a look at SubSubSubClassHaveVTable’s constructor which is generated by the compiler automatically:

PUBLIC	??_7SubSubSubClassHaveVTable@@6B@		; SubSubSubClassHaveVTable::`vftable'

;	COMDAT ??_7SubSubSubClassHaveVTable@@6B@
CONST	SEGMENT
??_7SubSubSubClassHaveVTable@@6B@ DD FLAT:?foo@ClassHaveVTable@@UAEXXZ ; SubSubSubClassHaveVTable::`vftable'
CONST	ENDS

; 104  :     {
; 105  :         T c;

	mov	DWORD PTR _c$4297[ebp], OFFSET ??_7ClassHaveVTable@@6B@
	mov	DWORD PTR _c$4297[ebp], OFFSET ??_7SubClassHaveVTable@@6B@
	mov	DWORD PTR _c$4297[ebp], OFFSET ??_7SubSubClassHaveVTable@@6B@
	mov	DWORD PTR _c$4297[ebp], OFFSET ??_7SubSubSubClassHaveVTable@@6B@

; 106  :     }

The testing result is illustrated as the chart below. As we can see for classes that have virtual functions, the performance of ctor/dtor may have 10~40% decreases for every Inheritance level.

image

Share/Save/Bookmark

Generate Assembly From C++ Code in Visual Studio

Sometimes we want to see the assembly code generated from a piece of C++ code. It could be very helpful for:

  • Better understanding about how compiler optimize our code
  • Better understanding about the dark side of the programming language that we are using to avoid the pitfalls.
  • Finding performance hints.
  • Debugging really tricky defects.

And I found that I am not the only person have this wish:

Larry’s Rule of Software Engineering, Part I: Every software engineer should know roughly what assembly language their code generates.

So I think the only question left is: how to generate assembly from our C++ source code? You can of course use a debugger to disassembly the binary file but apparently that is not an efficient way.

There is actually an option in Visual C++. Right click the cpp file and select ‘Properties’. In the property pages set Configuration Properties –> C/C++ –> Output Files –> Assembler Output to “Assembly With Source Code (/FAs)”:

C  _Assembly_Options After build your project you should be able to find the *.asm file generated under $(IntDir), as set in “ASM List Location” property.

You can make the similar change in your project property page. I would not recommend to do this as that will generate assembly for all cpp files in your project, which is not necessary in most cases, and will greatly slow down your compile speed.

Internally change the property just enable the /FAs switch for cl.exe. You can take a look at MSDN for more detailed information.

Share/Save/Bookmark

WTL Wizard for Visual Studio 2008

WTLAh … I realized that this is going to be the first post in my blog … wow!

While the latest version of WTL was released last year, it was updated with whole bunch of Vista support.  One thing that is really painful using non-MFC UI framework before is that there doesn’t have a good IDE support. A good news is that WTL 8.0 released with application wizards for both Visual C++ and Visual C++ Express. As easy as with MFC, a default project is created just with several mouse clicks.

wtlwizard

It is very easy to install the wizard. After the WTL package is unzipped, there is a folder named ‘AppWiz’. Under which there are some javascript files for installing the wizard automatically. You only need to pick proper script for the different version of Visual Studio you installed. All easy shots. While this is very nice but one last minor issue is that at that time Visual Studio 2008 has not been released yet. Thus there is no install script for Visual Studio 2008.

I poked around internet and saw some people asking the question here and there. Actually the solution is pretty straight forward: open ‘setup80.js’ and edit it with notepad. Correct any registry key as if they are in Visual Studio 2008. Save it back, run it and you should be all set.

If you do not want to modify the script yourself, here is the code code I modified. Copy the code and save it at %WTL%\AppWiz\setup90.js. If you are in Vista, make sure you run it as Administrator. You are using the code at your own risk. No warrant is promised or implied by CodeGem.org.

// Windows Template Library - WTL version 8.0
// Copyright (C) Microsoft Corporation. All rights reserved.
//
// This file is a part of the Windows Template Library.
// The use and distribution terms for this software are covered by the
// Common Public License 1.0 (http://opensource.org/osi3.0/licenses/cpl1.0.php)
// which can be found in the file CPL.TXT at the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by
// the terms of this license. You must not remove this notice, or
// any other, from this software.

// Setup program for the WTL App Wizard for VC++ 9.0 (Orcas)

main();

function main()
{
	// Decode command line arguments
	var bDebug = false;
	var bElevated = false;
	var Args = WScript.Arguments;
	for(var i = 0; i < Args.length; i++)
	{
		if(Args(i) == "/debug")
			bDebug = true;
		else if(Args(i) == "/elevated")
			bElevated = true;
	}

	// See if UAC is enabled
	var Shell = WScript.CreateObject("Shell.Application");
	if(!bElevated && Shell.IsRestricted("System", "EnableLUA"))
	{
		// Check that the script is being run interactively.
		if(!WScript.Interactive)
		{
			WScript.Echo("ERROR: Elevation required.");
			return;
		}

		// Now relaunch the script, using the "RunAs" verb to elevate
		var strParams = "\"" + WScript.ScriptFullName + "\"";
		if (bDebug)
			strParams += " /debug";
		strParams += " /elevated";
		Shell.ShellExecute(WScript.FullName, strParams, null, "RunAs");
		return;
	}

	// Create shell object
	var WSShell = WScript.CreateObject("WScript.Shell");
	// Create file system object
	var FileSys = WScript.CreateObject("Scripting.FileSystemObject");

	// Get the folder containing the script file
	var strValue = FileSys.GetParentFolderName(WScript.ScriptFullName);
	if(strValue == null || strValue == "")
		strValue = ".";

	var strSourceFolder = FileSys.BuildPath(strValue, "Files");
	if(bDebug)
		WScript.Echo("Source: " + strSourceFolder);

	if(!FileSys.FolderExists(strSourceFolder))
	{
		WScript.Echo("ERROR: Cannot find Wizard folder (should be: " + strSourceFolder + ")");
		return;
	}

	try
	{
		var strVC9Key = "HKLM\\Software\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir";
		strValue = WSShell.RegRead(strVC9Key);
	}
	catch(e)
	{
		try
		{
			var strVC9Key_x64 = "HKLM\\Software\\Wow6432Node\\Microsoft\\VisualStudio\\9.0\\Setup\\VC\\ProductDir";
			strValue = WSShell.RegRead(strVC9Key_x64);
		}
		catch(e)
		{
			WScript.Echo("ERROR: Cannot find where Visual Studio 9.0 is installed.");
			return;
		}
	}

	var strDestFolder = FileSys.BuildPath(strValue, "vcprojects");
	if(bDebug)
		WScript.Echo("Destination: " + strDestFolder);
	if(!FileSys.FolderExists(strDestFolder))
	{
		WScript.Echo("ERROR: Cannot find destination folder (should be: " + strDestFolder + ")");
		return;
	}

	// Copy files
	try
	{
		var strSrc = FileSys.BuildPath(strSourceFolder, "WTLAppWiz.ico");
		var strDest = FileSys.BuildPath(strDestFolder, "WTLAppWiz.ico");
		FileSys.CopyFile(strSrc, strDest);

		strSrc = FileSys.BuildPath(strSourceFolder, "WTLAppWiz.vsdir");
		strDest = FileSys.BuildPath(strDestFolder, "WTLAppWiz.vsdir");
		FileSys.CopyFile(strSrc, strDest);
	}
	catch(e)
	{
		var strError = "no info";
		if(e.description.length != 0)
			strError = e.description;
		WScript.Echo("ERROR: Cannot copy file (" + strError + ")");
		return;
	}

	// Read and write WTLAppWiz.vsz, add engine version and replace path when found
	try
	{
		var strSrc = FileSys.BuildPath(strSourceFolder, "WTLAppWiz.vsz");
		var strDest = FileSys.BuildPath(strDestFolder, "WTLAppWiz.vsz");

		var ForReading = 1;
		var fileSrc = FileSys.OpenTextFile(strSrc, ForReading);
		if(fileSrc == null)
		{
			WScript.Echo("ERROR: Cannot open source file " + strSrc);
			return;
		}

		var ForWriting = 2;
		var fileDest = FileSys.OpenTextFile(strDest, ForWriting, true);
		if(fileDest == null)
		{
			WScript.Echo("ERROR: Cannot open destination file" + strDest);
			return;
		}

		while(!fileSrc.AtEndOfStream)
		{
			var strLine = fileSrc.ReadLine();
			if(strLine.indexOf("Wizard=VsWizard.VsWizardEngine") != -1)
				strLine += ".9.0";
			else if(strLine.indexOf("WIZARD_VERSION") != -1)
				strLine = "Param=\"WIZARD_VERSION = 9.0\"";
			else if(strLine.indexOf("ABSOLUTE_PATH") != -1)
				strLine = "Param=\"ABSOLUTE_PATH = " + strSourceFolder + "\"";
			fileDest.WriteLine(strLine);
		}

		fileSrc.Close();
		fileDest.Close();
	}
	catch(e)
	{
		var strError = "no info";
		if(e.description.length != 0)
			strError = e.description;
		WScript.Echo("ERROR: Cannot read and write WTLAppWiz.vsz (" + strError + ")");
		return;
	}

	// Create WTL folder
	var strDestWTLFolder = "";
	try
	{
		strDestWTLFolder = FileSys.BuildPath(strDestFolder, "WTL");
		if(!FileSys.FolderExists(strDestWTLFolder))
			FileSys.CreateFolder(strDestWTLFolder);
		if(bDebug)
			WScript.Echo("WTL Folder: " + strDestWTLFolder);
	}
	catch(e)
	{
		var strError = "no info";
		if(e.description.length != 0)
			strError = e.description;
		WScript.Echo("ERROR: Cannot create WTL folder (" + strError + ")");
		return;
	}

	// Read and write additional WTLAppWiz.vsdir, add path to the wizard location
	try
	{
		var strSrc = FileSys.BuildPath(strSourceFolder, "WTLAppWiz.vsdir");
		var strDest = FileSys.BuildPath(strDestWTLFolder, "WTLAppWiz.vsdir");

		var ForReading = 1;
		var fileSrc = FileSys.OpenTextFile(strSrc, ForReading);
		if(fileSrc == null)
		{
			WScript.Echo("ERROR: Cannot open source file " + strSrc);
			return;
		}

		var ForWriting = 2;
		var fileDest = FileSys.OpenTextFile(strDest, ForWriting, true);
		if(fileDest == null)
		{
			WScript.Echo("ERROR: Cannot open destination file" + strDest);
			return;
		}

		while(!fileSrc.AtEndOfStream)
		{
			var strLine = fileSrc.ReadLine();
			if(strLine.indexOf("WTLAppWiz.vsz|") != -1)
				strLine = "..\\" + strLine;
			fileDest.WriteLine(strLine);
		}

		fileSrc.Close();
		fileDest.Close();
	}
	catch(e)
	{
		var strError = "no info";
		if(e.description.length != 0)
			strError = e.description;
		WScript.Echo("ERROR: Cannot read and write WTL\\WTLAppWiz.vsdir (" + strError + ")");
		return;
	}

	WScript.Echo("App Wizard successfully installed!");
}


Enjoy!

Share/Save/Bookmark

February 2010
M T W T F S S
« Jun «-»  
1234567
891011121314
15161718192021
22232425262728

Blogroll