How to find a DLL given a CLSID? - dll

I have a situation in which a managed DLL calls some unmanaged DLL. I know the CLSID of the unmanaged DLL, is there any way to find out what binary file houses that CLSID?

Normaly, you can just go to:
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\"GUID"
And find a key called "InProcServer32" for instance and there will be the default value that has the DLL. This is one simple way to do it.

Can you not just search for it in the registry using regedit and look for the binary path.

Based on BobbyShaftoe reply we can build a simple vbs script that reads that registry for us:
Dll_RegPath = "HKEY_CLASSES_ROOT\CLSID\<GUID>\InProcServer32\"
Paste the following to "test.vbs"
Sub Main
' used to find location of "System.Collections.ArrayList" progid dll
Const csGUID = "{6896B49D-7AFB-34DC-934E-5ADD38EEEE39}"
MsgBox srGetDllPathByGUID(csGUID)
End Sub
Function srGetDllPathByGUID( sGUID )
Const csRegPath = "HKEY_CLASSES_ROOT\CLSID\<GUID>\InProcServer32\"
Dim oShell: Set oShell = CreateObject("WScript.Shell")
Dim sReg: sReg = Replace( csRegPath, "<GUID>", sGUID ) ' build str
srGetDllPathByGUID = oShell.RegRead(sReg)
Set oShell = Nothing ' clean up
End Function
Call Main
You can also find ProgId by:
ProgID_RegPath = "HKEY_CLASSES_ROOT\CLSID\<GUID>\ProgID\"

I've found this question because I was troubleshooting some incorrectly installed application and my objective was to find and register ActiveX dll given the CLSID (which I've got from app sources). Hence my a little bit hacky approach.
I've issued a search in the directory where I believed the dll is located looking for CLSID in file contents. That did the trick, because it was stored in plain text in resources. I believe it's not always the case, but my problem was solved.

Related

How can I copy resource files from my Visual Studio VB NET application onto the local system when it's executed?

I want to be able to copy resources I've compiled into the program to a location on the local machine where the application is run. Mostly a few DLLs that my program will not use, but others will (think Installer type of application).. But I can't find a way to reference the resources by path in order to do a File.Copy, for instance:
File.Copy(My.Resources.conversion, BinPath & "conversion.dll")
...tells me "Value of type 'Byte()' cannot be converted to 'String'". And I get why, obviously the first parameter needs to be a string path and not the resource itself. Is there a better way to copy resources or how can I obtain a reference path to the resource so I can use File.Copy?
For what it's worth, I've also tried
File.WriteAllBytes(BinPath & "conversion.dll", My.Resources.conversion)
...which "works" to copy it where I want it, but then I get errors when I try to call regsvr32 on the
new dll: "The module ... was loaded but the entry-point DLLRegisterServer was not found."
If there's a better way altogether, I'm open to any idea.
The following steps work for me:
Change the 'Build Action' of the dll in 'Resources' file to 'Embedded Resource'.
Use the following method to save resource file to disk.
Public Sub WriteResourceToFile(ByVal resourceName As String, ByVal fileName As String)
Using resource = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)
Using file = New FileStream(fileName, FileMode.Create, FileAccess.Write)
resource.CopyTo(file)
End Using
End Using
End Sub
Example of usage:
WriteResourceToFile("yournameSpace.conversion.dll", BinPath & "conversion.dll")

Telling VBA to look at 64-bit registry view

I want to assign an ArrayList to a variable.
Sub Create_ArrayList()
Dim arrL As Object
'Creating an ArrayList, option 1 - fails
Set arrL = CreateObject("System.Collections.ArrayList")
'Creating an ArrayList, option 2 - fails
Set arrL = GetObject("New:{6896B49D-7AFB-34DC-934E-5ADD38EEEE39}")
End Sub
Both options fail :
Run-time error '-2146232576 (80131700)':
Automation error
I found that the CLSID exists in the registry in HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
The Office is 32-bit and Windows is 64-bit. I am not familiar with the registry, but it seems from what I have read, that 32-bit Office is trying to find ArrayList in some 32-bit Windows location (view), while it actually is in 64-bit location. Can it be the case? How to make the application get ArrayList from the correct location?
It seems, to access an alternative registry view, I should somehow use the flag KEY_WOW64_64KEY (value 0x0100), but the examples that I found are too big to comprehend (eg1, eg2). I don't want to edit registry data, I only want to tell VBA that I am using 64-bit Windows, so the object that I need is to be found somewhere else than it expects.

Why doesn't code work in VB.net, but works in VBA; GetObject

