gethostbyname or getaddrinfo that doesn’t block

gethostbyname and getaddrinfo block.  There is no way to stop them once they’ve started.  (BTW, getaddrinfo is recommended over gethostbyname as it’s IPv6 compatible.)  This little class will provide a neat way to get the info without blocking.  Classes extra::Thread and extra::auto_HANDLE are thread and and auto handle closing classes respectivly (is that english?).  When the user requires to cancel the request or connect, call m_hostNameGetter.Abort() from within MySocketClass.

The Class:

class CGetAddrInfoThread : private extra::Thread
{
public:
    CGetAddrInfoThread();
    void Abort();
    struct addrinfo *GetAddrInfo(const extra::tstring &host, ULONG nPort);
private:
    extra::auto_HANDLE m_abort;
    extra::tstring m_host;
    ULONG m_port;
    struct addrinfo *m_ret;
    virtual DWORD ThreadProc();
};

The Implementation

CGetAddrInfoThread::CGetAddrInfoThread() :
        m_port(0), m_ret(0), m_abort(0,0,CloseHandle)
{
}
void CGetAddrInfoThread::Abort()
{
    SetEvent(m_abort);
}
struct addrinfo *CGetAddrInfoThread::GetAddrInfo(const extra::tstring &host, ULONG nPort)
{
    if(!m_abort.Valid())
        m_abort = CreateEvent(0,TRUE,FALSE,0);
    m_host = host;
    m_port = nPort;
    m_ret = 0;
    ResetEvent(m_abort
    if(!Create().Valid())
        return 0
    HANDLE handles[] = {ThreadHandle(),m_abort};
    return ((WaitForMultipleObjects(sizeof(handles)/sizeof(handles[0]),
                handles,FALSE,INFINITE)==WAIT_OBJECT_0) ? m_ret : 0);
}
DWORD CGetAddrInfoThread::ThreadProc()
{
    char port[16];
    if(_ultoa_s(m_port,port,10))
        return 0;
    struct addrinfo aiHints;
    memset(&aiHints, 0, sizeof(aiHints));
    aiHints.ai_family = AF_INET;
    aiHints.ai_socktype = SOCK_STREAM;
    aiHints.ai_protocol = IPPROTO_TCP;
    if (::getaddrinfo(CT2CA(m_host.c_str()), port, &aiHints, &m_ret))
        m_ret = 0;
    return 0;
}

Using the class:

void MySocketClass::MyConnectingMethod()
{
    // do stuff
    struct addrinfo *aiList = m_hostNameGetter.GetAddrInfo(host,nPort);
    if(!aiList)
        return 0;
    // do stuff
}

Advertisements

Dictionary, IEquatable, Equals and GetHashCode

The docs for the .NET Dictionary class lead you to believe that implementing IEquatable for a dictionary key is good enough to get the dictionary to use your Equals method.  However, one must also override GetHashCode.  Given the way a dictionary works, this is logical but not documented.  The interesting part, is that the Remove method on the dictionary will use your Equals with or without GetHashCode being implemented.  In this way, you can add identical items and remove more than one with one remove.  Not that you’d want to.  Anyway, this class will work as a dictionary key:


public class MyDickKey : IEquatable<MyDickKey>
{
    private string m_comparableString;
    private string ComparableString
    { get {
        return (m_comparableString != null ?
            m_comparableString : GenComparableString()); } }
    private void ResetComparableString()
        { m_comparableString = null; }
    private string GenComparableString()
        {/*m_comparableString = something */}
    public MyDickKey() { }
    public bool Equals(MyDickKey rhs)
    {
        return this.ComparableString.Equals(rhs.ComparableString);
    }
    public override int GetHashCode()
    {
        return ComparableString.GetHashCode();
    }
};

Managed, Unmanaged and COM

I’m wrapping a legacy lib for use in a .NET envirment. 

Usefull: An Overview of Managed/Unmanaged Code Interoperability

Firstly, I tried to wrap the lib api with COM and use that from my C# app.  With speed and efficiecency a priority, this was unacceptable.  It involves callbacks (legacy lib) or events (COM) – the big slowdown happened in the call from the COM to it’s connection point (C#).  

The way forward is a managed/unmanaged C++ project.  All legacy objects are held as pointers to classes that wrap them.  A thin wrapper, one managed class for one legacy class.  Wrap the pointer in a nice template base class taking a type for the wrapped.  Have a pointer expose method that throws an exception if the wrapped pointer is null.  Ensure IDisposable is derived from and the ~ClassName, and !ClassName methods are both implemented (finalize and Dispose methods).  In the dispose, clean up the unmanaged pointer.

Callbacks from the native?  How to: Marshal Callbacks and Delegates Using C++ Interop.  It’s very important you don’t try this if the legacy callback expected is not __stdcall.  It will mess up the stack when you leave the callback.  This was my problem.  So, I created a class to set up the callback.  The legacy function that sets of the callback takes a callback and a void * as data.  For the callback I passed a normal callback function of my own, for the void * I passed a __stdcall callback as set up in the article.  When the normal callback gets called, I simple use the given void * as a pointer to a __stdcall callback and call that.  One thing about the article, gcnew GetTheAnswerDelegate, can be called passing an object as the first parameter, and a method as the second.  Getting your callback into an instance of a class.

Strings: How to: Convert System::String to wchar_t* or char*. A nice wrapper class can’t be resisted.

If COM is the required for some other reasons:  Don’t forget “oleautomation” attribute on your non-IDispatch interfaces (in your IDL).

This is a handy way to bind COM enums to a WPF control in XAML.  (Note the requirement of the loc namespace to point to where your COMTypes class is).  One can use System.Enum.Parse to get the selected enum value back from the combo box.

XAML:
<Window.Resources>
    <ObjectDataProvider MethodName="GetMyCOMEnums
        ObjectType="{x:Type loc:COMTypes}" x:Key="MyComEnums">
    </ObjectDataProvider>
</Window.Resources>
<ComboBox Height="26" Margin="400,29,0,0" Name="m_type" VerticalAlignment="Top" HorizontalAlignment="Left" Width="121" ItemsSource="{Binding Source={StaticResource MyComEnums}}" />

A class in your project:

public class COMTypes
{
   public static Array GetMyCOMEnums()
   {
       return Enum.GetValues(typeof(MyCOMLib.MyEnumType));
   }
}