Kaju self-updater talk (v.2.x)

I was testing out the Kaju self-updater for my app today. On Mac, all went smoothly as expected. However, on Windows (a 64-bit to 64-bit update), the update failed on installation. I am hoping someone knowledgeable here might be able to help point me in the right direction as to figuring out what’s going wrong.

  1. I open my app with “run as administrator”, so that Kaju has permissions to write to the Programs folder.
  2. Kaju detects an update is available
  3. I click download, and it downloads ok
  4. I click quit & install

After some processing, command prompt opens and there is an error saying “the file C:\Program Files\MAK95\MAK95.exe could not be located. Check that you have the correct name…”

When I look in C:\Program Files\MAK95, the main executable MAK95.exe indeed is missing. There is a new folder C:\Program Files\MAK95\MAK95.exe-20201902214544 which contains only the original MAK95.exe file.

I have included a truncated version of the generated error log below (as it’s very long), but hopefully I’ve left enough of the program flow for you to see what is happening. The problem seems to occur at the bottom, where it says “File creation error - The requested operation cannot be performed on a file with a user-mapped section open.” and “C:\Program Files\MAK95\MAK95.exe-20201902214544\MAK95.exe - Access is denied.”

The code used in Kaju is way above my skill level, so any help would be greatly appreciated!

Thanks,
Frank

[code]“STARTED ON 2020-19-02 21:45:44”
“Copying items to backup C:\Program Files\MAK95\MAK95.exe-20201902214544”
“Looking for item C:\Program Files\MAK95\cef.pak”
“…found, copying”
1 file(s) copied.
“…confirmed”

[… more files here …]

“Looking for item C:\Program Files\MAK95\locales”
“…found, copying”
C:\Program Files\MAK95\locales\am.pak
C:\Program Files\MAK95\locales\ar.pak

[… more files here …]

   53 file(s) copied.

“…confirmed”
“Looking for item C:\Program Files\MAK95\MAK95 Libs”
“…found, copying”
C:\Program Files\MAK95\MAK95 Libs\Appearance Pakx64.dll
C:\Program Files\MAK95\MAK95 Libs\Browser Pluginx64.dll

[… more files here …]

    9 file(s) copied.

“…confirmed”
“Looking for item C:\Program Files\MAK95\MAK95 Resources”
“…found, copying”
C:\Program Files\MAK95\MAK95 Resources\1_person_cheering.mp3
C:\Program Files\MAK95\MAK95 Resources\ANZCALogoMask.png

[… more files here …]

   58 file(s) copied.

“…confirmed”
“Looking for item C:\Program Files\MAK95\msvcp120.dll”
“…found, copying”
1 file(s) copied.
[… more files here …]

“Looking for item C:\Program Files\MAK95\swiftshader”
“…found, copying”
C:\Program Files\MAK95\swiftshader\libEGL.dll
C:\Program Files\MAK95\swiftshader\libGLESv2.dll
2 file(s) copied.
“…confirmed”
“Looking for item C:\Program Files\MAK95\v8_context_snapshot.bin”
“…found, copying”
1 file(s) copied.
“…confirmed”

[… more files here …]

“Moving the executable MAK95.exe to backup”
1 file(s) moved.
“…confirmed”
“All items moved to backup”
“Copying files from C:\Program Files\MAK95\MAK95- decompressed\MAK95 to folder C:\Program Files\MAK95”
C:\Program Files\MAK95\MAK95- decompressed\MAK95\cef.pak
File creation error - The requested operation cannot be performed on a file with a user-mapped section open.

“…confirmed”
“Removing backup”
C:\Program Files\MAK95\MAK95.exe-20201902214544\MAK95.exe - Access is denied.
“Removing decompressed folder”
“Launching new app”
“Removing temp folder”
[/code]

It sounds like something is keeping a file open. Are you certain your app is actually quitting?

Not sure if the case, but… When you “insert” some .EXE (move, copy…) into some systems with active antivirus, they can scan such files and those files will become “busy” during this time, with file handles in use at the antivirus part. The solution for such cases probably is “try again later” when the antivirus release it.

So perhaps some retry code with a delay in the copy script?

