从Windows服务调用MSIGetProductInfo会返回垃圾值

时间:2011-10-10 23:19:15

标签: windows windows-services installer

我正在处理两个应用程序,这两个应用程序都依赖于MSIGetProductInfo调用来检索用户在安装过程中输入的序列号(标准的可视演播室设置和部署项目)。

以下是我用来检索序列号的代码:

Int32 len = 512;
var builder = new StringBuilder(len);
MsiGetProductInfo("{98A0A10F-5E78-4FA6-83F6-6B356D75ADD4}", "ProductId", builder, ref len);
return builder.ToString();

第一个app,一个visual c#forms app,按照我的预期返回(当我输入1111-1111-1111-1111时,它表示如此)。但是,当我在c#windows服务的OnStart方法中放入相同的调用时,我会得到随机垃圾,例如Unicode字符,偶尔会出现一个字,并且每个运行时返回的值都不同。偶尔我会得到一个结果,其中包含包含MsiGetProductInfo调用的dll路径的一部分,例如“㷨H꿈Eindows \ system32 \ msi.dll”。

在我看来(没有受过教育的猜测),就像对dll函数的调用失败一样,它只是从未使用的内存位置返回数据。

我已经尝试过阅读其他属性,例如InstallSource和InstalledProductName,但这些也是服务中的垃圾(同时在表单应用程序中也很好)。我还认为它可能是权限问题,因此我尝试在NetworkService,LocalService和LocalSystem帐户下运行该服务但没有成功。

Forms应用程序和服务都是由同一个安装项目同时安装的,因此它们应该共享相同的ProductCode,对吗?如果没有,我可以看到服务无法检索产品信息的原因。

有没有人遇到这个或者知道是什么导致了这个?

2 个答案:

答案 0 :(得分:1)

我需要回答同样的问题。我在wix msi项目中添加了UserRegistrationDlg.wxs来收集PIDKEY。但是,我需要在程序首次运行时获取在安装时输入的序列,以完成激活/注册步骤。

这是我用来查询在运行时安装时输入的许可证密钥的代码(VB.NET,抱歉):

Declare Auto Function MsiGetProductInfo Lib "msi.dll" (ByVal product As String, ByVal [property] As String, <MarshalAs(UnmanagedType.VBByRefStr)> ByRef valueBuf As String, ByRef len As Long) As Int32

Private Shared Function GetMSIKey() As String

Dim lsResult As String = Nothing
Try
    Dim loUninstallHive As RegistryKey = Registry.LocalMachine.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Uninstall")
    For Each loProgramHiveID As String In loUninstallHive.GetSubKeyNames()
        Dim loProgramHive As RegistryKey = loUninstallHive.OpenSubKey(loProgramHiveID)
        Dim lsProgramName As String = NullReaders.StringValue(loProgramHive.GetValue("DisplayName"))

        If lsProgramName.StartsWith("<your app name>", StringComparison.OrdinalIgnoreCase) Then

            Dim loInstallLocation = loProgramHive.GetValue("InstallLocation")

            Dim lsHiveName As String = loProgramHive.Name
            Dim lsGUID As String = lsHiveName.Substring(lsHiveName.LastIndexOf("\") + 1)

            Dim lsIKC As String = New String(" ", 255)
            Dim liLen As Integer = 255
            MsiGetProductInfo(lsGUID, "ProductID", lsIKC, liLen)

            lsResult = lsIKC.Substring(6, 24).Replace(" ", "-")

            Exit For
        End If
    Next
Catch ex As Exception
    TraceLog.DoErr(ex)
End Try
Return lsResult
End Function

请注意,ProductID与大写字母D一起显得很重要。

子字符串/替换将取决于PIDTemplate中的Setup.wxs属性,例如:

 <Property Id="PIDTemplate"><![CDATA[12345<#### #### #### #### ####>@@@@@]]></Property>

最后,这是来自UserRegistrationDlg.wxs的行,它将许可证密钥框放入安装程序UI:

<Control Id="CDKeyEdit" Type="MaskedEdit" X="45" Y="159" Width="250" Height="16" Property="PIDKEY" Text="[PIDTemplate]" />

答案 1 :(得分:0)

这不是我的问题的答案,但我不想让问题得不到解决,以防其他人在同样的情况下结束。

通过在设置过程中将用户输入的序列号存储在注册表中,我解决了这个问题。通过创建值为“[PIDKEY]”的注册表项(字符串值),可以存储用户的序列号,然后在使用.NET RegistryKey类执行应用程序期间检索该序列号。

如果我有更多时间花在这个问题上,我会这样做,但感谢Harry帮助我走上正轨。