This site hosted by Free.ProHosting.com
Google

Creating Program Groups

Applies To: C++Builder 1 or higher
Category: Technical Articles

 
Overview
 
SETUP programs can create a Start Menu group and icons for your application. Sometimes however you may need to add new program groups directly from your application. There are two basic ways to do this:
 
Method 1: DDE Command-String Interface
 
From the MSDN® documentation:

Program Manager has a DDE command-string interface that allows other applications to create, display, delete, and reload groups; add items to groups; replace items in groups; delete items from groups; and close Program Manager. The following commands perform these actions:
 
AddItem
ExitProgman
CreateGroup
Reload
DeleteGroup
ReplaceItem
DeleteItem
ShowGroup
 
Multiple commands can be concatenated; each command must be contained in square brackets, and parameters must be contained in parentheses and separated by commas. For example, the following set of commands adds Winapp.exe to the Windows Applications group.
 
[CreateGroup("Windows Applications")]
[ShowGroup("My Group",1)]
[AddItem(winapp.exe,Win App,winapp.exe,2)]

 
Link To DDE Server
 
At this point, you might wonder how exactly to link to the PROGMAN server and send the appropriate commands. This can be easily done using the TDdeClientConv component distributed with C++Builder (you'll find it in the System tab).
 
So let's give it a try. Start a new application and add a TDdeClientConv component. Add two Buttons and set their Caption to "Create Group 'CBDN'" and "Add Item 'Visit CBDN'". Now, edit the OnCreate event of your main form and insert:
 
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    if(!DdeClientConv1->SetLink("PROGMAN", "PROGMAN"))
    {
        ShowMessage("Cannot connect to DDE server!\n"
            "Application will be terminated.");
        Application->Terminate();
    }
    Button2->Enabled = false;
}
 
This simple code sets a link to the PROGMAN DDE sever. You can terminate this link in the OnClose event of your form:
 
void __fastcall TForm1::FormClose(TObject *Sender,
    TCloseAction &Action)
{
    DdeClientConv1->CloseLink();
}
 
Now all you need to do is send the appropriate commands using the ExecuteMacro method. To simplify this process, I've decided to create two functions: CreateGroup and AddItem. Open the header file of your main unit and insert:
 
private:
    void __fastcall CreateGroup(String Group);
    void __fastcall AddItem(String Item, String Path);
 
Go back to the source code and add the following lines:
 
void __fastcall TForm1::CreateGroup(String Group)
{
    String Macro;
    Macro = "[CreateGroup(" + Group + ")]";
    if(!DdeClientConv1->ExecuteMacro(Macro.c_str(), false))
    {
        ShowMessage("Cannot create group: " + Group);
        return;
    }
    Button1->Enabled = false;
    Button2->Enabled = true;
}
 
void __fastcall TForm1::AddItem(String Item, String Path)
{
    String Macro;
    Macro = "[AddItem(" + Path + ", " + Item + ")]";
    if(!DdeClientConv1->ExecuteMacro(Macro.c_str(), false))
    {
        ShowMessage("Cannot add item: " + Item);
        return;
    }
    Button2->Enabled = false;
}
 
Finally, edit the OnClick event of your two Buttons and insert:
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    CreateGroup("CBDN");
}
 
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    AddItem("Visit CBDN", "http://www.cbdn.cjb.net/");
}
 
Method 2: Shortcuts
 
As you may already know, all Start Menu folders and shortcuts are stored in the Windows directory, usually in C:\WINDOWS\Start Menu. Instead of using DDE, you could simply retrieve the path of the Start Menu folder and create new folders and shortcuts in it.
 
Once again, I decided to create three functions that will simplify this process: CreateShortcut, CreateGroup and AddItem. Now start a new application, open the header file of your main unit and insert:
 
private:
    HRESULT CreateShortcut(LPCSTR pszShortcutFile,
        LPSTR pszLink, LPSTR pszDesc);
    void __fastcall CreateGroup(String Group);
    void __fastcall AddItem(String Item, String path,
        String Group);
 
