如何使用user-id参数下载MSI安装程序

问题描述 投票:11回答:3

我有一个.NET C#应用程序,包含在MSI安装程序 - “myprogram.exe”中。我有一个PHP网站和一个特定的页面,用户可以通过链接下载该程序。

我希望能够跟踪.NET应用程序上的某些事件。例如 - “程序已打开”。

将事件发送到我的服务器很容易,但是如何从php服务器获取用户ID,这样我就可以知道哪个用户在.NET应用程序上做了什么?

我考虑过将一个参数(user-id)传递给MSI安装程序,但找不到相应的方法。

如何在PHP用户标识和.NET应用程序之间建立链接?

澄清 -

许多人提出使用登录系统来绑定服务器和应用程序。

这确实是最简单的解决方案,但是在我的网站上我不强迫用户登录下载应用程序(我也没有在.NET应用程序中请求登录详细信息 - 可选)。如果我们不必询问登录详细信息,我认为我们不应该,用户体验会更好(使用该应用程序的步骤少得多) - 用户下载和使用桌面应用程序的机会更大。

考虑当前的流程是 - >网页 - 下载点击 - 运行 - 使用该应用程序(需要10秒)

登录 - >网页 - 注册(确认电子邮件?) - 重定向 - 下载点击 - 运行 - 应用程序登录 - 使用该应用程序(用户需要60-120秒)

c# php .net installer msitransform
3个回答
10
投票

从程序登录

最好的方法是让用户在程序中使用相同的凭据登录。这样,您的程序就可以使用安全的OAuth2身份验证与您的后端API进行通信。这也使得程序与互联网通信的用户透明。

在filename中包含user-id

另一种方法是在下载期间将user-id添加到安装程序的文件名中,并在安装程序运行时将其解压缩。您必须检查安装工具是否允许这样做。此外,只有当您的用户ID是UUID或类似的东西时才这样做,因为您不希望用户猜出其他ID。

App.config中

第三种选择是将user-id添加到App.config文件中。有两种方法可以做到这一点:

  1. 使用未压缩的App.config创建.msi,添加具有固定UUID的用户ID设置。您的PHP脚本可以在将其发送给用户之前查找UUID并将其替换为.msi二进制文件。请参阅MST转换下的代码段
  2. 使用自定义App.config按需构建.msi。这仅在您的Web服务器在Windows上运行或者您具有可以执行此任务的远程Windows构建服务器时才有效。

MST转换

您也可以使用MST转换并使用与我在App.config下针对第1点所解释的相同的二进制替换技巧。

对于这两个选项,您可以使用使用二进制安全函数的PHP脚本替换安装程序中的值,并将文件作为下载文件发送给用户:

<?php
$userId = // TODO get userId from the session or database
$data = file_get_contents("./my-installer.msi");
// I would use UUID's for template and userId, this way the size of the installer remains the same after replace
$data = str_replace("{fe06bd4e-4bed-4954-be14-42fb79a79817}", $userId, $data);
// Return the file as download
header("Cache-Control: public"); // needed for i.e.
header('Content-Disposition: attachment; filename=my-installer.msi');
header('Content-Type: application/x-msi');
header("Content-Transfer-Encoding: Binary");
echo $data;
?>

序列号

我能想到的最后一种方法是让程序在首次启动时询问序列号,并让您的网站为每个用户生成一个唯一的序列号。


7
投票

请注意,这很可能不是你想要做的。从来没有我会解释几种方法来做到这一点..

将MST文件与MSI一起使用:

您可以使用user-id属性创建MST文件,并在下载msi时为每个用户生成这些文件,并使用变换安装msi:

msiexec -i c:\temp\The.msi transforms=c:\temp\YourPerso.mst

在这里查看更多信息:Install a transform using the command line

MST文件在大型组织中使用很多,其中所有MSI都具有嵌入了序列号等的MST文件。

要制作MST文件,您需要下载并安装Microsofts Orca Tool, its part of the Microsoft Windows SDK

打开Orca并从MSI文件中创建MST文件。基本上你打开MSI文件导航到表“属性”,在那里你看到一个参数列表。请注意,在MSI文件中,您将看到需要默认值的参数。

在添加/更改参数之前,通过单击“变换” - >“新变换”上的菜单创建新的变换。

enter image description here

之后,您可以根据需要更改参数或添加新参数。完成参数更改后,使用“变换”菜单中的“生成变换”功能生成MST文件。

enter image description here

如果您随后使用HexEditor打开mst文件,则可以看到刚刚添加的属性:

enter image description here

您可以通过编辑值来编辑每次下载的文件,例如:

enter image description here

您当然可以(并且可能应该)使用WindowsInstaller.Installer的API以正确的方式执行此操作。这是一个例子:

private function createTransform(mstfile, msi, config)
    writeLog InfoLog, "Generating transform " & mstfile

    dim vars: set vars = configvars(config)

    dim createPropertyTable: createPropertyTable = "create table `Property` " & _
        "(`Property` char(72) not null, `Value` longchar localizable " & _
        "primary key `Property`)"
    dim addProperty: addProperty = "insert into `Property` (`Property`, `Value`) values (?, ?)"
    dim updateProperty: updateProperty = "update `Property` set `Value` = ? where `Property` = ?"

    dim wi: set wi = createObject("WindowsInstaller.Installer")
    dim base: set base = wi.openDatabase("base.msi", msiOpenDatabaseModeCreate)
    base.openview(createPropertyTable).execute
    dim tgt: set tgt = wi.openDatabase("tgt.msi", msiOpenDatabaseModeCreate)
    tgt.openview(createPropertyTable).execute
    dim props: set props = createObject("scripting.dictionary")
    dim view: set view = msi.openView("select `Property`, `Value` from `Property`") 
    view.execute        
    dim record: set record = view.fetch
    while not record is nothing
        props(record.stringdata(1)) = true
        base.openview(addProperty).execute record
        tgt.openview(addProperty).execute record    
        set record = view.fetch
    wend

    set record = wi.createRecord(2)
    dim prop
    for each prop in properties_
        on error resume next
        dim val: val = expand(vars, prop(DepPropertyValueIdx))
        if err then
            writeLog ErrorLog, err.description
            exit function
        end if
        on error goto 0
        writeLog InfoLog, "Property " & prop(DepPropertyNameIdx) & "=" & val
        if props.exists(prop(DepPropertyNameIdx)) then
            record.stringdata(2) = prop(DepPropertyNameIdx)
            record.stringdata(1) = val
            tgt.openview(updateProperty).execute record
        else
            record.stringdata(1) = prop(DepPropertyNameIdx)
            record.stringdata(2) = val
            tgt.openview(addProperty).execute record
        end if
    next
    if not tgt.generateTransform(base, mstfile) then
        writeLog ErrorLog, "Failed to create transform"
        exit function
    end if
    tgt.createTransformSummaryInfo msi, mstfile, 0, 0
    createTransform = true
end function

提示:要使用托管代码执行此操作,最好使用Microsoft.Deployment.WindowsInstaller.dll作为http://wix.codeplex.com/的一部分提供


为每个用户构建一个MSI:

恕我直言,使用Nullsoft(WiX,InstallShield,INNO等)做这件事要容易得多,并为每个用户构建一个MSI。为此,您将在例如an nsi script中嵌入一个唯一的用户ID,并为每次下载启动MSI构建。在安装过程中,唯一的用户ID将存储在文件,注册表项等中。我建议您使用此NSIS Wizard Editor快速启动基本的NSI安装脚本和build the MSI via a command line: makensis

注意:虽然“在MSI文件名中包含用户ID”比为每个用户构建MSI更容易,但用户可以轻松更改文件名。用户使用Orca审核MSI以查找内置用户ID的可能性要小得多。


最简单,最合乎逻辑的方式:

将事件发送到我的服务器很容易,但是如何从php服务器获取用户ID,这样我就可以知道哪个用户在.NET应用程序上做了什么?

做什么@Jhuliano Moreno然后@WouterHuysentruit推荐:

当您的应用程序第一次启动时,只需让用户使用他们的网站凭据登录该程序,并在配置文件,注册表项或数据库记录中记录他们的用户ID。基本上创建一个cookie,这样你下次打开程序时就会知道它们 - 或者让它们每次都登录。


4
投票

当调用该文件时发送UserID的参数,如果您在PHP中使用MVC框架,则需要一个获取msi文件的新控制器,并将其重命名为name-userID.exe,然后返回该文件进行下载通过浏览器。

© www.soinside.com 2019 - 2024. All rights reserved.