Thanks Kem and Rick for the replies. Sorry for my delayed response. Firstly, Kem, thanks so much for making Kaju freely available, it is extremely generous of you!

From the scripting logic in the KajuUpdateWindow.HandleOKButton method, if CurrentStage = Stage.WaitingToQuit, then it should quit. But what is the best way to check this actually happens?

I haven’t installed any anti-virus program. I should have only the in built Windows Security that comes with Windows 10. Would you know if that might cause a problem?

This is the testing I’ve done so far. Because I have manipulated the Kaju code a bit to incorporate it into my app - and potentially I screwed something up! - I thought I should test with an unadulterated app. So I built a Windows 64 bit version of the Update Test App (v1.0.0) on my Mac. It built ok - though it came up with the message “The kaju CLI is not available. Build it first.” Would that cause any issues? The Prepare Kaju script looks like it just creates the test files to upload.

I copied the built Kaju Update Test folder into the Program Files folder on my Windows 10 virtual machine and ran it. The available updates are detected and I click Download then Quit & Install. The same error occurs when Command Prompt is open saying “Could not locate C:\Program Files\Kaju Update Test\Kaju Update Test.exe”.

The Update log is similar, except that this time there were a lot of files not found, eg

"Looking for item C:\\Program Files\\Kaju Update Test\\am.pak" "...not found as file, trying as directory" "...NOT FOUND!"

Again, the log ended with these errors

[code]“All items moved to backup”
“Copying files from C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test to folder C:\Program Files\Kaju Update Test”
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\am.pak
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\ar.pak
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\bg.pak
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\bn.pak
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\ca.pak
C:\Program Files\Kaju Update Test\ew Kaju Update Test- decompressed\Kaju Update Test\cef.pak
File creation error - The requested operation cannot be performed on a file with a user-mapped section open.

“…confirmed”
“Removing backup”
C:\Program Files\Kaju Update Test\Kaju Update Test.exe-20202002152356\Kaju Update Test.exe - Access is denied.
“Removing decompressed folder”
“Launching new app”
“Removing temp folder” [/code]

This raises a couple more questions for me:

  1. In my app, for Kaju to work I need to open it with “run as administrator” otherwise it fails the Kaju.IsWriteableRecursive( executable.Parent ) check in UpdateChecker.PreCheck. But with Kaju Update Test, it worked when I opened it normally, not as administrator. My app was installed using InnoSetup, while Kaju Update Test I just copy & pasted into the Program Files folder. Is this difference in behaviour explainable, and if so, how do I set my app up so that Kaju works without having to run as administrator?
  2. Why would the update logs be different, with all the files in my app being found and copied, but for the Kaju Update Test, most files were not found? Does this help suggest a cause?

Next I will test the Kaju Update Test on a real Windows laptop, to see if my virtual machine is causing a problem…

To follow up:

I tested my app on a real Windows 10 laptop and the Kaju Updater function worked without a hitch :slight_smile: Repeated testing on my Windows 10 virtual machine consistently failed with the “requested operation cannot be performed on a file with a user-mapped section open” error. So I am going to put it down to an issue with running it on a virtual machine. Fortunately, my users shouldn’t have to do that as they can use the native Mac version.

Cheers,
Frank

Unfortunately I celebrated too soon. On 2nd testing on my Windows 10 laptop, the update failed with the same error. On 3rd test it worked!

I think I will have to try to change the workflow so that it downloads an InnoSetup installer instead that overwrites the previous version. Earlier in this thread some people have suggested this workaround when they have had similar problems. Would anyone have a working version that demonstrates how to do that?

Thanks,
Frank

The link to the project is a demo of what I picked up from the forum a long time ago and then made some minor fixes. The bulk is not my work…
https://1drv.ms/u/s!AvNnNHA7In4qqNtozraoOxVv2scjNQ?e=anbyKX
I use this for both MacOS and Windows and works ok. I am using this for a deployed application for the last 4 years with at least 15 updates over time. Limitations with this implementation:

  • it uses HTTP and there might be problems with certain hosting domains security requirements
  • It downloads dmg and .exe files and I had to change hosting company as my original company identified innosetup .exe as malware and it got blocked/removed automatically.
  • There is no protection at all against downloading a modified file and downloading malware. It has no security whatsoever which is my main reason of switching to Kaju sooner or later.