Go back to your main unit and add:
 
#include <shlobj.h>
 
HRESULT TForm1::CreateShortcut(LPCSTR pszShortcutFile,
    LPSTR pszLink, LPSTR pszDesc)
{
    HRESULT hres;
    IShellLink *psl;
 
    hres = CoCreateInstance(CLSID_ShellLink, NULL,
    CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
 
    if(SUCCEEDED(hres))
    {
        IPersistFile *ppf;
        hres = psl->QueryInterface (IID_IPersistFile,
        (LPVOID *)&ppf);
 
        if(SUCCEEDED(hres))
        {
            WCHAR wsz[MAX_PATH];
 
            hres = psl->SetPath(pszShortcutFile);
            if(!SUCCEEDED(hres))
                ShowMessage("Error in SetPath");
 
            hres = psl->SetDescription (pszDesc);
            if(!SUCCEEDED(hres))
                ShowMessage("Error in SetDescription");
 
            MultiByteToWideChar(CP_ACP, 0, pszLink, -1,
                wsz, MAX_PATH);
 
            hres = ppf->Save(wsz, true);
            if (!SUCCEEDED(hres))
                ShowMessage("Error in Save");
 
            ppf->Release();
        }
 
        psl->Release();
    }
 
    return hres;
}
 
void __fastcall TForm1::CreateGroup(String Group)
{
    LPITEMIDLIST pidl;
    char buf[MAX_PATH];
 
    SHGetSpecialFolderLocation(Handle, CSIDL_PROGRAMS, &pidl);
    SHGetPathFromIDList(pidl, buf);
 
    String path = buf;
    path += "\\" + Group;
 
    if(DirectoryExists(path))
    {
        ShowMessage("This group already exists: " + Group);
        return;
    }
 
    if(!CreateDirectory(path.c_str(), NULL))
    {
        ShowMessage("Cannot create group: + Group");
        return;
    }
 
    ShellExecute(Handle, "open", path.c_str(), NULL, NULL,
        SW_SHOWDEFAULT);
}
 
void __fastcall TForm1::AddItem(String Item, String Path,
    String Group)
{
    LPITEMIDLIST pidl;
    char buf[MAX_PATH];
 
    SHGetSpecialFolderLocation(Handle, CSIDL_PROGRAMS, &pidl);
    SHGetPathFromIDList(pidl, buf);
 
 
    String path, temp;
    path = buf;
    path += "\\" + Group;
    temp = path;
 
    if(!DirectoryExists(path))
    {
        ShowMessage("Cannot find group: " + Group);
        return;
    }
 
    path += "\\" + Item + ".lnk";
    if(FileExists(path))
    {
        ShowMessage("This item already exists: " + Item);
        return;
    }
 
    CoInitialize(NULL);
    HRESULT hres = CreateShortcut(Path.c_str(), path.c_str(),
        Item.c_str());
    CoUninitialize();
 
    if(!SUCCEEDED(hres))
    {
        ShowMessage("Cannot create shortcut: " + Item);
        return;
    }
 
    ShellExecute(Handle, "open", temp.c_str(), NULL, NULL,
        SW_SHOWDEFAULT);
}
 
(I won't have place to discuss the purpose of each line - you can consult the C++Builder documentation if you need more help.)
Create two Buttons, as described in Method 1. Edit their OnClick event:
 
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    CreateGroup("CBDN");
}
 
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    AddItem("Visit CBDN", "http://www.cbdn.cjb.net",
        "CBDN");
}
 
Conclusion
 
We've seen in this article many new techniques: setting a link to a DDE server, creating shortcuts using the IShellLink interface and retrieving the path of special folders using SHGetSpecialFolderLocation. As it often happens in the programming world, you have a choice of variety of methods to reach the same goal. The choice is yours!

C++Builder Developer's Network
Copyright © Yoto Yotov