How to pass multiple files to shell context menu command (Windows Explorer)

You can archive it with my program called singleinstance (sourcecode).
No shell extensions involved.

The main idea is that one instance of my program will be launched per file you have selected. It is checking if another instance of singleinstance program is running, and using Inter-Process Communication to notify the existing instance that other files have been selected.

Do not forget to set option MultiSelectModel=Player, otherwise number of files will be limited.

Usage:

Usage: singleinstance.exe "%1" {command} $files [arguments]

Optional arguments for singleinstance (not passed to command):

--si-timeout {time to wait in msecs}

Sample registry file:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\SystemFileAssociations\.txt\Shell\p4merge]
"MultiSelectModel"="Player"

[HKEY_CLASSES_ROOT\SystemFileAssociations\.txt\Shell\p4merge\Command]
@="\"d:\\singleinstance.exe\" %1 \"C:\\Program Files\\Perforce\\p4merge.exe\" $files --si-timeout 400"

Fast way of creating proportional thumbnails from images with GdiPlus and C++

This method is based on original method from http://danbystrom.se/2009/01/05/imagegetthumbnailimage-and-beyond/

but also supports embedded thumbnails without compression (RGB). If no embedded thumbnail found, full image is loaded into memory.

Usage:

PropertyItem* GetPropertyItemFromImage(Gdiplus::Image* bm, PROPID propId) {
    UINT itemSize = bm->GetPropertyItemSize(propId);
    if (!itemSize) {
        return 0;
    }
    PropertyItem* item = reinterpret_cast<PropertyItem*>(malloc(itemSize));
    if (bm->GetPropertyItem(propId, itemSize, item) != Ok) {
        free(item);
        return 0;
    }
    return item;
}

UINT VoidToInt(void* data, unsigned int size) {
    switch (size) {
        case 8:
            return *reinterpret_cast<UINT*>(data);
        case 4:
            return *reinterpret_cast<DWORD*>(data);
        case 2:
            return *reinterpret_cast<WORD*>(data);
        default:
            return *reinterpret_cast<BYTE*>(data);
    }
}

typedef IStream * (STDAPICALLTYPE *SHCreateMemStreamFuncType)(const BYTE *pInit, UINT cbInit);
SHCreateMemStreamFuncType SHCreateMemStreamFunc = 0;

bool IsVista() {
    static int isVista = -1;
    if (isVista == -1)
    {
        OSVERSIONINFO osver;
        osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

        isVista = (::GetVersionEx(&osver) &&
            osver.dwPlatformId == VER_PLATFORM_WIN32_NT &&
            (osver.dwMajorVersion >= 6));
    }
    return isVista != FALSE;
}

Gdiplus::Bitmap* BitmapFromMemory(BYTE* data, unsigned int imageSize) {
    if (WinUtils::IsVista()) {
        if (!SHCreateMemStreamFunc) {
            HMODULE lib = LoadLibrary(_T("Shlwapi.dll"));
            SHCreateMemStreamFunc = reinterpret_cast<SHCreateMemStreamFuncType>(GetProcAddress(lib, "SHCreateMemStream"));
            if (!SHCreateMemStreamFunc) {
                return 0;
            }
        }

        Gdiplus::Bitmap * bitmap;
        IStream* pStream = SHCreateMemStreamFunc(data, imageSize);
        if (pStream) {
            bitmap = Gdiplus::Bitmap::FromStream(pStream);
            pStream->Release();
            if (bitmap) {
                if (bitmap->GetLastStatus() == Gdiplus::Ok) {
                    return bitmap;
                }
                delete bitmap;
            }
        }
    } else {
        HGLOBAL buffer = ::GlobalAlloc(GMEM_MOVEABLE, imageSize);
        if (buffer) {
            void* pBuffer = ::GlobalLock(buffer);
            if (pBuffer) {
                Gdiplus::Bitmap * bitmap;
                CopyMemory(pBuffer, data, imageSize);

                IStream* pStream = NULL;
                if (::CreateStreamOnHGlobal(buffer, FALSE, &pStream) == S_OK) {
                    bitmap = Gdiplus::Bitmap::FromStream(pStream);
                    pStream->Release();
                    if (bitmap) {
                        if (bitmap->GetLastStatus() == Gdiplus::Ok) {
                            return bitmap;
                        }

                        delete bitmap;
                    }
                }
                ::GlobalUnlock(buffer);
            }
            ::GlobalFree(buffer);
        }
    }

    return 0;
}