I just cut and paste this from my main app so not sure if I missed out something.

Long term I still want to switch to Kaju because it is a much neater and more capable option, but on the other hand I don’t have the confidence yet to deploy it for an application where the users are having poor computer literacy and are 2 hours flying from my place. If there is a problem, I have no way of getting help identifying the problem. At the moment I am in the mood of “if ain’t broke, don’t touch it”. But I know it will break sooner or later.

To me this is a work around and not a long term fix.

Thanks for that Niels :slight_smile: I will have a look at the project and hopefully get some ideas. I’ve been working my way through following the logic of what Kaju does. The first couple times I tried to do that I got hopelessly confused, but now with a bit more knowledge, I’m starting to follow :slight_smile: . Here is my understanding of what Kaju does, I write it here to help me get it straight in my head and perhaps also help other newbies like me (someone please correct me if I’m wrong):

  1. Instantiate a New Kaju.UpdateChecker in Open event of your main window
    Checker = New Kaju.UpdateChecker
    Checker is a property on your main window (an instance of Kaju.UpdateChecker)

  2. Check if any new updates are available
    Initiate the check by running the method Checker.ExecuteAsync
    This does a PreCheck to make sure that the user has write permissions and that the xcopy tool is available
    Then passes on to the method FetchAsync which instantiates a Kaju.HTTPSocketAsync, uses its Get method to query your website and sets the PageReceived event handler as AsyncHTTP_PageReceived

  3. Data is received from your website and processed
    AsyncHTTP_PageReceived calls the method ProcessRaw, which checks that the data is not corrupted by using Crypto.RSAVerifySignature( tester, sig, ServerPublicRSAKey ) then passes the data to ProcessUpdateData. If new updates are available then KajuUpdateWindow.ChooseUpdate is called

  4. KajuUpdateWindow is shown, populated with the available updates, the user chooses the update then presses the “Download Update” button (btnOK) which calls HandleOKButton

  5. Starts downloading the update zip file
    HandleOKButton, when at the CurrentStage of Stage.ChoosingUpdate, uses hsSocket.Get (an instance of Kaju.HTTPSocketAsync) to download the update zip file into a temporary folder

  6. Once the file finishes downloading, it is decompressed
    In the hsSocket.FileReceived event, it uses shZipper.Decompress (an instance of Kaju.ZipShell) to decompress the file into a folder within the App’s main folder, called “AppName - decompressed”, then changes the CurrentStage to Stage.WaitingToQuit

  7. User presses the Quit & Install button (btnOK)
    HandleOKButton calls Kaju.StartUpdate which passes the KajuUpdateWindow.Initiater to the App.UpdateInitiater property then quits the App

  8. The App.UpdateInitiater destructor event calls RunScript, which on Windows calls RunScriptWindows, which creates a Shell batch file that continues to run after the App has quit

  9. The Shell script (which is saved in the constant kUpdaterScript) performs the following steps

  • Copies all the program files into a backup folder within App’s main folder, called “AppName-datetimestamp”
  • Moves the executable file into the backup folder
  • Copies all the new update files into the App’s main folder from the decompressed folder using xcopy
  • Deletes the backup folder and decompressed folder
  • Launches the new updated App with the parameter “–kaju-success” so that it can detect that an update just occurred

There is a lot of error checking and clean up along the way that I have not included (mainly because I don’t understand it well :p)

So what I am thinking of trying is to make the following changes:

  • Have an InnoSetup created Setup.exe uploaded to the website instead of a zip file
  • For Windows only, divert the flow at step 5:
  • Download Setup.exe into the Downloads folder instead of a temporary folder
  • When the user presses Quit & Install, simply run the Setup.exe

If I manage to get something working, I’ll post it back here!
Frank

Ok, I’m pretty pleased with myself, I’ve got it to work how I want and the user experience is quite streamlined, I feel :slight_smile: Plus I’ve learnt a bit more about how InnoSetup and shell work.