VBA code works great:
Sub testVBA()
Dim wb As Object ' Lotus123.Document
Set wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
VB.net code fails:
Sub TestVBNet()
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3", "Lotus123.Workbook")
End Sub
In VB.net I get a FileNotFoundException: "File name or class name not found during Automation operation."
As I can run it from VBA that means the file exists and that the class name exists. So why doesn't it work and how can I fix it in VB.net.
EDIT: I guess I'm not sure how to start diagnosing this: Obviously the class exists on my computer but somehow VB.net doesn't manage to find it. Maybe VB.net uses a different method to activate the class. Maybe a registry entry is missing. I am glad for any suggestions.
Edit 2: I also tried using CreateObject and got this error: "Cannot create ActiveX component." Not unexpected.
For some reason VB.net cannot find the class name "Lotus123.Workbook" so I tried getting the file without the class name and it works fine in XP.
Dim wb As Object ' Lotus123.Document
wb = GetObject("S:\Temp\T\0375D.WK3")
EDIT: In Win8 64bit the above doesn't work; just hangs.
The code below works in XP 32 bit as well as in Win8 64 bit. I checked with process monitor what is happening under the hood. CreateObject checks for the CLSID in the registry using the given object. Then it looks up the necessary info using the CLSID.
Public Shared Function GetLotusWB(ByVal sFile As String) As Object
'HKCU takes precedence if exists
'HKCU\Software\Classes\Lotus123.Workbook\CLSID
'HKCU\Software\Classes\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}
'normally this is used because Lotus123 doesn't create HKCU entries
'HKCR\Lotus123.Workbook\CLSID = {29130007-2EED-1069-BF5D-00DD011186B7}
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\InprocHandler32 = ole32.dll
'HKCR\CLSID\{29130007-2EED-1069-BF5D-00DD011186B7}\LocalServer32 = C:\Lotus\123\123w.exe
'using object as that sometimes works better
Dim LotusObj As Object = CreateObject("Lotus123.Workbook")
'get application
'need a reference to Lotus 123 else declare as Object
Dim LotusApp As Lotus123.Application = LotusObj.Application
'FAILS: LotusApp.Visible = True
'open file; also works fine As Lotus123.Document
Dim ldoc As Object = LotusApp.OpenDocument(sFile)
'visible and activate (must declare as Object else gives exception)
Dim appObject As Object = ldoc.Application
appObject.Visible = True
ldoc.Activate()
Return ldoc
End Function
This works great because it creates the "Lotus123.Workbook" which is used to get the application object.
Load the file into an Excel workbook. It should be able to convert the lotus123 workbook on the fly.
First of all, check to make sure your inclusions (I think under Tools menu, includes or references or something like that) include the library that references Lotus123.Document. Chances are it's in the "Microsoft Excel 14.0 Object Library" or similar.
I've heard it said that VB is not VBA!

Is it possible to specify a location in a user's home directory for a shared library in VBA (in Office for Mac)?

