Share
 
In windows if you ever changed the icon of a shortcut then you’ll know what the API PickIconDlg does. Basically this function will let you select any icon embedded as a resource in a .dll or .exe file for use in your application. Just select in the dialog the icon that you like and click ok. Here is a shot of the dialog, in my case I’ve given devenv.exe and this is how it looks for me…
PickIcon dialog screenshot

PickIcon dialog screenshot

So the API for invoking the API is as follows…

int Index = 2;
const DWORD BuffSize = MAX_PATH*2;
TCHAR Path[BuffSize] = {_T("C:\\Program Files (x86)\\Microsoft Visual Studio 10.0\\Common7\\IDE\\devenv.exe")};
const int Sel = PickIconDlg(GetSafeHwnd(), Path, BuffSize, &Index);
if(Sel)
{
 // The hinst parameter is assumed to have been initialized earlier.
 HMODULE hMod = ::LoadLibrary(Path);
 m_hIcon = ExtractIcon(hMod, Path, Index);
 // After some work destroy the icon
 FreeLibrary(hMod);
}

// Set the icon for this dialog using above extracted icon
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon 

Now see my dialog titlebar after executing this code snippet…

Dialog with new icon

Dialog with new icon

 

Have fun! :)

Share
 

Hmm so after a long time I’m back with a new post.

This time I came across a cool API called NtQueryObject. One purpose of the API is to find the name of object type it’s given. For example if you are passing a process handle this API will return “Process” as the string. The caveat of using this API is that it can be changed in the future. So be very careful with the signature of the function, cross check with the documentation before using. Use it at your own risk.

So let’s see the code, since API doesn’t have a lib associated with it we’ll have to use GetProcAddress to retrieve function address. The function signature of NtQueryObject looks like…

NTSTATUS NtQueryObject(__in_opt HANDLE Handle,
                        __in OBJECT_INFORMATION_CLASS ObjectInformationClass,
                        __out_opt PVOID ObjectInformation,
                        __in ULONG ObjectInformationLength,
                        __out_opt PULONG ReturnLength );

The second parameter to the API can be of two types. The one type of interest for this case is PUBLIC_OBJECT_TYPE_INFORMATION. If we pass this type as second parameter then the API is expecting a PUBLIC_OBJECT_TYPE_INFORMATION as third parameter. The contents of the structure is as follows…

typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
    UNICODE_STRING TypeName;
    ULONG Reserved [22];    // reserved for internal use
} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;

The first member in the structure is the one that returns the string, only issue we’ll have to allocate this in special manner. Please have a look in the below function to see how I’ve allocated memory for this structure.

void GetHandleTypeName(HANDLE hHandle, CString& TypeName)
{
 typedef NTSTATUS (NTAPI *NtQueryObjectPtr)(HANDLE Handle,
                   OBJECT_INFORMATION_CLASS ObjectInformationClass,
          PVOID ObjectInformation,
          ULONG ObjectInformationLength,
          PULONG ReturnLength);

HMODULE hMod = LoadLibrary(_T("NtDll.dll"));
 NtQueryObjectPtr QueryObj = (NtQueryObjectPtr)::GetProcAddress(hMod, "NtQueryObject");
 ASSERT(QueryObj);

ULONG OutSize = 0;
 NTSTATUS NtStatus = QueryObj(hHandle, ObjectTypeInformation, NULL, 0, &OutSize);
 PPUBLIC_OBJECT_TYPE_INFORMATION TypeInfo = (PPUBLIC_OBJECT_TYPE_INFORMATION)calloc(1, OutSize);
 ULONG InSize = OutSize;
 NtStatus = QueryObj(hHandle, ObjectTypeInformation, TypeInfo, InSize, &OutSize);
 TypeName = TypeInfo->TypeName.Buffer;
 free(TypeInfo);
}
// This is how we invoke the function note: GetCurrentProcess() call.
CString cs;
GetHandleTypeName(GetCurrentProcess(), cs);
MessageBox(cs);
Share
 

If you are creating a dialog with style WS_CHILD then make sure you also have DS_CONTROL  and DS_CONTROLPARENT enabled for the dialog.

The reason being that the dialog at a time is a control (embedded inside another window) and a control parent (housing other controls). If these styles are not specified calls to GetWindowRect and then a subsequent ScreenToClient will return top co-ordinates in negative leading to some confusion.

Share
 

