SolidWorks 2009 API - Advanced Product Development
SolidWorks 2009 API - Advanced Product Development
SolidWorks 2009 API - Advanced Product Development
API
Advanced Product Development
Published by AngelSix
©2008-2013 Luke Malpass
[email protected]
First Edition
Trademark Information
As well as the success of the first book and the feedback being
driving factors for this book I also enjoy sharing my knowledge and
experience to a wide audience. And so, I hope you enjoy this book as
much as the first!
For those of you who have been waiting for this book for months
sorry for the delay but I wanted to make sure I could dedicate myself
to this next book to make sure the standards you expect are kept up!
Introduction
Firstly this book is written with the presumption that the reader has
adequate knowledge of SolidWorks API programming (either from
experience or from reading my previous book) and so complete
beginners may struggle as basic steps are overlooked.
This book is truly a full product lifecycle journey and beginners right
through to industry pro’s will learn a thing or two from at least one
chapter.
As always, all feedback is greatly appreciated, please send
comments to [email protected].
I have tried to give the best explanation of all code provided on its
purpose, and what the point of every line of code is. I hope you enjoy
reading this book as much as I have enjoyed writing it.
HTTP://WWW.ANGELSIX.COM/CODE/SW2009.ZIP
Table of Contents
Contents
Setting Up ........................................................................ 11
Download and Install Visual Studio Express .............................13
The Project Setup ................................................................... 14
SolidWorks Add-ins ........................................................... 19
The Basic Add-in ..................................................................... 20
Testing the Add-in .................................................................. 38
Manually Registering for COM ................................................ 41
Menu’s & Property Pages ................................................... 43
Creating Menus ....................................................................... 44
Property Manager Pages......................................................... 53
Property Page Controls ........................................................... 67
Call-backs ................................................................................ 73
Add-ins Vs Stand-alones .................................................... 74
Key Differences ........................................................................75
Pros and Cons ......................................................................... 76
Making the right choice .......................................................... 78
Hybrids ................................................................................... 79
Planning and Product Design .............................................. 80
Why plan? ............................................................................... 81
Pre-development Stage .......................................................... 82
Initial Development Stage ...................................................... 89
8
Table of Contents
Adding Functionality ............................................................... 90
Debugging and Testing ........................................................... 91
Methods of Debugging............................................................ 92
Development ................................................................... 106
The Blueprint ........................................................................ 107
The Add-in Class ................................................................... 109
The PMP Layout ..................................................................... 117
Toggling Pages / Reacting to Events ..................................... 140
Setting up Hooks ................................................................... 144
Part Events ............................................................................ 146
Assembly Events ................................................................... 148
Drawing Events ..................................................................... 155
Tidy Up .................................................................................. 169
Enhancements ....................................................................... 171
Methods of Deployment ................................................... 174
Manual Installation ................................................................ 176
SFX Archives .......................................................................... 177
Installation Packages............................................................. 182
Creating An Installer .............................................................. 195
Licensing Your Product ..................................................... 231
Overview ............................................................................... 232
Self-implementation ............................................................. 233
Corporate Licensing .............................................................. 234
9
Table of Contents
Distribution and Sales ....................................................... 235
Preparing your Product for Market ....................................... 236
Online Distribution and Sales ................................................ 242
In-Store Distribution and Sales ............................................. 245
Marketing ............................................................................. 246
10
Setting Up
11
Setting Up
For those following on from the last book you will be glad to know
we will be picking up right were left off getting straight into
SolidWorks Add-ins. For those of you starting with this book a
SolidWorks Add-in differs from a SolidWorks macro in the sense that
a macro is commonly seen as a small program with a simple
structure performing basic tasks writing in a high-level language,
whereas the Add-ins we will be creating are written in a lower-level
language (lower being closer to actual computer machine code
therefore more powerful), will be more complex, and perform
multifaceted tasks. As well as this they will be registered in
SolidWorks in a special manner to make them loadable/unloadable
at any time through the add-ins menu of SolidWorks, or
automatically on start-up of a SolidWorks session, and will have the
added power of integrating into the menu systems of SolidWorks as
well as access to a few other “add-in only features”.
Before we start any project there are a few steps to getting set up
first.
12
Setting Up
Go to http://www.microsoft.com/express/download/default.aspx and
simply download and install either C# Express and/or VB.Net
Express. All guides to installing them are on the site.
When you have installed the program, run it. You may be prompted
to select layout style or preferred language, just select any.
13
Setting Up
For each new project the first thing you need to do is to start by
opening Visual Studio and creating a new Class Library Project (this
step will differ to a Windows Forms Applications Project for Stand-
Alone applications later).
Give the project any name you desire that will relate easily to the
projects purpose, and click OK:
This will create you a new class library project, with a simple file
called Class1.cs/Class1.vb by default.
14
Setting Up
With a blank slate project the next step is to add the desired
references to the SolidWorks COM objects so that we can use the
SolidWorks API.
If you do not see the References folder (usually in VB.Net) click the
Show All Files button first:
15
Setting Up
16
Setting Up
17
Setting Up
C#
using SldWorks;
using SWPublished;
using SwConst;
using SwCommands;
VB
Imports SldWorks
Imports SWPublished
Imports SwConst
Imports SwCommands
18
SolidWorks Add-ins
19
SolidWorks Add-ins
Start by creating a new Class Library project and give it any name
you desire. Once created, setup the references to SolidWorks
(including the solidworkstools.dll reference) and add the
using/Imports statements to the Class1 file that had been created by
Visual Studio.
20
SolidWorks Add-ins
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SldWorks;
using SWPublished;
using SwConst;
using SwCommands;
21
SolidWorks Add-ins
namespace MyFirstSWAddinCS
{
public class SwAddin
{
}
}
VB
Imports SldWorks
Imports SWPublished
Imports SwConst
Imports SwCommands
End Class
22
SolidWorks Add-ins
The next job is to make our class implement a SolidWorks Add-in
interface by telling it to derive from a SolidWorks Add-in interface.
To do this we do the following:
C#
VB
23
SolidWorks Add-ins
C#
#endregion
}
24
SolidWorks Add-ins
For VB.Net the trick is a little different. Notice the Implements line
has a blue line under it (indicating a problem).
VB
End Function
End Function
End Class
25
SolidWorks Add-ins
For our first add-in we are not bothering with any menu systems or
functionality we just want a way to know that our add-in is working
and loading/unloading correctly, so all we will do is show a message
to the user when connecting and disconnecting the add-in.
C#
26
SolidWorks Add-ins
iSwApp.SendMsgToUser2("Hello!",
(int)swMessageBoxIcon_e.swMbInformation,
(int)swMessageBoxBtn_e.swMbOk);
return true;
}
#endregion
}
27
SolidWorks Add-ins
For the connect message we type “Hello!” Feel free to type any
message you like.
VB
28
SolidWorks Add-ins
Attribute Tags
Whenever you are working with interoperability or COM
programming you almost always require Attribute Tags; these help
other programs understand information about your classes, such is
the case for a SolidWorks Add-in.
29
SolidWorks Add-ins
C#
using System.Runtime.InteropServices;
VB
Imports System.Runtime.InteropServices
Attribute tags are placed on the line directly above the class/property
they are defining. On the line above our SwAddin class we need to
define several attributes; a Globally Unique Identifier (GUID), the
COM visibility, and SolidWorks-specific information such as title,
description and whether to load the add-in automatically on start-up
of SolidWorks.
The GUID is needed for any COM object in order to identify itself
from all other objects on the system that are registered, and for that
reason we need a unique code; to get a GUID code use the tool called
GUIDCreator.exe in the example files provided under Chapter 1. This
tool is from the previous book.
Generate a GUID and copy/paste that code into the line directly
above the swAddin class within an attribute function Guid() in
quotations. As well as a GUID it is also mandatory to make our class
COM visible so that SolidWorks can actually see it; we do this by
setting the ComVisible attribute to true like so:
30
SolidWorks Add-ins
C#
[Guid("e3397eb9-2dc3-4c21-a9cf-26aa10dc9763"), ComVisible(true)]
public class SwAddin : ISwAddin
VB
<Guid("e3397eb9-2dc3-4c21-a9cf-26aa10dc9763"),ComVisible(True)> _
Public Class SwAddin
Implements SolidWorks.Interop.swpublished.SwAddin
That is all that is required to make a general COM class, but in order
to be a SolidWorks COM class we should also add the SolidWorks
specific COM attribute SwAddin to the class as well. Although this
isn’t strictly required it does make the class more programmatically
correct.
C#
[Guid("e3397eb9-2dc3-4c21-a9cf-26aa10dc9763"), ComVisible(true)]
[SwAddin(Description = "My addin description", Title = "My First Addin",
LoadAtStartup = true)]
31
SolidWorks Add-ins
VB
<Guid("e3397eb9-2dc3-4c21-a9cf-26aa10dc9763"),ComVisible(True)> _
<SwAddin(Description:=" My addin description ", Title:=" My First Addin
", LoadAtStartup:=True)> _
Public Class SwAddin
Implements SolidWorks.Interop.swpublished.SwAddin
When SolidWorks is looking for its add-ins all it does is look in the
following 2 registry entry:
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\AddIns
HKEY_LOCAL_MACHINE\SOFTWARE\SolidWorks\SolidWorks
2009\AddIns
32
SolidWorks Add-ins
As our class is to be a COM object we also need to register it with
COM interoperability on the system so when SolidWorks comes to
look for a COM object with our GUID, the operating system can
actually return our dll file to it from the COM library. For now we will
have this done automatically by Visual Studio.
C#
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type t)
{
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type t)
{
}
VB
33
SolidWorks Add-ins
End Sub
These functions are called when the dll is registered on the system
for COM. This is the perfect place to add the required registry entries
to the system. To do this we can use the .Net library Microsoft.Win32
to easily manipulate the registry. To register we open up the registry
and add the folders in the correct locations and the attributes to
match our SwAddin attributes for title and description, then close
the registry. To unregister we simply delete the folders.
C#
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type t)
{
Microsoft.Win32.RegistryKey hklm =
Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey hkcu =
Microsoft.Win32.Registry.CurrentUser;
34
SolidWorks Add-ins
addinkey.SetValue("Title", "MyFirstAddin");
keyname = "Software\\SolidWorks\\AddInsStartup\\{" +
t.GUID.ToString() + "}";
addinkey = hkcu.CreateSubKey(keyname);
addinkey.SetValue(null, 1);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type t)
{
//Insert code here.
Microsoft.Win32.RegistryKey hklm =
Microsoft.Win32.Registry.LocalMachine;
Microsoft.Win32.RegistryKey hkcu =
Microsoft.Win32.Registry.CurrentUser;
keyname = "Software\\SolidWorks\\AddInsStartup\\{" +
t.GUID.ToString() + "}";
hkcu.DeleteSubKey(keyname);
}
35
SolidWorks Add-ins
VB
keyname = "Software\SolidWorks\AddInsStartup\{" +
t.GUID.ToString() + "}"
addinkey = hkcu.CreateSubKey(keyname)
addinkey.SetValue(Nothing, 1)
End Sub
36
SolidWorks Add-ins
keyname = "Software\SolidWorks\AddInsStartup\{" +
t.GUID.ToString() + "}"
hkcu.DeleteSubKey(keyname)
End Sub
The single SetValue we add to the Current User registry is the Load
At Start-up entry. Setting the second parameter to 1 means load at
start-up, and 0 means not to. In this case we set it to 1.
37
SolidWorks Add-ins
Then under the Build tab check the box “Register for COM interop”.
38
SolidWorks Add-ins
Build your project to make it register with COM and create the dll.
You will get a warning once complete that the project cannot be
execute, just click OK to that.
That’s it; our Register function should have run and added our
registry entries. Take a look in the registry (Start->Run... regedit)
under the folders to see our add-in:
39
SolidWorks Add-ins
All that is left now is to test that our Connect and Disconnect
functions are running. As we set our add-in to run on start-up you will
get the “Hello!” and “Bye!” messages on opening SolidWorks and
closing SolidWorks.
40
SolidWorks Add-ins
cd C:\Windows\Microsoft.NET\Framework\v2.0.50727
Regasm D:\MyFirstSWAddinVB.dll
Regasm D:\MyFirstSWAddinVB.dll /tlb
41
SolidWorks Add-ins
Where D:\MyFirstSWAddinVB.dll is the location of the compiled dll
file; the second line with /tlb is not strictly required but is there if you
wish to support legacy applications accessing and using your dll file.
42
Menu’s & Property Pages
Creating Menus
Page Controls
Call-backs
43
Menus and Property Pages
Creating Menus
The next logical step to most add-ins is to add menus and toolbars
for the user to interact with to call functions of your add-in. Before
you add any functionality to an add-in you should always setup call-
backs; call-backs are functions within your program that get called
from another process (in this case SolidWorks) under certain events.
For example, say SolidWorks opened a document and you wanted to
be aware of this. You would have a call-back function that
SolidWorks calls for you when it opens a document. Call-backs are
used for many events as you will see soon.
C#
VB
44
Menus and Property Pages
The Command Manager
SolidWorks has functions for creating menus called AddMenuItem3.
Although this is a perfectly legitimate way to create a menu item, it
is used more for dynamic menus. If we were to create a menu item
using this command the item would not have an associated toolbar
item, and we would have to then call another function called
AddToolbar4, and then AddToolbarCommand2 for every menu
item.
To begin lets add a new command item for our add-in that will later
open a Property Manager Page (PMP), but for now just displays a
message to acknowledge it is working.
C#
ICommandManager iCmdMgr;
VB
45
Menus and Property Pages
C#
iCmdMgr = iSwApp.GetCommandManager(Cookie);
AddCommandMgr();
VB
iCmdMgr = iSwApp.GetCommandManager(Cookie)
AddCommandMgr()
C#
VB
46
Menus and Property Pages
Note that the VB function is classified as a Sub; the only major
difference between a Sub and a Function in VB is that Subs do not
return any values so it is correct to use a Sub in this instance.
Once the group is created all that is left is to create the items and
activate the command group to effectively show the menu/toolbar.
C#
cmdGroup.HasToolbar = true;
cmdGroup.HasMenu = true;
cmdGroup.Activate();
}
47
Menus and Property Pages
VB
cmdGroup.HasMenu = True
cmdGroup.HasToolbar = True
cmdGroup.Activate()
End Sub
48
Menus and Property Pages
The UserID is just an integer value that is unique for the
command manager of our add-in. Enter any number you like
here so long as you only enter that number once for this
command, if you add another group later give that another
number.
The second parameter Title is the name that will appear for the
main menu item, and for the Toolbar Title.
The third ToolTip is the text that will appear in the yellow box by
the mouse when you hover the mouse over the menu/toolbar.
The forth Hint is the text that will appear in the SolidWorks
status bar (bottom left of SolidWorks window).
After we have created our group we now add an item using the
AddCommandItem2 function. This asks for the following
parameters:
49
Menus and Property Pages
The Name is the menu/toolbar name that will appear for item.
The Position, HintString and ToolTip string are again like the
CreateCommandGroup properties.
The last 3 lines are self-explanatory; we tell the group that the global
setting for all items is to allow them to be shown in both menu and
toolbars and then to activate (show) the group. If we changed the
HasToolbar to false then even though we specified that our
command item is displayed in both menus and toolbars, it would be
overruled by the groups’ property.
50
Menus and Property Pages
The Item Call-back Function
In our code we have passed in the string “_cbCreatePMP” as the
name of our call-back function. This will be called when the user
clicks our menu item or toolbar button. In order for our add-in to
work we need to actually create a function called exactly that within
the class.
C#
VB
One last thing before the add-in is ready to compile; when the add-in
is unloaded we need to remove all command groups else they will
remain in the SolidWorks menus and toolbars and when the user
51
Menus and Property Pages
clicks them errors will occur. Add a function called
RemoveCommandMgr to your class, and call it in the first line of the
DisconnectFromSW function.
C#
VB
If you customise the toolbars you will see your group in there also.
52
Menus and Property Pages
In this class file add the usual using/Imports lines to the top of the
file and then add the implementing statement to implement from
the PropertyManagerPage2Handler6 class. Place the word public
before the class declaration:
53
Menus and Property Pages
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using SldWorks;
using SWPublished;
using SwConst;
using SwCommands;
using SolidWorksTools;
using SolidWorksTools.File;
namespace DrawingInfoPropertyPageCS
{
public class MyPMPManager : PropertyManagerPage2Handler6
{
}
}
VB
Imports System.Runtime.InteropServices
Imports SldWorks
Imports SWPublished
54
Menus and Property Pages
Imports SwConst
Imports SwCommands
Imports SolidWorksTools
Imports SolidWorksTools.File
End Class
55
Menus and Property Pages
If you are using C# go through each function and remove the line of
code VS created automatically in each function. Then for any
function that asks for a return type of bool add the following line:
return false;
return 0;
The constructor function has no return value and the same name as
the class. Our constructor will ask for an instance of a SolidWorks
Application object as we need this to create new pages:
C#
VB
56
Menus and Property Pages
End Sub
To show the working page without confusing you with more code
just yet we start by creating a blank page with a title.
C#
SldWorks.SldWorks swApp;
PropertyManagerPage2 pmPage;
int iErrors;
public bool OK;
VB
C#
57
Menus and Property Pages
pmPage =
(PropertyManagerPage2)swApp.CreatePropertyManagerPage("Drawing
Sheet Info",
(int)(swPropertyManagerPageOptions_e.swPropertyManagerOptions_Ok
ayButton |
swPropertyManagerPageOptions_e.swPropertyManagerOptions_Locked
Page), this, ref iErrors);
}
VB
58
Menus and Property Pages
There is no error handling done here but this does show you just how
simple it is to create a PMP.
C#
VB
One more line to add is in the AfterClose method; add the following
line to clear the PMP variable to release the variable:
C#
pmPage = null;
VB
pmPage = Nothing
59
Menus and Property Pages
That is our PMP class done with, time to move on to creating,
showing and correctly disposing of it within our add-in class;
MyAddin.
C#
VB
60
Menus and Property Pages
That is creating and showing sorted, but we must also correctly
dispose of our PMP once done. In the DisconnectFromSW function
add the following line:
C#
VB
C#
VB
61
Menus and Property Pages
End Sub
You will notice the green tick which is there because we specified the
OK option when creating the page.
62
Menus and Property Pages
Now you have had a taste of seeing a working PMP its time to add
some error handling in case things go wrong as well as expand our
PMP to check for a Drawing document before showing. If the user is
in a drawing we will then display information about it within the PMP
using some common controls.
Error Handling
Although the add-in works this time, it may not all the time and it is
best to have error handling wherever possible to prevent the
application from crashing itself and even SolidWorks, or behaving
oddly.
The only place we need to add error handling at the moment is in the
constructor of the PMP class. Let’s revise it to the following:
C#
try
{
// Create new page
pmPage =
(IPropertyManagerPage2)swApp.CreatePropertyManagerPage("Drawing
Sheet Info",
(int)(swPropertyManagerPageOptions_e.swPropertyManagerOptions_Ok
ayButton |
63
Menus and Property Pages
swPropertyManagerPageOptions_e.swPropertyManagerOptions_Locked
Page), null, ref iErrors);
OK = true;
}
catch (Exception e)
{
swApp.SendMsgToUser2("Error creating PMP: " + e.Message,
(int)swMessageBoxIcon_e.swMbWarning,
(int)swMessageBoxBtn_e.swMbOk);
OK = false;
}
}
VB
Public OK As Boolean
Public Sub New(ByVal app As SldWorks.SldWorks)
64
Menus and Property Pages
swApp = app
Try
' Create new PMP
pmPage = app.CreatePropertyManagerPage("DrawingSheet Info",
swPropertyManagerPageOptions_e.swPropertyManagerOptions_OkayB
utton Or
swPropertyManagerPageOptions_e.swPropertyManagerOptions_Locked
Page, Me, iErrors)
OK = True
Catch ex As Exception
swApp.SendMsgToUser2("Error creating PMP: " + ex.Message,
swMessageBoxIcon_e.swMbWarning, swMessageBoxBtn_e.swMbOk)
OK = False
End Try
End Sub
65
Menus and Property Pages
We create a new publicly accessible Boolean variable which
indicated whether our page initialised successfully. This can later be
used in our add-in class to determine if it was successful.
Then we add a Try/Catch block around the code and create a new
PMP as we did before, going on to check the iErrors variable passed
in as a reference to the CreatePropertyManagerPage function to
see if any errors occurred. If an error occurred display it to the user in
a message box and set the OK status to false.
That is the friendly errors dealt with but sometimes you can get
unfriendly errors that would otherwise crash your application were
they not caught in the Try/Catch. In the Catch block we show the
error message and set the OK variable to false.
C#
VB
66
Menus and Property Pages
If myPMP.OK Then
myPMP.Show()
End If
End Sub
All controls have their own properties that can be accessed once you
have created them. By default all controls must specify a caption,
alignment, tooltip and positioning and visibility.
When you create new controls you give them a unique ID. This is
then used to identify the control in the call-back functions; take a
look at all those functions we had to implement in the PMP class
(OnButtonPress, OnCheckboxCheck, OnOptionCheck etc...) and
notice they all provide an integer ID value. This means that when you
want process when a button is pressed, you can easily identify which
button was pressed and perform the relevant actions.
67
Menus and Property Pages
Adding controls
To begin adding items go to our MyPMPManager class. For each
control you add, it is wise to keep class-wide variables (variables
accessible to the entire MyPMPManager class by declaring them
directly inside the class, not inside a function within the class) of 2
things per control; a unique ID, and an instance of the control itself.
These help us identify the control and perform the relevant work in
our code.
Start by adding a new label and a textbox that will display the active
drawing filename.
C#
int uidLabelFilename;
PropertyManagerPageLabel ctrLabelFilename;
VB
68
Menus and Property Pages
In the constructor method after the Try/Catch block we want to start
creating our controls if the page was created successfully, so add the
following to call a new function we create next:
C#
if (OK)
AddControls();
VB
If OK Then AddControls()
Within this class we create our first control (a label) and add it to the
page, as well as showing the pre-defined header control mentioned
earlier using the SetMessage3 function:
C#
// Set IDs
69
Menus and Property Pages
uidLabelFilename = 1;
// Set Defaults
int iStandardOption =
(int)(swAddControlOptions_e.swControlOptions_Enabled |
swAddControlOptions_e.swControlOptions_Visible);
short sStandardAlign =
(short)swPropertyManagerPageControlLeftAlign_e.swControlAlign_LeftE
dge;
ctrLabelFilename =
(PropertyManagerPageLabel)pmPage.AddControl(uidLabelFilename,
(short)swPropertyManagerPageControlType_e.swControlType_Label,
"Drawing Filename here", sStandardAlign, iStandardOption, "Drawing
Filename");
}
VB
70
Menus and Property Pages
71
Menus and Property Pages
That is all there is to adding controls
to a PMP. Using your gained
knowledge try to add a second control
of type Button after the label,
showing the text “Update Details”.
You create another 2 variables to
store the ID and handle, set the ID to 2
in the AddControls function, and call
AddControl with the id and change
the type to button. If you struggle
take a look at the example in the files.
72
Menus and Property Pages
Call-backs
As discussed, call-backs are just methods that get called (run) by
getting “called” from another class, assembly or process when a
certain event happens (in this case our PMP class gets its call-back
methods called from the SW process).
The call-backs in our PMP are all of the functions that were created
beginning with On or After, such as OnButtonPress or AfterClose.
You will see these call-backs being use in the development of the
final product later in the book.
73
Add-ins Vs Stand-alones
Key differences
Hybrids
74
Add-ins Vs Stand-alones
Key Differences
At first glance most people think there is no difference other than
one is a dll/macro or one that shows as a menu item in the title bar,
and the other is an exe or macro that runs anything else. This is a
popular misconception. Let me clarify what exactly makes an add-in
an add-in, and anything else that doesn’t fit that group is
automatically a stand-alone by definition.
75
Add-ins Vs Stand-alones
Add-in Pros
- Running in-process on the same thread as SolidWorks, so
execution of code is slightly faster.
- Can use all SW API, including “in-process only” methods.
- Available to user directly in SolidWorks, so more integrated.
- Can use call-backs
Add-in Cons
- Not ideal for debugging or updating as have to close
SolidWorks and all machines using the Add-in every time you
want to make a single change.
- Harder to create and understand for beginners, often
clouding simple tasks in complicated COM registration, call-
back handling and other tasks.
- Harder to distribute to clients as requires installer package to
register the add-in, or instructions to them to perform a
manual registration. Further complications when registering
with multiple platforms.
76
Add-ins Vs Stand-alones
Stand-alone Pros
- Ideal for debugging, fast updating, no need to close
SolidWorks.
- Much easier to understand than add-ins and less code (1 line)
to connect to SolidWorks and begin using the API.
- Easiest type of program to distribute to client as only need to
provide the project files for them to run.
- Freedom to build and view visual aspects and general non-
SolidWorks related operations without having to open
SolidWorks every time; can run totally independently.
Stand-alone Cons
- Slight performance decrease due to running out-of-process.
- Cannot run in-process API functions.
- Not directly integrated into SolidWorks, which may be a
desired requirement.
- Cannot receive call-backs.
77
Add-ins Vs Stand-alones
In-process functions are few and far between but include functions
such as SldWorks::PreviewDoc and SldWorks::GetPreviewBitmap.
The general rule of thumb is if you do not need any of the above then
chose a stand-alone program for the benefits it gives on the
debugging, updating and simplicity side of things, but it is all
personal preference and some people just prefer add-ins regardless,
and others prefer stand-alone.
78
Add-ins Vs Stand-alones
Hybrids
Just because there are 2 ways to interact with SolidWorks does not
mean your product has to use a single method, you can easily
incorporate the advantages of both methods to get the best of both
worlds.
79
Planning and Product Design
Why Plan
Pre-development Stage
Adding Functionality
80
Planning and Product Design
Many leisure programmers and professionals alike know how to
create software programs to achieve their goals, but how many are
correctly thought out and structured and how many are just
designed as they go and never made to be the best they can be?
Why plan?
The first question you may ask yourself if you have developed
programs in the past, is why do you need to plan in the first place
when you can just develop your software as you go and it works once
you’re finished? There are many reasons why good planning and
design are important and although not essential, very beneficial.
The most important reason for planning is often the make sure you
are choosing the right approach to a solution before you get too far
down the line and realise it cannot be done that way, meaning you
will have wasted all that development time for nothing. For example,
a classic case in context of this book would be to create a standalone
application where call-backs turn out to be needed in your program.
Planning also gives you a good idea in your head before you start, of
the path you will be following during the development, and think
ahead of any potential problems or benefits that could be achieved
by approaching the project in a certain way.
81
Planning and Product Design
Pre-development Stage
The main structural planning and major choices are made at the pre-
development stage; these include choosing a language to develop
with, the platform, the type of application (exe, library, add-in,
service, com), and the structure and workflow of the project forms,
user interfaces, database structures and so on, that flow into the
initial development stage naturally.
Programming Language
When it comes to choosing a programming language the choice is
usually more down to preference rather than requirement these
days. The general rule is the lower the language (lower meaning
closer to the hardware/machine code) the more powerful the
language due to its closer relation with the machine it is to be used
on, and the better performance/speed benefits are possible. The
higher the language the more the programmer compensates
performance/speed for ease of use, speed of development and more
power for expansion. A list of common programming languages,
from low level to high level, is as follows:
• Assembly 80x86
• C
• C++
• .NET (C#, VB etc...)
• VB/VBA
As you can see normal VB/VBA ranks highly, which means it is very
obscured from the machine code itself, and so a lot of power is lost
using this language, but it gives the benefit that almost any
computer literate person can pick up the language and start
developing.
82
Planning and Product Design
Assembly is the lowest level language you can program, short of
implementing binary directly. To program assembly languages you
need not only to understand the language, but the platform it is to
work with, the hardware it is to access (CPU, Motherboard) and great
understanding of maths, logic, hardware and ideally microchips. It is
by no means a language to pick up like most others, but doing so
gives you great power and understanding of the entire
hardware/software configuration you are working with.
C++ was, and possibly still is (topic of great debate) the developers
standard of programming language and has been for a long time.
Implementing an object oriented (language that can have classes)
version of C. The language is not too high up to lose power or
performance, and is not too low to understand for non-rocket
scientists! C++ adapts itself perfectly to game programming, hacking
(or pen-testing as we now call it) and mathematical and system
programs and the likes; all the things that most hobbyist
programmers wonder “how the heck is that done?”.
With the evolution of .Net over the last 7 years since its early days
back in Feb 2002 when I started using it, it has come a long long way.
At first the main argument was that veteran C++ programmers
detested the framework as it had “poor performance” like VB and
was too high-level to be any use, and not for “real programmers”.
Overcoming this viewpoint in more recent years, the CLR (service
that runs .Net programs) has been greatly improved to give much
better performance, parrying with C++ in many areas now, with the
added bonus of much cleaner code, easier and faster development
and the likes. Recently for the C++ programmers there was also
released C++ .Net, which is all of the C++ language syntax, but
running on the .Net FrameWork. This language fills most of the
region when deciding a suitable language.
83
Planning and Product Design
Finally, we have VB and VBA; programming languages don’t get
much higher than this, or much simpler. The only words I would have
to say on them is they are good as a learning tool and quick and dirty
macro creator, nothing more. It can be pushed to great scales and
huge projects can be developed (and still are) entirely in VB and VBA,
but the reasoning behind them will not be from a judicious point of
view, but more along the lines of the programmer not wanting, or
having the ability to move forward.
When your choice is not tied down to external preferences and you
do not need to squeeze every last ounce of performance out of a
program, or do something very specialist like drivers then .Net is
almost always the standard choice.
84
Planning and Product Design
Type of Application
As well as deciding on a programming language you should also
decide on the type of application you are creating. This means, is
your project going to be a simple stand-alone application (exe), or a
library for indirect use by other applications (dll), a windows service
(to be run by the svchost application) or a combination of the lot.
Like the programming languages the type of applications all has
their benefits too:
EXE
The most common program created and often the only one created
by most everyday programmers is a stand-alone application with an
extension .exe standing for executable. As the name suggests the
program can be executed (run) directly, with no need to be invoked
or called by another. It is a self-contained file that is called directly;
this suites most applications.
DLL
85
Planning and Product Design
constantly updating its heuristics detection algorithms; this code
would be in a separate dll project and updated/compiled separately.
If it were not and were part of the main exe project, then every time
this little part was updating the entire program would have to be re-
compiled and released again!
There are many more applications for library projects, but the end
result is that the dll itself cannot be directly run.
WINDOWS SERVICE
86
Planning and Product Design
Structure, Interface & Design
Once you have your language and your type of application chosen,
it’s almost time to move on to the actually development, but just
before you do, take the time to think in advance exactly what you
will be doing for your initial development; how you plan to structure
the program, what you would like the user to see in an ideal world.
The way I do this is to think about how I would like the program to
act (forms, interface etc...), how it would ideally get the required
information to and from the user, but without even thinking about
the programming implications - just thinking about ideals.
Keeping notes and drawings on the forms and how I want them to
look, I start to make notes on what each structure has to accomplish,
and apply real programming logic to the thought process to see what
part of the ideals are possible, and what simply are not and then how
to work around them.
For example say you would like a program that displays SolidWorks
files in a window like Windows Explorer, but instead of the
thumbnails you ideally would like an actual editable model view
window for each item, where the model can be zoomed, pans and
manipulated on the fly. Now everything about this program is
possible except the edibility of the models, as there is no mechanism
in SolidWorks to do this, so you would have to sacrifice an ideal for a
realism by having a viewable read-only 3D model preview in place of
the edible one, and then on double-click or similar have your
program launch a SolidWorks instance and open that part. Improving
on that model you could think ahead to keeping a single SolidWorks
instance open in your program the first time an edit is attempted,
and then re-using it by just hiding/showing it as required, drastically
improving performance.
87
Planning and Product Design
At this stage you would not actually implement these functions or
interfaces but merely develop a sort of development and design
model that you intend to be as accurate as possible to what the final
product should look and behave like.
88
Planning and Product Design
Most programs have a main form, or main screen that the user is
presented with. Start there, and add your menu items if required,
controls as you visualised them in your pre-development planning,
and any other visual elements required. Expand the program by
adding all the forms that are required for the program.
The next stage is to get the program structured and logical flow
working; this is where you add event handlers to menu items,
buttons and form events, and add dummy (blank) functions to them
that are ready to fill to do what they should. For example, if your
main form has a button called “Show Users” that should open
another form and show all users of the program currently logged on,
then you would attach a new event handler to the button and within
that event handler function, add a call to another function called
“ShowUsers()”, that you then create but leave blank. Going through
your program adding these functions allows you to see the flow
easily before getting confused by actual coding.
89
Planning and Product Design
Adding Functionality
With your forms and controls created, and your structure and flow
implemented as much as possible, you now start to program the
actual working code that performs the tasks your program is
designed to do; the order in which you do this is usually the order
which the user would be going through your program. For example,
if your program was working with an SQL database as its data
source, the first step would be to present the user with a setup
screen to configure the server. With than done the next step may be
to create and manage users for the program, so that functionality
would come next.
With the majority of functionality added you will always find yourself
going back and tweaking the user interface or structure due to
programming problems that make it impossible, or simply the fact
you do not like way it works once it’s functional. In this case you
follow the same procedure as before, but on a smaller level. This
helps greatly in cleaning up the design from the functionality, and
helps create cleaner, better programs.
90
Planning and Product Design
91
Planning and Product Design
Methods of Debugging
Debugging is one of those things that never has a clear cut answer,
and never will; there are never really even a fixed set of methods for
debugging either. Every thing, concept and view on debugging is
always adapting and is always unique to each situation. The best way
to learn debugging is to get hands-on experience and adapt over
time to find your own way. No book, person or thing can tell you how
you can debug a program, only guide you and provide examples and
explanations, and that is all I will try to do here, to give you an
understanding of how I tackle problems in code and how to
overcome certain common scenarios.
Sometimes stepping line by line is not practical, and it’s much better
to just have break points at key stages, or going up another stage,
having break conditions or unit testing.
92
Planning and Product Design
The stepping technique
What better way to learn than by doing; to keep things very simple
just open the sample program ZeroDivideCrash under chapter 5 of
the source code:
C#
using System;
using System.Windows.Forms;
public class Form1 : Form
{
public Form1()
{
int i = 0;
int j = 1;
int k = j / i;
}
}
VB
End Class
93
Planning and Product Design
Most crashes give you more details when you have Visual Studio
installed, and offer a “Details” button, which can help you trace back
the error. In this case it does not, so we are starting blind. In these
types of cases you tend to ask the user where and when it crashes, if
it is consistent and other factors, to give you an idea of where to start
looking.
In this little program we would start at the first line on the form
constructor. Add a break-line to the first line (int i/Dim i) by left-
clicking in the grey bar to the left of the line. This will add a red circle
to indicate a break-point. When you run your project in debug mode
and the program counter reaches this point in your code, it will
break-out, stop, and let you debug.
94
Planning and Product Design
hosted environment to track its code and stop when it hits break-
points or exceptions.
If you step into the line this would follow any program jumps. If this
line was a function or call and the source-code was available then it
would move the stepping inside that function and stop at the first
line waiting for the next command.
If you step over (most common) then the debugger moves to the
next line of code and stops. This is the same as stepping into except
if the line was a function or call it would execute the entire function,
return, and then move the debug stepper, so anything inside that
function would not be stepped over line by line.
For this case we are just going to step over each line to pick up on the
erroneous line; press F10 to step over the first line. This will execute
95
Planning and Product Design
that line, move down one and stop on the next line (defining j
variable).
Finally try to step over the next line; this time instead of moving to
the next line the debugger will catch the exception that gets thrown
and display an exception message box showing details of the
exception. If you are using VB then due to the higher-level obscurity
of the language you do not get error-line notification like in C#.
VB C#
96
Planning and Product Design
VB has a different exception, this one is less descriptive and it is an
“InvalidOperation exception”, and merely states that an arithmetic
operation resulted in an overflow. Unless you know a bit more about
how operators work and the terms used most would not understand
what this message meant as easily as the C# message.
Let’s take one step back from this method and use just the normal
debugging environment alone to see what results we get. Remove
the break point from the first line by clicking in the left column again,
and re-run the project in debug mode (F5). The program runs
without breaks until a problem occurs, and then breaks out
automatically and enters stepping mode. This is how you tend to
start with debugging as you are not expected to step line by line
through a program often tens of thousands of lines long trying to
anticipate errors. More common is letting the debugger fault out,
and then back-tracing from there to the potential problem, forming
an image in your mind of the state of the program at that time as you
go, and understanding what is out of place and what is not.
Client-Side Debugging
More often than not, the errors or bugs that you do not pick up from
the initial stages of your design get picked up by the alpha/beta
testers or clients actually using your software. This is when the real
debugging takes place.
The first major disadvantage is the fact that the error is likely to
happen away from the developers’ (you’re) machine, and so no
source code, no Visual Studio debugging message, nothing. This is
where you have to rely on other techniques.
97
Planning and Product Design
Whenever a project gets large it is always a good idea to have certain
key elements in the design. One crucial element is a log; most
applications are going to fail at some point in their lifetime in the
scenario above, and you will have no way to trace back to a point in
code. If you add the option in your program to log events (usually to
a text file or SQL server) as it is running, then you can get the user
who experiences the problem to send you their log. To demonstrate
this purpose we are going to add a very basic logging function to the
ZeroDivideCrash sample.
Debug Logging
Take a look at the example in the Chapter 5 folder called
DebugLogging:
C#
using System;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
public Form1()
98
Planning and Product Design
{
InitialiseLog();
Log(" ");
Log("Form starting");
Log("========================");
99
Planning and Product Design
sLogFilename =
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembl
y().Location), "log.txt");
}
}
VB
Imports System.IO
Imports System.Reflection
InitialiseLog()
Log(" ")
Log("Form starting")
Log("========================")
100
Planning and Product Design
Once the InitialiseLog function has been called, you can call the Log
function to log an event anywhere in the program and at any time.
You may notice that we open and close the log file every time we
write to the log. We could improve performance by opening the log
once at the start of the program, and then closing it when finished.
The problem with this method is that if our program crashes
unexpectedly then the file may remain in an opened state and the
pointer to the file is lost. It is always best to use the first method
101
Planning and Product Design
unless performance is critical. If it is then the next method may be
more suitable.
Finally the actual event telling the log what happened or went wrong
and needs to be logged. The code below is very straight forward.
Find the example project with the example files.
C#
using System;
using System.Windows.Forms;
using System.Diagnostics;
namespace EventLogCS
{
public partial class Form1 : Form
{
public Form1()
{
102
Planning and Product Design
InitializeComponent();
}
EventLog.WriteEntry(tbSource.Text, tbEvent.Text);
}
}
}
VB
Imports System.Diagnostics
103
Planning and Product Design
EventLog.WriteEntry(tbSource.Text, tbEvent.Text)
End Sub
End Class
104
Planning and Product Design
conformity. I personally opt for custom debug logging in 90% of
cases.
105
Development
The Blueprint
Setting up Hooks
Part Events
Assembly Events
Drawing Events
Tidy Up
Enhancements
106
Development
Putting everything you have learned so far into practice it is time to
develop a fully-function working SolidWorks add-in following correct
development planning and structure, and then later take this
product right the way through deployment, licensing and
distribution and sales!
The Blueprint
After much thought while writing this book, I was trying to think of a
project that could be developed that would best cover some
advanced API mainly focused around the grey area of add-ins, event
call-backs, notifications and enumerators as well as touching on
custom properties, component trees and other functions, to show
good planning and structure, provide problems to solve, and all this
to be done in one add-in that is small enough to fit in the middle of a
book that covers so much more too.
For the Part page we display Created By, Creation Date and Modify
Date of the currently selected feature.
107
Development
For the Drawing page we display the current drawing filename and
an event-driven drop-down list of all current sheets in the list. When
the user changes the active sheet the drop-down selection changes
to match or alternatively the user can select a sheet from the drop-
down list and have it activate that sheet too.
For the active sheet the total number of views is displayed as well as
another event-driven drop-down list showing all views in the list. The
same thing happens; when the user selects a view the drop-down list
selection changes to match and vice-versa.
For the selected view the page displays the Referenced File,
Referenced Configuration, Orientation, Display Style and Scale.
108
Development
Create a new class project called SWInfo or similar. Add all the usual
SolidWorks references including the solidworkstools.dll. Add the
using/Imports entries and create a new class called swAddin
implementing the ISwAddin object with the COM registration
functions. This will give you a blank SwAddin class. If you have
forgotten some steps, just copy/paste the SwAddin class from the
DrawingInfo project back in chapter 2.
C#
SldWorks.SldWorks iSwApp;
ICommandManager iCmdMgr;
PMPInfo myPMP;
109
Development
VB
Note: the variable of type PMPInfo does not exist yet – this is going
to be the class we create later, but for now add it ready.
110
Development
Add the following ConnectToSW and DisconnectFromSW
functions:
C#
VB
111
Development
The function is just the same as any other add-in – it adds call-back
information, gets a command manager and calls an
AddCommandMgr function which adds a single item to a menu (see
chapter 2 again for information on this).
C#
112
Development
VB
So far if we were to run this add-in now we would have a blank add-in
that registers with COM, connects to SolidWorks and adds a single
menu item that calls a function called _cbCreatePMP in our project.
C#
113
Development
VB
That is almost all the Add-in class needs to do; however for our PMP
we want to automatically react to the active model changing so for
that we add event handlers to the SolidWorks object: cue the
AddEventHooks and RemoveEventHooks functions.
C#
114
Development
VB
These functions have one task – they add and remove a function
from being called when the ActiveModelDocChangeNotify is called
by SolidWorks whenever the active model is changed.
115
Development
C#
int iSwApp_ActiveModelDocChangeNotify()
{
_cbCreatePMP();
return 0;
}
VB
From there our add-in class receives this event message and raises
the local event in the iSwApp object called
ActiveModelDocChangeNotify; because we added a handler
(function associated with this event) to this event, the function
specified in the handler (iSwApp_ActiveModelDocChangeNotify)
gets called.
116
Development
You may want to review the finished SwAddin class from the
example files at this point to clarify you have everything correct.
C#
pmPage =
(IPropertyManagerPage2)swApp.CreatePropertyManagerPage("SwInfo",
(int)(swPropertyManagerPageOptions_e.swPropertyManagerOptions_Cl
oseDialogButton), this, ref iErrors);
VB
pmPage = app.CreatePropertyManagerPage("SwInfo",
swPropertyManagerPageOptions_e.swPropertyManagerOptions_CloseD
ialogButton, Me, iErrors)
117
Development
In the AddControls function delete all the lines of code except the
first (SetMessage3), and change that line to the following:
C#
pmPage.SetMessage3("Awaiting Initialisation",
(int)swPropertyManagerPageMessageVisibility.swImportantMessageBox
,
(int)swPropertyManagerPageMessageExpanded.swMessageBoxMaintai
nExpandState, "SWInfo");
VB
pmPage.SetMessage3("Awaiting Initialisation",
swPropertyManagerPageMessageVisibility.swImportantMessageBox,
swPropertyManagerPageMessageExpanded.swMessageBoxMaintainEx
pandState, "SWInfo")
This sets the yellow message box message at the top of the page.
This will get updated dynamically on model document changes, so
technically this initialisation message should never be seen.
Delete the uid* and ctr* variables that are no longer needed so the
only variables remaining are the swApp, pmPage, iErrors and OK.
C#
118
Development
pmPage.Show2((int)swPropertyManagerPageShowOptions_e.swPropert
yManagerShowOptions_StackPage);
}
VB
pmPage.Show2(swPropertyManagerPageShowOptions_e.swPropertyM
anagerShowOptions_StackPage)
End Sub
Then we show the page as normal but this time with the option of
StackPage; this allows the page to stay open throughout a lot more
situations than without it as we want our page to be visible and
displayed all the time.
The Properties
Throughout the program we will often want to cast the active model
to either DrawingDoc, PartDoc or AssemblyDoc. I neat trick to do
119
Development
this more cleanly in your code instead of constantly casting the
ModelDoc2 variable is to create a set of properties.
Properties are like variables with a twist; they have a Get and Set
function within them only. Get is called when the user asks for the
value, and Set is called when the properties is attempted to be set.
C#
VB
120
Development
Note the private variable declared first called _activemod. This is the
real variable that all the properties reference and cast.
121
Development
Now when we want to get the currently active model as a
DrawingDoc object all we need to do is state ActiveDrawing as a
variable.
C#
iStandardOption =
(int)(swAddControlOptions_e.swControlOptions_Enabled |
swAddControlOptions_e.swControlOptions_Visible);
iStandardGroupOption =
(int)(swAddGroupBoxOptions_e.swGroupBoxOptions_Expanded |
swAddGroupBoxOptions_e.swGroupBoxOptions_Visible);
iDisabledOption =
(int)swAddControlOptions_e.swControlOptions_Visible;
sStandardAlign =
(short)swPropertyManagerPageControlLeftAlign_e.swControlAlign_LeftE
dge;
VB
iStandardOption = swAddControlOptions_e.swControlOptions_Enabled
Or swAddControlOptions_e.swControlOptions_Visible
iStandardGroupOption =
swAddGroupBoxOptions_e.swGroupBoxOptions_Expanded Or
swAddGroupBoxOptions_e.swGroupBoxOptions_Visible
iDisabledOption = swAddControlOptions_e.swControlOptions_Visible
122
Development
sStandardAlign =
swPropertyManagerPageControlLeftAlign_e.swControlAlign_LeftEdge
Auto-set ID
As you will be aware by now every time you create a new control for
a PMP you need to give it a unique ID. Previously we have done this
manually by typing 1, 2, 3, 4 and so on sequentially for each new
variable. Although that is alright when you are creating small pages,
it gets rather tedious and messy when you are creating 10s or 100s of
controls.
C#
VB
123
Development
End Get
End Property
C#
124
Development
return (PropertyManagerPageNumberbox)parent.AddControl(id,
(short)swPropertyManagerPageControlType_e.swControlType_Numberb
ox, "", sStandardAlign, iDisabledOption, "");
}
125
Development
VB
126
Development
Return parent.AddControl(id,
swPropertyManagerPageControlType_e.swControlType_Combobox, "",
sStandardAlign, iStandardOption, "")
End Function
127
Development
We use the class variables iStandardOption, iDisabledOption and
sStandardAlign in each function to declare the options so they must
be available within the class and the functions must existing within
that same class.
myTextbox =
(PropertyManagerPageTextbox)parent.AddControl(1,
(short)swPropertyManagerPageControlType_e.swControlT
ype_Textbox, “My Textbox”, sStandardAlign,
iStandardOption, “”)
For each control you need to add 2 new variables to the class; title
them uidTypeName and ctrTypeName replacing Type with the
type of control (Label, Textbox, Button, Group etc...) and Name with
the name you want to remember the control by.
For each control; before creating the control call “uid* = NextID” on
the uid* variable for that control. It is a good idea to add a function
called SetIDs that initialises the _nid variable to 1 (_nid = 1) and has
all of the “... = NextID” lines of code in it, then call SetIDs() from the
128
Development
AddControls function first before creating any controls. This tidies
up the AddControls function more making it easier to read. See this
implementation in the source code provided.
129
Development
And here is the code within the AddControls function to create all of
the above:
C#
SetIDs();
// Filename
ctrLabelFilename = CreateLabel(uidLabelFilename, "Filename", "",
ctrGroupDrawingInfo);
ctrTextboxFilename = CreateReadOnlyTextbox(uidTextboxFilename, "",
"", ctrGroupDrawingInfo);
130
Development
// Total Views
ctrLabelTotalViews = CreateLabel(uidLabelTotalViews, "Total Views", "",
ctrGroupSheetInfo);
ctrNumboxTotalViews =
CreateReadOnlyNumbox(uidNumboxTotalViews, ctrGroupSheetInfo);
// Referenced File
ctrLabelReferenceFile = CreateLabel(uidLabelReferenceFile,
"Referenced File", "", ctrGroupViewInfo);
ctrTextboxReferenceFile =
CreateReadOnlyTextbox(uidTextboxReferenceFile, "", "",
ctrGroupViewInfo);
// Referenced Config
131
Development
ctrLabelReferenceConfig = CreateLabel(uidLabelReferenceConfig,
"Referenced Config", "", ctrGroupViewInfo);
ctrTextboxReferenceConfig =
CreateReadOnlyTextbox(uidTextboxReferenceConfig, "", "",
ctrGroupViewInfo);
// Orientation
ctrLabelOrientation = CreateLabel(uidLabelOrientation, "Orientation", "",
ctrGroupViewInfo);
ctrTextboxOrientation = CreateReadOnlyTextbox(uidTextboxOrientation,
"", "", ctrGroupViewInfo);
// Display Style
ctrLabelDisplayStyle = CreateLabel(uidLabelDisplayStyle, "Display
Style", "", ctrGroupViewInfo);
ctrTextboxDisplayStyle =
CreateReadOnlyTextbox(uidTextboxDisplayStyle, "", "",
ctrGroupViewInfo);
// Scale
ctrLabelScale = CreateLabel(uidLabelScale, "Scale", "",
ctrGroupViewInfo);
ctrTextboxScale = CreateReadOnlyTextbox(uidTextboxScale, "", "",
ctrGroupViewInfo);
132
Development
ctrGroupPartInfo =
(PropertyManagerPageGroup)pmPage.AddGroupBox(uidGroupPartInfo,
"Selected Feature Information", iStandardGroupOption);
// Created By
ctrLabelFeatCreatedBy = CreateLabel(uidLabelFeatCreatedBy, "Created
By", "", ctrGroupPartInfo);
ctrTextboxFeatCreatedBy =
CreateReadOnlyTextbox(uidTextboxFeatCreatedBy, "", "",
ctrGroupPartInfo);
// Created Date
ctrLabelFeatCreateDate = CreateLabel(uidLabelFeatCreateDate,
"Creation Date", "", ctrGroupPartInfo);
ctrTextboxFeatCreateDate =
CreateReadOnlyTextbox(uidTextboxFeatCreateDate, "", "",
ctrGroupPartInfo);
// Modify Date
ctrLabelFeatModifyDate = CreateLabel(uidLabelFeatModifyDate, "Modify
Date", "", ctrGroupPartInfo);
ctrTextboxFeatModifyDate =
CreateReadOnlyTextbox(uidTextboxFeatModifyDate, "", "",
ctrGroupPartInfo);
133
Development
ctrGroupAsmInfo =
(PropertyManagerPageGroup)pmPage.AddGroupBox(uidGroupAsmInfo,
"Assembly Information", iStandardGroupOption);
// Total Cost
ctrLabelTotalCost = CreateLabel(uidLabelTotalCost, "Total Cost", "",
ctrGroupAsmInfo);
ctrTextboxTotalCost = CreateReadOnlyTextbox(uidTextboxTotalCost, "",
"", ctrGroupAsmInfo);
// Update Cost
ctrButtonUpdateCost = CreateButton(uidButtonUpdateCost, "Update
Cost", "", ctrGroupAsmInfo);
// Part Cost
ctrLabelPartCost = CreateLabel(uidLabelPartCost, "Part Cost", "",
ctrGroupCostInfo);
ctrTextboxPartCost = CreateTextbox(uidTextboxPartCost, "", "",
ctrGroupCostInfo);
134
Development
VB
SetIDs()
' Filename
ctrLabelFilename = CreateLabel(uidLabelFilename, "Filename", "",
ctrGroupDrawingInfo)
ctrTextboxFilename = CreateReadOnlyTextbox(uidTextboxFilename, "",
"", ctrGroupDrawingInfo)
135
Development
136
Development
' Orientation
ctrLabelOrientation = CreateLabel(uidLabelOrientation, "Orientation", "",
ctrGroupViewInfo)
ctrTextboxOrientation = CreateReadOnlyTextbox(uidTextboxOrientation,
"", "", ctrGroupViewInfo)
' Scale
ctrLabelScale = CreateLabel(uidLabelScale, "Scale", "",
ctrGroupViewInfo)
ctrTextboxScale = CreateReadOnlyTextbox(uidTextboxScale, "", "",
ctrGroupViewInfo)
' Created By
ctrLabelFeatCreatedBy = CreateLabel(uidLabelFeatCreatedBy, "Created
By", "", ctrGroupPartInfo)
137
Development
ctrTextboxFeatCreatedBy =
CreateReadOnlyTextbox(uidTextboxFeatCreatedBy, "", "",
ctrGroupPartInfo)
138
Development
So far our add-in will now create a valid PMP with all of the controls
shown in the 3 preview images before. When the Show function is
called the ToggleView function gets called within it to correctly
show only 1 group at any one time, and that group should be based
on the active model type.
139
Development
In the SwAddin class when the active model changed we placed the
code:
myPMP.ActiveModel = iSwApp.ActiveDoc
C#
140
Development
} }
VB
Several things happen here; firstly we check that the PMP was
created successfully in the first place with the OK variable we set
inside the constructor function. Next we check that the active model
is actually a model not a blank SolidWorks screen. If all that is ok we
get the type of document that is active and store it in the variable
doctype.
141
Development
C#
ctrGroupDrawingInfo.Visible = drawing;
ctrGroupSheetInfo.Visible = drawing;
ctrGroupViewInfo.Visible = drawing;
ctrGroupPartInfo.Visible = part;
ctrGroupAsmInfo.Visible = assembly;
ctrGroupCostInfo.Visible = assembly;
}
VB
ctrGroupDrawingInfo.Visible = drawing
ctrGroupSheetInfo.Visible = drawing
142
Development
ctrGroupViewInfo.Visible = drawing
ctrGroupPartInfo.Visible = part
ctrGroupAsmInfo.Visible = assembly
ctrGroupCostInfo.Visible = assembly
End Sub
143
Development
Setting up Hooks
Going back to the ActiveModelChanged function, the first function
to be called in the list is SetupModelHooks; this function attached
event handlers to the active model based on its type as we want to
respond differently to each type (drawing, part or assembly):
C#
case swDocumentTypes_e.swDocASSEMBLY:
ActiveAssembly.NewSelectionNotify += new
DAssemblyDocEvents_NewSelectionNotifyEventHandler(ActiveAssembl
y_NewSelectionNotify);
break;
}}
144
Development
VB
145
Development
Part Events
Starting with the easiest event first; the part. The function of the
add-in working with parts is to display the currently selected features
information. When the user makes a new selection in an active part
model the ActivePart_NewSelectionNotify function is fired. Create
this function in the class and add the following code:
C#
int ActivePart_NewSelectionNotify()
{
SelectionMgr selmgr = (SelectionMgr)ActiveModel.SelectionManager;
Feature feat;
try
{
// This line will throw an exception and go to catch if selected object
is not a feature
feat = (Feature)selmgr.GetSelectedObject6(1, -1);
ctrTextboxFeatCreateDate.Text = feat.DateCreated;
ctrTextboxFeatCreatedBy.Text = feat.CreatedBy;
ctrTextboxFeatModifyDate.Text = feat.DateModified;
}
catch { return 0; }
return 0;
}
VB
146
Development
Return 0
End Function
With the feature to hand all that is needed then is to set the PMP
controls to the relevant information.
147
Development
Assembly Events
The assembly event is almost the same. Its function is to get the
currently selected component and pull in its Cost value (stored in a
custom property of the model called “Cost”), as well as provide a
button that tallies up all of the components costs to a Total Cost
field.
C#
int ActiveAssembly_NewSelectionNotify()
{
SelectionMgr selmgr = (SelectionMgr)ActiveModel.SelectionManager;
swSelectType_e seltype =
(swSelectType_e)selmgr.GetSelectedObjectType3(1, -1);
if (seltype == swSelectType_e.swSelCOMPONENTS)
{
Component2 com = (Component2)selmgr.GetSelectedObject6(1, -1);
selectedAsmModel = (ModelDoc2)com.GetModelDoc2();
UpdateSelectedComponentCost(false);
}
return 0;
}
148
Development
VB
Return 0
End Function
Here we get the selected object like in the part function and this time
check the type of it to see whether it is a component the user has
selected
C#
149
Development
{
if (set)
{
selectedAsmModel.DeleteCustomInfo2("", "Cost");
selectedAsmModel.AddCustomInfo3("", "Cost",
(int)swCustomInfoType_e.swCustomInfoText, ctrTextboxPartCost.Text);
}
else
ctrTextboxPartCost.Text = selectedAsmModel.get_CustomInfo2("",
"Cost");
}
VB
When setting the cost value of the part we could check to see if the
custom property already exists and if so set it using the set function,
else add it using the add function. For simplicity in this case we
150
Development
simply call delete first to remove any existing one, so the add call
after will always do the job.
Two more events of the assembly group in the PMP are the buttons
we created with the text “Update Cost” and “Set Cost” to update the
assembly Total Cost field or set the currently selected parts cost. We
need not add any event handler to capture these button clicks as it is
added as standard to any PMP. Go to the already created blank
OnButtonPress function and add the following:
C#
VB
151
Development
Checking the Id variable passed in to this function identifies which
button was pressed, based on the Id we gave to the button when we
created it. If you recall, all these values got stored in their respective
uid* variables so we check using them.
If the “Set Cost” button for the part cost was clicked, call the
UpdateSelectedComponentCost function we created just to set the
cost. If the “Update Cost” for the assemblies total cost was clicked
then call another function to calculate this cost:
C#
if (comps == null)
return;
float runningtotal = 0;
foreach (Component2 comp in comps)
{
// Get cost from component
ModelDoc2 mod = (ModelDoc2)comp.GetModelDoc2();
if (mod == null)
continue;
152
Development
float f;
if (float.TryParse(mod.get_CustomInfo2("", "Cost"), out f))
runningtotal += f;
}
ctrTextboxTotalCost.Text = runningtotal.ToString();
}
VB
Dim f As Double
If Double.TryParse(model.GetCustomInfoValue("", "Cost"), f) Then
runningtotal += f
Next
ctrTextboxTotalCost.Text = runningtotal.ToString()
End Sub
153
Development
Using a standard component traversal technique we traverse all
components of the current configuration and get their respective
models if possible. Then using that model we attempt to get the cost
custom property and convert it to a number. If that succeeds we add
it to a running total as we go, and finally set the PMP text box to that
value.
154
Development
Drawing Events
That’s the two easy ones out of the way – now for the big one, the
drawing events!
C#
VB
155
Development
End Sub
Creating the new selection function of the drawing - the reason for
monitoring the user selection is that when the user selects a view we
want that view to select in our PMP Views combo-box. Using the
same checks as in the other methods we check whether the selection
is a View, it selects the matching item in the combo-box using the
function above, and then stores the current View object in a variable
for use in other functions, and finally calls a function to deal with
view changes:
C#
int ActiveDrawing_NewSelectionNotify()
{
SelectionMgr SelMgr = (SelectionMgr)ActiveModel.SelectionManager;
swSelectType_e type =
(swSelectType_e)SelMgr.GetSelectedObjectType3(1, -1);
if (type == swSelectType_e.swSelDRAWINGVIEWS)
{
SelectComboboxItem(ctrComboboxViews, iDrawingViewsCount,
((View)SelMgr.GetSelectedObject6(1, -1)).Name);
selectedDrawingView = (View)SelMgr.GetSelectedObject6(1, -1);
DrawingViewSelectionChanged();
156
Development
}
return 0;
}
VB
Return 0
End Function
157
Development
So when a user selects a view, the variable selectedDrawingView
gets set to that view, and then the function
DrawingViewSelectionChanged gets calls – its job is to fill in the
text fields with information about the selected drawing view:
C#
VB
ctrTextboxReferenceFile.Text =
selectedDrawingView.ReferencedDocument.GetPathName()
158
Development
ctrTextboxReferenceConfig.Text =
selectedDrawingView.ReferencedConfiguration
ctrTextboxOrientation.Text =
selectedDrawingView.GetOrientationName()
ctrTextboxDisplayStyle.Text =
selectedDrawingView.GetDisplayMode2().ToString()
Dim dRatio() As Double = selectedDrawingView.ScaleRatio
ctrTextboxScale.Text = dRatio(0).ToString() + "/" +
dRatio(1).ToString()
End Sub
That takes care of the drawing views side of things, but what about
populating the Views combo-box with the views of the current sheet
in the first place? This is handled by the Sheets combo-box and its
functions.
159
Development
C#
VB
The PMP currently reacts when the user selects a View from the
drawing model window. It changes the Views combo-box selected
item and then calls the DrawingViewSelectionChanged function to
update the information. However, you will notice if you run the add-
in now that when the user changes the Views combo-box from the
PMP directly instead of selecting a view then the information will not
get updated. To correct this, and to respond to the change of
selection in the Sheets combo-box at the same time, add the
following to the already existing OnComboboxSelectionChanged
function:
C#
160
Development
{
ActiveDrawing.ActivateSheet(ctrComboboxSheets.get_ItemText(-
1));
DrawingSheetSelectionChanged();
}
else if (Id == uidComboboxViews)
ActiveModel.Extension.SelectByID2(ctrComboboxViews.get_ItemText(-
1), "DRAWINGVIEW", 0, 0, 0, false, -1, null, 0);
}
VB
161
Development
DrawingSheetSelectionChanged function to update the Sheets info
on the PMP, which in turn updates the Views combo-box via the
FillDrawingViewsList function.
The Views group will now be fully working and responding to sheet
changes, view selection and direct changing from the PMP. The
Sheets combo-box will call the function FillDrawingViewsList every
time it is changed, and set the Total Views control text box to the
total view count stored in the variable iDrawingViewsCount. So
once this FillDrawingViewsList function is created, the Sheets group
will also be fully functional:
C#
ctrComboboxViews.Clear();
if (views == null || views.Length == 0)
return;
iDrawingViewsCount = views.Length;
foreach (View v in views)
162
Development
ctrComboboxViews.AddItems(v.Name);
ctrComboboxViews.CurrentSelection = 0;
OnComboboxSelectionChanged(uidComboboxViews,0);
}
VB
iDrawingViewsCount = views.Length
For Each v As View In views
ctrComboboxViews.AddItems(v.Name)
Next
ctrComboboxViews.CurrentSelection = 0
OnComboboxSelectionChanged(uidComboboxViews, 0)
End Sub
Getting the currently active sheet, and calling the GetViews method
returns an array of View objects if any exist. Looping through each
view we add its name to the Views combo-box of the PMP, and
select the first view from the list.
163
Development
OnComboboxSelectionChanged function (presumably a bug), we
call it manually after.
All that is left now is to fill the actual Sheets combo-box with the
drawing models sheet names in the first place so that all this can
function. But when would you do this? This is where the
InitialiseInformation comes into play; the 3rd function that gets
called every time the ActiveModelChanged function gets called:
C#
SetDrawingFilename();
FillDrawingSheetList();
break;
164
Development
case swDocumentTypes_e.swDocPART:
pmPage.SetMessage3("Select features to show more details",
(int)swPropertyManagerPageMessageVisibility.swImportantMessageBox
,
(int)swPropertyManagerPageMessageExpanded.swMessageBoxMaintai
nExpandState, "Part Information");
break;
case swDocumentTypes_e.swDocASSEMBLY:
pmPage.SetMessage3("Select components to see cost information",
(int)swPropertyManagerPageMessageVisibility.swImportantMessageBox
,
(int)swPropertyManagerPageMessageExpanded.swMessageBoxMaintai
nExpandState, "Drawing Information");
CalculateAssemblyCost();
break;
}
}
VB
165
Development
SetDrawingFilename()
FillDrawingSheetList()
Case swDocumentTypes_e.swDocPART
pmPage.SetMessage3("Select features to show more details",
swPropertyManagerPageMessageVisibility.swImportantMessageBox,
swPropertyManagerPageMessageExpanded.swMessageBoxMaintainEx
pandState, "Part Information")
Case swDocumentTypes_e.swDocASSEMBLY
pmPage.SetMessage3("Select components to see cost
information",
swPropertyManagerPageMessageVisibility.swImportantMessageBox,
swPropertyManagerPageMessageExpanded.swMessageBoxMaintainEx
pandState, "Drawing Information")
CalculateAssemblyCost()
End Select
End Sub
We use this function to set the yellow message box of the PMP with
a message relevant to this type of document from the default of
“Awaiting Initialisation...”
C#
166
Development
ctrTextboxFilename.Text =
System.IO.Path.GetFileName(ActiveModel.GetPathName());
}
private void FillDrawingSheetList()
{
// Fill list
// Get sheet count to keep track
iDrawingSheetCount = ActiveDrawing.GetSheetCount();
ctrComboboxSheets.Clear();
ctrComboboxSheets.AddItems(ActiveDrawing.GetSheetNames());
VB
167
Development
DrawingSheetSelectionChanged()
End Sub
168
Development
Tidy Up
On last note with regards to PMP classes; always clean up after you
do not leave it up to .Net or SolidWorks.
For any variable defined in the class-scope that is of any type from a
SolidWorks library such as Component2, ModelDoc2, SldWorks,
PropertyManagerPageTextbox etc... must be set to null or
Nothing in the AfterClose function.
You are all done. Compile your project and run it to see the results.
Start SolidWorks and open up a Part, Drawing or Assembly (or all 3),
and watch how your PMP automatically appears and stays there,
showing information about the active model instantly and being fully
interactive.
Don’t forget to make your project properties “Register for COM” and
“COM Visible”!
169
Development
You will probably find yourself having to read over this section a few
times to understand the flow of the add-in and how it works, as well
as study the example code provided and the complete project
provided, but with patience and tolerance you will soon come to
grips with the project, and this will in turn open up a whole lot of
possibilities for expanding it into a program of your own.
170
Development
Enhancements
The development stages were designed to give you a firm grounding
into a working add-in that covers all advanced features of a PMP and
event handling, hooks, custom control functions and more. But the
add-in itself still remains in its early stages, and to create a truly
powerful tool you should add enhancements of your own using the
skills and knowledge learned here.
Taking the add-in to the next level here are some suggestions of
improvements that could be fairly easily implemented:
Part Improvements
- Add a new field to show the currently selected Feature
name.
-
171
Development
Assembly Improvements
- Add a ChangeCustomPropertyNotify hook to monitor when
the user changes the Cost custom property and have the
PMP automatically update
- When the “Set Cost” button is clicked, set the cost for the
current component, and then call the “Update Cost” function
automatically to update the new total cost.
- Add a new field to the component/part group to display the
currently selected component and part name.
- Improve the ActiveAssembly_NewSelectionNotify function
to detect Face, Edges and Body selections and then aquire
the relevant ModelDoc2 object from those also.
- Add checks for lightweight components (which will make the
acquisition of the ModelDoc2 from the Component object
fail), and warn the user about this and whether they would
like to resolve the component. If they select yet, resolve it for
them.
- Add checks and restrictions for costing input validation so
the user can only enter valid number information into the
fields.
- The current traversal of the assembly configuration is only
top-level. Improve the CalculateAssemblyCost function to go
multi-level.
- Check for Component Visibility/Suppression state and decide
whether to calculate based on that. Add this decision option
to a Settings form.
- Round up the total cost to decimal places and prefix/append
a currency symbol.
- Add the DP and currency symbol to a Settings form so the
user can specify them.
172
Development
- Add a field to the Settings form for the “Cost” value of the
Assembly Page function.
- When setting a components Cost value, set the model to
Dirty so it tells SolidWorks it needs saving.
Drawing Improvements
- Make the Drawing page View fields such as Orientation,
Referenced File and Config bi-directional so the user can
directly set these values as well as read them.
- Add AddItemNotify and DeleteItemNotify functions to the
drawing model and detect View/Sheet deletion and creation,
and update the combo-box controls as such.
- Add detection of user changing the active sheet and update
the Sheets combo-box to match
A good idea I had during this book but simply could not fit such a
project in, was to expand this add-in to function for every SolidWorks
object that can be selected by the user, and make them all
completely bi-directional; you could add a page for when the user
selects a Base Extrude feature for example that shows the direction,
distance and selected entities so the user would only have to click
the object with the left mouse and see it and be able to edit it
instantly without needing to go into an edit feature state. Imagine
expanding this to all features and beyond? The user would be able to
analyse, read and edit models, components and drawings 10 fold
faster with SWInfo. Take that another step further and add the
option to have a small checkbox constantly visible that disables bi-
directional processing so it just reads info not set it (to improve
speed and performance), then once the user wants to edit the page
just check the box! Get the idea yet? Have a think yourself and see
what you come up with, the potential is definitely there.
173
Methods of Deployment
Manual Installation
SFX Archives
Installation Packages
Creating An Installer
174
Methods of Deployment
You may think that deploying a product has only one or two options,
and it’s just a case of copying a single exe file to the computer it is to
be used on; however, once you get beyond basic programs (as you
have through the course of this book) there are many more stages to
installing a program that just copying a file.
175
Methods of Deployment
Manual Installation
The first method naturally used in the beginning is manual
installation; this involves the user of the computer copying the
required files to the relevant folders, registering library files with the
registration service, adding registry entries and anything else
required.
Other times the program has several files, but all located in the same
folder and no other addition requirements like library (dll)
registration, registry entries or the likes, so again this method would
be suitable; creating a single-file archive (.zip, .rar, .exe) using
WinZip, WinRAR or similar, and they distributing this file for the user
to extract to a folder of their choice.
Once programs get more complex or you would like to give a more
professional feel to your release, but the program still needs not any
registrations or registry entries, then the next up on the list is often
what is called an SFX archive.
176
Methods of Deployment
SFX Archives
One step up from manual installations is to create SXF archives (SelF
eXtracting archive); these are archives with an exe extension that the
user can just double-click to run and install your product. SFX
archives are limited in interface, functionality and size.
Using an SFX archive you are able to install files to specific locations
such as program files, start menu, user desktops etc.. or custom
locations, provide a title and description, logo and icon for the
installer, create shortcuts to any file you are installing on the fly, and
password protect them. This is often more than enough for most
basic applications, and even some professional larger scale ones.
Next select all of the files by either Ctrl+A, or dragging a box around
them so they are highlighted. Right-click any one of the files and
choose “Add to Archive”, to bring up the WinRAR interface with its
default options.
177
Methods of Deployment
Next click the “Advanced” tab and the SFX Options button to bring
up our SFX options:
178
Methods of Deployment
General
Path to extract - Folder name to
extract selected files to within
the "Program Files" system
folder. You may use \ characters
to denote creating sub-folders
such as shown in the example
image.
Update
Overwrite Mode - Select whether to automatically replace existing
files or to prompt the user to overwrite. For cleanliness I like to select
Overwrite all files as default.
License
If you want you can display a license file for the user to read and
accept first, much like the more professional installation packages.
179
Methods of Deployment
Load SFX logo from file - You can provide a bitmap image approx 90
x 300px in size to be displayed on the left hand side of the program
installation window.
Load SFX icon from file - As well as customising the main window,
you can provide your own windows shell icon to be displayed by the
exe file here.
Advanced
Request administrative access - This is usually best checked in case
the user is restricted from installing the files to the "Program Files"
folder.
Add Shortcut... – Use this to have the SFX archive create shortcuts
for the user in the typical locations such as desktop and program
files:
180
Methods of Deployment
Shortcut description – This is the description of the shortcut that
shows up in a tooltip when the user hovers the mouse over it for a
second or two.
Shortcut icon – If you have an icon file (.ico) in your archive that is
being installed you may use that as the icon for the shortcut by
typing in the exact name of the icon file including sub-folders and
filename extension, just like the Source file name option.
You may add as many icons as you like to the archive one at a time.
The most common practise is to create a shortcut for the main
program exe, and one for a help file if it exists.
With all your settings specified click the OK button to close the
advanced options, and then OK again to create your archive file.
WinRAR will do its bit and once complete you will have a new file in
the folder with the name you gave it, showing either the custom icon
defined, or the standard purple books icon of WinRAR.
181
Methods of Deployment
Installation Packages
Sometimes your program requires more than can be achieved using
simple methods of installation or you just want a more professional
package to give to your users. In that case you start to look at
dedicated installation deployment tools. The most common are as
follows:
Each deployment tool has its own company, structure and manual to
help the developers use their software. Whichever package you
choose dictates what methods and procedures you have to follow.
There is no one deployment creation guide that I can give you to
allow you to use all of the above packages, so I will focus on the first;
Visual Studio Setup Project. As well as being free in any Visual
Studio package, it more closely follows the interface we are used to.
If you only have the Express version of Visual Studio, download a trial
version of Professional to get you started.
182
Methods of Deployment
Creating an MSI Installer
What better practise than to create an installer package for our final
project created in the Development chapter?
Begin by creating a new folder to store all of the program files in.
Open up the bin folder of the SWInfo final project, and go into the
Debug or Release folder. In there you will find all of the output files.
183
Methods of Deployment
Change the name from Setup1 to something more desirable. Click
OK to create the project.
The initial screen looks similar to a Visual Studio project, with the
Solution Explorer at the right and the files within it, and the main
window to the left. Before we start to add functionality to our
installer lets clearly define the needs:
- SwInfo requires all files are copied to the same folder so they
can be found for referencing.
- Main dll file must be registered with COM.
- User must accept a license agreement.
- SolidWorks must already be installed.
From here you have 3 folders; Application Folder, User’s Desktop and
User’s Programs Menu. As running an installation file requires the
user to be logged onto the machine the installer has information
regarding the user’s desktop and start menu locations. Application
Folder is the folder the user selects to install the program during the
setup wizard.
184
Methods of Deployment
The reason it does this is because all of the other files
(solidworkstools.dll, Interop.SWPublished.dll etc...) are all
referenced in our .Net add-in file (added in the References folder of
our add-in VS project), and being integrated into Visual Studio, the
Setup Project automatically detects any dependencies and adds
them also.
For now the default location will do us. You may notice the location
is using special tags (denoted by the [] brackets), such as
[Manufacturer] and [ProductName]. These are tags specified in the
setup projects properties. This moves us on to the setup projects
properties.
185
Methods of Deployment
can further be used to display information in the Programs &
Features/Add-Remove Programs list when selecting to install,
remove or modify the installation.
DetectNewerInstalledVersion
– This tell the installer to look
for installed products of the
same ProductCode (unique ID),
and compare their version with
this version. If a higher version
is installed, or one of the same
verison, setup terminates.
InstallAllUsers – Specifies
whether the product is installed for all users; ignore this setting for
our purpose.
186
Methods of Deployment
Localization – This is not used until a much more advanced level not
covered in this book.
187
Methods of Deployment
Subject/SupportPhone/SupportUrl – Another product description
tag; shows in standard file information summary of Windows, and
some in Add/Remove programs.
- Author
- Description
- Manufacturer
188
Methods of Deployment
- ProductName
- Title
- Version
COM registration
If you browse around the file properties for the main SwInfo dll file
from the File System view you will come across a property called
“Register” with an option for “vsdraCOM”. At first you would
presume this is all that is needed to register our program for COM,
and effectively run our add-ins COM Registration functions.
The first is correct this function will register our add-in to COM, so it
is accessible as a COM object (so change the property to vsdraCOM
while you are here), but it will not take care of the second
requirement to run the add-ins COM registration functions and
thereby add the registry entries to the SolidWorks registry hive.
With the custom installer class included in your add-in project and it
has been compiled and updated (by replacing the existing file in the
folder we are using to store our installation files, the ones we added
189
Methods of Deployment
to the Application Folder), Open up the Custom Actions window
from View->Editor->Custom Actions. Here you are presented with 4
folders. For the Install, Rollback and Uninstall folders do the
following:
That is it! The add-ins custom COM functions will now run
automatically thanks to the custom DllInstaller class.
The custom functions play the role of using .Net to find out where
the regasm.exe file is located on the clients machine, and then
calling that file to register our dll with it.
190
Methods of Deployment
License Agreement
You have come this far to create a professional installation package
and what would it be without a license agreement.
191
Methods of Deployment
The problem with the first two is they tend to have many variations
in security to access or find the files; they may have been customised
or changed, or obscured in some way. The last method is the
simplest and most robust, and that is to detect the presence of a
SolidWorks folder in the windows registry; if it is there, chances are
SolidWorks is installed.
192
Methods of Deployment
The installer will check for the existence of a folder called
SolidWorks in the HKLM\Software hive, and if it is found set the
property (called REGISTRYVALUE1 by default).
193
Methods of Deployment
Once compiled you will have 2 files in the setup projects output
folder called Setup.exe and ProjectName.msi (where project name
is your projects name).
194
Methods of Deployment
Creating An Installer
If an SFX archive or manual install are too basic for your needs, but a
dedicated installation deployment tool is just overkill, or not bespoke
enough to suite your exact needs, then there is still a third option –
write your own installation package. Yes, write your own; that is
exactly what we are going to do now!
As well as doing all the above requirements you will then have a
framework that you can easily build upon afterwards, with all the
power of a .Net program at your disposal.
195
Methods of Deployment
To begin with create a new Visual Studio Windows project. Call it
SWInfoCustomInstaller or similar. On the main form add the
following items, given then the names and properties defined, and
align them to however you like. Here is an example of my layout:
196
Methods of Deployment
Change the Main Form properties as follows:
References
Required later is a reference to the Windows Script Host Object file;
click Project->Add Reference... select the COM tab and add the
“Windows Script Host Object” as a reference.
C#
using System.IO;
using System.Reflection;
using System.Collections;
using IWshRuntimeLibrary;
197
Methods of Deployment
VB
Imports System.IO
Imports System.Reflection
Imports System.Collections
Imports IWshRuntimeLibrary
Variables
Next create some variables to be used:
C#
Assembly asmMe;
AssemblyName nameMe;
List<string> filesToInstall;
List<string> filesToRegister;
Hashtable filesToShortcut;
string regasmPath;
string sDesktop;
string sStartMenu;
string sProgramFiles;
198
Methods of Deployment
VB
199
Methods of Deployment
Create a new folder in our project by going to Project->New Folder.
Call the folder “Contents”.
If the files do not show make sure you have the “All Files (*.*)”
selected in the file type.
To do this select each file from the Solutions Explorer and then in the
Properties windows change the Build Action to Embedded
Resource.
If you browse to the Visual Studio projects folder for this project now
(usually My Documents\Visual Studio 2008\Projects) you will notice a
200
Methods of Deployment
new folder within their called “Contents”, and in that folder are the
files we added. Whenever you want to update the installation files,
say if you are releasing a new version, there is no need to re-add the
files using Visual Studio. Instead just replace the files in this Contents
folder with the newer files using the normal Windows Explorer and
Copy/Paste method. The files in this folder are added to the main
.exe every time you compile the installer project.
Event Handlers
Now you have the layout sorted, and the installation files embedded,
it’s time to add some event handlers for functionality.
At the main forms visual design interface, double-click the title bar of
the form to add an event handler to the OnLoad event of the form.
Switch back to the form view and double-click the Browse button
(...) and the Install button to add two more events.
In the code view, for these 3 event handlers, add some blank
functions like so:
C#
201
Methods of Deployment
BrowseFolder();
}
VB
202
Methods of Deployment
Here are the 3 main functions of our program.
InitialSetup
This function will declare all of the information required to start a
new installation, such as getting the current users special folders
(Desktop, Start Menu, Program Files), setting the default installation
folder, finding the location of the regasm.exe file needed for
registering libraries, and also declaring what files are to be installed,
links and registered for this installation.
BrowseFolder
This is a very simple function; it displays a folder browser dialog to
the user to select a folder where they would like to install the
program.
StartInstall
This is the main function responsible for starting a new installation.
C#
203
Methods of Deployment
regasmPath =
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirec
tory() + @"regasm.exe";
204
Methods of Deployment
VB
205
Methods of Deployment
filesToInstall.Add("SWInfoCS.dll")
filesToInstall.Add("SWInfoCS.tlb")
206
Methods of Deployment
End Sub
We start by finding out the location of the regasm.exe tool that gets
installed with all versions of the .Net framework. This tool is used to
register our .Net add-in so it works with SolidWorks. The
GetRuntimeDirectory is the location of the .Net framework
installation, typically C:\Windows\Microsoft.NET\Framework\v?.?\.
Then within that folder is the regasm.exe file we need.
Next we create a new array of strings for files to install and files to
register (with regasm.exe). These strings reference the exact
filenames of files we have added to the Contents folder earlier, so
make sure you match them exactly.
Note that there is an additional file I have added (both to the string
array and to the actual Contents folder) called “readme.txt”. This is
just a text file where you can write some instructions to the user
about the SWInfo add-in and how to use it; if we didn’t have a text
file then once the add-in was installed, the user would have no
shortcuts or indication of what is installed on the system as a dll file
cannot be opened directly so it is nice to place a read me file on there
too so they have some indication. Feel free to add any type of
additional files you like to the installer.
207
Methods of Deployment
well as a name for the shortcut so a Hashtable is ideal as it links
these pairs of strings to each other. The only item we add a shortcut
to is the readme.txt file, and we call the shortcut “SWInfo Readme”.
The next 3 lines use the .Net GetFolderPath function to get special
information about the current user. As we give the user the option to
place a shortcut on the desktop, and start menu, and want the
default installation to go to the Program Files folder, we gather
those 3 pieces of information.
The last line sets the installation folders text box to the program files
location and a sub-folder called AngelSix, followed by another sub-
folder called SwInfo so by default the files would be installed to
“C:\Program Files\AngelSix\SwInfo”.
C#
208
Methods of Deployment
fb.Dispose();
}
VB
fb.Dispose()
End Sub
We set a description that appears in the title, and set the starting
folder of the browser to the current folder that is already selected in
209
Methods of Deployment
the text box before showing it to the user. From there we check if the
user clicked the OK button by analysing the DialogResult return
from the ShowDialog function, and if it is OK then set the
installation folder text box value to the folder the user selected.
All that our log function needs to do is take a string as an input, and
add it to the log text box on a new line, scrolling to the next line as it
goes to keep the latest message always in view. This is done like so:
C#
210
Methods of Deployment
VB
For example you would not want the user clicking the Install button
again once the process is half way though, calling the StartInstall
function all over again mid-process, or changing other options.
To handle this we create effectively 2 “states” if you like; one with all
items enabled, and the other with them disabled.
C#
211
Methods of Deployment
VB
When the user clicks the button we start a new installation, clear the
log, toggle the state to disabled, and begin an install.
C#
ToggleState(true);
}
212
Methods of Deployment
VB
C#
213
Methods of Deployment
// Register files
foreach (string file in filesToRegister)
if (!RegisterFile(Path.Combine(installDest, file)))
return false;
// Create shortcuts
if (cbCreateDesktop.Checked || cbCreateStartMenu.Checked)
{
foreach (DictionaryEntry entry in filesToShortcut)
{
string shortcutFile = (string)entry.Key;
string shortcutDesc = (string)entry.Value;
214
Methods of Deployment
if (!CreateShortcut(Path.Combine(installDest, shortcutFile),
sDesktop, shortcutDesc))
return false;
VB
215
Methods of Deployment
216
Methods of Deployment
Next
End If
End Function
Next we get the directory the user specified for installation, and call
the CreateDirectory function to create it if it doesn’t already exist. If
the user does not have permission to create a folder in that location
the program will catch the error and display the relevant log
message, then return from the installation immediately with a false
return value to indicate failure.
The same again for the filesToRegister list, only this time called
another function; RegisterFile.
And thirdly, if the user has specified to create shortcuts, loop each
item in the filesToShortcut hash table and call a function
CreateShortcut.
217
Methods of Deployment
That is it – If all of those processes succeed the installation was a
success so we return true.
C#
218
Methods of Deployment
VB
Now compile and run your project, specify an installation folder and
to create shortcuts or not, and click the Install button. Watch how
the log box gets updated once complete, and how the selected
installation folder gets created, and messages appear about
installing, registering and creating shortcuts.
219
Methods of Deployment
Testing at this stage is
always a good idea in a
program as it displays
whether the program is
flowing right in the first
place, if events are firing at
the right time and if it is
generally behaving as
expected.
RegisterFile
Starting with the shortest function first; to register an assembly
takes but one call to regasm.exe. As we already found the
regasm.exe file location previously and stored it in the regasmPath
variable, all that is left is to call it and pass in the argument
/codebase and the location of the assembly (our add-in dll).
C#
220
Methods of Deployment
{
// Execute regasm
System.Diagnostics.Process.Start(regasmPath, "/codebase \"" + file
+ "\"");
}
catch
{
Log("Failed to register " + file);
return false;
}
return true;
}
VB
Return True
End Function
221
Methods of Deployment
We passed in the filename of our add-in from the Install function.
Running Process.Start() with the regasm.exe filename, and passing
in the argument “/codename assemblyname” does the trick of
registering the specified file.
CreateShortcut
Next on the list is the create shortcut function; again very simple
once you know how. Using the Windows Script Hosting class
C#
222
Methods of Deployment
return true;
}
VB
Try
' Create a new instance of WshShellClass
Dim shell As WshShell = New WshShellClass()
223
Methods of Deployment
Return True
End Function
From there we have access to a shortcut object that we can set the
properties of, and save using the Save function.
InstallFile
Finally the last leg of the program, the most complicated part you
will come across, is the InstallFile function. No not worry too much if
you do not understand this function, just to know how to use it and
to know it works is enough. Its function is plain – to extract the files
we embedded into our assembly, back out and into the installation
folder.
224
Methods of Deployment
The embedded resource is retrieved to a Stream object using a
function from the assembly object called
GetManifestResourceStream; really you can think of this function
as being called GetEmbeddedFileAsStream.
From that Stream object, we read its data, and write it back out to
another file in the installation folder, effectively “copying” it to the
installation folder. I will not explain the inner workings of this
function for this purpose, but if you wish to fully understand it feel
free to email me or use the AngelSix forums.
C#
Stream s;
try
{
s = asmMe.GetManifestResourceStream(nameMe.Name +
".Contents." + embeddedFile);
if (s == null)
throw new NullReferenceException();
}
catch
{
Log("Error: Corrupt " + embeddedFile + " in installer.");
return false;
}
225
Methods of Deployment
try
{
using (FileStream newstream = new
FileStream(Path.Combine(destination, embeddedFile),
FileMode.Create))
{
byte[] buffer = new byte[32768];
int chunkLength;
while ((chunkLength = s.Read(buffer, 0, buffer.Length)) > 0)
newstream.Write(buffer, 0, chunkLength);
}
s.Close();
}
catch
{
Log("Error: Error copying " + embeddedFile + " to " + destination);
return false;
}
return true;
}
226
Methods of Deployment
VB
Dim s As Stream
Try
s = asmMe.GetManifestResourceStream(nameMe.Name + "." +
embeddedFile)
If s Is Nothing Then Throw New NullReferenceException()
Catch
Log("Error: Corrupt " + embeddedFile + " in installer.")
Return False
End Try
Try
227
Methods of Deployment
Return False
End Try
The only reason I have stopped there with this installation project is
because the book is dedicated to the full product life cycle and not
just the installation side of things, so I feel I have taken this project as
far as needs to be for the purpose of this book
But for those of you excited about this project and want to take it
further, and possibly even commercial (that’s what this book is
guided towards!), here are a few suggestions for improvements to
becoming a commercial installation deployment tool:
228
Methods of Deployment
- Uninstall.exe functionality
- Get selected drives free space / required space
- Create a Next/Previous, wizard style form layout
- Add license agreement rich text box
- Expand on try/catch and error message logging
- Provide a progress bar
229
Methods of Deployment
230
Licensing Your Product
Overview
Self-Implementation
Corporate Licensing
231
Licensing Your Product
OK so you have now successfully created a marketable software
program, and created a client-side installation package ready for
distribution. But if you have got this far the chances are that you
intend to make the program profitable, not free.
Overview
Although you can sell your program for a fee and provide the client
with the installation package after payment, nothing stops them
from installing your program on any number of machines, or
distributing it to other users. This is OK if you intend to sell the
program license as such, but normally a company or individual would
sell the program on a per-seat basis, so that it is locked to a single
machine at any one given time.
232
Licensing Your Product
Self-implementation
For those of you who wish to license a program, but are not fussed
about it being secure, hacked, or otherwise bypassed or feel that the
environment it is to be used in is under no threat suitable to justify
advanced licensing then self-implementation can be a quick, simple
and more importantly free way of licensing your program.
233
Licensing Your Product
Corporate Licensing
The option 95% of licensed software use are corporate licensing
products; by this I mean using products from a corporation that is
dedicated to developing protection software and hardware and has a
proven client base.
One thing to bear in mind with licensing products is that you are
using .Net compiled software here, so Win32 protection will not do;
you need .Net specific protection.
234
Distribution and Sales
Marketing
Accepting Payment
235
Distribution and Sales
You may realise by now you have covered every aspect of a products
life cycle from the initial thought and planning, right through to
product design, testing, installation and even licensing. One final
stage of this journey is to get some revenue from all your hard-
earned work.
Your product is digital so the first logical step is online sales, but you
still have the choice of in-store distribution also by offering a media
copy, boxed and pre-licensed.
First and foremost create a logo for your product; this can be done
using trial versions of Adobe Photoshop, Paintshop Pro or similar. A
logo is often best kept simple; the less in a logo the easier it is to
read, the better it is for mass-format (such as signs, laser cutting,
small print, portability) as you never know where your product make
take you and the last thing you want is to have to re-brand later
down the line due to complex logo designs. You can always add a
purely non-text part to represent a logo, and then a text version.
That way you can use either independently. Take a look at any large
company and their logo:
236
Distribution and Sales
Nothing complicated there; all logos have their text side, and some
have a vector-art side. Vector art is art drawn using solid
mathematical lines and curve data, not pixel art that is purely colours
per-dot on a media.
The reason the logos are kept this way even on the non-text
counterpart is for production; you can have a sticker, cloth branding,
laser etching, and tooling, signs, almost anything made from vector
data so it does not limit the company.
Enough talk let’s make a simple but elegant logo for our SWInfo
product. Grab yourself a copy of Photoshop CS4 trial and open it up.
237
Distribution and Sales
Click the text tool from the tools panel at the left (if you do not
see it go to Window->Tools). Your cursor will change to a text icon
with a caret. Click somewhere in your white workspace to start a text
object.
Type in the word “SWInfo” and then to escape the text tool edit
mode press the Move tool button at the top left of the panel.
That has escaped you from text edit mode (typing text) but we still
want to edit the text properties, so click the text tool icon again
to return to text mode (not text edit mode), and you will see a new
toolbar appear below the main menu:
Change the font family to Century Gothic, the style to Bold and the
size to 48 points.
Now the text logo has its shape add some colour to
make it more pleasing to the eye. With the text tool
selected from the panel, click and drag from start to
finish the “Info” part of the “SWInfo” text in the
workspace to select it, just like you would select
text in Word or Notepad. To apply a colour to this selection left-click
on the black box on the top text toolbar to the right of the paragraph
alignment icons: to bring up the colour dialog box.
238
Distribution and Sales
Set the colour to R74, G138, B75 to give it a mild blue tint.
Next, select the “SW” part of the text and do the same, only this time
change the colour to R98, G98, B98 to make it a faded grey.
Come out of text-edit mode by selecting the Move tool and then
back to text mode by selecting the Text tool, and add another text
layer by clicking on the white workspace again to start text-edit
mode. Make sure you click away from the “SWInfo” text or else you
will not create another text object, but instead enter edit-mode for
the “SWInfo” object. If that happens just come out of edit-mode and
try again.
Now type in our slogan “Power at your fingertips!”. Come out of edit
mode and change back to the Text tool, then change the font size
right down to 12 points.Click the Character/Paragraph tool as before
to show the dialog box, and change the Vertical Scale back to 100%
and the Tracking to 0. Set the font colour to R94, G126, B152.
Position the slogan text layer and position it just under the main text
object by selecting the Move tool, then click-dragging the mouse
over the text, or using the arrows on the keyboard. Position it to
something like this:
239
Distribution and Sales
Because we want to keep this simple, and I don’t want to overload
the book with Photoshop tutorials our vector art is still going to be a
text object, however I think you will be surprised at the end-result
even so.
Create another text object as before, but this time type a single “?”
as the character. Change the Font Family to Impact, the size to 100
points (not in the drop-down list so just type it straight into the
dropdown box) and the colour to R215, G215, B215.
And there you have it, one quick and simple logo ready for use. There
is no saying you cannot jazz it up for product packaging or websites
or business cards using effects and the likes, because you still have
your original base design if the media needs purely vector art data.
240
Distribution and Sales
241
Distribution and Sales
Approaching Resellers
Depending on the type of product you are selling determines which
resellers you should go to; books – Amazon, Waterstones; computer
hardware – eBuyer, Micro, Dabs; computer software – depends on
your sector but includes most of the above.
You approach most in the same way; start it with a phone call or
email to the company explaining a little about your product (no more
than 3 sentences as you do not want to overload them with detail at
the initial stage), and express the desire to sell your product with
them as the reseller.
The typical setup is they ask for a sample of 2-3 copies, which you
send to their head office for approval. Once approved you will be
sent some format documentation explaining the standard
agreement that they have; although each companies agreement is
likely to be different the structure tends to be the same:
242
Distribution and Sales
- You receive payment only for the items sold to the
customers, not items sent to the reseller, so post-payment.
- The reseller takes a cut of the profit (15-40%)
- You provide them with a Recommended Retail Price
Dealing with resellers takes the onus of sales mostly off yourself and
frees you up for your next project, at the cost of the profit cut taken.
Self-Sale
Selling a product yourself online is done through a website you own
or control. You may advertise or display it however you want. All you
need to do is provide a method of payment for the customers, and a
way to collect customer information.
All but the first method can be done by nearly any computer user
that already has a website up and running; chances are if you can get
that far you can add a PayPal button to your site or collect customer
information through a PHP or ASP form.
243
Distribution and Sales
Franchise
Once you establish a customer-base or get some market awareness
the next option is to look at franchises; if you are lucky you get
people approaching yourself asking to be a franchisee, if not you find
them.
A franchise is better than a reseller as you set your own sales terms
and profit cuts, and as is the general consensus with franchising it is
pre-payment; the franchisee pays in advance for bulk of your product
as a reduced cost of typically 10-20%, and that is your job done –
whatever they sell from that point on at whatever price is their profit.
244
Distribution and Sales
Obviously some stores have their own rules as always so some of the
above may vary as well as additional ones may apply.
The benefit of In-Store sales is the wider age group and audience
(online was not always around). You get much younger and much
older customers coming into stores compared with those that visit a
website.
245
Distribution and Sales
Marketing
Marketing methods can be explained in one page; create your
marketing bumph (logo, slogan, business card, website banners,
splash pages, leaflets, demos, video tutorials, customer testimonials,
etc...) and then get it out there.
There are many methods of marketing and each product has its own
target market, so always take that into consideration when
advertising as there is no point advertising a new Woman’s Dress
Shoe in a book store!
246