I'm using InstallShield 2012 Spring - Premier Edition and I'm trying to replace the existing install of our software (if it exists) with whatever is in the new Setup script when Setup script is run again.
I have read some things online that say to configure Minor and Major upgrade settings.
I have an InstallScript Project and I cannot find how to do Minor and Major upgrades, like it can be done with Basic MSI projects. I read online that this can be done with MSI projects by going to Installation Designer and then Media/Upgrades and then configure the upgrade. This option is not available within InstallScript projects.
What can I use with InstallScript projects to change this behavior? Thank you in advance.
I believe you need to add a few more event handlers to your main script. To start, you'll need to add the following handlers if they're not already present:
OnShowUI
OnUpdateUIBefore
OnUpdateUIAfter
OnMaintUIBefore
OnMaintUIAfter
The important thing is to have OnShowUI in order to run the appropriate "Before" method. Below is what I do; you'll need to do whatever you need to do in the other methods (mine are pretty domain-specific and I can't provide those directly).
//---------------------------------------------------------------------------
// OnShowUI
//
// This function drives the UI sequence and file transfer of the setup.
//
// The OnShowUI event is called directly by the framework to initiate
// the UI sequence and file transfer of the setup. By default this event
// displays UI that informs the end user that the maintenance setup has been
// completed successfully.
//---------------------------------------------------------------------------
function OnShowUI()
BOOL bMaintenanceMode, bUpdateMode;
string szIgnore, szTitle;
LIST listDirs;
number nFindAllDirsResult, nFindAllFilesResult;
BOOL lDirEmpty;
begin
// Enable dialog caching
Enable( DIALOGCACHE );
// Determine what events to show
bUpdateMode = FALSE;
bMaintenanceMode = FALSE;
// Remove this to disabled update mode
if (UPDATEMODE) then
// checking to make sure app still exists in orig location
if Is(PATH_EXISTS, TARGETDIR) then
// Also check for empty TargetDir
lDirEmpty = IsTargetDirEmpty();
if (lDirEmpty) then
// TARGETDIR is completely empty, so disable UPDATE mode
bUpdateMode = FALSE;
else
// TARGETDIR has some contents, so continue with UPDATE
bUpdateMode = TRUE;
endif;
else
// Turn off Update mode if original folder is gone
bUpdateMode = FALSE;
endif;
if (!bUpdateMode) then
// If Update mode is set but the original target is missing
// need to flag the installer to force full reinstall (otherwise it will
// think all features have already been installed (by analyzing the log))
FeatureReinstall();
endif;
endif;
// Remove this to disable maintenance mode.
if (MAINTENANCE) then
// checking to make sure app still exists in orig location
if Is(PATH_EXISTS, TARGETDIR) then
// Also check for empty TargetDir
lDirEmpty = IsTargetDirEmpty();
if (lDirEmpty) then
// TARGETDIR is completely empty, so disable Maint mode
bMaintenanceMode = FALSE;
else
// TARGETDIR has some contents, so continue with Maint
bMaintenanceMode = TRUE;
endif;
else
// Turn off maintenance mode if original folder is gone
bMaintenanceMode = FALSE;
endif;
if (!bMaintenanceMode) then
// If Maintenance mode is set but the original target is missing
// need to flag the installer to force full reinstall (otherwise it will
// think all features have already been installed (by analyzing the log))
FeatureReinstall();
endif;
endif;
// Show appropriate UI
if( bUpdateMode ) then
OnUpdateUIBefore();
else
if ( bMaintenanceMode ) then
OnMaintUIBefore();
else
OnFirstUIBefore();
endif;
endif;
// Move Data
OnMoveData();
if( bUpdateMode ) then
OnUpdateUIAfter();
else
if ( bMaintenanceMode ) then
OnMaintUIAfter();
else
OnFirstUIAfter();
endif;
endif;
// Disable dialog caching
Disable(DIALOGCACHE);
end;
I will say that in the OnUpdateUIBefore, I commented out the following code:
// Check whether the update is needed.
if( nResult = VERSION_COMPARE_RESULT_SAME ) then
// Note: This result should occur only for differential media, since the setup
// will display OnMaintUIBefore or OnFirstUIBefore by default if the versions match
// for full setup media.
szMsg = SdLoadString( IDS_IFX_WARNING_UPDATE_NOT_NEEDED );
SdSubstituteProductInfo( szMsg );
if( MessageBox( szMsg, MB_ICONEXCLAMATION | MB_YESNO ) != IDYES ) then
abort;
endif;
endif;
I don't remember why, but I suspect it was causing the Update mode to not work as I expected.
I automate my Installshield builds (via COM--see this answer for basic info if interested) and part of that process involves incrementing the minor version in order to trigger Update Mode when the new installer is ran against an older version.
Good luck!
Related
I am upgrading some software from 16 bit to 32 bit in VC++ using MFC, and I understand that in recent versions of MFC I can no longer access m_templateList in CDocTemplate, I must use GetFirstDocTemplatePosition and GetNextDocTemplate instead. That is no problem as far as enumerating templates is concerned (a dialog being opened only in the case where there is more than one template). My question is what approach is best to get round the fact that a reference to the template list is currently being passed to the dialog on creation, and a selected template is being returned? Here is the code:
void CMtApp::OnFileNew()
{
CString s;
if (m_templateList.IsEmpty())
{
TRACE0("Error : no document templates registered with CWinApp\n");
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return;
}
CDocTemplate* pTemplate = (CDocTemplate*)m_templateList.GetHead();
if (m_templateList.GetCount() > 1)
{
// more than one document template to choose from
// bring up dialog prompting user
COpenTypeDlg dlg(&m_templateList);
if (dlg.DoModal() != IDOK)
return; // none - cancel operation
pTemplate = dlg.m_pSelectedTemplate;
pTemplate->GetDocString(s, CDocTemplate::docName);
}
ASSERT(pTemplate != NULL);
ASSERT(pTemplate->IsKindOf(RUNTIME_CLASS(CDocTemplate)));
m_bNew = TRUE;
pTemplate->OpenDocumentFile(NULL);
}
You can pass the CWinApp to the dialog's ctor and the dialog can GetFirstDocTemplatePosition and GetNextDocTemplate itself. But you don't really need to pass the CWinApp because the dialog can use AfxGetApp to get it itself.
If you insist on passing a template list then build your own list based on what GetFirstDocTemplatePosition and GetNextDocTemplate return.
This line
publishedDoc.save(true, false, true);
causes the above stated error once in a while, on documents saved on the web where inline images where added via CKEditor. I can't pinpoint the circumstances that causes this error, so any hints or comments are greatly appreciated.
Here is a bit more code, so you get the context of that call:
function postSavePage(doc) {
if(doc.getItemValueString("status")=="To Be Published" || doc.getItemValueString("status")=="Save as Current Version" ) {
var saveAsCurrent = doc.getItemValueString("status")=="Save as Current Version";
var publishedDoc:NotesDocument = getCurrentlyPublishedDoc(doc);
if(!publishedDoc) {
publishedDoc = doc;
publishedDoc.replaceItemValue("status", "Published");
PublishedDoc.replaceItemValue("VERNUMBER", 1);
} else {
//******
//copy draft to temp doc
var tmpDoc:NotesDocument = database.createDocument();
doc.copyAllItems(tmpDoc, true);
//copy published to draft (for archiving)
publishedDoc.copyAllItems(doc, true);
if(saveAsCurrent) {
doc.replaceItemValue("status", "Archive (Saved As Current)");
} else {
doc.replaceItemValue("status", "Archive");
}
//copy temp (newly published) to published doc
tmpDoc.copyAllItems(publishedDoc, true);
//Make sure we set the version number if saved as current version
if(saveAsCurrent){
publishedDoc.replaceItemValue("VERNUMBER", doc.getItemValueInteger("VERNUMBER"));
}
publishedDoc.replaceItemValue("status", "Published");
updateRevisionData(doc);
if(!saveAsCurrent) {
setVersion(doc);
}
//save docs
doc.save(true, false, true);
publishedDoc.save(true, false, true);
}
}
}
Basically, this code manages verioning in a CMS-type application. Since many doc links are already present in existing content, I need to keep the UNID of the published document. That explains the nice little dance between the published doc, the temp doc and the doc, which is the draft: Draft content goes to the published version, published version goes to thte archives.
I have persistence set to "keep pages on disk" and persistence mode to "Entire page content". Not sure it makes a difference though...
Any clues? :D
Wrong event. If you want to amend document data when writing back to disk use QuerySave and don't do a document.save() on the current document.
So you might need to split your code.
Background: XPages uploads attachments (inline images are attachments) to a temp location and keeps track of them. When you save they are added into the note and discarded. The pointer to the temp files is only reset after the event chain (QuerySave, actual write to disk, PostSave). So by trying to save again you point to files that are gone.
Btw. Saving the current document (again) in a PostSave (or for that matter: premature save in QuerySave) is a popular Anti-pattern in Notes development
I am using an MDI application. I want to create a property sheet inside Frame Window area as shown by arrow in image below:
I have seen examples where we can use ShowWindow() function after creating property sheet but it creates property sheet which is not embedded in frame window.
Can we create propertysheet on frame window only like other controls as static box etc?
If you need to embed a resizable property sheet to the view, please take a look at BCGSoft size(http://www.bcgsoft.com) - the latest BCGControlBar from version shows how to do it:
http://www.bcgsoft.com/images/resizableform220.jpg
If you simply need a tabbed MDI windows, just create a Visual Studio-like application in MFC AppWizard (VS 2008 or later).
Hope, this helps.
Rob
Adding CMultiDocTemplate instances solved my problem. Here is code snippet. This is part of ProjectName.cpp file:
BOOL CEmuDiagnosticsClientApp::InitInstance()
{
// InitCommonControlsEx() is required on Windows XP if an application
// manifest specifies use of ComCtl32.dll version 6 or later to enable
// visual styles. Otherwise, any window creation will fail.
INITCOMMONCONTROLSEX InitCtrls;
InitCtrls.dwSize = sizeof(InitCtrls);
// Set this to include all the common control classes you want to use
// in your application.
InitCtrls.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&InitCtrls);
CWinAppEx::InitInstance();
// Initialize OLE libraries
if (!AfxOleInit())
{
AfxMessageBox(IDP_OLE_INIT_FAILED);
return FALSE;
}
AfxEnableControlContainer();
//Added new code
{
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_STRING_LOGGINGWINDOW,
RUNTIME_CLASS(CEmuDiagnosticsClientDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CLoggingWindow));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
}
//End: Added new code
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need
// Change the registry key under which our settings are stored
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
LoadStdProfileSettings(4); // Load standard INI file options (including MRU)
InitContextMenuManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(IDR_STRING_SIGNALWINDOW,
RUNTIME_CLASS(CEmuDiagnosticsClientDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(CSignalWindow)); //Changed Code
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// create main MDI Frame window
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame || !pMainFrame->LoadFrame(IDR_MAINFRAME))
{
delete pMainFrame;
return FALSE;
}
m_pMainWnd = pMainFrame;
// call DragAcceptFiles only if there's a suffix
// In an MDI app, this should occur immediately after setting m_pMainWnd
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line. Will return FALSE if
// app was launched with /RegServer, /Register, /Unregserver or /Unregister.
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The main window has been initialized, so show and update it
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
return TRUE;
}
In //Added New code section, created a new CMultiDocTemplate instance. CLoggingWindow is the class which I wanted to display in frame window.
Another class CSignalWindow I also wanted to display which is modified in //changed code area.
Things to remember:
-Dialog which you want to display must be derived from CFormView, not CDialog.
-Changed dialog property: Border -> None, Style -> Child and all other properties to false.
What is the best way to deal with document locking in xPages? Currently we use the standard soft locking and it seems to work fairly well in the Notes client.
In xPages I considered using the "Allow Document Locking" feature but I am worried that people would close the browser without using a close or save button then the lock would never be cleared.
Is there a way to clear the locks when the user has closed his session? I am seeing no such event.
Or is there an easier way to have document locking?
I realize I can clear the locks using an agent but when to run it? I would think sometime a night then I am fairly certain the lock should no longer really be active.
Here is code I'm using:
/* DOCUMENT LOCKING */
/*
use the global object "documentLocking" with:
.lock(doc) -> locks a document
.unlock(doc) -> unlocks a document
.isLocked(doc) -> returns true/false
.lockedBy(doc) -> returns name of lock holder
.lockedDT(doc) -> returns datetime stamp of lock
*/
function ynDocumentLocking() {
/*
a lock is an entry in the application scope
with key = "$ynlock_"+UNID
containing an array with
(0) = username of lock holder
(1) = timestamp of lock
*/
var lockMaxAge = 60 * 120; // in seconds, default 120 min
this.getUNID = function(v) {
if (!v) return null;
if (typeof v == "NotesXspDocument") return v.getDocument().getUniversalID();
if (typeof v == "string") return v;
return v.getUniversalID();
}
/* puts a lock into application scope */
this.lock = function(doc:NotesDocument) {
var a = new Array(1);
a[0] = #UserName();
a[1] = #Now();
applicationScope.put("$ynlock_"+this.getUNID(doc), a);
// print("SET LOCK "+"$ynlock_"+doc.getUniversalID()+" / "+a[0]+" / "+a[1]);
}
/* removes a lock from the application scope */
this.unlock = function(doc:NotesDocument) {
applicationScope.put("$ynlock_"+this.getUNID(doc), null);
//print("REMOVED LOCK for "+"$ynlock_"+doc.getUniversalID());
}
this.isLocked = function(doc:NotesDocument) {
try {
//print("ISLOCKED for "+"$ynlock_"+doc.getUniversalID());
// check how old the lock is
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) {
//print("no lock found -> return false");
return false;
}
// if lock holder is the current user, treat as not locked
if (v[0] == #UserName()) {
//print("lock holder = user -> not locked");
return false;
}
var dLock:NotesDateTime = session.createDateTime(v[1]);
var dNow:NotesDateTime = session.createDateTime(#Now());
// diff is in seconds
//print("time diff="+dNow.timeDifference(dLock)+" dLock="+v[1]+" now="+#Now());
// if diff > x seconds then remove lock, it not locked
if (dNow.timeDifference(dLock) > lockMaxAge) {
// print("LOCK is older than maxAge "+lockMaxAge+" -> returning false");
return false;
}
//print("return true");
return true;
// TODO: check how old the lock is
} catch (e) {
print("ynDocumentLocking.isLocked: "+e);
}
}
this.lockedBy = function(doc:NotesDocument) {
try {
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) return "";
//print("ISLOCKEDBY "+"$ynlock_"+doc.getUniversalID()+" = "+v[0]);
return v[0];
} catch (e) {
print("ynDocumentLocking.isLockedBy: "+e);
}
}
this.lockedDT = function(doc:NotesDocument) {
try {
var v = applicationScope.get("$ynlock_"+this.getUNID(doc));
if (!v) return "";
return v[1];
} catch (e) {
print("ynDocumentLocking.isLockedBy: "+e);
}
}
}
var documentLocking = new ynDocumentLocking();
You could take a page from the way webDAV works. There a servlet manages a "lock-list" of locked documents. The locks automatically expire after 10 minutes. Locks can be renewed or terminated trough calls. So when you edit a document you would request a lock, then kick off a CSJS timer that calls the relocking function every 8 minutes (so you have some margin for error) and the postSave calls the unlock (unless you stay in edit mode).
If a user closes the browser after 10 minutes the document is automatically unlocked. Since you are free how to implement the locking function, you can capture user/location and use that information in the "lock failed" display (you event could push that further and let the original author know about it or do some "retry" option.
It isn't simple to implement, but once implemented simple to use
ApplicationScope may be a good place to capture "locked" documents. After all, for applicationScope to expire, all users' sessions have to have expired, so anyone with the page open will not be able to save anyway.
Maybe capture UNID, user and time when someone edits a doc. Clear the value when the document is saved. Bear in mind that the user might close the browser etc. I've been discussing this approach internally and if we end up building this I would look to add it to OpenNTF. But we're unlikely to get onto it within the next month.
I Prefer to use a solution similar to Mr. Withers' answer. The main issue is how to deal with the unwanted and dreaded back button. It is easy to lock a document when it is opened, but there are many ways to close the XPage, and the user is not limited to just the navigation you provide but also can, as he stated, close the browser completely, use the back button, etc. So, the best way that I can think of is to create a few java objects which we will use in the application and session scopes.
The first step is to create a "LockedDocument" class. As we know, the documents are not serializable and we do not want to save the document itself in this object, we want to save the UNID and the time it was saved. We want to do it this way so that we can manage to clear the object after a given time (like thirty minutes to an hour). This class should also implement the comparable interface in order to sort the collection by this time so that the oldest documents are first and the newest documents are last.
Next we create another class that holds a list or a map with these LockedDocuments. This class must also have a thread (implement Runnable) that will check all documents every five minutes or so, I did not test this yet, but it should work). Any document that was locked thirty to sixty minutes ago (predefined) will be unlocked (deleted from the list). It is important that the list be sorted as described above and that the loop is "broken" when a time less than the locktime is reached in order to prevent unwanted processing.
The next step would be to include the user specific list in the sessionScope. This list is the LockedDocuments that this current user has. It is set when the user changes the document's status to editable, and is checked before the document is set to editable to prevent one document from being opened in multiple tabs by the same user. The lock is once again checked onquerysave(). Once a main page is opened, the lock is automatically released. The onquerysave() must also check to make sure the documents UNID is in the sessionScope list, or if the document is new before allowing a save.
quick recap
Any UNID saved in the applicationScope LockedDocumentList would not be editable by anyone unless it exists in their own sessionScope list.
It is possible to warn a user that their lockedTime is approaching and reset the timer.
The class containing a list with the locked documents must be a singleton
There are probably ways to improve this answer, and I am sure I am missing something. It is just a thought.
There might be a better way to handle this, but it is the best I found.
You can remove the Domino lock in window.onunload event:
window.onunload = function(){
dojo.xhrGet(...
}
No need to reinvent the wheel.
Ok, I'm working on my final dilemna for my project. The project is an IPv4 endpoint updater for TunnelBroker's IPv6 tunnel. I have everything working, except for the timer. It works, however if the user disables the "automatic update" and reenables it, the application crashes. I need the timer to be on an thread outside of the EDT (in such a way that it can be destroyed and recreated when the user unchecks/checks the automatic update feature or changes the amount of time between updates).
What I'm pasting here is the code for the checkbox that handles automatic updates, and the timer class. Hopefully this will be enough to get an answer on how to do this (I'm thinking either it needs to be a worker, or use multi-threading--even though only one timer will be active).
private void jCheckBox1ItemStateChanged(java.awt.event.ItemEvent evt) {
// TODO add your handling code here:
// if selected, then run timer for auto update
// set time textbox to setEditable(true) and get the time from it.
// else cancel timer. Try doing this on different
// class to prevent errors from happening on reselect.
int updateAutoTime = 0;
if (jCheckBox1.isSelected())
{
updateAutoTime = Integer.parseInt(jTextField4.getText())*60*1000;
if (updateAutoTime < 3600000)
{
updateAutoTime = 3600000;
jTextField4.setText(new Integer(updateAutoTime/60/1000).toString());
}
updateTimer.scheduleAtFixedRate(new TimerTask() {
public void run()
{
// Task here ...
if (jRadioButton1.isSelected())
{
newIPAddress = GetIP.getIPAddress();
}
else
{
newIPAddress = jTextField3.getText();
}
strUsername = jTextField1.getText();
jPasswordField1.selectAll();
strPassword = jPasswordField1.getSelectedText().toString();
strTunnelID = jTextField2.getText();
strIPAddress = newIPAddress;
if (!newIPAddress.equals(oldIPAddress))
{
//fire the tunnelbroker updater class
updateIP.setIPAddress(strUsername, strPassword, strTunnelID, strIPAddress);
oldIPAddress = newIPAddress;
jLabel8.setText(newIPAddress);
serverStatus = updateIP.getStatus().toString();
jLabel6.setText(serverStatus);
}
else
{
serverStatus = "No IP Update was needed.";
jLabel6.setText(serverStatus);
}
}
}, 0, updateAutoTime);
}
else
{
updateTimer.cancel();
System.out.println("Timer cancelled");
System.out.println("Purged {updateTimer.purge()} tasks.");
}
}
As I mentioned, this works once. But if the user deselects the checkbox, it won't work again. And the user can't change the value in jTextField4 after they select the checkbox.
So, what I'm looking for is this:
How to make this so that user can select and deselect the checkbox as they want (even if it's multiple times in a row).
How to make this so the user can change the value in jTextField4, and have it automatically cancel the current timer, and start a new one with the new value (I haven't done anything with the jTextField4 at all, so I'll have to create an event to cover it later).
Thanks, and have a great day:)
Patrick.
Perhaps this task would be better suited to a javax.swing.Timer. See Timer.restart() for details.
Note that Timer is relatively inaccurate over long time periods. One way to account for that is to have it repeat frequently but perform it's assigned task only one a certain time has been reached or passed.
Would I be able to wrap everything in the "task" portion of the call to Swing Timer, or do I have to create another class that handles the task?
You might want to wrap the grunt work in a SwingWorker to ensure the EDT is not blocked.
..I'm assuming that I would have to create the timer as a class-level declaration .. correct?
Yes, that is what I was thinking.