It’s quite handy to show the standard windows open with dialog, which lists all installed applications along with a browse button. In Windows XP there is no straight forward way of doing this.

In Windows Vista onwards Microsoft has provided a shell API for this purpose. It’s called SHOpenWithDialog.

Some sample code (couldn’t test this though since I’m on XP at home)…

OPENASINFO Info = { 0 };
Info.oaifInFlags = OAIF_EXEC | OAIF_ALLOW_REGISTRATION;
SHOpenWithDialog(NULL, &Info);

A screenshot of the dialog in XP…

Open with dialog

Open with dialog

Share
 

Recently a user asked this question in MSDN forums. He had MBCS enabled for his application and also wanted to enable unicode characters in a particular edit control. Note MFC only create unicode controls if UNICODE is defined in project settings.

So in order to explicitly create controls that support UNICODE characters we use the ‘W’ functions. For example: for our user he should use “CreateWindowW” function. Note the last ‘W’ in the function. ANSI windows are created by calling “CreateWindowA” function (this also means that we can explicitly create a non-unicode control).

Also make sure you use only UNICODE API’s for a control created in this manner. For e.g. always call GetWindowTextW instead of GetWindowTextA. Never use MFC on such a control if it’s not created by MFC framework, I mean don’t call CWnd::Attach on this control. Mainly because MFC is operating in non-unicode environment but our control is a UNICODE one.

Now once such a control is created you can paste characters of different languages, for e.g. Chinese, Japanese, etc even though the application is not a UNICODE supporting one.

Share
 

The main reason for posting this issue is to help MFC/Win32 beginners. But anyway it’s a good read ;) , so once upon a time…

If you’re a beginner with window’s ComboBox controls then you might end up with a bald head thinking and thinking about a weird behavior of combo’s after it’s creation. The combo as such is a great control but there are certain important things to keep in mind while creating a combo.

So what are the problems? Well make it singular, “Problem”. One and the only problem with this control is shown in this screenshot…

Drop down not visible

Drop down not visible

I’ve clicked the drop down arrow but cannot see the dropdown. This one is created via resource editor. The problem here is, just adding a combo is not enough but we’ve also got to set the size of the drop down via resource editor. So follow these steps, as shown in the screen shot to fix this issue…

Resizing a combo drop down

Resizing a combo drop down

So that’s it. The white drag rectangle denotes combo drop down size. Press Ctrl + T to test this. Please note that this is not a bug but a feature of resource editor since there isn’t an another way to set drop down size for a combo via resource editor. But I agree that they should’ve set a better default drop down size instead of a zero height drop down.

Now this was via resource editor. It’s bit different when creating dynamically via code using CComboBox::Create function. Note that you have to give a valid height value. This height value will be the dropdown size for this combo.

See this e.g.

m_Combo.Create( WS_VISIBLE | WS_TABSTOP | WS_BORDER | CBS_DROPDOWNLIST | CBS_SORT,
                CRect( 10, 10, 200, 300 ),
                this,
                IDC_COMBO1 + 1 );
m_Combo.SetFont( GetFont(), TRUE );

Hope you’ve noticed the last big value given to Combo via CRect, 300. That’s the drop down height. Normally beginners set it to 30 or 40 which results in this issue.

See the resultant screenshot…

Result of properly creating a combo

Result of properly creating a combo

Not a big deal for experts but for beginners, well I’ve been a beginner too, it’s one painful issue. :)

Share
 

So what does SendInput API do?

SendInput API is a helper function to simulate keyboard and mouse inputs. It’s an ideal function to insert characters into a password which otherwise is not possible. Here is what MSDN says about this function…

“The SendInput function inserts the events in the INPUT structures serially into the keyboard or mouse input stream. These events are not interspersed with other keyboard or mouse input events inserted either by the user (with the keyboard or mouse) or by calls to keybd_event, mouse_event, or other calls to SendInput.”

Here is a simple and basic function which sends a given text to a foreground window (my earlier function was crap so I replaced it with this new one that works for all texts, haven’t tried out with foreign keyboards though)…

