Aug
31
2011

Registry Permissions, OLE, & The Never Ending MSI Installer

Recently, we have had a Gatekeeper client that was experiencing issues when attempting to utilize our Forms module.  In essence, every time a form was opened through Gatekeeper, the Microsoft Office installer would launch.  If it had happened once and then quit, it wouldn't have been such an issue, but it kept happening every single time a form was opened.

Our forms module stores the documents in the database as BLOBs, and then writes them out to the hard drive to be opened in Microsoft Word using OLE.  This isn't ground-breaking development here.  This technology has been around for quite a while.

We tried numerous things, but I am only going to document the final approach, which as of this writing, has worked successfully in all cases.

Realizing that the issue had to be permission based - be it file based or registry based - we set off on a search using our old friend google.com.  After a few failed searches, we finally came across this article, that discussed the issue with the never ending MSI installer.  It seems that this is a recurring issue on Windows based machines, as the article comes from Microsoft.  It boils down to the permissions on the registry keys/subkeys, and traditional Windows folders (Windows, Program Files, etc.) being corrupted in some way.  Luckily, the article shows how to repair them without re-installing Windows on the machine, using a small utility named SubInACL.  After reviewing the information, we decided it was worth a try - and I'm glad we did.

After a few trial runs, it was decided this was the fix we needed - but now we had one other problem to solve.  This fix needs to be automated AND the command prompt needs to be ran as administrator, and our users don't have those kinds of permissions.  

Naturally, our developer prowess kicked in and we started brainstorming.  We could definitely write a 'wrapper' program around the SubInACL application, but how would we be able to elevate it so that it can with the proper credentials? 

CreateProcessWithLogonW, that's how.

We could embed credentials inside of our application, and kick off the SubInACL application with those credentials automagically - perfect!

Here's how we did it:

1. We didn't want to use a domain admin account, so the IT staff created a special account for us that would have local administrator rights on the machines.

2. Create a .NET application to use the CreateProcessWithLogonW Windows API command to launch the SubInACL program to perform the registry permission fix.

3. Launch Gatekeeper, open a form, & let the MSI finish (& this time, it finishes successfully and never shows up again!)

Since I'd also like to test the code formatting on our new blog, I'm going to post the contents of that application (minus credentials) so that you can see how we solved this issue:

Imports System.Runtime.InteropServices

