COM/OLE can connect by GUID but not by class string - com

I have an MFC application that exposes a bunch of OLE objects for the application, and open documents.
I can connect to the server using the GUID of the application class (e.g.: in ruby for windows: WIN23OLE.new('{12345678-1234-1234-1234-12345678}')) but when I try to connect using the class name WIN32OLE.new('MyApp.Application'), it always fails with "Invalid Class String" error (HRESULT error code:0x800401f3). The same thing happens
There are no errors returned by the OLE initialisation in the MFC app, and once a connection is made by GUID, then it works fine.
I'm just really curious why the class string approach isn't working. Any ideas?

The class string is called a ProgID (short for programmatic ID), and it's really just a human-readable version of the ClassID. ProgIDs are stored in the registry under HKEY_CLASSES_ROOT, for example picking one at random from my registry:
HKEY_CLASSES_ROOT\Microsoft.XMLDOM
Under this key there is another key called CLSID:
HKEY_CLASSES_ROOT\Microsoft.XMLDOM\CLSID
And inside that key is a REG_SZ value which contains the ClassID:
{2933BF90-7B36-11D2-B20E-00C04F983E60}
So basically the way it works is that COM is going to try to find the CLSID in the registry under the specified ProgID. I'm guessing it's not there or it's inaccessible somehow. If you want to figure it out for sure, crack open REGEDIT.EXE and take a look to see if the expected registry settings are there. If they aren't, there's your answer about why it's not working (for some reason the registration of the COM component isn't creating the ProgID keys).
If the settings are there, I would recommend running Process Monitor (sysinternals.com) and set up some registry filters to see what is happening when the registry is scanned for that ProgID.
Here's a little more info about ProgIDs:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd542719(v=vs.85).aspx

Related

Custom DLL to avoid the IE web browser control for "unsafe controls" in a tightly controlled and regulated environment

Scenario:
My company has a legacy (read that as 32 bit) windows form application that will be around for quite some time in the future. This application uses an embedded web browser control that is supplied pages that are contained within the database that it maintains. It was built like this so we could extend/modify as needed. I say this so that I can validate that security is not a concern. Only the application and developers with the correct tools have access to the pages or database. The application is only available inside the office.
There are some processes that I need to accomplish using ActiveX objects that are embedded within the pages/application. One of the biggest and most annoying thing that happens is the ActiveX security warning when I got to create instances of things like “scripting.filesystemobject”. Example:
Set oFSO = CreateObject("Scripting.FileSystemObject")
My solution is to create a DLL that is installed locally on each machine that needs access to the extended functions, have the all the functions (whole DLL ??) marked as safe so that the web browser control does not present the security warning. I have been searching using google and came across very few examples, and all of which are in C# which is not my strongest language.
I’ve had to convert from C to Vb.Net visual basic to get what I have now. When I go to register my DLL, I get the following error message:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319>regasm
Z:\VBNet2017\APIInternal\APIInternal\bin\Debug\APIinternal.dll /tlb
Microsoft .NET Framework Assembly Registration Utility version 4.8.4084.0
for Microsoft .NET Framework version 4.8.4084.0
Copyright (C) Microsoft Corporation. All rights reserved.
Types registered successfully
RegAsm : error RA0000 : Type 'APIInternal.API.Accupay' has an invalid default COM
interface: 'APIInternal.API.Accupay'
UPDATE: Thank you Hans; the error is gone. I've also made some changes in the source code; I changed the ProgID to something that closely resembles where and what this is for. I'm still having issues in creating the object in VB Script.
This is the output from the current version of the code. This is the code, stripped down for clarity:
Option Strict On
Imports System.Runtime.InteropServices
Imports System.IO
Namespace API
Public Interface IAccupay
<DispId(1)>
Function GetFiles(ByVal Folder As String) As List(Of String)
End Interface
<Guid("8B4B5CEF-8B3A-49A1-9053-E909F82D9E73"),
ProgId("AddIn.Accupay"), ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(GetType(IAccupay)), ComVisible(True)>
Public Class Accupay
Implements IAccupay
Private Function GetFiles(Folder As String) As List(Of String) Implements IAccupay.GetFiles
Return Directory.GetFiles(Folder).ToList
End Function
End Class
I have tried just about every combination of ProgID, Name space, Interface name and class name to get this error to go away without any luck. I do know there are other elements that need to be addressed or added, such as error trapping and, if I’m not mistaken, how to actually implement the ObjectSafetyOption which I still don’t know how to do.
I have been using the Guide at the bottom of this article:
Is it possible to mark an ActiveX object as safe so that IE settings need not be changed?, the second answer, but I haven’t had any success.
Please, can someone point me in the right direction, maybe show me what’s wrong with the code that I have and how to physically implement the ObjectSafteyOption that is needed for the web control. Links, additional reading, code examples or comments on how to get this fixed and working would really be appreciated.
Thank you for reading and any help you send my way, Fred
PS: If you need more information, or have a better solution, please don’t hesitate to reply or comment.
UPDATE:
With the code that I have now, I am able to access the DLL in VB.Net visual basic:
Imports System
Imports APIInternal.API
Module Program
Sub Main(args As String())
Dim API As New Accupay
Dim FileList = API.GetFiles("C:\Windows\")
For Each Item As String In FileList
Console.WriteLine(Item)
Next
End Sub
End Module
However, I still can't seem to get the correct calling for a VB Script/html page:
Set Test = CreateObject("Test.Accupay")
Which returns the VB Script error "ActiveX Component can't create object: Test.Accupay or any other iteration of the parts of the name that I tried. I think part of this is that I don't understand how the creation of the project leads to the creation of the object in a com base environment like VB Script.
Fred
The answer to this problem is two fold: You must target the correct platform (X86) AND use the 32 bit version of regasm. Once I realized this was the issue, I was able to create the DLL and use it's functions in the Web Browser control without the active X warning. One example is I can now open the default browser (in this case, NOT IE/EDGE) from a link within the WB Control and another is to get the contents of a folder for further processing within the WB page.

My registered COM object cannot be ran as InProc

I am using pywin32 to register a COM object. The COM object get registered and it is avaialable in the registry editor at Computer\HKEY_CLASSES_ROOT\CLSID{B7B60366-B784-451F-BD6A-E7E733DB4E63}.
The problem is that the path in InProc32 includes only pythoncom37.dll. The only way can make the object work is to rename the inproc folder, so that it searches for LocalServer by default.
I believe several problems in the behavior of this object are connected to it not being ran as inproc, hence I really want to get it to run as inproc.
Any Ideas why this is the case? What can I do to change it?

Calling COM component method from IBM Notes 9 fails

When calling a 32 bit COM component method registered in sysWOW64 fails with an error message:
"type mismatch in method OleVarToLsVal, Unknown found, Unknown
expected"
Its win7 64 bit, but the Notes client is installed by default as a 32 bit application. The code looks like:
dim c as Variant
dim n as Variant
set c = createobject("MSWC.counters")
n = c.Get("xx")
When debugging the call, the object is set and testable with "isObject(c)", (although you can't inspect each method/property in detail in LotusScript debug).
The method is supposed to return a primitive long. I've tried setting n as long, clng-ing the values, cstr-ing the values, the parameter, strconv the parameter, using a variable for the parameter, all to no avail.
The exact same code run by WScript VBS host (in syswow64) runs the code as expected.
So, does anybody know:
If Notes 9 COM value marshalling is working for any components?
Is Notes 9 COM set to recognize the 'wow64' alternate 32 bit registry
Are there some COM related marshalling settings somewhere in the registry I can check (if so what/where are they)?
Is there some setting to tell Notes to use 32 bit components (like IIS 32bit compatibility option)
Is there anything I need to do or could do in the main OS to 'redirect or configure' COM
Or is Notes just broken again and nobody cares?
Any help appreciated - Thanks.
The easiest and probably most productive way to solve this would be to open a PMR with IBM. They should be able to answer this quite quickly.
Well, 7 years on (and seriously obsolete!) just an update for anybody looking for an answer... There are a couple of Notes settings needed and not all COM/Active-X componenets or data types are supported by LotusScript, so even if Notes is setup correctly, you still may not be able to acces/use any specific component or some methods in the component.
The user must be allowed to run unrestricted agents/code in the 'Sign or run unrestricted methods and operations:' in the security section of the server(s) document.
The Notes client execution control list ('ECL') must allow access to 'External programs' either by default or to the code-signer. An ECL warning box will ask the user to continue if the external access has not been granted.
If you try to execute an unsupported method or unsupported data type, then further errors will be issued either by LotusScript or COM/Active-X error reporting. The Notes developer help file for 'CreateObject' gives a bit more detail about unsupported data types:
LotusScript does not support identifying arguments for OLE methods or properties by name rather than by the order in which they appear, nor does LotusScript support using an OLE name by itself (without an explicit property) to identify a default property.
Results are unspecified for arguments to OLE methods and properties of type boolean, byte, and date that are passed by reference. LotusScript does not support these data types.
Relying on the 'default property' to access a default method is a common mistake and requires you to pay extra attention to the component details. It is easy to assume the component is not working, but in fact you're just not using it properly.
One way to test this is to try to open a common object available on all Windows machines (maybe others?) maybe 'FileSystemObject' (FSO) or VbScript 'regExp' component. If these work, you can build on that. Getting the 32/64bit registration correct for your client install is another element to test/get right.
For my issues, I suspect that I was using unsupported methods or data types and having used COM/Active-X in Notes occasionally, its all worked ok in general.

How to find COM object given only server.create instance?

I have inherited a classic ASP application and looking through the code I see a custom COM object reference
Server.CreateObject("DBaseManager.Recordset")
Now, unfortunately looking through to source code there are no .dlls provided so therefore the COM dll must still exist on the live server.
In order to get the code working on my Dev Server I need to get a copy of the dll so I can register it on my Dev server.
Does anyone have any recommendations about how I might be able to find the COM dll that makes the above call?
Thanks and best wishes
Mark
Registry search - under HKEY_CLASSES_ROOT, locate the DBaseManager.Recordset key. Under that key, there should be a CLSID key, with a default value containing a guid.
Now, search for that guid under HKEY_CLASSES_ROOT\CLSID. There should be a subkey under that key called InprocServer32 (if it's an in-process COM library), which in turn should have a default value giving the path to the DLL.
Of course, if the DLL in question is part of a larger product or SDK, merely installing the dll on your dev server may not be sufficient. You may have to locate and install the whole product/SDK in order for it to actually work on another machine.

Tracking COM object error in application

I was using an application and it was working perfect. After some months of not using it, I tried to run it and it doesn't work. It shows a message box saying that it cannot instance a COM object.
Do any know how to track errors in COM objects?
You can use ProcessMonitor and try to find the registry key that may be incorrect.
The other option is to use http://www.moduleanalyzer.com, it intercepts CoCreateInstance showing all created COM objects and the return values.
Run Depends tool on COM object DLL to verify it has all the necessary dlls, re-register the COM dll/exe.
Any HRESULTS from debugging/logs? Any changes in apartment models?
You cannot change the apartment type once you've set one. So if the object cannot use one of the models and you try to CoCreate it, it will fail. That's why you never call CoInit from inside DLL main thread.