我有一个.NET C#应用程序,包含在MSI安装程序 - “myprogram.exe”中。我有一个PHP网站和一个特定的页面,用户可以通过链接下载该程序。
我希望能够跟踪.NET应用程序上的某些事件。例如 - “程序已打开”。
将事件发送到我的服务器很容易,但是如何从php服务器获取用户ID,这样我就可以知道哪个用户在.NET应用程序上做了什么?
我考虑过将一个参数(user-id)传递给MSI安装程序,但找不到相应的方法。
如何在PHP用户标识和.NET应用程序之间建立链接?
澄清 -
许多人提出使用登录系统来绑定服务器和应用程序。
这确实是最简单的解决方案,但是在我的网站上我不强迫用户登录下载应用程序(我也没有在.NET应用程序中请求登录详细信息 - 可选)。如果我们不必询问登录详细信息,我认为我们不应该,用户体验会更好(使用该应用程序的步骤少得多) - 用户下载和使用桌面应用程序的机会更大。
考虑当前的流程是 - >网页 - 下载点击 - 运行 - 使用该应用程序(需要10秒)
登录 - >网页 - 注册(确认电子邮件?) - 重定向 - 下载点击 - 运行 - 应用程序登录 - 使用该应用程序(用户需要60-120秒)
从程序登录
最好的方法是让用户在程序中使用相同的凭据登录。这样,您的程序就可以使用安全的OAuth2身份验证与您的后端API进行通信。这也使得程序与互联网通信的用户透明。
在filename中包含user-id
另一种方法是在下载期间将user-id添加到安装程序的文件名中,并在安装程序运行时将其解压缩。您必须检查安装工具是否允许这样做。此外,只有当您的用户ID是UUID或类似的东西时才这样做,因为您不希望用户猜出其他ID。
App.config中
第三种选择是将user-id添加到App.config
文件中。有两种方法可以做到这一点:
App.config
创建.msi,添加具有固定UUID的用户ID设置。您的PHP脚本可以在将其发送给用户之前查找UUID并将其替换为.msi二进制文件。请参阅MST转换下的代码段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;
?>
序列号
我能想到的最后一种方法是让程序在首次启动时询问序列号,并让您的网站为每个用户生成一个唯一的序列号。
请注意,这很可能不是你想要做的。从来没有我会解释几种方法来做到这一点..
将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文件中,您将看到需要默认值的参数。
在添加/更改参数之前,通过单击“变换” - >“新变换”上的菜单创建新的变换。
之后,您可以根据需要更改参数或添加新参数。完成参数更改后,使用“变换”菜单中的“生成变换”功能生成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
提示:要使用托管代码执行此操作,最好使用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,这样你下次打开程序时就会知道它们 - 或者让它们每次都登录。
当调用该文件时发送UserID的参数,如果您在PHP中使用MVC框架,则需要一个获取msi文件的新控制器,并将其重命名为name-userID.exe,然后返回该文件进行下载通过浏览器。