Public Class ACLReset
    Structure PROCESS_INFORMATION
        Public process As IntPtr
        Public thread As IntPtr
        Public processId As Integer
        Public threadId As Integer
    End Structure

    <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
    Structure STARTUPINFO
        Public cb As Integer
        Public reserved As String
        Public desktop As String
        Public title As String
        Public x As Integer
        Public y As Integer
        Public xSize As Integer
        Public ySize As Integer
        Public xCountChars As Integer
        Public yCountChars As Integer
        Public fillAttribute As Integer
        Public flags As Integer
        Public showWindow As UInt16
        Public reserved2 As UInt16
        Public reserved3 As Byte
        Public stdInput As IntPtr
        Public stdOutput As IntPtr
        Public stdError As IntPtr
    End Structure

    <DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Public Shared Function CreateProcessWithLogonW(ByVal userName As String, ByVal domain As String, ByVal password As String, ByVal logonFlags As UInt32, ByVal applicationName As String, ByVal commandLine As String, ByVal creationFlags As UInt32, ByVal environment As UInt32, ByVal currentDirectory As String, ByRef startupInfo As STARTUPINFO, ByRef processInformation As PROCESS_INFORMATION) As Boolean
    End Function

    <DllImport("kernel32.dll", CharSet:=CharSet.Unicode, SetLastError:=True, ExactSpelling:=True)> _
    Public Shared Function CloseHandle(ByVal hObject As IntPtr) As Integer
    End Function

    <DllImport("Kernel32.dll", SetLastError:=True)> _
    Public Shared Function WaitForSingleObject(ByVal handle As IntPtr, ByVal milliseconds As UInt32) As UInt32
    End Function

    <DllImport("Kernel32.dll", SetLastError:=True)> _
    Public Shared Function GetStdHandle(ByVal handle As IntPtr) As IntPtr
    End Function

    <DllImport("kernel32.dll", SetLastError:=True)> _
    Public Shared Function GetExitCodeProcess(ByVal process As IntPtr, ByRef exitCode As UInt32) As Boolean
    End Function

    Public Infinite As System.UInt32 = Convert.ToUInt32(&HFFFFFFF)
    Public Startf_UseStdHandles As Int32 = &H100
    Public StdOutputHandle As Int32 = -11
    Public StdErrorHandle As Int32 = -12

    Private Sub bgWorker_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles bgWorker.DoWork
        Dim sInfo As New STARTUPINFO
        Dim pInfo As New PROCESS_INFORMATION
        Dim userName As String = Nothing

        Dim runAsUser As String = "USERNAME"
        Dim runAsDomain As String = "DOMAIN"
        Dim runAsPassword As String = "PASSWORD"

        Dim MyPointer As IntPtr = Marshal.AllocHGlobal(4)
        Dim MyErrorPointer As IntPtr = Marshal.AllocHGlobal(4)
        Dim exitCode As System.UInt32 = Convert.ToUInt32(123456)

        Marshal.WriteInt32(MyErrorPointer, StdErrorHandle)
        Marshal.WriteInt32(MyPointer, StdOutputHandle)

        userName = Environment.UserName

        bgWorker.ReportProgress(10)

        sInfo.cb = System.Runtime.InteropServices.Marshal.SizeOf(sInfo)
        sInfo.reserved = Nothing
        sInfo.flags = sInfo.flags And Startf_UseStdHandles
        sInfo.stdOutput = MyPointer
        sInfo.stdError = MyErrorPointer
        sInfo.showWindow = Convert.ToUInt16(0)
        bgWorker.ReportProgress(20)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                "cmd.exe",
                "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /subkeyreg HKEY_CURRENT_USER /grant=administrators=f /grant=system=f /grant=restricted=r /grant=" + userName + "=f /setowner=administrators > %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)
        GetExitCodeProcess(pInfo.process, exitCode)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(30)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                    "cmd.exe",
                    "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /keyreg HKEY_CURRENT_USER /grant=administrators=f /grant=system=f /grant=restricted=r /grant=" + userName + "=f /setowner=administrators >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(40)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                "cmd.exe",
                "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /subkeyreg HKEY_LOCAL_MACHINE /grant=administrators=f /grant=system=f /grant=users=r /grant=everyone=r /grant=restricted=r /setowner=administrators >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(50)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                "cmd.exe",
                "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /keyreg HKEY_LOCAL_MACHINE /grant=administrators=f /grant=system=f /grant=users=r /grant=everyone=r /grant=restricted=r /setowner=administrators >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(60)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                "cmd.exe",
                "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /subkeyreg HKEY_CLASSES_ROOT /grant=administrators=f /grant=system=f /grant=users=r /setowner=administrators >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(70)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                "cmd.exe",
                "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /keyreg HKEY_CLASSES_ROOT /grant=administrators=f /grant=system=f /grant=users=r /setowner=administrators >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(80)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                                  "cmd.exe",
                    "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /subdirectories %programfiles%\ /grant=administrators=f /grant=system=f /grant=users=e >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(90)

        CreateProcessWithLogonW(runAsUser,
                runAsDomain,
                runAsPassword,
                Convert.ToUInt32(1),
                                  "cmd.exe",
                    "/C """"c:\Program Files\Windows Resource Kits\Tools\subinacl.exe"""" /subdirectories %windir%\ /grant=administrators=f /grant=system=f /grant=users=e >> %temp%\subinacl_output.txt",
                Convert.ToUInt32(0),
                Convert.ToUInt32(0),
                "c:\Program Files\Windows Resource Kits\Tools\",
                sInfo,
                pInfo)

        WaitForSingleObject(pInfo.process, Infinite)

        CloseHandle(pInfo.process)
        CloseHandle(pInfo.thread)

        bgWorker.ReportProgress(100)
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        bgWorker.RunWorkerAsync()
    End Sub

    Private Sub bgWorker_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bgWorker.ProgressChanged
        ProgressBar1.Value = e.ProgressPercentage
    End Sub

    Private Sub bgWorker_RunWorkerCompleted(sender As System.Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bgWorker.RunWorkerCompleted
        Threading.Thread.Sleep(5000)
        Application.Exit()
    End Sub
End Class

Now, there isn't a lot (if any) error handling going on here, and that's simply because we were in somewhat of a rush to find a solution. At some point, I'll probably revisit this application and refine it a bit.

The code above is simply elevating a command prompt with the provided credentials, and then passing in parameters to that prompt to run the SubInACL utility with the parameters being passed to it that were outlined on the blog post above.  It does utilize a background worker, only because I have a progress bar on the interface that gets updated after each command prompt terminates.  If you have any more questions about the what or why of the code, please leave a comment!

Comments (13) -

Josh T

A very eloquent solution to a tough issue! I felt the stress of this issue personally as I was one of the developers to try implementing several attempted solutions, including rewriting the psiforms.dll (a VB6 dll that communicated with the Office OLE controls) in VB.net, and re-writing the forms.pbd to exclude the psiforms.dll which we though was originally causing the issue. Anyway, a special thanks to Google and you, Calvin, for finally wrapping this one up!

Calvin Allen

Thanks, Josh, but I couldn't have done it without you!

window bench

Just letting you know that I really like your site, and I come here every day for the valuable information you post. Thanks so much!

msi credit solutions

Just letting you know that I really like your site, and I come here every day for the valuable information you post.

mobile software development

I was after browsing by making use of an on ugg boots outlet clearance collection purchasing list and I found out this on collection underwear store that carried one of the most delightful, ribbons Ultra Tall UGG Boots Saleteddy lingerie. Unfortunately, the "people that be" in the purchasing list failed to mention the fact that women's outfits store I purchased the underwear from was in actuality dependent from the US and ugg classic mini boots sale never in Australia as I initially supposed.The first time I knew about it experienced been when my demand card costs arrived and I saw the charge. The teddy I purchased cost me extra than i experienced been ready to spend credited toward the ugg classic mini boots disparity in between USD and AUD along using the currency conversion demand from my exclusive demand card company. Furthermore, I really experienced to wait ugg classic mini boots chestnut around 10 times preceding to I obtained the underwear which i ordered.In some cases, indeed, on collection stores which could be practically in Australia (read: stores that ugg classic mini boots black dispatch to Australia) are just not as good as stores which could be really in Australia.

software application development

Thank you…If you'd like to get more comments on your blog, I believe it’s important to construct a sense of community. Participate on other people’s sites and write posts that people like to examine.

Cottage in Yorkshire

I like this information given and it has given me some sort of commitment to succeed for some reason, so keep up the good work.

steve

thanks for this

What is SEO

blog is absolutely fantastic! All great information can be helpful in some or the other way. Keep updating the blog, looking forward for more content. Keep updating the blog, looking forward for more content.

help me lose weight fast

Thanks for contributing your important time to post such an interesting & useful collection.It would be knowledgeable & resources are always of great need to everyone. Please keep continue sharing.

SEO process

Thanks for the post. I really enjoyed reading it and I think you made some good points! I am mainly involved with new technology to remove tatoo by laser without pain I really got a lot from this. Take care!

personalised gifts

Just had to say what a great article this is, have been looking for this information all over the web and stumbled upon it here so thanks for taking the time.

realizzazione siti

Excellent report! I truly was pleased with our learning. I'm hoping to learn to read considerably more of your stuff. There's no doubt you might exceptional awareness in addition to perception. I'm really prompted with these particular resources.

mulberry outlet

Mine entire life I've never billed someone else regarding our issues. I actually attempted the idea not way back when. Ahhh... sooo greater in that possition.<a href=" http://www.bestmulberryoutlet.co.uk/"; title="Mulberry Outlet"><strong>Mulberry Outlet</strong></a>

History of Guitar

This page appears to get a good amount of visitors. How do you advertise it? It offers a nice individual twist on things. I guess having something authentic or substantial to talk about is the most important thing.

sbobet

http://www.bolabet.com/sbobet.php
visit for latest sports here !

Anton

Thanks for sharing your post with us and thank you in the first place for taking the time to write it of course ;)
Cheers.
http://gitaarlerenspelen.nu

dental loupes

Ford Scanner is fully Ford compliant PC-based scan tool designed to use through USB port connection.It read and clear manufacturer specific
http://www.dentaluck.com/15-dental-loupes
AK-47

Pingbacks and trackbacks (1)+

Add comment

biuquote
  • Comment
  • Preview
Loading

Calendar

<<  May 2012  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar