Thursday, December 29, 2005

wxWidgets resources compile again every time you hit "Run" (F5)

Another wxWidgets related answer...

Symptoms:
You include the wxWidgets resources in your project (wxrc.rc). Now everytime you hit 'Run' (F5) Visual Studio (7) thinks it has to rebuild some of the sources and the resources.

Solution:
Create a file 'inc_wxrc.rc' with the following contents:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by mikadoApp.rc
//
#include
Do not add wx.rc to your project directly. Either add 'inc_wxrc.rc' to the project or, if you have a .rc file that includes wx.rc, include 'inc_wxrc.rc' instead.

Advertisment (for my own product):
The wxVisualSetup Project Wizard does this automatically and even add a version resource to your binary. It is available for €39.95 for Visual Studio 2002, 2003 and 2005, all versions.

Best regards

Hajo

Tuesday, December 27, 2005

Compile error using XRC and Unicode (VC++ 8.0 Express)

Since I've seen this asked several times now, I'll blog the answer:

You are using wxWidgets XRC Resources and compile a Unicode application (which is default on Visual Studio 8.0 Express) and are getting an error similar to
xrcdemo.cpp:63:1: pasting "LL" and "L"UsernameField"" does not give a valid preprocessing token
xrcdemo.cpp: In member function `void LoginDialog::InitWidgetsFromXRC()':
The error is in how you are using the XRC macros. XRC macros don't accept string constants enclosed in _T(). Use string contants without _T(), even in Unicode builds.

Summary: Use _T() with XRC methods, do not use _T() with XRC macros.


Suppose you have

wxXmlResource::Get()->LoadDialog(this, NULL, _T("LoginDialog"));
UsernameField = XRCCTRL(*this, _T("UsernameField"), wxTextCtrl);


You must enclose strings in _T(), if you want to be able to compile your application in both, ANSI and Unicode builds. So LoadDialog(this, NULL, _T("LoginDialog")); is the correct way to specify 'LoginDialog' as the XRC resource.

But, the XRC macros that take a string use _T() internally already. So XRCCTRL(*this, _T("UsernameField"), wxTextCtrl) is wrong, because XRCCTRL is a macro and it adds _T() to the 'UsernameField' parameter itself. The compiler then sees something like

xrcctrl_function(*this, _T( _T("UsernameField") ), wxTextCtrl)


and prints an error.

The correct use of the XRCCTRL and similar macros is to pass in strings without _T(), even in Unicode builds.

UsernameField = XRCCTRL(*this, "UsernameField"), wxTextCtrl);

Some more background for the interested: _T() is a simple macro that simply adds the literal 'L' in front of a quoted string in Unicode builds and does nothing in ANSI builds. Unicode strings are wide-character strings (wchar_t). Wide-character string constants must be preceded by an L in C++.

"This is a char string (1byte), or ANSI string"
L"This is a wide-char string (2byte/4byte under linux), or Unicode string"


Using XRCCTRL(*this, _T("UsernameField"), wxTextCtrl) would expand to xrcctrl_function(*this, LL"UsernameField", wxTextCtrl), which is what the compiler error message says.

Best regards

Hajo Kirchhoff

P.S. Please have a look at wxVisualSetup for Visual Studio 2002/2003 or 2005 to support this blog.

Friday, December 23, 2005

wxVisualSetup for Visual Studio 2005 released

I have just released the next version of wxVisualSetup, the package that integrates the wxWidgets GUI class library into Microsofts Visual Studio IDEs. If you are using wxWidgets and Microsoft Visual Studio, you can find more information at http://www.litwindow.com/wxvisualsetup

Thursday, December 22, 2005

Your application crashes (fails to initialise) with Visual Studio 8.0 and wxWidgets DLLs

If you are using Visual Studio 8.0 and wxWidgets DLLs and your application crashes immediately after starting or cannot be initialised properly, here is the fix:

  1. Open the file $(WXWIN)\src\msw\main.cpp
  2. Search for the function 'DllMain'
  3. Remove the entire function
  4. Recompile wxWidgets DLLs
Background:

When you open the wxWidgets .dsp files with Visual Studio 8.0, the solution conversion wizard adds the preprocessor symbol _WINDLL to the solution. This symbol is not defined when you compile the DLLs with older Visual Studios (.NET or 2003). When it is defined, the function DllMain gets compiled and included. This is causing the crash.

Monday, December 19, 2005

Upgrading your wxWidgets project to Visual Studio 8.0

Ok, so I finally found some time to start using my brand-new MS Visual Studio 2005 and try and compile my projects with it. As usual, moving to the next major version of a Microsoft Compiler involves a lot of work. Here is what I found so far:

Warnings, warnings, warnings...
The first thing you'll notice are hundreds of warnings. In an effort to make software more secure, MS has depreciated many runtime library functions that where the cause of so many buffer overflows. No more strcpy, strdup, localtime etc... Same with std::copy and other functions that are being used by the C++ standard runtime library.
I am not sure I really like this. This seems like a good idea at first, but only for new projects. For all other projects, people are probably simply going to disable the warnings. That is what I recommend when you upgrade your projects. Especially your wxWidgets projects, since the recommended replacement functions strcpy_s, localtime_c etc... are MS-specific and not found in any other compiler I know of.

So, in your project, define these macros

#define _CRT_SECURE_NO_DEPRECATE
tell the compiler to stop complaining about using localtime etc...

#define _SCL_SECURE_NO_DEPRECATE
stop complaining about std::copy etc...

The best place to do this is in your precompiled header file such as stdwx.h, if you have one. Of course you can add these defines to the project file itself, but you'll have to add them to all configurations.

POSIX vs. ISO
More warnings: Microsoft also depreciated all Posix names. Did you know strcpy should actually be called _strcpy in the ISO standard? I know now. All Posix names - and there are many - have been depreciated in favor of the new ISO standard. Good idea? Probably. Someone has to make a start. But unless you are willing to spend time changing your code
and your gnu or whatever other compiler you are using supports it, you'll want to disable these warnings as well:

#define _CRT_NONSTDC_NO_DEPRECATE
to stop complaining about old POSIX names

Manifest
If you include the wx.rc file - which you should -, you will get an error when you compile your project with VC 8.0: invalid COFF or something. The new 8.0 linker has the ability to generate a manifest file on the fly and the .dsp/.sln conversion wizard enables this by default. So now your application has two manifest files, one included via wx.rc and the new one generated by the linker. The linker does not like this. You have two options.
  1. Define wxUSE_NO_MANIFEST to stop using the wxWidgets manifest file. You must change the file settings for your .rc file and include this define there. Using the precompiled header file as mentioned above won't work.
  2. Disable the linker|manifest option: Open the properties for your project. Select 'all configurations', open the linker settings, manifest file and set 'generate manifest' to no.
I recommend the first option as it lets the linker create a manifest with all the correct DLL side-by-side information. If you disable the linker|manifest option, a debug build may not find the MSVCP80D.DLL for example and you have to copy the DLL into your path. If the linker creates the manifest file, all the correct DLL paths are being used.

Saturday, July 09, 2005

Intellisense stops working for a class...

Symptoms: Intellisense stops working for an entire class.
Conditions:
  • You are exporting symbols with a preprocessor macro #defined to _declspec(dllexport or dllimport)
  • You are exporting individual members of a class as opposed to the entire class
I have a love/hate relationship with my Visual Studio Intellisense. It is a great feature ... if it works. I'm working on an C++ ODBC wrapper class right now and Intellisense did not like my odbc::statement class. I am using an LWODBC_API define that expands to _declspec(dllimport/dllexport) to import or export my classes and members. But I did not want to export the entire odbc::statement class. The class contains a couple of map<> and other constructs that cannot be accessed by 'clients of the DLL'. You know the error.

class LWODBC_API statement {
public:
statement();
~statement();
protected:
my_map m_member;
};

will give a 'Warning: m_member needs DLL-interface to be accessed by clients of...'.

To circumvent that I decided to export the statement members individually.

class statement {
public:
LWODBC_API statement();
LWODBC_API ~statement();
protected:
my_map m_member;
};
Now 'my_map' is no longer exported and the warning is gone.

But this confuses Intellisense and the entire class 'statement' will not work anymore, nor will any class after 'statement'. You might not even notice this rightaway, since Intellisense will use the 'statement' information it has in it's *.ncb database. But any new members you add will not show up.

It took me a while of deleting declarations in the header file, closing the project, deleting the *.ncb database, reopening the project and trying Intellisense until I found out that

LWODBC_API ~statement();

was the culprit. The Intellisense parser fails on this, even when it properly resolves LWODBC_API to _declspec(dllexport).

The workaround is simple:

Before the class declaration, preferably at the top of the header file, add

#ifdef INTELLISENSE_STOPS_WORKING_WORKAROUND
#undef LWODBC_API
#define LWODBC_API
#endif


This makes Intellisense believe that LWODBC_API is actually defined empty and Intellisense will now see
~statement();

and be happy.

But there is one catch. It would be nice if one could add the workaround right after the LWODBC_API definition and have it done once and for all headers. But this does not work if the LWODBC_API definition is contained in a different header file.

The workaround must be in the same header file where Intellisense stumbles.

If you have multiple header files that Intellisense fails to parse correctly, you must include the workaround in each file. And you must include the workaround after the #include statement that includes the LWODBC_API definition.

I'm back to work now and much relieved that I have my Intellisense back for my class.

Hajo

Welcome to my logbook

Hi all,
I just found out why Visual Studio's Intellisense stopped working for one of my classes and decided to share this information.

I am software developer working with Visual Studio .NET 2003 under Windows using wxWidgets and boost and a lot of other stuff. In the past I frequently hunted down obscure bugs or found workarounds for bugging problems and wrote about them on my main page. If you want to know for example how to create a (reasonably) secure MS Access database via ODBC, you will find the answer at http://www.litwindow.com/Knowhow/knowhow.html.

But changing my webpage everytime I find a little software programming gem or a workaround is too much of a hassle. So I decided to start a blog where I will be sharing what I find. I hope this will be useful to someone. If it is, feel free to spread the word and add your comments.

Best regards

Hajo Kirchhoff