Sunday, October 26, 2008

Setup.dll Sample and Walkthrough: Terms & Conditions / End User License Agreement for a Smart Device CAB installer

Visual Studio Smart Device CAB projects are extremely helpful in many ways for building a cab installer, even thought they have some pending issues, like a more natural MSBUILD integration, they allow us to have a cleaner and dynamic definition of our mobile app installation based on our source projects instead of relying just in binary and .inf files.

Sometimes we need to perform some custom actions before and after the installation or uninstallation. Typically we can need to check for pre requisites or show a Terms & Conditions/EULA before the installation and continue only if the user accepts it or, as another example, we can need to remove temporary files and registry entries after uninstallation.

In this post I want to show you a very simple way to implement a Setup.dll which shows a Terms & Conditions dialog which requires the explicit user acceptance to continue the installation.

What we need to start?

We'll use Visual Studio 2008 but this procedure also works with VS2005. You'll need Visual C++ and Smart Devices support installed.

I recommend you to take a look at this article in MSDN regarding Setup.dll files for better context.

Choose any of your mobile applications (or create a new empty one) and create a new Smart Device Cab project for it, as I describe in this post. Once you got it, we're ready to start.

I'll use a port of SplashScreenSample for VS2008. For instance, my solution explorer looks like this:

image

Creating a new Setup.dll

First thing to do is to create a new Visual C++ Smart Device DLL project. Right click on the solution and select "Add" - "New Project".

The "Add New Project" dialog will pop up. Please select Visual C++ / Smart Device in "Project types" and use the "Win32 Smart Device Project" template.

I'll call it "EULASetup". Once you've entered the name just press "OK".image

Nice... and now it's wizard time. Let's follow the "Win32 Smart Device Project Wizard"

image

Select the platform corresponding to your project. In this particular sample I'm targeting Windows Mobile 6 Standard:

image

As we want to implement a very simple DLL, just select "DLL" as Application Type and don't check anything else ("Precompiled headers" cannot be modified so let them checked).

image

Once we press "Finish" the wizard will create a new native C++ project in our Solution. The new project should look like this:

image

Showing the EULA dialog at installation begin

If it's not already open, please open EULASetup.cpp by double clicking on it. This class currently contains the DLLMain entry point for the library. We actually don't need this function in our setup.dll so this function can be removed.

What we need is to implement a function to be called when the user tries to install the cab. This function is Install_Init. In this function we'll show the terms & conditions for our application installation and according with the user response we can continue the installation or cancel it.

Our code needs to include "ce_setup.h" to get the required knowledge of Install_Init signature types, in this sample codeINSTALL_INIT, an enumeration which gives us the two possible result values: codeINSTALL_INIT_CONTINUE or codeINSTALL_INIT_CANCEL.

For simplicity, we can put the text in a string directly hardcoded within the source. I'm calling it "Message". Install_Init will show the message using the MessageBox API, and handling a "Yes" response continuing with the installation or a negative response canceling it.

It's important to remark that Install_Init may be called more than once, with different values for the installation directory; for example when the current directory doesn't have enough room. So we need to handle the fFirstCall parameter value to only show the dialog the first time our function is called.

How looks our code? please replace the entire content of EULASetup.cpp with the following:

#include "stdafx.h"
#include "ce_setup.h"

// This is a variable containing the text to be displayed
// in the Terms & Conditions dialog

TCHAR Message[] = _T("TERMS & CONDITIONS\r\n ")
_T("Selecting YES you're accepting our terms & conditions.\r\n")
_T("This is just a sample application.\r\n")
_T("From http://www.mobilepractices.com\r\n")
_T("You can replace this text with your own.\r\n")
_T("We're using a setup.dll to show this dialog.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Extra line to force vertical scrollbar.\r\n")
_T("Last line.\r\n")
;

// This function will be called when the user
// tries to install the cab. According to its return
// value the installation continues or is cancelled.
// As this could be called more than once
// (i.e. if there is not enough space on the target)
// we should take care about fFirstCall parameter
// to show the dialog only once.

