Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Packing VB6 app quandry

Status
Not open for further replies.

NeilFrank

Programmer
Mar 12, 2000
167
CA
Having trouble with a packaged version of my VB6 app.

I am using Inno Setup which works well, allowing me to run my app on a Windows 7 machine without any versions of VB installed. So far so good . . .

However, the debug data that gets continuously generated as the app runs seems to be vanishing.

The routine that creates debug is as follows:

Code:
'Routine opens and closes AppFlowDebug.dat EACH TIME data is written
'out, ensuring that full contents will be preserved in the event of a crash.
'Help from:
'[URL unfurl="true"]http://www.tek-tips.com/viewthread.cfm?qid=1471210[/URL]
'VB6 Black Book pp 546 - 564

Public Sub WriteToAppFlowDebug(sNewDebugString As String)

    Dim sCurrentRecord As String, sLastRecordText As String, sTempArr() As String
    Dim iLoop As Integer, iRep As Integer, iStart As Integer, iStop As Integer 

    If glFlowDebugRecordCount = 0 Then
        Open App.Path & "\PsychExpertAppFlowDebug.dat" For Output As #100
        Write #100, "[1] " & sNewDebugString & "  [[x 1]]"
        glFlowDebugRecordCount = 1    
    Else
        Open App.Path & "\PsychExpertAppFlowDebug.dat" For Input As #100
        ReDim Preserve sTempArr(1 To glFlowDebugRecordCount)

        'Each record in AppFlowDebug has format Text [[x #]], where # gives the number   
        'of times the last record has occurred.  This will eliminate multiple repetitious   
        'records.
        'Put all current records in AppFlowDebug.dat into the corresponding element of 
        'sTempArr and note if the last record is a repeat, ie# >1
        
        For iLoop = 1 To glFlowDebugRecordCount
            Input #100, sCurrentRecord
            sCurrentRecord = Replace(sCurrentRecord, ";", "|") 'Since ;s mess up Instr            
            sTempArr(iLoop) = sCurrentRecord
            If iLoop = glFlowDebugRecordCount Then
                 iStart = InStr(1, sCurrentRecord, "[[x ", 1) + 4
                 iStop = InStr(1, sCurrentRecord, "]]", 1)
                 sLastRecordText = Left(sCurrentRecord, iStart - 7)
                 iRep = Val(Mid(sCurrentRecord, iStart, iStop - iStart))
            End If
        Next iLoop
        
        'If the text of the debug record about to be added is the same as the last current   
        'record  don't add a new element to sTempArr, just augment #
        
        iStart = InStr(1, sLastRecordText, "]", 1) '5/10/08
        sLastRecordText = Right(sLastRecordText, Len(sLastRecordText) - (iStart + 1)) 
        If sLastRecordText = sNewDebugString Then
            sTempArr(glFlowDebugRecordCount) = "[" & glFlowDebugRecordCount & "] " & _ 
             sLastRecordText & "  [[x " & CStr(iRep + 1) & "]]"
        
        'Otherwise add it as a new record with # =1
        Else
            glFlowDebugRecordCount = glFlowDebugRecordCount + 1
            ReDim Preserve sTempArr(1 To glFlowDebugRecordCount)
            sTempArr(glFlowDebugRecordCount) = "[" & glFlowDebugRecordCount & "] " & _
             sNewDebugString & "  [[x 1]]"
        End If
        Close #100

        'Update AppFlowDebug.dat using sTempArr
        Open App.Path & "\PsychExpertAppFlowDebug.dat" For Output As #100
        For iLoop = 1 To glFlowDebugRecordCount
            Write #100, sTempArr(iLoop) 
        Next iLoop
    End If
    Close #100

End Sub

