Binglong's profileBinglong's spaceBlogGuestbookNetwork Tools Help

Binglong's space

July 04

Merge Module for Deployment of Visual Studio Applications

In a previous post Deploy Visual Studio Applications, creating an installer for a single application was discussed. There is however another very important deployment related object, merge module. If you develop large and complex applications, sooner or later you will need to understand merge modules when deploying your applications.
 
A merge module is the output of a Visual Studio setup/deployment project (so-called merge module project), just like an installer is the output of a Visual Studio setup/deployment project. Create a merge module is therefore very similar to creating an installer. Below is a simple comparison of the two:
 
File extensions:
  • An installer has the file extension .msi.
  • A merge module has the file extension .msm.
Audiences:
  • An installer is intended to be run on a target machine to install applications for the end user.
  • A merge module is sent to the application developer, such that it can be included in the application's deployment project, and packed in the final installer (.msi) file.
Contents:
  • An installer can pack the EXEs, DLLs, controls, data files, documents, such that the target applications can run correctly. An installer can also pack contents from one or more merge modules.
  • A merger module normally packs DLLs, controls, class libraries, documents, which are meant to be shared by applications.\

In short, a merge module is like a sealed form of shared components for multiple applications. You may create a merge module when you have multiple applications that all use it. These applications may be distributed in one installer for an application suite, or they may be distributed in multiple installers. When you develop an application using a third party library, they may offer a merge module to ease the deployment of your application.

The benefit of using a merge module is that, each merge module has a unique version identifier, and Windows Installer will be able to handle the dependency and conflict for different applications, such that installation or uninstallation will not break other applications.

 
July 01

Locale in C and C++

Locale

Locale is an object intended to isolate character encoding and formats from the rest of the program to encapsulate culture-, region-, and country-specific choices and preferences.

The Default Locale (The Classic Locale, The C Locale, or The Minimal Locale)

All C programs (and C++ programs, as one of C++’s efforts is to be best compatible with C) have the “C” locale in stock, which is a neutral locale with minimal locale information across all compilers/systems, and not affected by the current system locale or current program’s global locale. In another word, this default locale can be brought to the program’s global locale to reset it, in case you messed up with the global locale. The default locale happens to be a minimal US locale, for example, the currency symbol is $.

  • In C, a call of setlocale(LC_ALL, “C”) resets the program’s global locale to the default/classic “C” minimal locale.
  • In C++, calling static function std::locale::classic() returns a const reference to the default “C” locale.

The default locale is conceptually read-only.

The System Default Locale (The User Default Locale, The Environment Default Locale)

All C programs (and C++ programs, as one of C++’s efforts is to be best compatible with C) also have access to the system or user default locale, which is supplied by the run-time environment. In most systems, the system/user default locale is set by the system or user profile, and could be very different from the “C” locale. For example, in Windows XP, you can find the settings in Control Panel | Date, Time, Language, and Regional Options.

  • In C, a call of setlocale(LC_ALL, “”) resets the program’s global locale to the environment default locale.
  • In C++, you can construct an locale object with an empty string “” to get the user’s default locale.

Without platform specific API, the user default locale should be read-only in a C/C++ program accessing only standard C/C++ libraries.

The Global Locale

In every C/C++ program there is a global locale. The C/C++ run-time libraries use the global locale extensively.

  • In C, many functions, such as those in string.h, ctype.h, stdio.h and ctime.h, like isspace(), isupper(), can change its behavior if the global locale is changed.
  • In C++, every iostream object has a C++ locale object, which is initialized to the global locale object by default when the stream object is created.

The global locale can be changed from either C or C++.

  • In C, you can call setlocale() to change the global locale.
  • In C++, you can change the global locale by calling static function std::locale::global().
  • In C++, you can create a std::locale object with a copy of the global locale by calling its default constructor.

Because there is only one shared global locale, changing this locale in C or C++ will be visible from the other language too.

All C/C++ programs start with the global locale initialized to the default “C” locale.

The C++ Locale

In C++, locale can be used much more flexible than in C. You can create std::locale objects, and use it in various places. For example, you can a imbue (ios::imbue) a locale object to a stream object, therefore the stream will use the specific locale to format the characters, no matter what the current global locale is. To make your culture sensitive code portable, you should make use of a std::locale object, and let the caller specify it for his/her preferred localization choices.

