A Journey 2 Eternity

Archive for April 2009

DT_WORDBREAK and DT_VCENTER flag never work together. So here is an alternative.

void CEMCHotButton::DrawCaption (CDC* pDC, LPDRAWITEMSTRUCT lpDrawItemStruct, CRect& rectCaption, DWORD dwFormat) 
{
	// Get caption text
	CString strCaption;
	GetWindowText (strCaption);

	if(!strCaption.IsEmpty()) {
		if((GetStyle() & BS_MULTILINE) == BS_MULTILINE) {
			dwFormat &= ~DT_SINGLELINE;
			dwFormat &= ~DT_VCENTER;
			dwFormat |= DT_WORDBREAK;

			RECT rct = rectCaption;
			int height = ::DrawText(pDC->m_hDC, (LPCTSTR) strCaption, strCaption.GetLength(), &rct, DT_CALCRECT | DT_WORDBREAK);
			rectCaption.top = rectCaption.CenterPoint().y – height / 2;

			::DrawTextEx (pDC->m_hDC, strCaption.GetBuffer(0), strCaption.GetLength(), &rectCaption, dwFormat, NULL);
		}
	}
}

A topmost window is one which stays on top of other windows even while it’s not in focus. Normally all application popup windows will go to background once they lose focus but a topmost window doesn’t.

void SetTopMost( HWND hWnd, const BOOL TopMost )
{
	ASSERT( ::IsWindow( hWnd ));
	HWND hWndInsertAfter = ( TopMost ? HWND_TOPMOST : HWND_NOTOPMOST );

	::SetWindowPos( hWnd, hWndInsertAfter, 0, 0 , 0 , 0, SWP_NOMOVE | SWP_NOSIZE );
}

The second parameter passed to ::SetWindowPos is the one that does the trick. It’ called hwndInsertAfter, if we specify the after window as HWND_TOPMOST then we get a topmost window, if we specify after window as HWND_NOTOPMOST then topmost setting is unset and our window becomes a normal window.

When working in MFC/ATL and then using a resource dll can at times cause bugs because resource handle is not properly set. For e.g. if you have a dialog resource in a dll and you instantiate a CDialog which results in an error because resource cannot be located in this application or dll since it’s in the resource dll. So solution for above problem will be to set correct resource handle.

MFC provides two functions…

1. AfxGetResourceHandle

2. AfxSetResourceHandle

So we should first save our current resource handle and then set new resource handle before loading such a resource. Also don’t forget to set our old handle since its good practice.

Internally MFC calls FindResource and LoadResource using this handle, so if it’s not properly set this will cause failure in loading resources.

In ATL it’s quite similar except that we’ve got a new function called AddResourceInstance which adds our new resource handle to existing list of handles. So when a look up is done given resource handle is also used. Following functions are provided in ATL to work with resource handles…

1. AddResourceInstance

2. GetResourceInstance – Similar to AfxGetResourceHandle

3. SetResourceInstance – Similar to AfxGetSetResourceHandle

For newbies this is always a painful bug to resolve as they don’t know what went wrong since they expect this to be automagically done.

Sample code snippet:

class CEMCResourceHandle
{
public:
	CEMCResourceHandle() {
		m_hPrevInst = AfxGetResourceHandle();
		m_hDLLInstance = ::LoadLibrary(szESIMFCControlsDLLName);
		if (m_hDLLInstance) {
			AfxSetResourceHandle(m_hDLLInstance);
		}
	}

	virtual ~CEMCResourceHandle() {
		AfxSetResourceHandle(m_hPrevInst);
		if(m_hDLLInstance != NULL) {
			FreeLibrary(m_hDLLInstance);
		}
	}

	//! Retrieves the current resource instance
	//!
	//! \return Return the current resource instance
	HINSTANCE GetCurrentInstance() const { return m_hDLLInstance; }

	//! Retrieves the previous resource instance
	//!
	//! \return Return the previous resource instance
	HINSTANCE GetPrevInstance() const { return m_hPrevInst; }

private:
	//! Handle of previous resource instance
	HMODULE m_hPrevInst;

	//! Handle of loaded DLL resource instance
	HMODULE m_hDLLInstance;
};

// Just made an temporary instance of CEMCResourceHandle class
// Load resource from the DLL
CEMCResourceHandle resHandle;

// Set bitmap to the MoveUp button
m_btnUp.SetBitmap(::LoadBitmap(resHandle.GetCurrentInstance(), MAKEINTRESOURCE(IDB_BITMAP_UPARROW)));

If you have ever used ShBrowseForFolder(), you’ve probably noticed the pidlRoot member of the BROWSEINFO structure. It can be used to set the “root” folder to browse from. It’s usually set to NULL, which means the “Desktop” folder.

Ever wanted to use another folder as the root for the “Browse For Folder” common dialog, but didn’t know how to create an ITEMIDLIST for a given path? Here’s a function that does it.