// Based on original method from http://danbystrom.se/2009/01/05/imagegetthumbnailimage-and-beyond/
Gdiplus::Bitmap* GetThumbnail(Gdiplus::Image* bm, int width, int height, Gdiplus::Size* realSize = 0) {
    using namespace Gdiplus;
    if (realSize) {
        realSize->Width = bm->GetWidth();
        realSize->Height = bm->GetHeight();
    }
    Size sz = AdaptProportionalSize(Size(width, height), Size(bm->GetWidth(), bm->GetHeight()));
    Bitmap* res = new Bitmap(sz.Width, sz.Height);
    Graphics gr(res);

    gr.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
    UINT size = bm->GetPropertyItemSize(PropertyTagThumbnailData);
    if (size) {
        // Loading thumbnail from EXIF data (fast)
        enum ThumbCompression { ThumbCompressionJPEG, ThumbCompressionRGB, ThumbCompressionYCbCr, ThumbCompressionUnknown }
            compression = ThumbCompressionJPEG;

        PropertyItem* thumbnailFormatItem = GetPropertyItemFromImage(bm, PropertyTagThumbnailFormat);
        if (thumbnailFormatItem) {
            UINT format = VoidToInt(thumbnailFormatItem->value, thumbnailFormatItem->length);
            if (format == 0) {
                compression = ThumbCompressionRGB;
            } else if (format == 1) {
                compression = ThumbCompressionJPEG;
            } else {
                compression = ThumbCompressionUnknown;
            }
            free(thumbnailFormatItem);
        } else {
            PropertyItem* compressionItem = GetPropertyItemFromImage(bm, PropertyTagThumbnailCompression);
            if (compressionItem) {
                WORD compressionTag = *reinterpret_cast<WORD*>(compressionItem->value);
                if (compressionTag == 1) {
                    compression = ThumbCompressionRGB;
                    PropertyItem* photometricInterpretationItem = GetPropertyItemFromImage(bm, PropertyTagPhotometricInterp);
                    if (photometricInterpretationItem) {
                        WORD photoMetricInterpretationTag = VoidToInt(photometricInterpretationItem->value, photometricInterpretationItem->length);
                        free(photometricInterpretationItem);
                        if (photoMetricInterpretationTag == 6) {
                            compression = ThumbCompressionYCbCr;
                        }
                    }

                } else if (compressionTag == 6) {
                    compression = ThumbCompressionJPEG;
                }

                free(compressionItem);
            }
        }

        int originalThumbWidth = 0, originalThumbHeight = 0;
        if (compression == ThumbCompressionJPEG || compression == ThumbCompressionRGB) {
            PropertyItem* thumbDataItem = GetPropertyItemFromImage(bm, PropertyTagThumbnailData);
            if (thumbDataItem) {
                if (compression == ThumbCompressionJPEG) {
                    Bitmap* src = BitmapFromMemory(reinterpret_cast<BYTE*>(thumbDataItem->value), thumbDataItem->length);

                    if (src) {
                        gr.DrawImage(src, 0, 0, sz.Width, sz.Height);
                        delete src;
                        free(thumbDataItem);
                        return res;
                    }
                } else if (compression == ThumbCompressionRGB) {
                    PropertyItem* widthItem = GetPropertyItemFromImage(bm, PropertyTagThumbnailImageWidth);
                    if (widthItem) {
                        originalThumbWidth = VoidToInt(widthItem->value, widthItem->length);
                        free(widthItem);
                    }
                    PropertyItem* heightItem = GetPropertyItemFromImage(bm, PropertyTagThumbnailImageHeight);
                    if (heightItem) {
                        originalThumbHeight = VoidToInt(heightItem->value, heightItem->length);
                        free(heightItem);
                    }
                    if (originalThumbWidth && originalThumbHeight) {
                        BITMAPINFOHEADER bih;
                        memset(&bih, 0, sizeof(bih));
                        bih.biSize = sizeof(bih);
                        bih.biWidth = originalThumbWidth;
                        bih.biHeight = -originalThumbHeight;
                        bih.biPlanes = 1;
                        bih.biBitCount = 24;

                        BITMAPINFO bi;
                        memset(&bi, 0, sizeof(bi));
                        bi.bmiHeader = bih;

                        BYTE* data = reinterpret_cast<BYTE*>(thumbDataItem->value);
                        BYTE temp;
                        // Convert RGB to BGR
                        for (unsigned int offset = 0; offset < thumbDataItem->length; offset += 3) {
                            temp = data[offset];
                            data[offset] = data[offset + 2];
                            data[offset + 2] = temp;
                        }
                        Bitmap src(&bi, thumbDataItem->value);

                        if (src.GetLastStatus() == Ok) {
                            gr.DrawImage(&src, 0, 0, sz.Width, sz.Height);
                            free(thumbDataItem);
                            return res;
                        }
                    }

                } else {
                    // other type of compression not implemented
                }
                free(thumbDataItem);
            }
        }
    } 
    // Fallback - Load full image and draw it  (slow)
    gr.DrawImage(bm, 0, 0, sz.Width, sz.Height);

    return res;
}