BOOL SendText( LPCSTR lpctszText )
{
   vector< INPUT > EventQueue;

   char Buff[120] = {0};
   GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, Buff, sizeof(Buff));
   HKL hKeyboardLayout = ::LoadKeyboardLayout( Buff, KLF_ACTIVATE );

   const int Len = strlen( lpctszText );
   for( int Index = 0; Index < Len; ++Index )
   {
      INPUT Event = { 0 };

      const SHORT Vk = VkKeyScanEx(lpctszText[Index], hKeyboardLayout);
      const UINT VKey = ::MapVirtualKey( LOBYTE( Vk ), 0 );

      if( HIBYTE( Vk ) == 1 ) // Check if shift key needs to be pressed for this key
      {
          // Press shift key
          ::ZeroMemory( &Event, sizeof( Event ));
          Event.type = INPUT_KEYBOARD;
          Event.ki.dwFlags = KEYEVENTF_SCANCODE;
          Event.ki.wScan = ::MapVirtualKey( VK_LSHIFT, 0 );
          EventQueue.push_back( Event );
      }

      // Keydown
      ::ZeroMemory( &Event, sizeof( Event ));
      Event.type = INPUT_KEYBOARD;
      Event.ki.dwFlags = KEYEVENTF_SCANCODE;
      Event.ki.wScan = VKey;
      EventQueue.push_back( Event );

      // Keyup
      ::ZeroMemory( &Event, sizeof( Event ));
      Event.type = INPUT_KEYBOARD;
      Event.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP;
      Event.ki.wScan = VKey;
      EventQueue.push_back( Event );

      if( HIBYTE( Vk ) == 1 )// Release if previouly pressed
      {
           // Release shift key
          ::ZeroMemory( &Event, sizeof( Event ));
          Event.type = INPUT_KEYBOARD;
          Event.ki.dwFlags = KEYEVENTF_SCANCODE| KEYEVENTF_KEYUP;
          Event.ki.wScan = ::MapVirtualKey( VK_LSHIFT, 0 );
          EventQueue.push_back( Event );
      }
   }// End for

   if( hKeyboardLayout )
   {
       UnloadKeyboardLayout( hKeyboardLayout );
   }

   return ::SendInput( EventQueue.size(), &EventQueue[0], sizeof( INPUT ));
}
Share
 

CComboBox has a function called SetHorizontalExtent which doesn’t work. SDK equivalent is CB_SETHORIZONTALEXTENT which also doesn’t work. The reason for this bug is pretty lame, because WS_HSCROLL style for combo box is not set, which in turn the VS dialog editor does not provide :( . So a workaround is to open .rc file in a text editor and add WS_HSCROLL style manually for this combo.

This is how a combo box will look with a horizontal scrollbar…

Horizontal scrollbar in a combo box

Horizontal scrollbar in a combo box

Here is a sample code from MSDN which is adapted a bit for this purpose…

void CDialoTestDlg::AddHScroll( CComboBox& Cmb )
{
  // Find the longest string in the combo box.
  CString str;
  CSize sz;
  int dx = 0;

  TEXTMETRIC tm = { 0 };
  CDC* pDC = Cmb.GetDC();
  CFont* pFont = Cmb.GetFont();

  // Select the listbox font, save the old font
  CFont* pOldFont = pDC->SelectObject(pFont);

  // Get the text metrics for avg char width
  pDC->GetTextMetrics(&tm);

  for (int i = 0; i < Cmb.GetCount(); i++)
  {
    Cmb.GetLBText(i, str);
    sz = pDC->GetTextExtent(str);

    // Add the avg width to prevent clipping
    sz.cx += tm.tmAveCharWidth;

    if (sz.cx > dx)
      dx = sz.cx;
  }

  // Select the old font back into the DC
  pDC->SelectObject(pOldFont);
  Cmb.ReleaseDC(pDC);

  // Set the horizontal extent so every character of all strings can
  // be scrolled to.
  Cmb.SetHorizontalExtent(dx);
}// End AddHScroll

Setting horizontal extent is useful to prevent the combo’s drop down width from extending beyond screen limits. Note that there is another function called SetDroppedWidth which sets the drop down’s width but with no scrolling.

Share
 

Use function PathIsDirectoryEmpty. Some working facts about this API…

  1. Returns false if directory does not exist
  2. Returns true is directory is empty
  3. Return false if directory contains nested directories but no files
  4. Returns false if there are files in directory
  5. Returns false if given path points to a directory
  6. C:\ is considered a directory
  7. Does not auto expand environment variables in given path
  8. Ok with spaces in path

Usage…

CString Directory = _T( "C:\\Nibu babu thomas" ); // This directory exists
const BOOL Empty = PathIsDirectoryEmpty( Directory ); // Call returns true
ASSERT( Empty );
Share
© 2012 bits and bytes Suffusion theme by Sayontan Sinha