BOOL GetItemIdListFromPath (LPWSTR lpszPath, LPITEMIDLIST *lpItemIdList)
{
	LPSHELLFOLDER pShellFolder = NULL;
	HRESULT hr;
	ULONG chUsed;

	// Get desktop IShellFolder interface
	if (SHGetDesktopFolder (&pShellFolder) != NOERROR) {
		return FALSE; // failed
	}

	// convert the path to an ITEMIDLIST
	hr = pShellFolder->ParseDisplayName (
				NULL, // owner window
				NULL, // reserved (must be NULL)
				lpszPath, // folder name
				&chUsed, // number of chars parsed
				lpItemIdList, // ITEMIDLIST
				NULL // attributes (can be NULL)
			);

	if (FAILED(hr)) {
		pShellFolder->Release();
		*lpItemIdList = NULL;
		return FALSE;
	}

	pShellFolder->Release();

	return TRUE;
} // GetItemIdListFromPath

It’s written in C++, but converting it to plain C is just a matter of changing a couple of lines. To do it, change all interface calls like: pShellFolder->ParseDisplayName() to pShellFolder->lpVtbl->ParseDisplayName(pShellFolder, …), and similar changes to other calls.

Two things: The function receives the path as a UNICODE (LPWSTR) string, so you’ll have to convert ANSI strings to unicode using MultiByteToWideChar() before calling the function. Finally, remember that you should free the memory referenced by the pointer to the ITEMIDLIST you get from the function by calling the Free() method of the IMalloc interface, which you can get using SHGetMalloc().

BOOL IsPathShared( LPCTSTR lpctszPath_i )
{
	SHFILEINFO shFileInfo = { 0 };

	return ( SHGetFileInfo( lpctszPath_i, 0, &shFileInfo, sizeof( shFileInfo ), SHGFI_ATTRIBUTES ) && 
				( shFileInfo.dwAttributes & SFGAO_SHARE ) == SFGAO_SHARE );
}
BOOL IsShortcut( LPCTSTR lpctszPath_i )
{
	SHFILEINFO shFileInfo = { 0 };
	return (( SHGetFileInfo( lpctszPath_i, 0, &shFileInfo, sizeof( shFileInfo ), SHGFI_ATTRIBUTES )) && 
				( shFileInfo.dwAttributes & SFGAO_LINK ) == SFGAO_LINK );
}

Ever wondered how to resolve a shortcut! There is a hidden helper function if you are using MFC, it’s called AfxResolveShortcut. I customized it a bit, so that we can use it independently without MFC, and here is the finished product.

BOOL ResolveShortcut( HWND hWnd_i, LPCTSTR lpctszFileIn_i, LPTSTR lptszFileOut_o, const int nPathLength_i )
{
	// Object for resolving link
	IShellLink* psl = NULL;
	*lptszFileOut_o = 0; // assume failure

	if (!hWnd_i) {
		return FALSE;
	}

	SHFILEINFO info;
	if ( ( SHGetFileInfo(lpctszFileIn_i, 0, &info, sizeof(info), SHGFI_ATTRIBUTES) == 0) || !(info.dwAttributes & SFGAO_LINK)) {
		return FALSE;
	}

	CoInitialize( 0 );
	// Create instance
	HRESULT hCreateRes = CoCreateInstance( CLSID_ShellLink, 0, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>( &psl ));
	if ( FAILED( hCreateRes ) || psl == NULL ) {
		return FALSE;
	}

	IPersistFile *ppf = NULL;
	if (SUCCEEDED(psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf))) {
		USES_CONVERSION; // T2COLE needs this

		if ( ppf != NULL && SUCCEEDED( ppf->Load( T2COLE( lpctszFileIn_i ), STGM_READ))) {
			// Resolve the link, this may post UI to find the link
			if ( SUCCEEDED(psl->Resolve( hWnd_i, SLR_ANY_MATCH))) {
				psl->GetPath(lptszFileOut_o, nPathLength_i, NULL, 0);
				ppf->Release();
				psl->Release();
				CoUninitialize();

				return TRUE;
			}
		}

		if (ppf != NULL) {
			ppf->Release();
		}
	}

	psl->Release();
	CoUninitialize();

	return FALSE;
}// End ResolveShortcut

//Lets test the above code…
int main() 
{
	TCHAR chBuffer[MAX_PATH] = { 0 };
	ResolveShortcut( hSomeWindow, _T( “C:\\shortcut to msdev.lnk” ), chBuffer, MAX_PATH );

	MessageBox( hSomeWindow, chBuffer, _T( “Hurray shortcut resolved” ), MB_OK | MB_ICONINFORMATION );

	return 0;

}// End main

Pages

Categories

April 2009
M T W T F S S
« Dec   May »
 12345
6789101112
13141516171819
20212223242526
27282930  

Blog Stats

  • 27,261 hits