tags:

views:

351

answers:

2

Using this as reference, I'm trying to create an asynchronous pluggable protocol that is only temporarily available to my app (and not registered systemwide). I'm using CoInternetGetSession and then calling RegisterNameSpace to do it. However, when I make the call to RegisterNameSpace I get an AccessViolation exception: Attempting to read or write protected memory.

Any idea what's going on?

My code looks like this:

[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000001-0000-0000-C000-000000000046")]
[ComVisible(true)]
public interface IClassFactory
{
    void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject);
    void LockServer(bool fLock);
}

/* Custom class to act as a class factory that create's an instance of the protocol */
[Guid("0b9c4422-2b6e-4c2d-91b0-9016053ab1b1")]
[ComVisible(true),ClassInterface(ClassInterfaceType.AutoDispatch)]
public class PluggableProtocolFactory : IClassFactory
{
    public Type AppType;
    public PluggableProtocolFactory(Type t)
    {
        this.AppType = t;
    }
    public void CreateInstance(IntPtr pUnkOuter, ref Guid riid, out IntPtr ppvObject)
    {
        riid = ProtocolSupport.GetGuid(this.AppType);
        IInternetProtocol p = Activator.CreateInstance(this.AppType) as IInternetProtocol;
        ppvObject = Marshal.GetComInterfaceForObject(p, typeof(IInternetProtocol));
    }

    public void LockServer(bool fLock)
    {
        var b = fLock;
    }

}

[ComVisible(true)]
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("79eac9e7-baf9-11ce-8c82-00aa004ba90b")]
public interface IInternetSession
{
    void CreateBinding(); // Not Implemented
    void GetCache(); // Not Implemented
    void GetSessionOption(); // Not Implemented
    void RegisterMimeFilter([MarshalAs(UnmanagedType.Interface)] IClassFactory pCF, ref Guid rclsid, [MarshalAs(UnmanagedType.LPWStr)] string pwzType);
    void RegisterNameSpace([MarshalAs(UnmanagedType.Interface)] IClassFactory pCF, ref Guid rclsid, [MarshalAs(UnmanagedType.LPWStr)] string pwzProtocol,
                           UInt32 cPatterns, [MarshalAs(UnmanagedType.LPArray,ArraySubType=UnmanagedType.LPWStr)] string[] ppwzPatterns, UInt32 dwReserved);
    void SetCache(); // Not Implemented
    void SetSessionOption(); // Not Implemented
    void UnregisterMimeFilter(IClassFactory pCF, [MarshalAs(UnmanagedType.LPWStr)] string pwzType);
    void UnregisterNameSpace(IClassFactory pCF, [MarshalAs(UnmanagedType.LPWStr)] string pwzProtocol);
}

[ComVisible(false)] public interface IComRegister
{
    void Register(Type t);
    void Unregister(Type t);
}

[ComVisible(false), AttributeUsage(AttributeTargets.Class, AllowMultiple=true) ] 
public class AsyncProtocolAttribute : Attribute, IComRegister
{
    public string Name;
    public string Description;

    [DllImport("urlmon.dll",PreserveSig=false)]
    public static extern int CoInternetGetSession(UInt32 dwSessionMode /* = 0 */, ref IInternetSession ppIInternetSession, UInt32 dwReserved /* = 0 */);

    public void Register(Type t)
    {
        IInternetSession session = null;
        CoInternetGetSession(0, ref session, 0);
        Guid g = new Guid("79EAC9E4-BAF9-11CE-8C82-00AA004BA90B");
        session.RegisterNameSpace(new PluggableProtocolFactory(t), ref g, this.Name, 0, null, 0);

    }

The CreateInstance method in PluggableProtocolFactory never gets called. (A break point there never gets hit) so something else is happening inside the RegisterNameSpace method.

I tried running both as an administrator and a normal user. Same error in both occasions.

A: 

You're calling the constructor of your PluggableProtocolFactory in your RegisterNameSpace method, but not the CreateInstance method. I think that's where your problem lies, there's never an instance of IInternetProtocol being created to be passed to your method.

Tony
The RegisterNameSpace method takes an IClassFactory as its first parameter. I assume that the RegisterNameSpace method will call CreateInstance itself when its ready to do so? Isn't that the purpose of a ClassFactory interface?
HS
No it will not call it by itself, you have to call it in the constructor of PluggableProtocolFactory or call it in your Register method. You should review what an interface is for and what it does.
Tony
The whole point of having an interface is for the consuming method to be able to use it. If the consuming method is not going to make use it, why would it be required to declare it in the interface at all? Sorry, that just makes no sense. Here's a thought experiment: Let's say I do call CreateInstance manually. What parameters would I pass to it? What would I do with the value it returns? How would RegisterNameSpace make use of it? And if RegisterNameSpace doesn't make use of it, then how would it matter if I actually called it or not?
HS
A: 

OK, figured it out: The declaration of the IInternetSession interface was wrong:

Here's a better one I picked up from monoblog:

[ComVisible(true), Guid("79eac9e7-baf9-11ce-8c82-00aa004ba90b"),InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInternetSession
{
    [PreserveSig]
    int RegisterNameSpace(
        [In] IClassFactory classFactory,
        [In] ref Guid rclsid,
        [In, MarshalAs(UnmanagedType.LPWStr)] string pwzProtocol,
        [In]
            int cPatterns,
        [In, MarshalAs(UnmanagedType.LPWStr)]
            string ppwzPatterns,
        [In] int dwReserved);

    [PreserveSig]
    int UnregisterNameSpace(
        [In] IClassFactory classFactory,
        [In, MarshalAs(UnmanagedType.LPWStr)] string pszProtocol);

    int Bogus1();

    int Bogus2();

    int Bogus3();

    int Bogus4();

    int Bogus5();
}
HS