In concept, a std::locale is just a container of facets. A std::locale instance actually is a pointer to a ref-counted locale implementation instance. Therefore, copying or assignment of std::locale objects is effortless.

Creating a new locale object however involves creating a set of facet objects. The default constructor of std::locale constructs a copy of the current global locale.

Facet

An object does the actual work of a service in a C++ locale. Facet is not available in C. In C++, facets are used to replace the monolithic C struct lconv, and allows more manageable and flexible locale service configuration.

In C++ standard, there are 6 standard facet categories: collate, ctype, monetary, numeric, time and messages. Each category has one or more standard facet classes. You can create new facet classes as long as your new class derives from locale::facet, and defines a static member called id of type locale::id. That also means you can override the standard facets by deriving from them (since they derive from locale::facet), such that your facet changes the service provided by them.

Facets should always be owned by a locale and you should not create facet objects locally, due to that its destructor is protected. Before you can use a facet, you need to call free function std::use_facet<Facet>(const std::locale&) to obtain a const reference to the facet in the locale.

When creating a locale object, it copies all the standard facets from the global local or locale specified by a locale name. You can only add or replace one customized facet with a locale constructor. There is no way to delete a facet from a locale.

Locale Name

A locale is often identified by a locale name in C/C++. A locale name refers to a predefined locale by the run-time system. Other than the standard C locale names, such as “C”, “”, and “POSIX”, the availability of a locale name and its meaning is platform specific, and therefore non-portable.

  • In C, call setlocale() with the locale name to set the global locale to the specified locale. If the locale name is not recognized or supported, it returns a null pointer.
  • In C++, one of the std::locale constructors take a locale name parameter of type const char*. If the locale name is not recognized or supported, it throws std::runtime_error.

You have to check after you set a locale name outside of C/C++ standard; you cannot assume a locale name is available in an arbitrary run-time environment.

  • On POSIX systems, you can list the names of supported locales by “locale –a”. On Linux, the locale name would be like “en_US.UTF-8” and so on.
  • On Windows, the locale name for C/C++ standard library functions is defined in MSDN: Language and Country/Region Strings. Examples are “Chinese_China.936”, “German_germany”, or simply “Chinese”.

Reference:

http://www.cplusplus.com/reference/std/locale/
http://www.cplusplus.com/reference/clibrary/clocale/setlocale/
http://www.cantrip.org/locale.html
http://www.unc.edu/depts/case/pgi/pgC++_lib/stdlibug/fac_3537.htm
http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/0fb287af-bb58-4c60-a3da-14fe84c16948
http://msdn.microsoft.com/en-us/library/9dzxxx2c(VS.80).aspx
http://msdn.microsoft.com/en-us/library/hzz3tw78.aspx

June 24

GnuCash and Online Banking for Credit Card Accounts

GnuCash 2.2.9 is the latest stable version as of today June 25, 2009. It is free and open source personal financial software, replacing Quicken and Microsoft Money. Overall the features are very neat, and I just started to use it.

I first exported my credit card accounts from Money 2007 to loose QIF files (for complete and long account names etc). This has to be done one by one for individual accounts: one QIF file corresponds to one credit card account. Then I imported them to GnuCash without problem.

I then tried to make GnuCash connects to my credit card account servers online. The first thing to do is: go to Tools | Online Banking Setup..., this will launch a standalone tool aqBanking, which manages the online accounts. Unlike the rest of the world, which uses HBCI, in the US the banks use the OFXDirectConnect protocol. For details, see GnuCash Wiki: http://wiki.gnucash.org/wiki/Setting_up_OFXDirectConnect_in_GnuCash_2. The credit card companies' OFXDirectConnet settings can be found here: http://wiki.gnucash.org/wiki/OFX_Direct_Connect_Bank_Settings.

I have to say, aqBanking is good, but the configuration for users and accounts is a little messy. For the sake of records, I put the things that work on my GnuCash here.

In User Configuration | General tab, put these:

