I'm writing a script to copy and move DLLs from the bin folder to a mapped drive, and I need to register/unregister the DLLs during the process. I've figured out how to do all of this, but there's a catch. The program I'm working on utilizes VB6 COM DLLs and VB.NET .NET DLLs. I understand that COM DLLs use regsvr32.exe, and .NET DLLs use regasm.exe, but I am interested in programmatically calling the correct function, based upon the DLL I am moving. Is there a way to determine what time I am using in Powershell?
Call
[Reflection.Assembly]::LoadFile( `mydll.dll`)
It should raise a BadImageFormatException if it is not a .Net dll.
As per MSDN:
"This exception is thrown when the file format of a dynamic link library (.dll file) or an executable (.exe file) does not conform to the format that is expected by the common language runtime. In particular, the exception is thrown under the following conditions:
...
An attempt is made to load an unmanaged dynamic link library or executable (such as a Windows system DLL) as if it were a .NET Framework assembly. The following example illustrates this by using the Assembly.LoadFile method to load Kernel32.dll."
Related
The XML serializer in System.Xml.Serialization namespace generates the XmlSerializationReader and XmlSerializationWriter classes dynamically using CodeDOM.
I want to have a look at these classes in the disassembler.
Where does the serializer store the temporary assembly file that has this dynamically generated code? I have been trying to find it out for the last 30 minutes in Reflector but to no avail.
All I gather from Reflector is that it stores it in some temp directory. I looked up C:\temp but there's no .dll there. I looked up C:\tmp and there are some PNG files there that seem like they are not really PNG's but some binary files generated by Windows for storing temporary data.
If you know, please tell me.
Alright, these two threads out of the dozen or so pertaining to this issue that I looked up reveal the answer.
This thread says it is the folder C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Temp
http://snavin.wordpress.com/2013/04/01/xmlserializer-is-running-does-not-have-sufficient-permission-to-access-the-temp-directory/
But from the stack trace posted in the original question on this thread, it appears that the temp folder of the windows environment, which can be found out by calling System.IO.Path.GetTempPath() is where the dll is saved.
ASP.NET Access to the temp directory is denied
There is an easy way: use sgen.exe to generate necessary assembly by yourself.
On my machine it looks like:
C:>"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\sgen.exe" "C:\Projects\BIN\My.dll"
Microsoft (R) Xml Serialization support utility
[Microsoft (R) .NET Framework, Version 4.6.1055.0]
Copyright (C) Microsoft Corporation. All rights reserved.
Serialization Assembly Name: My.XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
Generated serialization assembly for assembly c:\projects\bin\My.dll --> 'c:\projects\bin\My.XmlSerializers.dll'.
How can I generate the code for both x86 and x64 using MIDL?
I've created an IDL file in Visual Studio 2010, and when I compile the product as in x86 mode and afterwards in x64 I've got to "touch" the IDL file so it will regenerate the code relevant for x64. Can I somehow tell MIDL to generate both codes into the same file?
An IDL file defines an interface, that interface could use 64-bit platform features or 32-bit platform features. An IDL can be used to generate a stub; if an interface doesn't have 32-bit-platform-specific definitions or 64-bit-platform-specific definitions, supposedly on stub can be generated (i.e. one IDL file). But, that depends on the interfaces that you're exposing. Short answer: if you define you interfaces to be 32-bit and 64-bit compatible you shouldn't need two different IDL files--otherwise you need two different IDL files.
Without knowing what "touch" (which generally means updating the date/time of a file, w.r.t. software engineering) means, it's hard to say specifically what you need to do.
If you're referring to the files under the Generated Files folder, you won't see them change if all you change is the target platform (well, unless you've placed #ifdef blocks in the IDL that use platform-specific defines). Remember, the output of MIDL is source code, not binaries. The names of the datatypes used in the generated code won't change, so the output from MIDL will be the same even though the machine architecture the compiler is targeting is different.
You can verify this by making copies of the XXX_i.h and XXX_i.c files and comparing them between platforms. To do this, Build, make copies, Rebuild, then compare the files; the only thing that should be different is the timestamp.
So, to get back to your original question: you're already doing it!
I know this is an old question, but should anyone else hit this here is how I solved it.
In the project containing the IDL file, I added a pre-build event to all platforms and configurations that deleted the MIDL output files like this...
if exist $(ProjectName).h del $(ProjectName).h
if exist $(ProjectName)_i.c del $(ProjectName)_i.c
if exist $(ProjectName)_p.c del $(ProjectName)_p.c
I could have gotten away with just deleting the proxy (_p) file as that's the only platform specific MIDL generated file.
If your proxy stub source files have different names or extensions, use those.
My exe depends on ntdll, user32 and kernel32. I save these dlls as a local copy and change the first letter as "V".
I then edit the exe's Import dll name as Vernel32.dll from kernel32. The application works fine by loading vernel32.dll in local space.
Next i edit the exe's import dll spec as vtdll as ntdll, the process loads vtdll from local, runs its code and throws an _stackhash exception on vtdll instructions.
I need this for developing my appliction to bundle all windows dependencies. Does any body have any idea, Why ntdll cant be run in local space.
No! You cannot try to replace ntdll. It is mapped by the kernel into every single process, probably before any of your code is even loaded. It has an intricate connection with the kernel. It knows all the correct system call numbers. Try using ntdll from NT 5.1 and it will crash on NT 6.1. ntdll hosts the system call entry and exit code. The kernel-user callback dispatcher code. The thread start function which the kernel knows the address of. The user exception dispatcher. The user APC handler. I could go on, but I won't.
I don't see why you're trying to "bundle" these DLLs with your program. There is no way a Windows install won't have these DLLs. And that's ZERO chance for ntdll.dll since I don't see how without the session manager and CSR you are going to run your program in the first place.
I find the idea to "bundle" system DLL as not a good idea.
First of all it is illegal to redistribute this DLLs together with your application. Seconds you should understand that a DLL can create some global objects and the usage of two copies of the same DLL (vtdll.dll and ntdll.dll) can not work. You don't wrote how you modified imports of the dlls. If you do it on the disk it is illegal and moreover it break the signature of the files (open file properties of any of the dlls and look at "Digital Signatures" tab).
If you do want to experiment with different copies of system dlls you can better use DLL redirection (see http://msdn.microsoft.com/en-us/library/ms682600.aspx) through creating of files with the name myapp.exe.local where myapp.exe is the name of your application. It can be required to delete some entries from HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs to do this. You should understand that your computer will run slowly after this and I recommend to make such experiments better inside a virtual machine which you can easy restore if it will be no more booted.
Thanks for the information. It helped me to do a research on it.
I am not bundling the dlls for my own application. I am doing it for existing applications to provide a windows cross platform independence solution.
I tried the dll redirection technique which you have posted, with all applications.
It works well with all dlls except NTdll and User32.dll
User32.dll:
It loads user32.dll from local space only and not kernel space. I confirmed it. But on executing its instructions, it results in the null address access exception (c0000005) with fault module name StackHash_5964
ntdll:
The application on booting, it loads ntdll from system32 and again loads ntdll from local space, which may cause the error as you said (global object sharing violation)
This happens only for ntdll and not for user32.dll.
Is there any way we can make load ntdll once(only form local space) and avoid the errors caused by user32.dll in local space.
I tried the references sent by you and here are the results.
User32.dll
I couldnot build user32.dll having these below functions.
IsThreadDesktopComposited = user33.IsThreadDesktopComposited,
User32InitializeImmEntry = user33.User32InitializeImmEntry
It produces a linker error (Unreolved external symbol "IsThreadDesktopComposited")
Hence i left 100 such functions out of 800 functions in user32.dll. The DLL was built finally.
I then placed the dll in local space along with user33.dll. On running the application, it says the 100 missed functions procedure entry points are not found.
Ntdll.dll
I tried removing known dlls. But its inacccesible for modify or delete operations. I could just read. I am the admin and ran regedit as administrator.
Is it possible to do such implementatipons for ntdll or user32.dll.
I guess, am coming with repeated times.
Thanks for all your help.
But, If you have any other ways or any suggestions you can make, that would be grateful
This seems so trivial, yet I can't get it to work..
I have an msi.dll wrapper (named Interop.WindowsInstaller.dll) which I need to sign. The way to do it is by signing it upon import (this specific case is even documented in MSDN: http://msdn.microsoft.com/en-us/library/zec56a0w.aspx).
BUT - no matter how I do it (w/ or w/o a keyfile, w/ or w/o adding "/delaysign"), the generated assemly's size is always 36,864 bytes and when viewing the DLL's properties there is no "Digital Signatures" tab (needless to say - the DLL is NOT signed).
What am I missing here?? (... HELP!...)
[Note: Eventually I got a hint from Karel Zikmund on this thread, which helped me solve the mystery. I'll paste my reply here - for the greater good].
So, I used the following line to sign-upon-import the assembly:
tlbimp C:\WINDOWS\system32\msi.dll /out:Interop.WindowsInstaller.dll /keyfile:MyKey.snk
I then copied the file to the appropriate location and built the project, but each time the build failed on the following error: Assembly generation failed -- Referenced assembly 'Interop.WindowsInstaller' does not have a strong name.
I thought the problem was with the tlbimp line, but after reading Karel Zikmund's reply and verifying that the DLL is strong-named (using sn -vf Interop.WindowsInstaller) I found out the problem.
Adding a reference to the "Microsoft Windows Installer Object Library" COM object actually planted a code block into the .csproj file.
I didn't realize it, but this block caused the DLL file to be regenerated from scratch upon each time the project was built. The generated file, of course, was not strong-named anymore.
The way I resolved it was to remove the reference to "Microsoft Windows Installer Object Library" from the project, and add a direct file reference to the imported (and already signed) Interop.WindowsInstaller.dll file.
When I run my script directly from the Powershell console it works. When I run my script in PowerGUI and try instantiate an object, I get an error:
Exception calling ".ctor" with "3" argument(s): "Could not load file or assembly 'MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=77f676cc8f85d94e' or one of its dependencies. The system cannot find the file specified."
If I put all of the needed DLLs in $PSHOME, the script will successfully run from the console but not PowerGUI. If I move the DLLs to a local directory and load the DLLs with reflection, the script will not run in PowerGUI nor the powershell console.
[reflection.assembly]::loadfile('c:\mylibs\mylib.dll')
What do I need to do to get the script to run in PowerGUI? Ideally, I'd like the DLLs in a different directory than $PSHOME.
You should be using [Assembly]::LoadFrom as opposed to LoadFile. LoadFile is intended for loading assemblies that cannot be loaded in the normal assembly loading context such as the case where you are trying to load two versions of the same assembly. It does not use the normal probing rules so that is why it doesn't automatically load dependencies. Here's an excerpt from the documentation for LoadFile.
Use the LoadFile method to load and
examine assemblies that have the same
identity, but are located in different
paths. LoadFile does not load files
into the LoadFrom context, and does
not resolve dependencies using the
load path, as the LoadFrom method
does. LoadFile is useful in this
limited scenario because LoadFrom
cannot be used to load assemblies that
have the same identities but different
paths; it will load only the first
such assembly.
If you are using PowerShell 2.0 you may wish to use Add-Type instead:
Add-Type -Path c:\mylibs\mylib.dll
And if all else fails, run Fuslogvw.exe to find out why binding fails.
Use set-psdebug -trace 2 to see what it is attempting to call exactly.
This could be because PowerGUI is a different PowerShell host so its 'local folder' is PowerGUI's folder in Program Files, and not $pshome - where you put the DLLs.