This blog has moved to http://ThePowerShellGuy.com
Greetings /\/\o\/\/
I started this afer reading a post from Alex K. Angelopoulos (MVP)
in m.p.w.s.scripting
MSH: Accessing ADSI interfaces and Q on WinNT Provider
He concluded getting the NT provider from MSH is not possible.
you can connect to AD LDAP:// GC://, but not the NT provider :
this will all fail :
$entryPC = New-object System.DirectoryServices.DirectoryEntry
$entryPC.set_Path =
"WinNT:\\."$entryPC.mshbase.Path =
"WinNT:\\."$entryPC.path =
"WinNT:\\."$entryPC.get_path()
Strange thing is that if you do the same thing from .NET it WILL work.
(code below will show this:)
I used the Microsoft.VisualBasic.VBCodeProvider to compile
some inline VB.NET code on the fly and return the object back to MSH.
and if you pass back the created object to MSH it will work.
so after running this code, you can do things like this :
MSH C:\> $r.get_path()
WinNT://.
MSH C:\> $r.get_children() foreach {$_.name} select -first 5
ACTUser
Administrator
ASPNET
ASWM USER
Guest
MSH C:\> $r.get_path()
WinNT://.
MSH C:\> $r.set_path("WinNT://./administrators,group")
MSH C:\> $r.get_path()
WinNT://./administrators,group
so far so good.
but then :
Now I did want the members of the Group. hmm, because we still have the .NET wrapper (DirectoryEntry) around the WinNT provider I didn't see the Members .
but I can call Invoke and I get the Members Back (as Com Object)
$r.Invoke("members")
MSH C:\> $r.Invoke("members").getType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------True False __ComObject System.MarshalByR...
I can't do anything with it again ;-(
so I got to the winFX SDK-dir and imported the TypeLib :
F:\Data\PrepRoom>tlbimp c:\windows\system32\activeds.tlb /out:activedsNET.dll /namespace:ActiveDS
and loaded it into MSH [System.Reflection.Assembly]::LoadFile("g:\monad\activeDSnet.dll") GAC Version Location
--- ------- --------
False v2.0.50215 g:\monad\activeDSnet.dll
MSH C:\> [System.DirectoryServices.DirectoryEntry]$m = $r.Invoke("Members")
Cannot convert "System.__ComObject" to "System.DirectoryServices.DirectoryEntry". Error: "The value provided for adsObject does not implement IADs.".At line:1 char:44+ [System.DirectoryServices.DirectoryEntry]$m <<<< = $r.Invoke("Members")
MSH C:\> [ActiveDS.IADsMembers]$m = $r.Invoke("Members")
Cannot convert "System.__ComObject" to "ActiveDS.IADsMembers".At line:1 char:25+ [ActiveDS.IADsMembers]$m <<<< = $r.Invoke("Members")
Ofcourse it does work in the @' VD.NET block
'@. (dim group as ActiveDS.IADsGroup = entryPC.nativeobject)
PS 2 returning the ActiveDS.IADsGroup,TLB loaded also in MSH). gives this fine red error, and the shell will exit ( but the message is fair enough (only why quit the shell)
MSH G:\> $r gm An error has occurred that was not properly handled. Additional information is shown below. The command shell will exit. Unhandled Exception: System.Runtime.InteropServices.InvalidComObjectException: C OM object that has been separated from its underlying RCW cannot be used. .............. .............
so we need a way to bind the RCW in MSH,
anyone some Idees ?
anyway you still can do alot of things from the current workaraund :
start stop services, get local SID's etc. on the Winnt: provider.
only if you get an IADs object back it will not work.
for the rest I like the Way I can put the "VB.NET code online, ofcourse if you implemt this i na function you can better complile the DLL but this is nice for testing.
gr /\/\o\/\/
cls
$provider = new-object Microsoft.VisualBasic.VBCodeProvider
$params = new-object System.CodeDom.Compiler.CompilerParameters
$params.GenerateInMemory = $True
$refs = "System.dll","Microsoft.VisualBasic.dll","System.Data.DLL","System.management.dll","System.DirectoryServices.dll"
$params.ReferencedAssemblies.AddRange($refs)
# VB.NET EXAMPLE
$txtCode = @'
imports system
Class mow
function main() as System.DirectoryServices.DirectoryEntry
Dim entryPC As New System.DirectoryServices.DirectoryEntry
entryPC.Path = "WinNT://."
return entryPC
end function
end class
'@
$results = $provider.CompileAssemblyFromSource($params, $txtCode)
$mAssembly = $results.CompiledAssembly
$i = $mAssembly.CreateInstance("mow")
$r = $i.main()