If I run the setup file from Inno on the same machine (XP) that I use when working with my app, all is well: the exe that is created in Program Files runs perfectly. But when I move the setup file to my Windows 7 machine and use it to create the exe there, in either Program Files (x86) or Program Files, I hit a wall. Although the app runs fine, and although it appears (from Message Box messages I've added to my debug routine) that Filename.dat IS being written to, the resulting filename.dat has nothing new in it.

Any thoughts?
 
I forgot to note how to make use of the debug-writer. For example:

Code:
Private Sub optModule_Click(Index As Integer)
WriteToAppFlowDebug ("Splash.optModule(" & Index & ")")
 
On the windows 7 machine, you may also need to elevate the program permissions by selecting 'Run As Administrator'.
In addition, on 64 bits machines, VB6 program should only be installed in Program Files (x86), not Program Files.

If at first you don't succeed, then sky diving wasn't meant for you!
 
I'll take a stab...

You are probably bumping into filesystem virtualization and by digging into the VirtualStore directory of users running your program you'll probably find your data.

This is both a packaging problem and a program problem. Read/write data does not belong under the Program Files directories whether 64-bit or 32-bit.


What you are meant to do is create a folder for program data during installation just as you do for the program's static files. Then the program should be writing there.

Where that folder goes depends on many things: Is it meant to be visible to the user (via Explorer)? Is there a separate file for each user who runs your program (settings, etc.)? Is there a common file for all users?

Once you figure that out there are locations you should be using for each variation of those cases.

Most of the time when all users need to touch the same file your installer should create a "Company\App" folder structure under the special folder CommonAppData a.k.a. ProgamData. Your installer will also need to set the desired security (permissions) on that folder or any files it copies into it in order to get the desired behavior. If you don't change the permissions then folders and files inherit "owner" access which may well tie your program in knots with hard to diagnose undesired behavior.

The approved technology is Windows Installer. The steps required are well documented, see LockPermissions Table.

The LockPermissions Table is used to secure individual portions of an application in a locked-down environment. It can be used with the installation of files, registry keys, and created folders.

Windows post-XP is locked down by default because too many users casually run as admins. Many Win2k/XP systems in corporate environments are locked down by group policy and local administrative action.

Those using the VSI 1.1 tool provided by Microsoft for use by VB6 programmers as an alternative to the more limited PDW will need a post-build script to add a few items VSI 1.1 did not provide IDE support for.


If you insist on using legacy scripted setup tools all bets are off, and you'll have to see what your tool provides.


Beyond that, you'll need to change your program to look up the necessary special folder and add your app subfolder structure to it instead of using App.Path. This is usually simple enough:

"Main module"
Code:
Option Explicit

Public DataPath As String

Private Sub Main()
    Const ssfCOMMONAPPDATA = &H23
    
    'Assumes of course that you set CompanyName and ProductName in
    'your Project Properties.
    With CreateObject("Shell.Application").NameSpace(ssfCOMMONAPPDATA).Self
        DataPath = .Path _
                 & "\" & App.CompanyName _
                 & "\" & App.ProductName
    End With
    
    Form1.Show
End Sub
 
Sample post-build WSH script for use with VSI 1.1 Installer projects. Takes care of creating and setting security on the CommonAppData folder during installs as well as setting the package to "per machine" from the default and generally undesired "per user" setting.

In VSI be sure to define the CommonAppData folder structure (in the FileSystem view within the IDE), then build your .MSI database. After that you can run this script against the database (MSI files are databases) - assuming you have updated the two Const values in the script (see comments).

"FixupMSI.wsf"
Code:
<job id="FixupMSI">
  <!-- Perform two tasks:

       1. Set the MSI for a "per-machine" installation for all users.

       2. Create an application data folder beneath CommonAppDataFolder that
          Everyone has FULL CONTROL for.  This folder must have been defined
          in the MSI already (in its Directory table).

          This folder is expected to be:

             {CommonAppDataFolder}\<CompanyName>\<AppName>

          ... though in reality we are just looking for the folder beneath
          the folder beneath the CommonAppDataFolder.

          We further assume that in the VSI 1.1 project this folder structure
          has been created within the FileSystem section of the Project.

       As written, this script is expected to reside in the same folder as
       the VSI 1.1 .SLN file.  It is looking for the MSI database in a folder
       beneath its own folder.

       To change it for your own program you'll need to change two Const
       values based on your own project's names for the MSI and the primary
       program you're installing.
  -->
  <object id="Installer" progId="WindowsInstaller.Installer"/>
  <reference object="WindowsInstaller.Installer"/>
  <script language="VBScript">
    Option Explicit
    Dim MSIFile, MSIDB, MSIRecord, MSIView, strDir, strCompon
    Const FILE_ALL_ACCESS = &H001F01FF&
    Const PROGRAMNAME = "NWSView.exe"
    Const MSIDBLOCATION = "Output\Disk_1\NWSView.msi"

    Function ScriptPath()
      ScriptPath = Left(WScript.ScriptFullName, _
                        InStrRev(WScript.ScriptFullName, "\"))
    End Function

    Function IsLike(ByVal CompoundName, ByVal AfterPipe)
      IsLike = StrComp(Mid(CompoundName, InStr(CompoundName, "|") + 1), _
                       AfterPipe, _
                       vbTextCompare) = 0
    End Function

    'INITIALIZE ========================================================

    'Open MSI database.
    MSIFile = ScriptPath() & MSIDBLOCATION
    Set MSIDB = Installer.OpenDatabase(MSIFile, msiOpenDatabaseModeTransact)

    'STEP ONE ==========================================================

    'Insert Property ALLUSERS = 1.
    Set MSIView = MSIDB.OpenView( _
        "INSERT INTO `Property` (`Property`, `Value`)" _
      & " VALUES ('ALLUSERS', '1')")
    With MSIView
      .Execute
      .Close
    End With

    'STEP TWO ==========================================================

    'Locate our data folder in the MSI database.
    Set MSIView = MSIDB.OpenView( _
        "SELECT `Directory` FROM `Directory`" _
      & " WHERE `Directory_Parent` = 'CommonAppDataFolder'")
    With MSIView
      .Execute
      strDir = .Fetch.StringData(1)
      .Close
    End With
    Set MSIView = MSIDB.OpenView( _
        "SELECT `Directory` FROM `Directory`" _
      & " WHERE `Directory_Parent` = '" & strDir & "'")
    With MSIView
      .Execute
      strDir = .Fetch.StringData(1)
      .Close
    End With

    'Now strDir has the directory index of our data directory.

    'Get Component index of our program.
    Set MSIView = MSIDB.OpenView( _
        "SELECT `Component_`, `FileName` FROM `File`")
    With MSIView
      .Execute
      Set MSIRecord = .Fetch
      Do Until MSIRecord Is Nothing
        If IsLike(MSIRecord.StringData(2), PROGRAMNAME) Then
          strCompon = MSIRecord.StringData(1)
          Exit Do
        End If
        Set MSIRecord = .Fetch
      Loop
      .Close

      If MSIRecord Is Nothing Then
        'ERROR EXIT ============================================
        MsgBox "Program not found in File table", _
               vbOkOnly Or vbExclamation, _
               "FixupMSI"
        Set MSIView = Nothing
        Set MSIDB = Nothing
        WScript.Quit
      Else
        Set MSIRecord = Nothing
      End If
    End With

    'Now strCompon has the component index of our program.

    'Create CreateFolder table.
    Set MSIView = MSIDB.OpenView( _
        "CREATE TABLE `CreateFolder` " _
      & "(`Directory_` CHAR(72) NOT NULL," _
      & " `Component_` CHAR(72) NOT NULL" _
      & " PRIMARY KEY `Directory_`, `Component_`)")
    With MSIView
      .Execute
      .Close
    End With

    'Insert new CreateFolder record to be referenced in the
    'LockPermissions table.
    Set MSIView = MSIDB.OpenView( _
        "INSERT INTO `CreateFolder`" _
      & " (`Directory_`, `Component_`)" _
      & " VALUES ('" & strDir & "', '" & strCompon & "')")
    With MSIView
      .Execute
      .Close
    End With

    'Create LockPermissions table.
    Set MSIView = MSIDB.OpenView( _
        "CREATE TABLE `LockPermissions` " _
      & "(`LockObject` CHAR(72) NOT NULL," _
      & " `Table` CHAR(32) NOT NULL," _
      & " `Domain` CHAR(255)," _
      & " `User` CHAR(255) NOT NULL," _
      & " `Permission` LONG" _
      & " PRIMARY KEY `LockObject`, `Table`, `Domain`, `User`)")
    With MSIView
      .Execute
      .Close
    End With

    'Insert new LockPermissions record to set our data directory to
    'full permissions for Everyone.
    Set MSIView = MSIDB.OpenView( _
        "INSERT INTO `LockPermissions`" _
      & " (`LockObject`, `Table`, `User`, `Permission`)" _
      & " VALUES ('" & strDir & "', 'CreateFolder', 'Everyone'," _
      & " " & CStr(FILE_ALL_ACCESS) & ")")
    With MSIView
      .Execute
      .Close
    End With

    'TERMINATE =======================================================

    Set MSIView = Nothing
    MSIDB.Commit
    Set MSIDB = Nothing
    MsgBox "MSI Database Updated", vbOkOnly, "FixupMSI"
  </script>
</job>
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top