Wednesday, June 22, 2005

Scripting ActiveX control in WSH

Last week is quite frustrating for me. I have a ActiveX control created by MFC ActiveX wizard. The control has been used in web browser (scripted by javascript) and runs very well. Recently I need to script it in WSH, but every method invoked failed with 'catastrophic failure' error message.

The worse part is that it took me significant amount of time to find the answer from google group. I picked wrong words at the beginning which unfortunately generated lots of noise.

The lesson: think harder about the search string instead wasting time on irrelevant results ("catastrophic failure activex wsh" is the best for this issue). And it's better if I go directly to the related news groups/maillists (microsoft.public.scripting.wsh in this case).

Here I just quote the answer from Kyle Alons:

"First, a little background: ActiveX controls are normally persisted to/from some type of permanent storage, and they do this by implementing one or more IPersist* interfaces. Containers are supposed to call one of the IPersist methods to initialize or load the control upon creation.

COleControl, which all MFC controls derive from, enforces that this initialization has taken place before allowing any calls to be made to the control (COleDispatchImpl::Invoke, line 1209 of OLEDISP1.CPP calls IsInvokeAllowed, which checks a protected m_bInitialized member variable for TRUE; this variable is set in the IPersist* implementations in CTLPSTM.CPP).

Since WSH never calls the IPersist* methods, the above test fails, and the result is the 'catastrophic failure' error message. The test control container (and VB6, which I also tested), are smart enough to detect when a control class has been instantiated and call an IPersist*->InitNew method to initialize the control.

One workaround is to add the statement m_bInitialized = TRUE; in your control's constructor. This may be an acceptable workaround in this case, because the control isn't truly being hosted and its state isn't being loaded from anywhere. This seems like an oversight by the WSH team, since most other environments don't require the workaround; on the outside chance that the MS developers are actually reading this, maybe they can address it.


Monday, June 20, 2005

Unit Testing for Network Programming

Network program is error-prone to write and hard to test, how can we make our life easier with the help of unit testing? Let's start with short examples:

Which function is easier to test?

