__declspec(novtable) is a storage-class attribute and Microsoft specific applied to classes declaration.
It's purpose is to tell the compiler to not add the default vtable initialization\deinitialisation code in the class ctor\destructor.
When the compiler sees a class with virtual methods, it automatically adds vtable init\deinit code in the class ctor\destructor methods. Remember that the vtable is pointer to an array of pointers of the class virtual methods. I guess the initializatio is needed at the ctor time because at the time when your ctor method code is entered, the inner workings of the C++ object need to be initialized. Note that although the vtable is available on thee ctor code, its useless to use it, i.e. calling any virtual method. This is because the vtable of any derived class is not yet initialized.
So, using __declspec(novtable) we will not have the vtable initialized\deinitialized in the ctor\destructor methods.
This means that we cannot use the vtable in any way. More specifically, it means we cannot call any of the object's virtual method.
This has interesting effects. The obvious use is for interfaces, classes with pure virtual functions.
class __declspec(novtable) IFoo
{
public:
virtual ~IFoo()
{
}
virtual void foo() = 0;
};
class FooImpl : public IFoo
{
public:
virtual void foo();
};
IFoo* p = new FooImpl();
p->foo();
So the ctor\dctor of IFoo doesn't have any vtable initialization code as FooImpl has.
When we call foo() on the object the compiler sees that we use a pointer to an object and the method is virtual. Hence, it will use the vtable to get to the method. Vtable is a hidden pointer data in a class. It is always the first data when looking at the raw object in memory.
The FooImpl ctor initialized the vtable so the call is legit and always succeeds.
Remember that although the we call the Foo method of a pointer to IFoo, the vtable hidden data points to the FooImpl.
The vtable pointer is always the same in the raw object in all class derivations.
Another legit code is the following one:
class __declspec(novtable) A {
virtual void func1() { cout<<1; }
virtual void func2() { cout<<2; }
virtual void func3() { cout<<3; }
};
class A1 : public A {
// no func1, func2, or func3
};
It is OK to write:
A* p = new A1();
p->func1();
p->func2();
p->func3();
because A1 ctor initialize the object vtable.
And its NOT OK to write:
A* p = new A();
A->func1(); // undefined behaviour
p->func2(); // undefined behaviour
p->func3(); // undefined behaviour
It is OK to write:
A p;
p.func1();
p.func2();
p.func3();
because calling methods not using a pointer does not make use of vtable.
When using the novtable specifier, always think if the vtable is needed.
For example, you cannot use novtable for a class calling virtual methods in ctor\destructor. This is because the novtable told the compiler to omit the code which initialize the vtable before entering the ctor code.
This is very well described in the paper:
http://msdn.microsoft.com/msdnmag/issues/0300/c/
Thursday, March 13, 2008
References in favor of pointers use
Always try to use reference instead of pointer to data wherever is possible.
A pointer to a data always raises the question if the pointer holds valid data, if it's 0 (NULL) for instance.
A reference shows that an object is passed and we don't do any data validation anymore, as for pointers.
// foo needs an instance of Foo
void foo(Foo* pF)
{
_ASSERTE(pF != 0);
....
}
// foo needs an instance of Foo
void foo(Foo& f)
{
....
}
It makes code writing and reading easier.
Using pointer variable is necessarily though in some situations.
You might want an optional parameter to your method:
// foo does NOT need an instance of Foo
void foo(Foo* pF)
{
if(pf != 0)
{
// use Foo object
....
}
else
{
// do something else in absence of a Foo object
...
}
}
This coding style and other good ones are mentioned in the paper http://www.research.att.com/~bs/JSF-AV-rules.pdf
The paper is mentioned on the home page of the father of C++.
A pointer to a data always raises the question if the pointer holds valid data, if it's 0 (NULL) for instance.
A reference shows that an object is passed and we don't do any data validation anymore, as for pointers.
// foo needs an instance of Foo
void foo(Foo* pF)
{
_ASSERTE(pF != 0);
....
}
// foo needs an instance of Foo
void foo(Foo& f)
{
....
}
It makes code writing and reading easier.
Using pointer variable is necessarily though in some situations.
You might want an optional parameter to your method:
// foo does NOT need an instance of Foo
void foo(Foo* pF)
{
if(pf != 0)
{
// use Foo object
....
}
else
{
// do something else in absence of a Foo object
...
}
}
This coding style and other good ones are mentioned in the paper http://www.research.att.com/~bs/JSF-AV-rules.pdf
The paper is mentioned on the home page of the father of C++.
Monday, February 04, 2008
MSI remember-to's
I am working with a wix installer and i needed to perform a specific task on uninstall.
After coding and compiling the msi, I tried to uninstall the product but I got a nasty error saying that the uninstallation cannot continue. The first thing i thought was 'shit, I cannot uninstall it'.
But here's a solution: MSI copies the original MSI file in the folder:
C:\WINDOWS\Installer
and the filename is in the form of 9c2ebd.msi
You can sort the files by 'Creation Date' if you know when did u install it.
I used then Orca editor to remove the uninstall custom actions which caused the uninstall to be unsucessful.
Also, another useful to know location is
C:\DOCUME~1\[user]\LOCALS~1\Temp
this is the location where the MSI log are written during installation\uninstallation. sort files by 'Creation Date'to see the log.
After coding and compiling the msi, I tried to uninstall the product but I got a nasty error saying that the uninstallation cannot continue. The first thing i thought was 'shit, I cannot uninstall it'.
But here's a solution: MSI copies the original MSI file in the folder:
C:\WINDOWS\Installer
and the filename is in the form of 9c2ebd.msi
You can sort the files by 'Creation Date' if you know when did u install it.
I used then Orca editor to remove the uninstall custom actions which caused the uninstall to be unsucessful.
Also, another useful to know location is
C:\DOCUME~1\[user]\LOCALS~1\Temp
this is the location where the MSI log are written during installation\uninstallation. sort files by 'Creation Date'to see the log.
Thursday, November 15, 2007
RichEdit common contrl
i'll keep this post update with all the things about Rich Edit.
#1 The RichEdit Version
The current versions of RichEdit control and their modules are:
ver 1.0 contained in Riched32.dll
ver 2.0 contained in Riched20.dll
ver 3.0 contained in Riched20.dll
ver 4.1 contained in Msftedit.dll
The Rich Edit 3.0 (Riched20.dll) is included in releases of Microsoft Windows 2000 and above.
#2 Initialization
You need to explicitly load the RichEdit library in your application in order to use the control. Hence, to use the 3.0 version call:
if(NULL == LoadLibrary("RichEd20.dll"))
{
// cannot load RichEdit v3.0
}
Open and select a file in windows explorer
Many applications can open an windows explorer window an select a file. This is done by running explorer.exe with some command line switches.
There is a KB with the documented windows explorer switches:
The KB says that the supported versions include old OSes like Windows 95 but I think that 'select' switch for example works only for Windows XP and above.
Here is another KB for XP Windows Explorer.
A solution for older Windows OSes is to use Shell API i think.
I have not tried this but one solution would be to:
call CreateProcess( "explorer.exe" )
enumarate from ROT the windows explorer instances
connect to a instance and use the Shell API to navigate to the file.
There is a KB with the documented windows explorer switches:
The KB says that the supported versions include old OSes like Windows 95 but I think that 'select' switch for example works only for Windows XP and above.
Here is another KB for XP Windows Explorer.
A solution for older Windows OSes is to use Shell API i think.
I have not tried this but one solution would be to:
call CreateProcess( "explorer.exe" )
enumarate from ROT the windows explorer instances
connect to a instance and use the Shell API to navigate to the file.
Monday, October 08, 2007
smallest EXEcutable
This is an interesting page discussing techniques to achieve the smallest EXE possible.
http://www.phreedom.org/solar/code/tinype/
More, it says that if you add an UNC path as a name of an imported DLL in an executable, Windows will download and execute that file !
the server needs to be a WebDAV server.
http://www.phreedom.org/solar/code/tinype/
More, it says that if you add an UNC path as a name of an imported DLL in an executable, Windows will download and execute that file !
the server needs to be a WebDAV server.
Thursday, October 04, 2007
the one and only, the TB_ADDSTRING message !
If you try to use TB_ADDSTRING with the string from the resource then you might had blown your head trying to figure out what the heck is happening, why does it fail:
const int iIndex = ::SendMessage(
hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
iIndex is returned -1 (indicating an error).
Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:
No, it's not documented at all.
Something was fishy from the start though.
When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?
The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.
In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:
TBBUTTON tbbtn = {0};
...
tbbtn.iString = _T("Text");
or
TBBUTTON tbbtn = {0};
...
tbbtn.iString = 0; // index
The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:
a character array with one or more null-terminated strings:
LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);
or from a string in the executable resource:
::SendMessage(hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||
In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));
Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.
const int iIndex = ::SendMessage(
hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
iIndex is returned -1 (indicating an error).
Now, I looked at my MSDN from disk and then looked at the online version. Nada. But the old friend Mr. Google saved the day (he always does). I got this page:
The problem with TB_ADDSTRING or the AddString method that is simply
a wrapper to this message is that the first character in the string
resource is used as separator and is replaced with a NULL character.
So your string resource must be something similar to: "|Button text||"
where the pipe character is replaced with '\0'.
This is because the string you pass with TB_ADDSTRING message must
have a double null terminator. You can also use the TB_ADDSTRINGS
message with which you can set all the button texts using a string
like this: "|Button 1|Button 2|Button 3|Button 4||".
This is not well documented or not documented at all, I don't
remember. Anyway keep in mind that the text added to the button with
TB_ADDSTRING is not retrieved using TB_GETBUTTONINFO, but only using
TB_GETBUTTONTEXT, this is why if you use this method instead of using
TB_SETBUTTONINFO, the chevron menu does not display the text of the
menu items
No, it's not documented at all.
Something was fishy from the start though.
When you create the toolbar using the CreateToolbarEx API, you pass an array of TBBUTTON structs. TBBUTTON has its last field either an zero-based index or a pointer to string buffers (the last string needed to be terminated by an extra null to indicate the end of the list).
Zero-based index of the button string, or a pointer to a string buffer that contains text for the button.
But the question is: In the case when you specified in the TB_ADDSTRING a resource string id, what's the zero-based index from TBBUTTON for ?
The problem is that the documentation is incomplete due to what the API tried to achieve. It wanted to provide several ways to add strings but the documention failed to explain exactly.
In toolbar controls you can set buttons text in at least 2 ways:
One way is when you create the toolbar using CreateToolbarEx API.
In each TBBUTTON structure you can put either a straight text buffer or a zero-based index of the button string:
TBBUTTON tbbtn = {0};
...
tbbtn.iString = _T("Text");
or
TBBUTTON tbbtn = {0};
...
tbbtn.iString = 0; // index
The index value is the index from the string list you set to the toolbar control using the TB_ADDSTRING message. The string list can be from 2 sources, either from:
a character array with one or more null-terminated strings:
LPCTSTR szTexts[] = {_T("Text1"), _T("Text2\0") };
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)szTexts);
or from a string in the executable resource:
::SendMessage(hToolBar,
TB_ADDSTRING,
hResource,
IDS_TBSEARCH); // string id from module's resource
but in the latter case the resource string needs to be in a special form. Its first character will be used by the Common controls API as string separator, so
IDS_TBSEARCH can look like this: |Text1|Text2||
In the first case, you can use TB_ADDSTRING at a time, to add each string to the control internal list.
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text1\0"));
::SendMessage(hToolbar, TB_ADDSTRING, NULL, (LPARAM)_T("Text2\0"));
Another way to set a toolbar button text is using the TB_SETBUTTONINFO message but you are limited to pass only a string buffer.
Subscribe to:
Posts (Atom)