For Windows only, an InnoSetup installer is registered in the Kaju Admin app instead of a zip file and uploaded to your website. I’ve followed the usual Kaju workflow, up till the point where the file has been downloaded. So the benefits of Kaju checking the integrity of the downloaded data, using Crypto.RSAVerifySignature, and the downloaded file, by checking against the hash, are retained.

When the user presses Quit & Install, the following sequence occurs:

  1. The setup.exe (which is downloaded into a temp folder) is launched
  2. App quits
  3. UAC dialog box appears asking if the user wants to allow Setup to make changes (get admin privileges)
  4. The wizard shows only the ready to install page and user click Install
  5. The wizard shows the installation complete page and the user can launch the updated app or close the wizard
  6. A small .bat file is created that deletes the setup.exe file then deletes itself

It’s worked out better than I had hoped, I’m starting to see how powerful InnoSetup is. You can use the same installer as for a new installation, as long as AppId is kept the same, InnoSetup will automatically install into the same directory and update the version info in the registry.

Here is a dropbox link to a Kaju Update Test Altered project which demonstrates the changes that I made. All the changes are labelled with “//MyKajuChanges”. Below is the InnoSetup script that I use to make the installer, which is based on the Xojo Documentation 64-bit example with bits of code that I found on various forums. I hope this can help other people who may have experienced similar problems to me.

Kem, is it ok for me to share this? I’ve kind of bastardised your code and possibly introduced some errors in logic.

InnoSetup Script
; Sample script for creating an installer for a 64-bit Xojo desktop app