User Settings
  User Name: [Your real name, e.g., Joe Smith]
  User Id: [Your username when you logon to the bank's website]
  Customer Id: [same as User Id]
Bank Settings
  Country: United States of America
  Bank Id: [see below]

In User Configuration | OFX tab, put these:

Bank Settings
  FID: [see below]
  ORG: [see below]
  Broker Id: [leave empty]
Connection Settings
  Server URL: [see below]
  HTTP Version: [see below, if 1.0 does not work, try 1.1]
Server Options
  Support Account List Download: [see below, check]

I only have experience with some credit card banks, and I list the settings I have (source: http://wiki.gnucash.org/wiki/OFX_Direct_Connect_Bank_Settings, made clearer for aqBanking in GnuCash 2.2.9):

Bank Bank Id FID ORG Server URL HTTP Version
American Express 3101 3101 AMEX https://www99.americanexpress.com/myca/ofxdl/us/download?request_type=nl_desktopdownload 1.0
Chase 10898 10898 B1 https://onlineofx.chase.com/chase.ofx 1.0
Citi 24909 24909 Citigroup https://secureofx2.bankhost.com/citi/cgi-forte/ofx_rt?servicename=ofx_rt&pagename=ofx 1.0
Discover 031100649 7101 Discover https://ofx.discovercard.com/ 1.0
Bank of America 051000017 6805 HAN https://ofx.bankofamerica.com/cgi-forte/ofx?servicename=ofx_2-3&pagename=bofa 1.0

In the table above, Bank of America is not working, even though I tried different combinations of Bank Id/FID/ORG/HTTP Version.

One recommendation is that, after you input everything for User Configuration, click OK to save the settings first. Then choose the user and click Edit, therefore make sure your input was saved. (Sometimes, I noticed that Country could be reset to Germany, so do above to make sure.) Then go to OFX tab, and enable Support Account List Download, then click Get Accounts. You need to input your online banking password, and possibly accept the bank's certificate permanently. If it is successful, you should see the log window showing the retrieved account numbers and so on. Now you can create a new GnuCash account or assign the retrieved account to an existing GnuCash account. When everything is done, you return GnuCash, and see the accounts appearing in the Accounts view. Click one of these online-backed accounts, and choose Actions | Online Actions, e.g., Get Transactions..., you are prompted for the password again, then GnuCash downloads the up-to-date transactions from the bank to your GnuCash immediately.

June 22

AVI File Reading API in Microsoft VfW

Avi is a big wrapper that can house multiple streams (video, audio etc.) with various compressions from different codecs. For applications that are mostly working in the background, e.g., computer vision applications focusing on processing image frames in the videos, DirectShow is too complex and an overkill, as it is mostly designed for playback. In such a case, the AVIFile functions and macros supplied by Microsoft Video for Windows (VfW) are more relevant. The VfW AVI support should use the same DirectShow facility with dynamically configurable filter/codec support in Windows. That basically means that if you get an avi file with unsupported encoding, installing the corresponding codec to Windows should make the application written with VfW AVI API work immediately without any code change.

The typical call sequence for reading frames of a stream in an avi file is below:

AVIFileInit(); // ==> library initialized
  AVIFileOpen();  // ==> PAVIFILE
    AVIFileInfo();
    AVIFileGetStream(); // ==> PAVISTREAM
      AVIStreamInfo();
      AVIStreamStart();
      AVIStreamLength();
      AVIStreamGetFrameOpen(); // ==> PGETFRAME
        AVIStreamGetFrame(); // repeat for frames, random seek with frame number here.
      AVIStreamGetFrameClose();// <== PGETFRAME
    AVIStreamRelease(); // <== PAVISTREAM
  AVIFileClose(); // <== PAVIFILE
AVIFileExit(); // <== library uninitialized

It is quite simple to use. The only drawback here is that the code is not portable: it only works with Windows.

Reference:

MSDN: AVIFile Functions and Macros
CodeProject: Extracting AVI Frames

June 12

ActiveX Control in a Dialog Box to Receive Arrow Key Messages

ActiveX Control

Even though .NET is now very popular for component development on Windows, ActiveX controls are still very useful for some people who mostly live in the C/C++ world due to their specific requirements on component performance such as speed and low level control of the hardware. ActiveX controls, however once built, can be used quite extensively. They can be used in native C/C++ Windows applications, or in C#/.NET applications, since putting an ActiveX control into a C# form is made very easy by Microsoft specifically.

Message Loop

In a Windows application, there is normally a message loop. The typical message loop is like below:

while( GetMessage(&msg, NULL, 0, 0) )
{
    if( TranslateAccelerator(hWnd, hAccelTable, &msg) )
        continue;    // msg translated and processed as accelerator command
    if( IsDialogMessage(hDialog, &msg) )
        continue;    // msg processed by dialog box
    TranslateMessage(&msg);    // put WM_CHAR message to queue if translatable
    DispatchMessage(&msg);    // call the window procedure to process msg
}

where, GetMessage() retrieves a message from the main thread message queue (if none, it blocks). Note that the system does not generate WM_CHAR messages directly; you have to call TranslateMessage() to translate WM_KEYDOWN messages to WM_CHAR messages if you need them. For example, when you press ‘a’ key, using code above, your window procedure will be processing WM_KEYDOWN/’a’ and WM_CHAR/’a’ as a consequence. Some keys are not translatable, for example, RIGHT arrow key does not generate WM_CHAR.

Modal/Modeless Dialog Box

Even though an ActiveX control can be used by putting into a window directly through dynamically creation, it sees more use as a control in a dialog box (often done at UI design time). This is possible in Visual C++. In Visual C#, a form is essentially a dialog box anyway. In following discussion we assume the dialog box is modal.

As MSDN states in Dialog Box Programming Considerations and Dialog Box Keyboard Interface, in a dialog certain key or key combinations are intercepted and interpreted by the system (or in some sense, the dialog box), such that a control would not receive them. For example, ENTER and ESC keys are only sent to the dialog box procedure as WM_COMMAND messages with IDOK and IDCANCEL wParam respectively; arrow keys (LEFT, RIGHT, UP, DOWN) are used by the dialog box to move the input focus among the controls in the dialog box. The way that Windows does this is through Win32 API function DialogBox(). When you call DialogBox() to create a modal dialog box, this function enters a message loop that has the behavior described above, which is desired for a dialog box.

If your application has modeless dialog boxes, which do not have a message loop, MSDN asks the programmer to modify their main application message loop to assist these modeless dialog boxes. You need to call IsDialogMessage() for each modeless dialog box for any message before translate and dispatch it; otherwise the modeless dialog boxes would behave strangely. The example code above does this for one such a dialog box.

Control Grabs Arrow and Other Special Keys (WM_GETDLGCODE)

The modal dialog box (or IsDialogMessageBox() call for modeless dialog box) in general grabs the arrow key and other special key messages. However, the dialog box still gives the chance to a control to process those keys. This is required because some controls use the them for their own use, e.g., arrow keys to navigate its own contents. For example an edit control uses LEFT and RIGHT arrow keys to move the caret among the characters. To accommodate these controls, the dialog box sends WM_GETDLGCODE message to the control after it grabs these key messages. The control can express its need to process the key by returning DLGC_WANTARROWS and so on, then the dialog box will route the key message to the control; otherwise these key messages will be processed by the dialog box. Therefore, if an ActiveX control wants to process these keys, it can process WM_GETDLGCODE accordingly.

PreTranslateMessage() in MFC

If you are using MFC, the application class, CWinApp/CWinThread (or its derived class) processes a message differently than the message loop example above. It calls CWinThread::PreTranslateMessage(), and only when it returns FALSE then TranslateMessage() and DispatchMessage() are called. The base PreTranslateMessage() performs accelerator-key translation; your override must call the base, but you can add more processing for any message.

By default, CWinThread::PreTranslateMessage() walks from the window where the message is generated to the application’s main frame window through the parent chain, calling CWnd::PreTranslateMessage() of the windows individually until one of them process it. So, other than override CWinThread::PreTranslateMessage(), you can also override CWnd::PreTranslateMessage() for your control. Your control will see arrow and other special keys, as well as any other messages here. For example, to process arrow keys in your control within a dialog box, you can detect if the message is WM_KEYDOWN with VK_DOWN (and so on), and if it is, you can call OnKeyDown() directly in PreTranslateMessage() and return TRUE to indicate the message is processed. My experience is that this even works for an MFC ActiveX control in a C# form.

Experienced MFC programmers discourage abuse of PreTranslateMessage() because it could do a lot of harm in careless hands.

 
Thanks for visiting!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.

Binglong