views:

36

answers:

1

I'm working on a project that's a .NET extension to a rather large classic ASP project, using a lot of C++ COM objects that have been around in our code base forever. Unfortunately, there's a lot of hack-ish code on the C++ side, and I fear that I'm not experienced enough to work out the problem I've encountered.

In a nutshell, I can instantiate the COM object in question and Visual Studio tells me that I should be able to make calls to its "methods" (in quotes because they're actually exposed as parameterized properties). However, any method I try to call gives me the error "Indexed property 'CoreAspLib.IComUser.LoginUser' has non-optional arguments which must be provided." The thing is, I'm using the exact same parameters that were used back in the classic ASP world, and even the IntelliSense help for the properties tells me I'm using the right set of parameters. For example, the "LoginUser" property's signature is "dynamic IComUser.get_LoginUser(string username, string password)" and I'm calling it with two strings, but I still get the "non-optional arguments" error.

Here's some relevant code - first, the class where I'm trying to make the method call. CComUtils is a helper class that just takes the COM's GUID and a reference to the target object, creates an instance of the desired COM object, and assigns the target reference to the new object.

public static class CurrentUser
{
    public static void Authenticate(string userName, string password)
    {
        CoreAspLib.ComUser userObject = Cache.Request<CoreAspLib.ComUser>("UserComObject");

        if (userObject == null) {
            Guid userGuid = new Guid("BF748C0A-450D-4EAF-8C39-A36F6B455587");
            CComUtils.CreateCOMInstance(userGuid, ref userObject);
            Cache.Request("UserComObject", userObject);
        }

        var result = userObject.LoginUser(sUserName:"foo", sPassword:"bar");

        if (result.Failed) {
            throw (new System.Security.Authentication.InvalidCredentialException("Bad username or password".tr()));
        } else {
            FormsAuthentication.SetAuthCookie(userName, false);
        }
    }
}

Here's the actual method signature from the COM object's source:

STDMETHODIMP CComUser::get_LoginUser(BSTR bsUserName, BSTR bsPassword, IDispatch ** pResult)

Here's the interface definition from the IDL:

[
    object,
    uuid(1F4D8A57-BDE1-4D47-A9BC-5F541A0ED184),
    dual,
    nonextensible,
    helpstring("IComUser Interface"),
    pointer_default(unique)
]
interface IComUser : IDispatch{
    [propget, id(1), helpstring("property LoginUser")] HRESULT LoginUser([in] BSTR sUserName, [in] BSTR sPassword, [out, retval] IDispatch ** pResult);
    [propget, id(2), helpstring("property m_nUserID")] HRESULT m_nUserID([out, retval] ULONG* pVal);
    [propget, id(3), helpstring("property m_nUserRightsMask")] HRESULT m_nUserRightsMask([out, retval] ULONG* pVal);
    [propget, id(4), helpstring("property SetPassword")] HRESULT SetPassword([in] BSTR sPassword, [in,optional] VARIANT nUserID, [out, retval] IDispatch ** pResult);
    [propget, id(6), helpstring("property VerifyLdapCredentials")] HRESULT VerifyLdapCredentials([in]BSTR sUserName, [in]BSTR sPassword, [in]BSTR sLdapServer,[in]ULONG nLdapPort,[in]BSTR sAuthorizeDn,[in]VARIANT_BOOL bSecure, [out, retval] IDispatch ** pResult);
    [propget, id(7), helpstring("property CreateUser")] HRESULT CreateUser([in] BSTR sUserName, [in] BSTR sPassword, [in]ULONG nUserRightsMask, [in] ULONG nAuthenticationType, [in] ULONG nHomeGroupID, [in] ULONG nLanguageID, [out, retval] IDispatch** pVal);
    [propget, id(8), helpstring("property m_nLanguageID")] HRESULT m_nLanguageID([out, retval] ULONG* pVal);
};

And finally, here's the coclass definition:

[
    uuid(BF748C0A-450D-4EAF-8C39-A36F6B455587),
    helpstring("ComUser Class")
]
coclass ComUser
{
    [default] interface IComUser;
};

What should I do?

Edit: I should mention that I wasn't initially using named parameters; I just tried that in my efforts to make the error go away.

+1  A: 

Hmm, your code suggest that you do in fact use C# 4.0 since you're using named parameters. That version does support indexed properties in COM interop scenarios. Indexer syntax however requires [square brackets]. No idea if it still works with more than one argument, the examples all have just one. Try this first:

 var result = userObject.LoginUser["foo", "bar"];

Or punt the problem with:

 var result = userObject.get_LoginUser("foo", "bar");

Please let us know what you find out, I'm interested to learn if indexed properties with multiple arguments is supported.

Hans Passant
Man, you nailed it first try. I switched the parentheses to square brackets and immediately the error went away.
Isochronous
Woohoo [pumps fist]. Hopefully I'll get a helpful vote out of it some day. I'll help, thanks for reporting back.
Hans Passant
yeah, I'd totally vote you up, but I've got no reputation yet.
Isochronous