#define XojoAppName “MyApp”
#define MyAppURL “https://mywebsite.com/
#define AppGUID “{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}”
#define MyAppVersion GetFileVersion(AddBackslash(SourcePath) + “MyApp.exe”)
#define MyAppPublisher “Frank Yung-Tai Sun”
#define MyAppExeName “{#XojoAppName}.exe”

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
VersionInfoVersion=1.0.0.0
SetupLogging=yes
SignTool=signtool
SignedUninstaller=yes
AppId={{#AppGUID}
AppName={#XojoAppName}
AppVersion={#MyAppVersion}
AppVerName={#XojoAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={commonpf}\{#XojoAppName}
; Since no icons will be created in “{group}”, we don’t need the wizard
; to ask for a Start Menu folder name:
;DefaultGroupName={#XojoAppName}
DisableProgramGroupPage=yes
AllowNoIcons=yes
LicenseFile=C:\path\to\{#XojoAppName} End User License Agreement.rtf
OutputDir=.
OutputBaseFilename=Setup{#XojoAppName}v{#MyAppVersion}_Win64
Compression=lzma
SolidCompression=yes
ChangesAssociations=yes
ArchitecturesInstallIn64BitMode=x64
; Require Windows 7 SP1 or later
MinVersion=6.1.7601

[Languages]
Name: “english”; MessagesFile: “compiler:Default.isl”

[Tasks]
Name: “desktopicon”; Description: “{cm:CreateDesktopIcon}”; GroupDescription: “{cm:AdditionalIcons}”; Flags: unchecked

; These directories will be created by the installer inside the DefaultDirName
; (defined above).
[Dirs]
Name: “{app}\{#XojoAppName} Libs”
Name: “{app}\{#XojoAppName} Resources”
Name: “{app}\locales”
Name: “{app}\swiftshader”

; These are the files to include. By default you want to include
; the EXE plus the Libs and Resources folders
; but you can include any other files you like as well.
[Files]
Source: “.\{#XojoAppName}\{#XojoAppName}.exe”; DestDir: “{app}”; Flags: ignoreversion signonce
Source: “.\{#XojoAppName}\"; DestDir: “{app}”; Flags: ignoreversion recursesubdirs createallsubdirs
Source: ".\{#XojoAppName}\{#XojoAppName} Libs\
”; DestDir: “{app}\{#XojoAppName} Libs”; Flags: ignoreversion recursesubdirs createallsubdirs
Source: “.\{#XojoAppName}\{#XojoAppName} Resources\"; DestDir: “{app}\{#XojoAppName} Resources”; Flags: ignoreversion recursesubdirs createallsubdirs
Source: ".\{#XojoAppName}\locales\
”; DestDir: “{app}\locales”; Flags: ignoreversion recursesubdirs createallsubdirs
Source: “.\{#XojoAppName}\swiftshader\*”; DestDir: “{app}\swiftshader”; Flags: ignoreversion recursesubdirs createallsubdirs

; NOTE: Don’t use “Flags: ignoreversion” on any shared system files

; Creates icons/links in the Start Menu and/or the desktop if the user chooses during installation.
[Icons]
;Name: “{group}\{#XojoAppName}”; Filename: “{app}\{#XojoAppName}.exe”;
Name: “{commonprograms}\{#XojoAppName}”; Filename: “{app}\{#XojoAppName}.exe”;
Name: “{commondesktop}\{#XojoAppName}”; Filename: “{app}\{#XojoAppName}.exe”; Tasks: desktopicon;

; Give the user the option to run the app after the installation is finished.
[Run]
Filename: “{app}\{#XojoAppName}.exe”; Description: “{cm:LaunchProgram,{#XojoAppName}}”; Flags: nowait postinstall skipifsilent

; This specifies the Visual C++ Windows Runtime Redistributable to also install because
; it is required by Xojo apps made with 2016r1 or later.
[Files]
Source: “.\Windows Universal Runtime\Installers\VC_redist.x64.exe”; DestDir: {tmp}

[Run]
Filename: {tmp}\VC_redist.x64.exe; Parameters: “/install /quiet /norestart”; StatusMsg: “Installing 64-bit Windows Universal runtime…”; Flags: waituntilterminated; Check: InstallVCRuntime();

[Code]
// Most of this code is pinched from https://stackoverflow.com/questions/2000296/inno-setup-how-to-automatically-uninstall-previous-installed-version

function GetUninstallString(): String;
var
sUnInstPath: String;
sUnInstallString: String;
begin
sUnInstPath := ExpandConstant(‘Software\Microsoft\Windows\CurrentVersion\Uninstall\{#emit SetupSetting(“AppId”)}_is1’);
sUnInstallString := ‘’;
if not RegQueryStringValue(HKLM, sUnInstPath, ‘UninstallString’, sUnInstallString) then
RegQueryStringValue(HKCU, sUnInstPath, ‘UninstallString’, sUnInstallString);
Result := sUnInstallString;
end;

function IsUpgrade(): Boolean;
begin
Result := (GetUninstallString() <> ‘’);
end;

function UnInstallOldVersion(): Integer;
var
sUnInstallString: String;
iResultCode: Integer;
begin
// Return Values:
// 1 - uninstall string is empty
// 2 - error executing the UnInstallString
// 3 - successfully executed the UnInstallString

// default return value
Result := 0;

// get the uninstall string of the old app
sUnInstallString := GetUninstallString();
if sUnInstallString <> ‘’ then begin
sUnInstallString := RemoveQuotes(sUnInstallString);
if Exec(sUnInstallString, ‘/SILENT /NORESTART /SUPPRESSMSGBOXES’,’’, SW_HIDE, ewWaitUntilTerminated, iResultCode) then
Result := 3
else
Result := 2;
end else
Result := 1;
end;

// Uninstalling old version doesn’t seem to be necessary unless app file structure has changed
// procedure CurStepChanged(CurStep: TSetupStep);
// begin
// if (CurStep=ssInstall) then
// begin
// if (IsUpgrade()) then
// begin
// UnInstallOldVersion();
// end;
// end;
// end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False;

if ((PageID = wpLicense) or (PageID = wpSelectTasks)) and (IsUpgrade()) then
begin
Result := True;
end;
end;

// Adapted from https://stackoverflow.com/questions/3618257/is-it-possible-to-accept-custom-command-line-parameters-with-inno-setup
{ Check if there is a command-line parameter “kajuupdate=true” }
function IsKajuUpdate(): Boolean;
begin
Result := False;
if ExpandConstant(’{param:kajuupdate|false}’) = ‘true’ then
Result := True;
end;

{ If installer is being run by a kaju update then user already has VCRuntime }
function InstallVCRuntime(): Boolean;
begin
Result := not IsKajuUpdate();
end;

// Adapted from https://stackoverflow.com/questions/28763220/inno-setup-delete-the-installer-after-the-install-process
procedure CurStepChanged(CurStep: TSetupStep);
var
strContent: String;
intErrorCode: Integer;
strSelf_Delete_BAT: String;
begin
if (CurStep=ssDone) and (IsKajuUpdate()) then
begin
// strContent := ‘:try_delete’ + #13 + #10 +
// ‘del "’ + ExpandConstant(’{srcexe}’) + ‘"’ + #13 + #10 +
// ‘if exist "’ + ExpandConstant(’{srcexe}’) + ‘" goto try_delete’ + #13 + #10 +
// ‘del %0’;

strContent := 'for /L %%a in (1,1,100) do (' + #13 + #10 +
      'del "' + ExpandConstant('{srcexe}') + '"' + #13 + #10 +
      'if not exist "' + ExpandConstant('{srcexe}') + '" goto end_loop' + #13 + #10 +
      ')' + #13 + #10 +
      ':end_loop' + #13 + #10 +
      'del %0';
strSelf_Delete_BAT := ExtractFilePath(ExpandConstant('{tmp}')) + 'SelfDelete.bat';
SaveStringToFile(strSelf_Delete_BAT, strContent, False);
Exec(strSelf_Delete_BAT, '', '', SW_HIDE, ewNoWait, intErrorCode);

end;
end;

I can only tell you my preference, and that’s 1) you share it on GitHub as a fork, 2) submit your additions in a pull request so I can consider adding them to the main project, and 3) make sure your changes are clearly marked so there is no confusion between the main project and yours.

Nice work though. This is something I’ve wanted to add on both the Mac and Windows sides for some time, but just haven’t found the time.

Hi Kem, ok thanks. It’ll probably take me a while, but will do - GitHub is something I’ve been avoiding so far at it seems so intimidating to learn! But gotta take the plunge some time, i guess. It’ll be good to contribute something back to this community.

One user just sent me an error log with an IOException from Kaju:

[quote]2020-02-25, 15:07:08 An error happened:
2020-02-25, 15:07:08 Class/Method: KajuUpdateWindow.HandleOKButton
2020-02-25, 15:07:08 Time: Tuesday, February 25, 2020 15:07:08 1496171
2020-02-25, 15:07:08 Type of Error: Xojo.IO.IOException
2020-02-25, 15:07:08 --------------------------
2020-02-25, 15:07:08 Stack:
2020-02-25, 15:07:08
Sub Xojo.Net.HTTPSocket.Send(Xojo.Net.HTTPSocket, Xojo.IO.FolderItem)
Sub KajuHTTPSocketAsync.Get(string, FolderItem)
Sub KajuUpdateWindow.KajuUpdateWindow.HandleOKButton(KajuUpdateWindow.KajuUpdateWindow)
Sub KajuUpdateWindow.KajuUpdateWindow.btnOK_Action(KajuUpdateWindow.KajuUpdateWindow, PushButton)
Sub Delegate.IM_Invoke(PushButton)
Sub AddHandler.Stub.15()
Sub Application._CallFunctionWithExceptionHandling()[/quote]

Then I had a look at the code:

[code]Public Sub Get(url As String, file As FolderItem)
Disconnect

SetSecure url

dim path as string = file.NativePath.DefineEncoding( Encodings.UTF8 )
dim f as new Xojo.IO.FolderItem( path.ToText )
super.Send “GET”, url.ToText, f

End Sub[/code]

The user must have managed to loose access to his own download folder on Mojave.

I’ve been successfully using Kaju v2.1 for macOS updates for all but one user, who never got the prompt an updates was available, whether or not a silent check was being made. The app menu option to check for an update never displayed any result, even if simply “No updates available”.

It was only a single user with this issue, and was friendly and didn’t really mind having to install himself when I sent links, but it always struck me as odd. He did not have the firewall turned on, or anything other malware or virus detection software active, but I still attributed it to something unique about his machine.

This weekend we were in the same city, so he gave me a chance to sit together and debug the issue. And to my initial surprise, when I stepped through the code in debug it worked and told me there were no updates available. So then I removed all my breakpoints but still ran in debug mode and it still worked. Compiled the same source, and it failed to report anything! So I figured it had to be timing dependent, and looked closer at how Kaju.UpdateCheckerFetchAsync performed this section of code:

dim url as string = UpdateURL
dim http as Kaju.HTTPSocketAsync = GetAsyncHTTPSocket
http.Get url

with the GetAsyncHTTPSocket method doing this:

if AsyncHTTP is nil then
  AsyncHTTP = new HTTPSocketAsync
  AddHandler AsyncHTTP.PageReceived, WeakAddressOf AsyncHTTP_PageReceived
  AddHandler AsyncHTTP.Error, WeakAddressOf AsyncHTTP_Error
end if

return AsyncHTTP

So on a whim, since it seemed to be timing dependent and since I already had the MBS plugins available, changed to this:

if AsyncHTTP is nil then
  AsyncHTTP = new HTTPSocketAsync
  AddHandler AsyncHTTP.PageReceived, WeakAddressOf AsyncHTTP_PageReceived
  AddHandler AsyncHTTP.Error, WeakAddressOf AsyncHTTP_Error
end if

// Add slight delay to allow time for AddHandlers to complete
SleepMBS(0.05)

return AsyncHTTP

to add a 1/20 second delay before the socket was returned.

It then worked even in compiled mode. This user’s machine was the fastest of any of my users – including me – and it at least seems the socket was getting returned and the .Get url completing faster than the AddHandler’s had taken effect. Or so it seems.

And perhaps 0.05 seconds is overkill and another shorter delay tactic would suffice. But I’m ok with adding 1/20 second delay into an operation so am just leaving it as it for now.

So this is more for the sake of reporting the issue I saw and the eventual circumvention in the hopes of helping others.

This was using Kaju v2.1, and Xojo 2019R1.1, with client machine running 10.14.16 (Mojave) with firewall etc off.

I’ve just tried implementing Kaju into a project and I’m getting problems in Xojo 2019 r3.1.

When saving a configuration from the Kaju admin app, it throws an UnsupportedFormatException from the Kaju FolderItemAlias.Resolve method, at the point that it’s constructing a FolderItem using previously stored zSaveInfo.

I haven’t modified the admin app at all, so am I doing something wrong or perhaps missed a setup step?

I posted a workaround a while ago. But I don’t remember what I did to fix the problem.

Ok, as it’s not me, I’ve done some more digging and as far as I can tell this might be related to FolderItem.GetSaveInfo which returned a String being deprecated and replaced by FolderItem.SaveInfo, which now returns a MemoryBlock. Although the official documentation seems confused about this (the docs for FolderItem.FromSaveInfo state it takes a String as its parameter, which I can’t believe is correct) and the compiler doesn’t seem to care either.

What confuses me is that various people have clearly been using Kaju successfully recently (active conversation thread above) without encountering this issue.

[quote=483212:@Austin Goudge]Ok, as it’s not me, I’ve done some more digging and as far as I can tell this might be related to FolderItem.GetSaveInfo which returned a String being deprecated and replaced by FolderItem.SaveInfo, which now returns a MemoryBlock. Although the official documentation seems confused about this (the docs for FolderItem.FromSaveInfo state it takes a String as its parameter, which I can’t believe is correct) and the compiler doesn’t seem to care either.

What confuses me is that various people have clearly been using Kaju successfully recently (active conversation thread above) without encountering this issue.[/quote]

String and memoryblock are essetially the same.

Var myString As String = "hello world"
Var myMB As MemoryBlock = myString

Var output As String = myMB

System.DebugLog(output) // Reads "hello world"

Hmm, thanks Derk, it’s not that then! The app does seem to be borking when constructing a FolderItem using the stored SaveInfo though, and the debugger is showing this content as binary. The old docs for FolderItem.GetSaveInfo clearly state that this is a String representation of the path, so it seems something has changed.

The SaveInfo and GetSaveInfo both returned a special string, it’s not only a path. It made up of a specific line, not to be changed manually.

You’d only use the result to load back into FolderItem.FromSaveInfo so you know where the file should be (if it’s still there…)

The docs are unclear there i think. or i may be wrong with the new api @Paul Lefebvre ?