0

I have a .NET assembly which is built to expose COM interop. A simple code sample is:

namespace ClassLibraryWithUri
{
    [ComVisible(true)]
    public class UriGenerator
    {
        public UriGenerator()
        { }

        public Uri GetUri()
        {
            return new Uri("http://www.google.com");
        }
    }
}

then I try to consume within Visual Basic script:

dim myObj
Set myObj = WScript.CreateObject("UriGenerator.UriGenerator")
Dim myUri
Set myUri = myObj.GetUri()
Dim url 
Set url = myUri.ToString()
WScript.Echo(url)

but I get error: Microsoft VBScript runtime error: Object required, at the line Set url = myUri.ToString()

My question is: why? Are Uri supported? I have not found anything on google and on Stack OVerflow Am I doing something wrong?

Thanks for your help.

peval27
  • 1,239
  • 2
  • 14
  • 40
  • It will be because the `Uri` class is not exposed through com, try converting the `Uri` to a `string` before returning it. – user692942 Mar 21 '17 at 17:30
  • thanks for the answer. That's a pity. I was replacing string with Uri to provide a better experience to .NET developers. I guess the same will apply if I try from C++? Is there a documentation of what types are COM interop? – peval27 Mar 21 '17 at 17:34
  • The best way I've found to check is look in the registry under `HKEY_CLASS_ROOT` for the associated `progid` used by `CreateObject` using `regedit.exe`. – user692942 Mar 21 '17 at 17:37
  • sorry, `progid` associated to what? – peval27 Mar 21 '17 at 17:42
  • This should help - [A: Error ASP 0177: 8007007e Server.CreateObject fails for COM DLL](http://stackoverflow.com/a/35985827/692942) *(the answer is related to com registration in IIS but com is registered in the same way regardless of whether it is used by web applications or plain old client script)*. – user692942 Mar 21 '17 at 17:49
  • 1
    All built-in reference types are not COM-visible (special case for string). Imagine .net core on linux for example - there is no COM at all. Here is the mapping of the supported types: https://msdn.microsoft.com/en-us/library/sak564ww(v=vs.100).aspx – Nikolay Mar 21 '17 at 23:20
  • @Nikolay thanks for the reference! – peval27 Mar 22 '17 at 10:08

1 Answers1

0

I believe you'll have to create a COM wrapper class for .NET's Uri class in order for this to work. I was curious as to the effort it would take, and I wrote the following. This is more or less what you'll have to do. Everywhere you want to use a URI in your public interface, use IUri as defined below.

// COM doesn't have constructors, so in order to create Url objects from COM clients, you need a factory
[ComVisible(true)]
public interface IUriFactory
{
    IUri Create1(string uriString);
    IUri Create2(string uriString, UriKind uriKind);
    IUri Create3(IUri baseUri, string relativeUri);
    IUri Create4(IUri baseUri, IUri relativeUri);
}

[ComVisible(true)]
public interface IUri
{
    string GetComponents(UriComponents components, UriFormat format);
    string GetLeftPart(UriPartial part);
    bool IsBaseOf(IUri uri);
    bool IsWellFormedOriginalString();
    IUri MakeRelativeUri(IUri uri);
    string ToString();
    string AbsolutePath { get; }
    string AbsoluteUri { get; }
    string Authority { get; }
    string DnsSafeHost { get; }
    string Fragment { get; }
    string Host { get; }
    UriHostNameType HostNameType { get; }
    bool IsAbsoluteUri { get; }
    bool IsDefaultPort { get; }
    bool IsFile { get; }
    bool IsLoopback { get; }
    bool IsUnc { get; }
    string LocalPath { get; }
    string OriginalString { get; }
    string PathAndQuery { get; }
    int Port { get; }
    string Query { get; }
    string Scheme { get; }
    string[] Segments { get; }
    bool UserEscaped { get; }
    string UserInfo { get; }
}

[ComVisible(true)]
public enum UriKind
{
    RelativeOrAbsolute = System.UriKind.RelativeOrAbsolute,
    Absolute = System.UriKind.Absolute,
    Relative = System.UriKind.Relative,
}

[ComVisible(true)]
public enum UriComponents
{
    Scheme = System.UriComponents.Scheme,
    UserInfo = System.UriComponents.UserInfo,
    Host = System.UriComponents.Host,
    Port = System.UriComponents.Port,
    Path = System.UriComponents.Path,
    Query = System.UriComponents.Query,
    Fragment = System.UriComponents.Fragment,
    StrongPort = System.UriComponents.StrongPort,
    NormalizedHost = System.UriComponents.NormalizedHost,
    KeepDelimiter = System.UriComponents.KeepDelimiter,
    SerializationInfoString = System.UriComponents.SerializationInfoString,
    AbsoluteUri = Fragment | Query | Path | Port | Host | UserInfo | Scheme,
    HostAndPort = StrongPort | Host,
    StrongAuthority = HostAndPort | UserInfo,
    SchemeAndServer = Port | Host | Scheme,
    HttpRequestUrl = SchemeAndServer | Query | Path,
    PathAndQuery = Query | Path,
}

[ComVisible(true)]
public enum UriFormat
{
    UriEscaped = System.UriFormat.UriEscaped,
    Unescaped = System.UriFormat.Unescaped,
    SafeUnescaped = System.UriFormat.SafeUnescaped,
}

[ComVisible(true)]
public enum UriPartial
{
    Scheme = System.UriPartial.Scheme,
    Authority = System.UriPartial.Authority,
    Path = System.UriPartial.Path,
    Query = System.UriPartial.Query,
}

[ComVisible(true)]
public enum UriHostNameType
{
    Unknown = System.UriHostNameType.Unknown,
    Basic = System.UriHostNameType.Basic,
    Dns = System.UriHostNameType.Dns,
    IPv4 = System.UriHostNameType.IPv4,
    IPv6 = System.UriHostNameType.IPv6,
}

[ComVisible(true)]
public class UriFactory : IUriFactory
{
    public IUri Create1(string uriString){ return new Uri(new System.Uri(uriString)); }
    public IUri Create2(string uriString, UriKind uriKind) { return new Uri(new System.Uri(uriString, (System.UriKind) (int) uriKind)); }
    public IUri Create3(IUri baseUri, string relativeUri) { return new Uri(new System.Uri(((Uri) baseUri).Inner, relativeUri)); }
    public IUri Create4(IUri baseUri, IUri relativeUri) { return new Uri(new System.Uri(((Uri) baseUri).Inner, ((Uri) relativeUri).Inner)); }
}

[ComVisible(true)]
internal class Uri : IUri
{
    internal readonly System.Uri Inner;
    internal Uri(System.Uri inner) { Inner = inner; }
    public string GetComponents(UriComponents components, UriFormat format) => Inner.GetComponents((System.UriComponents) components, (System.UriFormat) format);
    public string GetLeftPart(UriPartial part) => Inner.GetLeftPart((System.UriPartial) part);
    public bool IsBaseOf(IUri uri) => Inner.IsBaseOf(((Uri) uri).Inner);
    public bool IsWellFormedOriginalString() => Inner.IsWellFormedOriginalString();
    public IUri MakeRelativeUri(IUri uri) => new Uri(Inner.MakeRelativeUri(((Uri) uri).Inner));
    public string AbsolutePath => Inner.AbsolutePath;
    public string AbsoluteUri => Inner.AbsoluteUri;
    public string Authority => Inner.Authority;
    public string DnsSafeHost => Inner.DnsSafeHost;
    public string Fragment => Inner.Fragment;
    public string Host => Inner.Host;
    public UriHostNameType HostNameType => (UriHostNameType) (int) Inner.HostNameType;
    public bool IsAbsoluteUri => Inner.IsAbsoluteUri;
    public bool IsDefaultPort => Inner.IsDefaultPort;
    public bool IsFile => Inner.IsFile;
    public bool IsLoopback => Inner.IsLoopback;
    public bool IsUnc => Inner.IsUnc;
    public string LocalPath => Inner.LocalPath;
    public string OriginalString => Inner.OriginalString;
    public string PathAndQuery => Inner.PathAndQuery;
    public int Port => Inner.Port;
    public string Query => Inner.Query;
    public string Scheme => Inner.Scheme;
    public string[] Segments => Inner.Segments;
    public bool UserEscaped => Inner.UserEscaped;
    public string UserInfo => Inner.UserInfo;
}
Michael Gunter
  • 12,528
  • 1
  • 24
  • 58
  • that's definitely a possibility, thanks for your help – peval27 Mar 22 '17 at 10:09
  • Thanks for asking. I opted to provide an alternate method which returns string (using Uri.ToString()). The method which returns Uri is not COM visible. It seemed easier than adding lot of code. – peval27 Apr 07 '17 at 15:19