codeINSTALL_INIT Install_Init( HWND hwndParent,
BOOL fFirstCall,
BOOL fPreviouslyInstalled,
LPCTSTR pszInstallDir )
{
if (!fFirstCall

::MessageBoxW(0, Message,
_T("SplashScreenSample")
, MB_YESNO) == IDYES)
return codeINSTALL_INIT_CONTINUE;
else
return
codeINSTALL_INIT_CANCEL;
}


Exporting Install_Init function using a .DEF file

As our Install_Init function should be visibly externally to allow WCELOAD.EXE to call it during the .cab file processing; it means that we need to export it, and in this sample we'll use a .def file for doing it.

Please, press right click on EULASetup (the project) and select Add - New Item...

image

Please select "Module-Definition File (.def)" and enter "EULASetup" as its name. Press "Add".

A new "EULASetup.dll" is included in your project. If it's not already open in the editor, please open it and replace the body with the following:

LIBRARY    "EULASetup"
EXPORTS
Install_Init @1

In this .def file we're just exporting Install_Init. Save the changes and build it. No errors should be found.

Adding the Setup.dll to our Smart Device CAB Project

Finally, we need to add "EULASetup.dll" as the setup.dll for our Smart Device CAB Project. This is really straightforward. Right click on the Installer project and "Add - Project Output..."

image

What we're doing is adding the output of build EULASetup to the installer. In fact, we're adding EULASetup.dll into the cab installer. The way to do it is selecting EULASetup as the project and "Primary Output" as what we want to include. Press "OK".

image

Now we have in our installer project the Primary Output from EULASetup.
image

The final step is to select it as the Setup.dll for our installer. Just select the Installer project by clicking on it
image

In the Properties pane for "CE Setup DLL" select (Browse...)
image

The Primary Output of a project is placed by default into the "Application Folder" of the CAB File System. So, in the following dialog please select "Application Folder" and press "OK" to open that folder
image

In the Application Folder select "Primary Output from EULASetup (Active)" and press "OK" again.
image

And... that's it! We've included EULASetup as the Setup DLL for our installer.

We're ready to rebuild the project and generate the .CAB File. Now when we try to install SplashSampleInstaller.CAB, we'll first get the following dialog:

image

If the user selects "No", the installation will be aborted. It will only continue if the user selects "Yes". I know the text could be improved a lot, and you even can use a more elaborated dialog, but... that's your homework!

Hope you like this walkthrough, and most important, it helps you. If you want to see the source code, just download it from the following link:

17 comments:

FroggEye said...

This is a great example! I do have a question though. I want to check some prerequisites first, if not installed then install them. Of course I would have to show the prerequisites on the screen and if they are or are not installed (Similar to installing SQL or VS). I am a VB prgrammer and have done very, very little in C# and even less in C++ so this is all foregin to me. Any blogs you have or know of any examples?

Pappu said...

IT is fine working on standard device.I need to show EULA on professional device by displaying EULA in full window,not in messagebox.Means i need soft keys to accept or decline of EULA in professional device.if i use messagebox,am unable to access through soft keys.so please can you provide solution for this.

Ulys said...

Great walkthrough as I'm trying to put custom action in my own CAB install.

Can you see a reason why I do not get the messagebox with a WM6 Professional (Pocket PC) emulator ?
The installation succeeds though.
I'm using VS2005 with WM6 Professional SDK.

Ulys said...

Great walkthrough as I'm trying to put custom action in my own CAB install.

Is there a reason why I do not get the messagebox with a WM6 Professional (Pocket PC) emulator ?
The installation succeeds though.
I'm using VS2005 with WM6 Professional SDK.

Jose Gallardo said...

Thanks for your comments.
FroggEye and Pappu, I'll try to post soon about a Dialog approach for Professional/Classic Editions (PocketPC).

Ulys: You should get just a message box in WM6 Professional. If you don't get it, it looks like you're not selecting it as the "CE Setup DLL" for the Smart Device Cab Project.