Gdiplus::Bitmap* GetThumbnail(const CString& filename, int width, int height, Gdiplus::Size* realSize) {
    using namespace Gdiplus;
    Image bm(filename);
    if (bm.GetLastStatus() != Ok) {
        return 0;
    }
    return GetThumbnail(&bm, width, height, realSize);
}

Gdiplus::Size AdaptProportionalSize(const Gdiplus::Size& szMax, const Gdiplus::Size& szReal)
{
    int nWidth;
    int nHeight;
    double sMaxRatio;
    double sRealRatio;

    if (szMax.Width < 1 || szMax.Height < 1 || szReal.Width < 1 || szReal.Height < 1)
        return Size();

    sMaxRatio = szMax.Width / static_cast<double>(szMax.Height);
    sRealRatio = szReal.Width / static_cast<double>(szReal.Height);

    if (sMaxRatio < sRealRatio) {
        nWidth = min(szMax.Width, szReal.Width);
        nHeight = static_cast<int>(round(nWidth / sRealRatio));
    } else {
        nHeight = min(szMax.Height, szReal.Height);
        nWidth = static_cast<int>(round(nHeight * sRealRatio));
    }

    return Size(nWidth, nHeight);
}

Deploying Windows applications on Linux(Wine) with InnoSetup

First, we need to detect Wine in the InnoSetup script.

function LoadLibraryA(lpLibFileName: PAnsiChar): THandle;
external 'LoadLibraryA@kernel32.dll stdcall';
function GetProcAddress(Module: THandle; ProcName: PAnsiChar): Longword;
external 'GetProcAddress@kernel32.dll stdcall';

function IsWine: boolean;
var  LibHandle  : THandle;
begin
  LibHandle := LoadLibraryA('ntdll.dll');
  Result:= GetProcAddress(LibHandle, 'wine_get_version')<> 0;
end;

The IsWine function returns true, if the installer is launched under Wine. My application (Image Uploader) is extensively using Microsoft Gdi+ library, but Wine's implementation of this library is incomplete, so I had no other choice but to install the native version of gdiplus.dll. The important thing is to tell Wine to use the native library instead of the built-in one, it can be achieved by writing a setting to the Wine's registry.

procedure CurStepChanged(CurStep: TSetupStep);
begin
 if  CurStep=ssPostInstall and IsWine   then
  begin
     filecopy(expandconstant('{tmp}\gdiplus.dll'),expandconstant('{app}\gdiplus.dll'),false);

     RegWriteStringValue(HKEY_CURRENT_USER, 'Software\Wine\DllOverrides',
    'gdiplus', 'native, builtin');
 end;
end;

There are some other issues when Image Uploader is running in Wine, for example, Wine is ignoring WM_CTLCOLORSTATIC message's result, also Richedit's EM_FORMATRANGE is not working. But I hope I'll find a workaround for these issues too.

Symstorm - plugin adding support for Symfony2 in PhpStorm

Symfony 2 helper plugin for PHPStorm 6

Quickly navigate to your controller's action method by URL.

Press Ctrl+Shift+K in your project, paste url into the text box and your controller class will be opened in text editor.

It uses "php app/console router:dump-apache" to get project's routes, so php executable must be in your env path variable.

 

JAR: http://yadi.sk/d/4T2lPA5tKqfwx 

CRX: http://yadi.sk/d/hEIolS_SKqg28 (Install in Google Chrome, Opera)

 

Sources: https://github.com/zenden2k/symstorm

Small plugin for supporting Kohana in PhpStorm

Kohana 3 helper plugin for PHPStorm

It provides two simple features:

  • Navigate to a template from controller (from View::factory('...') )
  • Navigate to a controller by URL

Quickly navigate to your controller's action by URL. In order to enable this feature, you need to add the following code in the end of your boostrap.php:

if ( !empty( $_GET['ks_secret_key'] ) && ($_SERVER['REMOTE_ADDR'] == '127.0.0.1' 
        || $_GET['ks_secret_key'] == 'your Secret Key' ) ){ 
     $req = Request::factory(); 
     die( 'KS;1;'.$req->directory().';'.$req->controller() .';'.$req->action() ); 
}

For older versions (Kohana 3.0):

if ( ( !empty( $_GET['ks_enable'] ) && $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ) 
         || !empty( $_GET['ks_secret_key']) && $_GET['ks_secret_key'] == 'your secret key' ) {
     $req = Request::instance(); 
     die( 'KS;1;'.$req->directory.';'.$req->controller .';'.$req->action ); 
}

Now you can press Ctrl+Shift+P in your project, paste url into the text box and your controller class will be opened in code editor. Enjoy!

You can get compiled jar here: https://github.com/zenden2k/kohanastorm/releases

Sources: https://github.com/zenden2k/kohanastorm