Why isn't handles.exe discovering my DLL while ProcessExplorer can? - vb.net

The problem:
On a windows server 2012 r2 box, I'm trying to use Chef to programmatically replace a .dll command component (aka a vb 6 library that I've registered on the box using regsvr32.exe) but when I try to copy over the file, the app pool of the website has a lock on it. I'm not sure if it matters, but the w3wp process is set to run as 32 bit via IIS.
My Solution (which isn't working):
In order to fix it, I was thinking about using a command line tool to find the reference to the dll and then recycling the app pool that's using it. Unfortunately, while I can get SysInternals' process explorer to find the dll, Handles.exe (the supposed command line version of process explorer) does not return anything. I was hoping that someone might be able to tell me how I was using handles incorrectly, or if there was a better tool for this.
Process Explorer - it has found my dll ComHelper.dll
Handles via command line - it has not found my dll ComHelper.dll
-- Edit --
This is the output of handles when I point it at w3wp while running as Admin

I would suspect you are running into access issues. Are you running Handle from an elevated command prompt ? Are you able to get any output covering handles in w3wp.exe (by using the pid of the process in handle.exe command line) ?
Looking at the handle enum output of w3wp.exe it seems,
listdll.exe -d ComHelper.dll
may be what you are looking for. Handle seems to be focused on files opened not dlls loaded. listdll is a tool that can be downloaded from sysinternals.

Alright so 32 bitness did matter. I ended up having to resort to powershell as opposed to trying to use handles. The code for finding a PID that has a lock on your file is scattered around the internet, but here's the link:
http://blogs.technet.com/b/heyscriptingguy/archive/2013/12/01/weekend-scripter-determine-process-that-locks-a-file.aspx (it's marv the robot's answer at the bottom)
For the record, this is what was suggested
$lockedFile="C:\Windows\System32\acproxy.dll"
$isLocked = $false
Get-Process | foreach{
$processVar = $_;$_.Modules | foreach{
if($_.FileName -eq $lockedFile){
$isLocked = $true
$processVar.Name + " PID:" + $processVar.id
}
}
}
This is what I had translated it into with my powershell noobishness
$lockedFile = "E:\Components\___ComHelper.dll"
$list = Get-Process
foreach ($process in $list)
{
foreach ($module in $process.Modules)
{
if ($module.FileName -ne $lockedFile) { continue }
$process.Name + " PID:" + $process.Id
}
}

Related

why can't I execute a simple powershell rasdial command in my runspace in a vb.net winforms app?

I've built an app that contains a PowerShell runspace.
It works perfectly for everything I've thrown at it.... until today.
One bit of functionality I've added detects when the user is trying to access corporate resources and will highlight if their VPN has become disconnected.
We use an always on VPN with certificate auth, so no username/password is required.
The runspace, and the app it is contained within run in the context of the user.
I've been able to demonstrate this by outputting the current environment user from within the runspace, which comes back as me (i.e. not system or administrator or anything silly).
I have a really simple bit of PowerShell that will attempt 4 times to reconnect the VPN before reporting back a failure, checking before each retry for a success.
This code works perfectly if I run ISE (standard, NOT elevated), but when I pass this code to be executed I get an error coming back from rasdial, which simply says 'the data is invalid'.
The PowerShell is simple enough...
$myvpn = Get-VpnConnection | Where-Object {$_.ServerAddress -eq "vpn.example.com"}
$VPNStatus = ( $myvpn ).ConnectionStatus
if ($VPNStatus -eq "Connected")
{
$output= 0
}
else
{
$attempt = 0
DO
{
Write-output "not connected"
rasdial $myvpn.name
$check = (Get-VpnConnection | Where-Object {$_.ServerAddress -eq "vpn.example.com"}).ConnectionStatus
start-sleep -Seconds 4
$attempt ++
} Until ($attempt -gt 3 -or $check -eq "Connected")
if ($check -eq "Connected")
{
$output= 0
}
else
{
$output= 1
}
}
I've got no idea why it won't work in the runspace, but will work in normal PowerShell with ease. My first suspicion was the context, but like I said I've disproven that theory.
I've googled the hell out of the error and can't seem to find any related results.
I've also tried to work around it, spawning an actual PowerShell session (ie not in a runspace) and passing in the command, but that also fails when spawned from my app.
Has anyone seen this before or similar behaviour when using runspaces/pipelines etc. within vb.net?
Are there any weird permissions pitfalls that I may have overlooked?
Edit: Expanded the powershell to be fully inclusive
So I've got to the bottom of this just now... Turns out the PS command will only work in an x64 shell, and my app's runspace was being spawned x86.
I changed the app to target x64 and it worked first time.
This post on Technet was the breadcrumbs that got me over the finish line.

T-SQL Database Backup Cleanup Script logging of files to be deleted before delete

I was having issues with the SQL maintanence plan cleanup task not deleting one particularly large database backup each night, it works for days then fails then starts working again and it always works correctly on the small databases.
I researched this maintanence plan cleanup task issue and tried everything I could think of to get it working, changed extension matching to *, added a \ at the end of folder path, changed age no NONE so all files would be deleted regardless of age, and still sometimes this backup is not getting deleted.
So I implemented this SQL JOB using the script below to see if that would work, but the same issue again, intermittently the large backup file is not deleted, when I run the task manually it seems to always delete it.
My question here is, is there a way to firstly get a list of files that match the delete criteria and write them to a log file before actually attempting to delete the files, that way I could at least see if for some reason the large backup file is not matching the criteria to be deleted in the first place.
Any assistance to otherwise delete the old backup files using T-SQL without using xp_cmdshell and without using a batch or powershell script would be appreciated.
declare #dt datetime
select #dt=dateadd(hh,-22,getdate())
EXECUTE master.dbo.xp_delete_file 0,N'Z:\SQLBackups\',N'BAK',#dt,1
The version of SQL server I'm having the issue with:
Microsoft SQL Server Management Studio 10.50.4042.0
Microsoft Analysis Services Client Tools 10.50.4042.0
Microsoft Data Access Components (MDAC) 6.1.7601.17514
Microsoft MSXML 3.0 6.0
Microsoft Internet Explorer 9.10.9200.17609
Microsoft .NET Framework 2.0.50727.5485
Operating System 6.1.7601
After creating a powershell script to delete the files and finding the error "Another process is using the file" I made this script to check what process that is using the handle64.exe program and found it was the Commvault agent CLBackup.exe that was locking the file.
Backup schedule conflict is the cause of the issue.
$log = ($MyInvocation.MyCommand.Path).TrimEnd("ps1") + "log"
$handlelog = ($MyInvocation.MyCommand.Path).TrimEnd(".ps1") + "-Handle.log"
$1 = gci 'Z:\SQLBackups' | %{gci $_.fullname -Filter '*.BAK' | ? {$_.LastWriteTime -le (get-date).addhours(-22)}}
write-output "$(get-date -format g) Files will be deleted: $1" >> $log
$1 | % {remove-item $_.fullname -force -Confirm:$FALSE}
if (!!($error)) {
write-output "$(get-date -format g) Open Handles on .BAK files:" >> $handlelog
$exec = "$env:SystemRoot\system32\cmd.exe /c" + (Split-Path($MyInvocation.MyCommand.Path)) + "\handle64.exe .BAK -u -nobanner -accepteula"
Invoke-Expression -Command:$exec >> $handlelog
$error >> $log
}

VB.net and powershell variables

I have a Visual Studio form running with VB.net and I'm collecting info needed to setup an AD user. In the end, this info will need to simply be passed to Powershell with no return info needed. Before that though, I need it to check if a printer code has already been assigned to someone before allowing it to be submitted to another user. I have a simple powershell script written up for it.
(We use the Pager field to store the printer code.)
Import-Module ActiveDirectory
$Page = $args[0]
Get-ADUser -Filter { Pager -like $Page } | FT Name
I setup the code I found HERE, and attempted to modify it to my script but it keeps crashing on
Dim results As Collection(Of PSObject) = MyPipeline.Invoke()
It gives me: An unhandled exception of type 'System.Management.Automation.ParseException' occurred in System.Management.Automation.dll
If I run his little 6+5 basic example script, it works, but when I try to retrieve info and return a name, it doesn't like it. How can I get it to return the name of the person if it find it? And since it won't run, I'm not even sure if passing the printer code as $args[0] is going to work yet.
Your results is expecting a collection of PowerShell objects. When you pipe the Get-ADUser command to Format-Table, it effectively strips the object down to a stream of strings. Try without the | FT Name.
Import-Module ActiveDirectory #if you're using powershell 3 or later, this may be redundant
# $Page = $args[0] # don't need to do this
$results = Get-ADUser -Filter { Pager -like $args[0] }
Write-Verbose $results
#Write-Verbose $results.Name #try this if the above one works
Update:
Write-Verbose may be causing an issue.
Try this:
Get-ADUser -Filter { Pager -like $args[0] }
Just that one line as the total PS code. (Assuming you have PowerShell 3.0 or later, you don't need Import-Module) That line will return objects of type TypeName: Microsoft.ActiveDirectory.Management.ADUser (from `Get-ADUser username | Get-Member).
You may also be able to use the .Net object type directly, without PowerShell. I'm not knowledgeable about .NET beyond what I picked up working with PowerShell.
Accessing AD using .NET, info from MSDN.

How do you stop a user-instance of Sql Server? (Sql Express user instance database files locked, even after stopping Sql Express service)

When using SQL Server Express 2005's User Instance feature with a connection string like this:
<add name="Default" connectionString="Data Source=.\SQLExpress;
AttachDbFilename=C:\My App\Data\MyApp.mdf;
Initial Catalog=MyApp;
User Instance=True;
MultipleActiveResultSets=true;
Trusted_Connection=Yes;" />
We find that we can't copy the database files MyApp.mdf and MyApp_Log.ldf (because they're locked) even after stopping the SqlExpress service, and have to resort to setting the SqlExpress service from automatic to manual startup mode, and then restarting the machine, before we can then copy the files.
It was my understanding that stopping the SqlExpress service should stop all the user instances as well, which should release the locks on those files. But this does not seem to be the case - could anyone shed some light on how to stop a user instance, such that it's database files are no longer locked?
Update
OK, I stopped being lazy and fired up Process Explorer. Lock was held by sqlserver.exe - but there are two instances of sql server:
sqlserver.exe PID: 4680 User Name: DefaultAppPool
sqlserver.exe PID: 4644 User Name: NETWORK SERVICE
The file is open by the sqlserver.exe instance with the PID: 4680
Stopping the "SQL Server (SQLEXPRESS)" service, killed off the process with PID: 4644, but left PID: 4680 alone.
Seeing as the owner of the remaining process was DefaultAppPool, next thing I tried was stopping IIS (this database is being used from an ASP.Net application). Unfortunately this didn't kill the process off either.
Manually killing off the remaining sql server process does remove the open file handle on the database files, allowing them to be copied/moved.
Unfortunately I wish to copy/restore those files in some pre/post install tasks of a WiX installer - as such I was hoping there might be a way to achieve this by stopping a windows service, rather then having to shell out to kill all instances of sqlserver.exe as that poses some problems:
Killing all the sqlserver.exe instances may have undesirable consequencies for users with other Sql Server instances on their machines.
I can't restart those instances easily.
Introduces additional complexities into the installer.
Does anyone have any further thoughts on how to shutdown instances of sql server associated with a specific user instance?
Use "SQL Server Express Utility" (SSEUtil.exe) or the command to detach the database used by SSEUtil.
SQL Server Express Utility,
SSEUtil is a tool that lets you easily interact with SQL Server,
http://www.microsoft.com/downloads/details.aspx?FamilyID=fa87e828-173f-472e-a85c-27ed01cf6b02&DisplayLang=en
Also, the default timeout to stop the service after the last connection is closed is one hour. On your development box, you may want to change this to five minutes (the minimum allowed).
In addition, you may have an open connection through Visual Studio's Server Explorer Data Connections, so be sure to disconnect from any database there.
H:\Tools\SQL Server Express Utility>sseutil -l
1. master
2. tempdb
3. model
4. msdb
5. C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIENTS\CORE.DATA.C
LIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF
H:\Tools\SQL Server Express Utility>sseutil -d C:\DEV*
Failed to detach 'C:\DEV_\APP\VISUAL STUDIO 2008\PROJECTS\MISSICO.LIBRARY.1\CLIE
NTS\CORE.DATA.CLIENT\BIN\DEBUG\CORE.DATA.CLIENT.MDF'
H:\Tools\SQL Server Express Utility>sseutil -l
1. master
2. tempdb
3. model
4. msdb
H:\Tools\SQL Server Express Utility>
Using .NET Refector the following command is used to detach the database.
string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);
I have been using the following helper method to detach MDF files attached to SQL Server in unit tests (so that SQ Server releases locks on MDF and LDF files and the unit test can clean up after itself)...
private static void DetachDatabase(DbProviderFactory dbProviderFactory, string connectionString)
{
using (var connection = dbProviderFactory.CreateConnection())
{
if (connection is SqlConnection)
{
SqlConnection.ClearAllPools();
// convert the connection string (to connect to 'master' db), extract original database name
var sb = dbProviderFactory.CreateConnectionStringBuilder();
sb.ConnectionString = connectionString;
sb.Remove("AttachDBFilename");
var databaseName = sb["database"].ToString();
sb["database"] = "master";
connectionString = sb.ToString();
// detach the original database now
connection.ConnectionString = connectionString;
connection.Open();
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "sp_detach_db";
cmd.CommandType = CommandType.StoredProcedure;
var p = cmd.CreateParameter();
p.ParameterName = "#dbname";
p.DbType = DbType.String;
p.Value = databaseName;
cmd.Parameters.Add(p);
p = cmd.CreateParameter();
p.ParameterName = "#skipchecks";
p.DbType = DbType.String;
p.Value = "true";
cmd.Parameters.Add(p);
p = cmd.CreateParameter();
p.ParameterName = "#keepfulltextindexfile";
p.DbType = DbType.String;
p.Value = "false";
cmd.Parameters.Add(p);
cmd.ExecuteNonQuery();
}
}
}
}
Notes:
SqlConnection.ClearAllPools() was very helpful in eliminating "stealth" connections (when a connection is pooled, it will stay active even though you 'Close()' it; by explicitely clearing pool connections you don't have to worry about setting pooling flag to false in all connection strings).
The "magic ingredient" is call to the system stored procedure sp_detach_db (Transact-SQL).
My connection strings included "AttachDBFilename" but didn't include "User Instance=True", so this solution might not apply to your scenario
I can't comment yet because I don't have high enough rep yet. Can someone move this info to the other answer so we don't have a dupe?
I just used this post to solve my WIX uninstall problem. I used this line from AMissico's answer.
string.Format("USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{1}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tEXEC sp_detach_db [{1}]\nEND", dbName, str);
Worked pretty well when using WIX, only I had to add one thing to make it work for me.
I had took out the sp_detach_db and then brought the db back online. If you don't, WIX will leave the mdf files around after the uninstall. Once I brought the db back online WIX would properly delete the mdf files.
Here is my modified line.
string.Format( "USE master\nIF EXISTS (SELECT * FROM sysdatabases WHERE name = N'{0}')\nBEGIN\n\tALTER DATABASE [{0}] SET OFFLINE WITH ROLLBACK IMMEDIATE\n\tALTER DATABASE [{0}] SET ONLINE\nEND", dbName );
This may not be what you are looking for, but the free tool Unlocker has a command line interface that could be run from WIX. (I have used unlocker for a while and have found it stable and very good at what it does best, unlocking files.)
Unlocker can unlock and move/delete most any file.
The downside to this is the apps that need a lock on the file will no longer have it. (But sometimes still work just fine.) Note that this does not kill the process that has the lock. It just removes it's lock. (It may be that restarting the sql services that you are stopping will be enough for it to re-lock and/or work correctly.)
You can get Unlocker from here: http://www.emptyloop.com/unlocker/
To see the command line options run unlocker -H
Here they are for convenience:
Unlocker 1.8.8
Command line usage:
Unlocker.exe Object [Option]
Object:
Complete path including drive to a file or folder
Options:
/H or -H or /? or -?: Display command line usage
/S or -S: Unlock object without showing the GUI
/L or -L: Object is a text file containing the list of files to unlock
/LU or -LU: Similar to /L with a unicode list of files to unlock
/O or -O: Outputs Unlocker-Log.txt log file in Unlocker directory
/D or -D: Delete file
/R Object2 or -R Object2: Rename file, if /L or /LU is set object2 points to a text file containing the new name of files
/M Object2 or -M Object2: Move file, if /L or /LU is set object2 points a text file containing the new location of files
Assuming your goal was to replace C:\My App\Data\MyApp.mdf with a file from your installer, you would want something like unlocker C:\My App\Data\MyApp.mdf -S -D. This would delete the file so you could copy in a new one.

how to get the information of system log of taskscheduler under the command line with WMIC?

I need to know whether the taskscheduler has run successfully. I found difficult to get a return value by the wmic command, because they aren't included in the system, security, application hardwareevents and so on logfile. Following is my attempts:
wmic ntevent "eventcode=140" get message /value
The above code returns the message: "no instance(s) availalbe."
As I don't know which kind of logfile includes the log records of taskschedule, I select them by the eventcode.
wmic ntevent where "logfile='system' and eventcode='140'" get message /vule
No matter, logfile='appliaction' or logfile='security', at the end I can't get the result I want.
What i need to point out is the log records are allocated in Microsoft-Windows-TaskScheduler/Operational in graphical interfaces.
Unfortunately, wmic can only be used on classic logs. List them by
wmic NTEventlog get LogfileName or by powershell Get-EventLog -AsString -List:
Application
HardwareEvents
Internet Explorer
Key Management Service
PreEmptive
Security
System
TuneUp
Windows PowerShell
Switch to wevtutil: wevtutil enum-logs enumerates the available logs and
wevtutil qe Microsoft-Windows-TaskScheduler/Operational /q:"*[System[Provider[#Name='Microsoft-Windows-TaskScheduler'] and (EventID=140)]]" /uni:false /f:text
command could be a starting point for you. Pretty weird syntax and result hard do parse. Moreover:
The primary focus of WEVTUTIL is the configuration and setup of
event logs, to retrieve event log data the PowerShell cmdlet
Get-WinEvent is easier to use and more flexible:
Switch to powershell: (Get-WinEvent -ListLog *).LogName enumerates the available logs and one can simply filter and format result from next command:
Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational
For instance, to format the result in list view to see all properties:
Get-WinEvent -LogName Microsoft-Windows-TaskScheduler/Operational | Format-List *
Read more in Powershell and the Applications and Services Logs.