11

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

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

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

我考虑将参数(用户 ID)传递给 MSI 安装程序,但找不到解决方法。

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

澄清 -

许多人提出使用登录系统在服务器和应用程序之间进行绑定。

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

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

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

4

3 回答 3

10

从程序登录

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

在文件名中包含用户 ID

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

应用程序配置

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

  1. 创建App.config未压缩的 .msi,添加具有固定 UUID 的用户 ID 设置。您的 PHP 脚本可以查找 UUID 并将其替换为 .msi 二进制文件,然后再将其发送给用户。请参阅MST 转换下的代码片段
  2. 使用自定义按需构建 .msi App.config。这仅在您的网络服务器在 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;
?>

序列号

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

于 2015-08-25T21:42:30.317 回答
7

请注意,这很可能不是您想要做的。无论如何,我会解释几种方法来做到这一点..

将 MST 文件与 MSI 一起使用:

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

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

在此处查看更多信息:使用命令行安装转换

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

要制作 MST 文件,您需要下载并安装Microsofts Orca Tool,它是 Microsoft Windows SDK 的一部分

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

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

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

您当然可以(并且可能应该)使用 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

提示:要使用托管代码执行此操作,您最好使用http://wix.codeplex.com/Microsoft.Deployment.WindowsInstaller.dll中提供的那些


为每个用户构建一个 MSI:

恕我直言,使用 Nullsoft (WiX、InstallShield、INNO 等)并为每个用户构建一个 MSI会容易得多。为此,您将在例如nsi 脚本中嵌入一个唯一的用户 ID,并为每次下载启动一个 MSI 构建。在安装过程中,唯一的用户 ID 将存储在文件、注册表项等中。我建议您尝试使用此 NSIS 向导编辑器快速启动基本的 NSI 安装脚本并通过命令行构建 MSI:makensis

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


最简单和最合乎逻辑的方法:

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

做@Jhuliano Moreno 然后@WouterHuysentruit 推荐的:

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

于 2015-08-26T03:11:22.080 回答
4

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

于 2015-08-25T21:30:35.647 回答