2

Our company has switched from using InstallShield Express to using Inno Setup (5.5.2 version). We've got years of old installs utilizing InstallShield, but have always relied on InstallShield's Upgrade Code GUID to handle the uninstall of the previous version.

I need to be able to uninstall any previous InstallShield installed version from our new Inno Setup installer.

After some research it looks like I need to call MsiEnumRelatedProducts() and then uninstall any found products.

I found this link http://www.microsofttranslator.com/bv.aspx?from=de&to=en&a=http%3A%2F%2Fwww.inno-setup.de%2Fshowthread.php%3Fs%3D415e3895fda3e26e42739b004c0f51fb%26t%3D2857 (original in German http://www.inno-setup.de/showthread.php?s=415e3895fda3e26e42739b004c0f51fb&t=2857). It looks like he got pretty close, but he never posts his final solution.

Code he says works (but crashes for me):

type
  TProductBuf = array[0..39] of char;

function MsiEnumRelatedProducts(lpUpgradeCode:string;
  dwReserved, iProductIndex:cardinal; 
  var lpProductBuf:TProductBuf) : cardinal;
external 'MsiEnumRelatedProductsW@msi.dll setuponly stdcall';

function InitializeSetup : boolean;
var
  ret, i, j : cardinal;
  ProductBuf : TProductBuf;
  ProductCode : string;

begin
  Result := true;
  i := 0;
  repeat
  ret := MsiEnumRelatedProducts('{#UPGRADE_CODE}', 0, i, ProductBuf);
    if ret=0 then
    begin
      ProductCode := '';
      for j := 0 to 39 do
      begin
        if ProductBuf[j] = #0 then
          break;
        ProductCode := ProductCode + ProductBuf[j];
      end;
      Result := uninstallOther(ProductCode);
    end;
    i := i+1;
  until ret <> 0;
end;

He says this makes it easier?

SetLength(ProductCode, Pos(#0, ProductCode) - 1);

I'm new to Pascal scripting and I'm getting stuck on the whole SetLength() part. What does it replace in the function he says works, but crashes?

Since the other persons says to switch to string, should I get rid of this:

type
  TProductBuf = array[0..39] of char;

If anyone could show me a final working function in English, it would be awesome!!!

Thanks in advance!

Edit: I am using the ANSI version of the Inno Setup Compiler.

Jim Billig
  • 344
  • 2
  • 14

1 Answers1

1

Here's an untested translation, which should just print out the related product GUIDs in message boxes. The code should work with ANSI as well as with Unicode versions of InnoSetup:

[Code]
#ifdef UNICODE
  #define AW "W"
#else
  #define AW "A"
#endif

#define UPGRADE_CODE "<your upgrade here>"

const
  ERROR_SUCCESS = $00000000;
  ERROR_NOT_ENOUGH_MEMORY = $00000008;
  ERROR_INVALID_PARAMETER = $00000057;
  ERROR_NO_MORE_ITEMS = $00000103;
  ERROR_BAD_CONFIGURATION = $0000064A;

function MsiEnumRelatedProducts(lpUpgradeCode: string; dwReserved: DWORD;
  iProductIndex: DWORD; lpProductBuf: string): UINT;
  external 'MsiEnumRelatedProducts{#AW}@msi.dll stdcall';

function InitializeSetup: Boolean;
var
  I: Integer;
  ProductBuf: string;
begin
  Result := True;

  I := 0;
  SetLength(ProductBuf, 39);

  while MsiEnumRelatedProducts('{#UPGRADE_CODE}', 0, I, ProductBuf) = ERROR_SUCCESS do
  begin
    MsgBox(ProductBuf, mbInformation, MB_OK);
    I := I + 1;
    SetLength(ProductBuf, 39);
  end;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • Sorry it has taken me some time to get back to this, but I was out for personal reasons for a bit. – Jim Billig Aug 06 '13 at 19:30
  • No problem :-) Maybe I should also take back to this post and test it. Till now I've made at least correction to the ANSI/Unicode mismatch in your original code. You were importing Unicode version of the function, but passing ANSI string (because `string` stands for an ANSI string in ANSI InnoSetup). – TLama Aug 06 '13 at 19:38
  • I hit CR and posted before I finished. Thanks for your code sample, but I've still got one issue. The import of user32.dll should be msi.dll, which I fixed. After that, I'm getting two Access Violation errors somewhere in the while MsiEnumRelatedProducts loop. I get two of them, but I only have one previous product installed. I have to guess that it is in the SetLength(ProductBuf, 39) call as the installer executes just fine without any previous products installed. – Jim Billig Aug 06 '13 at 19:38
  • Of note, this Access Violation is mentioned in the comments on the original post in German. – Jim Billig Aug 06 '13 at 19:42
  • Uhm, strange... There's written in the post you linked, that you should not use `var` for the parameter `lpProductBuf`, but according to [`documentation`](http://msdn.microsoft.com/en-us/library/windows/desktop/aa370103(v=vs.85).aspx) you should (it's an output parameter). But well, this seems like another documentation failure... Even [`JEDI library`](http://sourceforge.net/p/jedi-apilib/code/1114/tree/jwapi/trunk/Win32API/JwaMsi.pas#l957), quite a rock solid code base, defines it without `var`. And, sorry for that library name. It was my copy/paste mistake... – TLama Aug 06 '13 at 19:58
  • It works now. Post #5 mentions "var is not allowed", so I guess that's what is meant. Can you explain to me what `SetLength(ProductBuf, 39)` does? – Jim Billig Aug 06 '13 at 20:52
  • It *preallocates* the `ProductBuf` string variable to be 39 chars long. That function doesn't do it for you; it just expects to have 39 chars long buffer passed to the `lpProductBuf` parameter, which is just that `ProductBuf` variable. I've checked that post, but I don't believe in forums. MSDN should be the preferred source of information. And [`there is the parameter defined as output`](http://msdn.microsoft.com/en-us/library/windows/desktop/aa370103(v=vs.85).aspx), so if that MSDN reference would be correct, it should be defined as `var` or as `out` in InnoSetup, but it seems to be wrong. – TLama Aug 06 '13 at 20:58