Friday, August 12, 2011

Heroku Cedar

If you are developing on MINGW32 and looking to deploy to Heroku Cedar, make sure you update your Heroku gem.  2.2.3 was broken, 2.4.1 seems to be working.

Friday, May 13, 2011

Git

I have been working on some ROR and the sweet marriage of Git, Github and Heroku may very well sell me on Git for everything.

Tuesday, March 22, 2011

ADSI Login Window

An atrociously documented call the IADsUser interface to AD: put_LoginHours and get_LoginHours.  I believe the total documentation says:

"Time periods for each day of the week during which logons are permitted for the user. Represented as a table of Boolean values for the week, each indicating if that time slot is a valid logon time. Be aware that the representation is provider and directory-specific." 

I am using C++, so the 'values' are stored as an array of bytes where each bit is an hour so: 

byte lh[21]; //((24 hours*7 days)/8 bits)

You can find that in other blogs along with the information that it starts at midnight Sunday morning (first hour
1:00- 1am Sunday).  BUT since this is a COM interface it takes a VARIANT* as a parameter.  Howdo you get the array values in and out of the variant?  Like so:

        VARIANT var;
        VariantInit(&var);
        hr=user->get_LoginHours(&var);
        BYTE lh[21];
        memset(lh,0,sizeof(byte)*21);
        VariantArrayToBytes(lh,21,&var);
        VariantClear(&var);
//use byte array

and

        BYTE lh [21];//hours in a week
//fill byte array as needed
        VARIANT var;
        VariantInit(&var);
        hr = BytesToVariantArray(lh, 21, &var);
        if (SUCCEEDED(hr))
            user->put_LoginHours(var);
        else{
           //err
        }
        VariantClear(&var);


where:

HRESULT VariantArrayToBytes(PBYTE pValue, ULONG cValElements, VARIANT *pVar)
{
    HRESULT hr;
    SAFEARRAY *psa = V_ARRAY(pVar);
    CHAR HUGEP *pArray = NULL;
    hr = SafeArrayAccessData(psa, (void HUGEP * FAR *) &pArray);
    if (SUCCEEDED(hr))
    {
        memcpy(pValue, pArray, cValElements);
        SafeArrayUnaccessData(psa);
        hr = S_OK;
    }
    return hr;
}

HRESULT BytesToVariantArray(PBYTE pValue, ULONG cValElements, VARIANT *pVar)
{
    HRESULT hr;
    SAFEARRAY *psa;
    SAFEARRAYBOUND ab;
    CHAR HUGEP *pArray = NULL;

    ab.lLbound = 0;
    ab.cElements = cValElements;

    psa = SafeArrayCreate(VT_UI1, 1, &ab);
    if (psa == NULL)
    {
        hr = E_OUTOFMEMORY;
    }else{
        hr = SafeArrayAccessData(psa, (void HUGEP * FAR *) &pArray);
        if (SUCCEEDED(hr))
        {
            memcpy(pArray, pValue, ab.cElements);
            SafeArrayUnaccessData(psa);
            V_VT(pVar) = VT_ARRAY | VT_UI1;
            V_ARRAY(pVar) = psa;
            hr = S_OK;
        }
        else
        {
            if (psa)
                SafeArrayDestroy(psa);
        }
    }
    return hr;
}

Launching external process from a service (vc++/win32)

Microsoft has managed to make this much more difficult than it used to be.  CreateProcessWithLogonW() no longer functions they way it should.  It does require an SID from the user that is launching the process, so LOCALSYSTEM will not work.  However, even if you change the ID the service is running as, the process will not launch properly.

I am posting this example as there are wildly conflicting information out there and most is incorrect.  You may need to change the permissions of the launching user.

This will work:

HANDLE hToken;
if(LogonUser(<ID>,<DOMAIN>,<PWD>,LOGON32_LOGON_INTERACTIVE,0,&hToken))
{
STARTUPINFO si;
ZeroMemory( &si, sizeof(STARTUPINFO) );
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
if(!CreateProcessAsUser(hToken,  NULL, <CMDLINE>
, NULL, NULL,TRUE, CREATE_NEW_CONSOLE, NULL, NULL,&si,&pi))
{
    DWORD dw=GetLastError();
   //FAILED!
}else{
    //SUCCESS!
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}else{
    DWORD dw=GetLastError();
   //FAILED!
}

Tuesday, March 8, 2011

C++ vs C#

It is a toolbox, nothing more.  Take out the tool you need to do the job.  If you wish to wax lyrical about a particular wrench that is just your favorite or perhaps the one you use the most, that is fine.  However, remember that even diehard gearheads that worship their 6point socket set still have a hammer in the toolbox.

Do not use one language for all things.  Even if it is perl.

All that said here is my opinion on when to select one over the other.  If you are utilizing more than a small subset of .NET functionality use C#.  Managed C++ with CLI is a crutch for interfacing some specific item that is third party and not in your power to change.  (It probably should be bundled into a DLL to avoid the crutch at all)  C++ should be reserved for large systems where you don't want or need clunky chucks of library coming along for the ride.  IE Where the design may be counter to the assumptions of the people providing the .NET bits that might otherwise be used for that functionality (which leads to a long term issue with the ability to optimize).

And there you have it, not earth shattering.  Use the wrench that fits better, they are both adjustable to some degree.

Monday, March 7, 2011

Refactoring

Most people know when refactoring is needed on a large scale.  The daunting task looming over as an attempt is made to wedge one more feature into the now unrecognizable scope...  it is time for the dreaded refactor!

On the small scale what refactoring is may be invisible and if ignored leads to the above sink or time and money.  Probably most developers refactor a bit without conscientiously doing so.  The DRY principle in play, the breaking apart an bloated function into multiple calls for clarity, etc.  Paying particular attention to these small reasons to correct yourself will make it more likely that you can see the win in the medium scale refactors that are the big wins that allow you to avoid ever having to do a big refactor. 

Still with me?  Good.