int funcion1(const char* filename)
{ and process file...

int function2(std::istream& stm)
...process file...

The answer is function2: it avoids the dependency on external resource, so you can create a string and pass it in (see more on C++ coding standard item 63).

This is a basic technique that every unit testing book will teach. To apply it to network programming, we need to choose a stubable high level abstraction (instead of the low level socket as integer).

I use ACE as network library. While it provides very nice high level abstractions like ACE_SOCK_Stream, those classes are not designed to be base class (members are not virtual). This is understandable since lots of network programmers prefer to get the best performance they can.

So we have two choices here:

1. Use C++ template, and make the substitution happen at compile time.


2. Create a wrapper to provide dynamic polymorphism (C++ coding style item 64).

I choose the later since it provides more flexibility.

Here is what the code looks like:

class ISockStream
virtual ~ISockStream(){}

virtual ssize_t send (const void *buf,
size_t n,
int flags = 0,
const ACE_Time_Value *timeout = NULL) = 0;

virtual ssize_t recv (void *buf,
size_t n,
int flags = 0,
const ACE_Time_Value *timeout = NULL) = 0;

template <class _ACE_PEER_STREAM>
class StreamAdapter : public ISockStream
ssize_t send (const void *buf,
size_t n,
int flags = 0,
const ACE_Time_Value *timeout = NULL)
return stream_.send(buf, n, flags, timeout);

ssize_t recv (void *buf,
size_t n,
int flags = 0,
const ACE_Time_Value *timeout = NULL)
return stream_.recv(buf, n, flags, timeout);

class MockStream : public ISockStream
ssize_t send (const void *buf,
size_t len,
int flags = 0,
const ACE_Time_Value *timeout = NULL)
//save the data somewhere (std::string for example)
//so that we can test the result later.

ssize_t recv (void *buf,
size_t len,
int flags = 0,
const ACE_Time_Value *timeout = NULL)
//generate fake data,
//we can even trigger failure (return -1).


In real product, we are going to use things like StreamAdapter<ACE_SOCK_Stream>. While in unit testing, MockStream will be sufficient.

Wednesday, June 15, 2005

Parser generator used for popular languages

Recently I need a parser generator in my new project, so I spent a while to check what other people are using. Here is the result:

Jython: JavaCC (with asdl)
Python: YACC
IronPython: Hand made recursive descent parser

Rhino/SpiderMonkey: Hand made recursive descent parser

Beanshell: JavaCC (with JJTree)
Groovy: Antlr

Boo: Antlr

Ruby: YACC
JRuby: Jay

Mono C#: Jay

Perl: YACC

Eclipse JDT: Jikes Parser generator

CFront: YACC, but Stroustrup actaully likes to have a hand made recursive descent parser, see [5]

[1] coyote: Syntax coloring
[2] Pydev parser... the future??!?!?!
[3] Jan Arne Petersen on jruby-devel
[4] Java Abstract Syntax
[5] Stroustrup, The Design and Evolution of C++
[6] JDT Core HowTo: Generate the Parser

To be continued...

Thursday, June 09, 2005

"-ko" in tortoise cvs

When managing third party libraries, I like to create a repository in house (for C++, it includes headers and compiled libs). Pragmatic Version Control Using CVS also recommends this approach.

One of the annoying thing you may encounter is the keyword expansion feature of CVS (I do not like it, so do the authors of the above cvs book). It means when importing/adding files, you have to specify "-ko"(keyword off) in command line. For tortoise CVS, you need to:

1. Select all the files you want to add;
2. Right Click, find "keywords" option, set it to "disabled(0)".

code or data?

I am thinking the two most common security problems - buffer overflow for binary program and cross site scripting for website - share the same nature: instead of treating user's input as pure data, the system/runtime may execute them as code, which in turn grant the user the ability to 'customize' some of the behavior of the program.

Even though the modern compilers(like java and C#) fight hard to make buffer overrun disappear, those "executable data" will not go away (sql injection for example), and the restriction of sandbox can not help too much.

In , researches start to add build-in support for data access and concurrency. Security may be on the list somedays, although now I have no idea how it fit in.

Friday, June 03, 2005

Suppressing splash screen of source insight

Source insight is my editor of choice for 6 years. The reason? It is super fast, especially when your are searching symbols in monster projects, like linux kernel, ACE and TAO.

One small issue I feel annoying is the splash screen. The manual did a poor job here, it only tells me how to suppress the splash screen if I launch the program from command line (that is what I do most of the time), but occasionly I just want to launch it from windows explorer (by clicking an associated file), the splash screen just on my way.

I sent their support an email, and their support(Ray Gram) emailed me back the solution in ten minutes! Well, I really fell shame for not asking the question ealier:)

Since I can not find it via google (it is not in the current manual either) , let's share it:

You can suppress the splash screen with the -s command line option.
For example:

Insight3 -s

You can also do it by adding a registry value. Under the key:

HKEY_CURRENT_USER\Software\Source Dynamics\Source Insight\3.0\Options

Create a text value named "NoSplash" with a value of "1".

Ray Gram
Source Dynamics, Inc.

Thank you, Ray!

Thursday, June 02, 2005

Memory footprint

That was two months ago, I need to reduce the memory footprint of one of my application. I checked the "Mem Usage" column in windows task manager, the result is surprsing: the process uses more than 10M just after it hits main().

The good news is it did not take me long to figure out that "Mem Usage" means the size of the current working set, which includes the shared data, dynamic libaries (DLL) for example.

Even the minimal hello world program requires lots of system dlls to run, so it takes about 1,000K after startup. If you add winsock, it soon becomes 1,500K.

The article of Joseph M. Newcomer explained the topic very well. And this one touched .NET.