I'm currently using the VBA code similar to the following to specify a location of a shared library for use in communicating (passing a string) from an Office application to a desktop application. The VBA code/macros will need to exist in an add-in (.ppa).
Private Declare Sub sharedLibPassString CDecl Lib "/Users/myUserName/Library/Application Support/myCompanyName/MySharedLib.dylib" Alias "PassString" (ByVal aString As String)
In code from a VBA Macro, I then can do the following.
Call sharedLibPassString(myString)
I've got the communication working, but I'd like to replace the /Users/myUserName/ part with the current user's home directory. Normally on a Mac, you'd specify ~/Library/Application Support/..., but the ~/ syntax doesn't work, producing a "File not found" runtime error.
I discovered that using the following Environment Variable method gets me the ~/ location that I need:
Environ("HOME")
However, I don't see a way to make this part of the CDecl Lib statement, since, as far as I can tell, Environ is evaluated at runtime.
Is there any way to specify a location of a shared library in the user's home directory (~/) in VBA?
Here are a few notes about my environment/approach:
I'm using a Mac, though I believe if there is a solution it would be similar on a PC.
I don't believe it shouldn't matter, but the Office application I'm using is PowerPoint (2011).
The reason I'm trying to access an area inside of the Application Support directory, instead of the default location for shared libraries is because I'd like the Desktop application to place the shared library in a location without an installer, and without requiring a user's or administrator's privileges. If there is a better solution or location to accomplish the same task, this would be very helpful as well.
Sorry for giving a long response, I just wanted to make sure I explained this fairly well.
From this page (Anatomy of a Declare Statement) we read that
The Lib keyword specifies which DLL contains the function. Note that
the name of the DLL is contained in a string within the Declare
statement.
(emphasis added)
From experimentation, the VBE scolds me if I try to give anything but a string constant.
The only work around that I'm aware of requires rewriting the string constant at runtime.
Here is an example of how this could be done: Let's say your delaration statement is in Module1 in your current project, and that you deliberately wrote the declaration in this format at the top of your module:
Private Declare Sub sharedLibPassString CDecl Lib _
"/Users/myUserName/Library/Application Support/myCompanyName/MySharedLib.dylib" _
Alias "PassString" (ByVal aString As String)
You can access that module via code through this (requires permissions to VBA in trust Center listed under Developer Macro Settings):
Dim myModule
set myModule = ActivePresentation.VBProject.VBComponents("Module1").CodeModule
Once you've gained the CodeModule, you can replace the 2nd line directly:
myModule.ReplaceLine 2, Environ("HOME") & " _"
Mission accomplished!
If you do this, you will need to update the path prior to attempting to call your declared sub. There must be a break in execution that allows VBA to recognize the change. Also, you will not be able to modify the code while in break mode.
Now if I were doing this, I'd want to make sure I replace the right line, to not break code. You can check the contents of the 2nd line by calling this myModule.Lines(2,1) which will return the string value of the line.
However, here is a more robust solution that will find the correct line and then replace it (assumes myModule has already been defined as listed above):
Dim SL As Long, EL As Long, SC As Long, EC As Long
Dim Found As Boolean
SL = 1 ' Start on line 1
SC = 1 ' Start on Column 1
EL = 99999 ' Search until the 99999th line
EC = 999 ' Search until the 999th column
With myModule
'If found, the correct line will be be placed in the variable SL
'Broke search string into two pieces so that I won't accidentally find it.
Found = .Find("/Users/myUserName/Library/Application Support/myCompanyName/" & _
"MySharedLib.dylib", SL, SC, EL, EC, True, False, False)
If Found = True Then
'Replace the line with the line you want, second paramater should be a string of the value.
.ReplaceLine SL, Environ("HOME") & " _"
End If
End With
I don't have a Mac, so this is an incomplete answer and I don't know if it will help, but it's a couple of ideas.
I know on Windows, VB doesn't load an external library until you first try to call a function declared with it, and if you specify only the filename in the declare statement, it will use the system path to look for it. Once I did the same thing you are doing, loading a library from a dynamic path, by specifying only a filename, then making a system API call to set the current working directory to the directory of the library before loading it. Changing the PATH environment variable would probably also work.
Second idea: you could hard-code the path to a filename in the /tmp directory; then automatically copy the desired library to that location before loading it. Watch out for the file being in use by another process, but that's only an error if it is a different version of the file to the one that you want.

vb.net application works with files dragged onto the exe, but crashes if there's a space in the file's path

I'm developing an application in vb.net. You drag any type of file onto the exe, and a window pops up with some options for the file, then it saves the file to a different location, works some SQL magic, etc. It works great for the most part.
The only issue I've found is that if the path of the file contains any spaces, the application will crash immediately with the error window: http://i.stack.imgur.com/mVamO.png
I'm using:
Private filename as String = Command$
This is located right inside my form's class declaration, not within a sub/function.
Without this line, my program runs fine (although useless, without accessing the file).
I've also tried (I think this was it, I don't have the code with me at the moment):
Private filename as String = Environment.CommandLine
And it had the same issue.
So, in vb.net, is there a way to drag a file onto an exe and use that path name, even if there are spaces in the path name?
Windows will put double-quotes around the passed command line argument if the path to the dragged file contains spaces. Trouble is, you are using an ancient VB6 way to retrieve the argument, you see the double quotes. Which .NET then objects against, a double quote is not valid in a path name. Use this:
Dim path = Command$.Replace("""", "")
Or the .NET way:
Sub Main(ByVal args() As String)
If args.Length > 0 then
Dim path = args(0)
MsgBox(path)
'' do something with it..
End If
End Sub
If possible, do post your code as it's pretty much anything that can go wrong. Normally, after receiving CommandLine Arg, I would try to use a System.IO.File wrapper and use built-in mechanisms to verify file and then proceed with it further using IO as much as possible. If you are attempting to directly manipulate the file, then the spaces might become an issue.
In addition, there is a way to convert long file path + name to old DOS’s 8.3 magical file path + name. However, I’ll go into R&D after I see what you are doing in code.