I've being trying to post more about this topic for a while, but I didn't have the time yet. Sorry about that! I'll try to do it soon!

dominikg said...

Hi,

Great and very detailed examples. Thank you!!

I develop an application where the user has to enter a password upon installation, which has to be entered each time the application starts. Additionally deinstallation should be protected by this password as well.

The application is developed in .net cf 2.0 (c#) and the code to set and ask for a password already exists. Is there any way to reuse that code?

If not, what would be the best/most simple way to ask the user for a password from setup.dll?

And last but not least, is it possible to automatically start the application after installation?

devthx said...

Great post. It work on my professional device.

Prezimir said...

Thanks for a great walkthrough - unfortunately only a standard device solution. I hope you find time soon to provide a professional device alternative as it does not seem trivial at all. If you have any hints/suggestions/examples, these would be very welcome too.

Ju said...

Thanks you for this article, very helpful !

But I have a little issue: the scroll line isnt "forced".

I'm getting a long long box, with no view on the yes/no buttons :/

I work on a Samsung i900 (Omnia).

Thanks you for your reply ;)

Sam said...

great article on setting up the EULA stuff... great site, many great examples of .net ce and how to use it. for this post/article, the terms and condition/agreement verbage are hard coded into the code... lets say if i have the verbage in a flat file (txt file), what would be the easiest way to load the verbage from the flat file into my code to have it show up when installing???????

thank u very much for ur help

much appreciated

Sam said...

hello everyone,

i'm not a c++ developer, and so I need some help in the explanation. i was looking at the code, sepcifically the code below:


if (!fFirstCall::MessageBoxW(0, Message, _T("SplashScreenSample"), MB_YESNO) == IDYES)
return codeINSTALL_INIT_CONTINUE;
else
return codeINSTALL_INIT_CANCEL;


what does the following mean:
_T("SplashScreenSample")

thank u very much

Jose Gallardo said...

Hi Sam,
I'll try to clarify

_T("SplashScreenSample")

is just a string constant, like in the c# "SplashScreenSample".
_T() is a C++ macro that stands that the following string should be taken as a TCHAR string. Windows Mobile and CE use Unicode characters, so their strings contain two bytes for each character.
This macro lets the compiler know that this will be a Unicode string if you are building a Unicode app, which is the case for windows Mobile.
HTH!

Sport said...

Thank you for this and your other fine articles. Unfortunately, I am perplexed by a compilation failure in trying to build this code:

Error 1
error C2653: 'fFirstCall' : is not a class or namespace name
t:\InitSetup\InitSetup.cpp 35 InitSetup

Warning 2
warning C4806: '==' : unsafe operation: no value of type 'bool' promoted to type 'int' can equal the given constant
t:\InitSetup\InitSetup.cpp 36 InitSetup

..might anyone have a clue where I could be going wrong? Any help would be appreciated, thanks.

Anonymous said...

Thanks. This is a great.

Peter Lum said...

namespace error.


Please check below:
When using VC++ express (in my case) and a basic starting template (win32 console), it creates a cupple default files stdafx.h and stdafx.cpp. Keep the header in mind.


[ ... ]

code would refuse to work [compile], kept giving [namespace] errors [ ... ] take iostream include declaration and placed it in the stdafx.h header and removed it from the main source and the issue will be resolved

Ben said...

Your series is by far the most helpful resource on the web for creating a Setup DLL. For a realistic EULA, though, the full dialog approach with scrollable edit box is a must.

No good resource exists for building a Setup DLL with a full fledged dialog. Not *just* begging for myself, but if you can post about this, it would be the one and only usable walkthrough of this process on the Internet.

Regardless, thanks for getting us started. Fantastic work.

Anonymous said...

For completeness I'll answer Sport's error question. If you copy/paste the code from the web page you miss a '||' which causes the error as it should be:

if (!fFirstCall || ::MessageBoxW(0, Message, _T("SplashScreenSample"), MB_YESNO) == IDYES)

Note the || after the !fFirstCall