How to use SetComputerName from the windows api? - winapi

I'm trying to change the name of the computer programmatically. Occasionally we have to wipe a system and restore it's database in an upgrade. I'm trying to have all the system settings be read out of the database and be set up automatically. Most of it is pretty simple stuff, but changing the name of the system is really throwing me for a loop.
EDIT: code edited to reflect changes from comments
if(dbHostName.length() > MAX_COMPUTERNAME_LENGTH)
{
dbHostName.truncate(MAX_COMPUTERNAME_LENGTH);
}
LPCTSTR cname = dbHostName.toStdWString().c_str();
bool nameset = SetComputerNameEx(ComputerNamePhysicalDnsHostname, cname);
if(nameset) qDebug() << "Computer name changed to" << dbHostName;
else qDebug() << "Computer name NOT changed!";
I'm taking a QString in, making sure it's not too long, converting it to a standard wide string, converting that to a LPCTSTR and then attempting to use that to change the computer name.
This returns false: Computer name not changed!

Giving credit to #user4581301 and #IInspectable for contributing the suggestions that led to the the below solutions. Both of these worked, I chose the second one because there does not appear to be an agreement on how best to convert a string to an LPCTSTR object.
if(dbHostName.length() > MAX_COMPUTERNAME_LENGTH)
{
dbHostName.truncate(MAX_COMPUTERNAME_LENGTH);
}
std::wstring wstring = dbHostName.toStdWString();
LPCTSTR cname = wstring.c_str();
SetComputerNameEx(ComputerNamePhysicalDnsHostname, cname);
And this is the actual solution I selected, but again, they both worked on Windows 8.1.
if(dbHostName.length() > MAX_COMPUTERNAME_LENGTH)
{
dbHostName.truncate(MAX_COMPUTERNAME_LENGTH);
}
std::string sname = dbHostName.toStdString();
LPCSTR cname = sname.c_str();
SetComputerNameExA(ComputerNamePhysicalDnsHostname, cname);
Edit 5/24/18: Incidentally this also works, and is much more concise
bool nameSet = SetComputerNameEx(ComputerNamePhysicalDnsHostname, dbHostName.toStdWString().c_str());

Related

QLocale detects system language incorrectly on Windows with language pack installed

I am attempting to detect current system language with QLocale:
QLocale::Language sysLangId = QLocale::system().language();
However, it's not working correctly. I'm on Russian Windows 7 with English language pack applied, but language() returns Russian instead of English. Is there any workaround?
When I was working on Localization in Qt, I used
QString locale = QLocale::system().name();
When I tested getting the locale, I found it was dependent on the Format in the Region and Language settings:
Control Panel > Region and Language > Format
Hope that helps.
I've found 2 ways to solve my problem. The Qt way is to use QLocale::system().uiLanguages().
On my system it returns a list with a single item "en-US". The problem with that is I need a language name, like "english", so I'd have to add a map for converting language code to language name. It's no big deal, but I've decided to use WinAPI:
QString sysLangName;
const LANGID langId = GetUserDefaultUILanguage();
WCHAR langName[1000] = {0};
if (GetLocaleInfoW(MAKELCID(langId, SORT_DEFAULT), LOCALE_SENGLANGUAGE, langName, sizeof langName / sizeof langName[0] - 1) != 0)
sysLangName = QString::fromWcharArray(langName);
I had the same problem and I solved with this code.
QString local = QLocale::languageToString(QLocale::system().language());
To get the language name you can simply use QLocale::languageToString(QLocale::system().language()); or maybe QLocale::system().nativeLanguageName(); but the real problem is as you mentioned that the QLocale::system() does not always match the actual system locale on windows. This can be observed if you change the locale during program execution. In this case the QLocale::system() does not get up-to-date and returns the old value. Here is a workaround I used in Qt5:
class WinEventFilter : public QAbstractNativeEventFilter
{
public:
bool nativeEventFilter(const QByteArray &eventType, void *message, long *result)
{
if (((MSG*)message)->message == WM_WININICHANGE )
{
// Workaround - in Qt5 the system locale is not up to date and we have to manually update it.
#ifdef _DEBUG
QLibrary lib("Qt5Cored.dll");
#else
QLibrary lib("Qt5Core.dll");
#endif
void (* func)() = lib.resolve("?updateSystemPrivate#QLocalePrivate##SAXXZ");
if (func)
func();
else
qDebug()<<"! Unable to resolve updateSystemPrivate()";
// Workaround end
qDebug()<<"WM_WININICHANGE"<<QLocale::languageToString(QLocale::system().language());
}
return false;
}
};
and my application class constructor looks like this:
MyApplication::MyApplication( int & argc, char ** argv )
: QApplication(argc, argv)
{
WinEventFilter *pFilter = new WinEventFilter(this);
installNativeEventFilter(m_pEventFilter);
}
Hope this helps.

How to use VerQueryValue?

I've an exe that will need to retrieve version infomation from a specific dll (ex : FileDescription). My codes already called the GetFileVersionInfoSize and GetFileVersionInfo. But I'm not sure how to apply the VerQueryValue, even after going through http://msdn.microsoft.com/en-us/library/ms647464(v=vs.85) and other examples.
Can someone explain/shed some light on how to apply VerQueryValue and its usage? Thanks.
To get the FileDescription via VerQueryValue, just copy and paste the example code from the VerQueryValue documentation, and modify it as appropriate.
The basic idea behind that example code is:
Use the second form (\VarFileInfo\Translation) to get the list of translations.
Then use the third form (\StringFileInfo\lang-codepage\string-name) to get the string(s).
(The first form () is just for the VS_FIXEDFILEINFO, a set of numerical values for parts of the version number, the flags, etc.)
The example code gets the FileDescription for each language. If you know you only have one language (e.g., because you're looking at your own app, and it isn't translated), you can skip the loop and just return the first one. For more general use, you want to pick the best match for the user's language and return that one.
This is a working example, after many try and errors. I'm using Borland C++, so minor details may need to be changed for incompabible environments.
#include <Windows.h>
std::string GetAppVersion()
{
DWORD dwHandle;
TCHAR fileName[MAX_PATH];
GetModuleFileName(NULL, fileName, MAX_PATH);
DWORD dwSize = GetFileVersionInfoSize(fileName, &dwHandle );
TCHAR buffer[dwSize];
VS_FIXEDFILEINFO* pvFileInfo = NULL;
UINT fiLen = 0;
if ((dwSize > 0) && GetFileVersionInfo(fileName, dwHandle, dwSize, &buffer))
{
VerQueryValue(&buffer, L"\\", (LPVOID*)&pvFileInfo, &fiLen);
}
if (fiLen > 0)
{
char buf[25];
int len = sprintf(buf, "%hu.%hu.%hu.%hu",
HIWORD(pvFileInfo->dwFileVersionMS),
LOWORD(pvFileInfo->dwFileVersionMS),
HIWORD(pvFileInfo->dwFileVersionLS),
LOWORD(pvFileInfo->dwFileVersionLS)
);
return std::string(buf, len);
}
else
{
return std::string("(Unknown)");
}
}

Force GetKeyNameText to english

The Win32 function GetKeyNameText will provide the name of keyboard keys in the current input locale.
From MSDN:
The key name is translated according to the layout of the currently
installed keyboard, thus the function may give different results for
different input locales.
Is it possible to force the input locale for a short amount of time? Or is there another alternative to GetKeyNameText that will always return the name in English?
Update: This answer does not work. It actually modifies the keyboard settings of the user. This appear to be a behavior change between Windows versions.
CString csLangId;
csLangId.Format( L"%08X", MAKELANGID( LANG_INVARIANT, SUBLANG_NEUTRAL ) );
HKL hLocale = LoadKeyboardLayout( (LPCTSTR)csLangId, KLF_ACTIVATE );
HKL hPrevious = ActivateKeyboardLayout( hLocale, KLF_SETFORPROCESS );
// Call GetKeyNameText
ActivateKeyboardLayout( hPrevious, KLF_SETFORPROCESS );
UnloadKeyboardLayout( hLocale );
WARNING: GetKeyNameText is broken (it returns wrong A-Z key names for non-english keyboard layouts since it uses MapVirtualKey with MAPVK_VK_TO_CHAR that is broken), keyboard layout dlls pKeyNames and pKeyNamesExt text is bugged and outdated. I cannot recommend dealing with this stuff at all. :)
If you're really-really want to get this info - then you can load and parse it manually from keyboard layout dll file (kbdus.dll, kbdger.dll etc).
There is a bunch of undocumented stuff involved:
In order to get proper keyboard layout dll file name first you need to convert HKL to KLID string. You can do this via such code:
// Returns KLID string of size KL_NAMELENGTH
// Same as GetKeyboardLayoutName but for any HKL
// https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/windows-language-pack-default-values
BOOL GetKLIDFromHKL(HKL hkl, _Out_writes_(KL_NAMELENGTH) LPWSTR pwszKLID)
{
bool succeded = false;
if ((HIWORD(hkl) & 0xf000) == 0xf000) // deviceId contains layoutId
{
WORD layoutId = HIWORD(hkl) & 0x0fff;
HKEY key;
CHECK_EQ(::RegOpenKeyW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts", &key), ERROR_SUCCESS);
DWORD index = 0;
while (::RegEnumKeyW(key, index, pwszKLID, KL_NAMELENGTH) == ERROR_SUCCESS)
{
WCHAR layoutIdBuffer[MAX_PATH] = {};
DWORD layoutIdBufferSize = sizeof(layoutIdBuffer);
if (::RegGetValueW(key, pwszKLID, L"Layout Id", RRF_RT_REG_SZ, nullptr, layoutIdBuffer, &layoutIdBufferSize) == ERROR_SUCCESS)
{
if (layoutId == std::stoul(layoutIdBuffer, nullptr, 16))
{
succeded = true;
DBGPRINT("Found KLID 0x%ls by layoutId=0x%04x", pwszKLID, layoutId);
break;
}
}
++index;
}
CHECK_EQ(::RegCloseKey(key), ERROR_SUCCESS);
}
else
{
WORD langId = LOWORD(hkl);
// deviceId overrides langId if set
if (HIWORD(hkl) != 0)
langId = HIWORD(hkl);
std::swprintf(pwszKLID, KL_NAMELENGTH, L"%08X", langId);
succeded = true;
DBGPRINT("Found KLID 0x%ls by langId=0x%04x", pwszKLID, langId);
}
return succeded;
}
Then with KLID string you need to go to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts\%KLID% registry path and read Layout File string from it.
Load this dll file from SHGetKnownFolderPath(FOLDERID_System, ...) (usually C:\Windows\System32) with LoadLibrary() call.
Next you need to do GetProcAddress(KbdDllHandle, "KbdLayerDescriptor") - you're receive pointer that can be casted to PKBDTABLES.
There is kbd.h header in Windows SDK that have KBDTABLES struct definition (there is some stuff involved to use proper KBD_LONG_POINTER size for x32 code running on x64 Windows. See my link to Gtk source at the end).
You have to look at pKeyNames and pKeyNamesExt in it to get scan code -> key name mapping.
Long story short: The GTK toolkit have the code that doing all this(see here and here). Actually they are building scan code -> printed chars tables from Windows keyboard layout dlls.

Drive Letter to Device Instance ID

How do I get from a drive letter to a device instance ID?
My process starts with a device arrival message. I have been successful in getting the drive letter from the arrival message and in opening the dvd tray.
I have searched the various Setup API items; but I haven't found anything that gets me from a drive letter to a device instance ID.
A solution in C# or VB.NET would be ideal, but I'm willing to figure it out from any other language as long as I can see the API calls.
Thanks in advance...
You cannot do it directly.
The link is to use STORAGE_DEVICE_NUMBER. You can use DeviceIoControl with IOCTL_STORAGE_GET_DEVICE_NUMBER on your device name to populate this structure. Put this value to one side.
You then need to get device infomation on your system using SetupDiGetClassDevs setting the GUIDS as approriate, indicicating the drives your are insterested in. Then enumerate through the devices using SetupDiEnumDeviceInfo. Then enumerate the interfaces using SetupDiEnumDeviceInterfaces and finally get the information using SetupDiGetDeviceInterfaceDetail. In this structure returned you can get a DevicePath you can use to get the STORAGE_DEVICE_NUMBER as above. Match this with the STORAGE_DEVICE_NUMBER from your drive letter, and you have now linked a driver letter to your structure. Phew! Inside this structure is a DevInst.
i know it's late for you now but not for everybody ^^
I had the same need and this is main line of how I did it:
-You need a window to receive device arrival and removal (as you said)
-Then you create a DeviceNotificationFilter initiated to dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
-Then in the message loop of your window you look for VM_DEVICECHANGE
-When u receive it if wParam == DBT_DEVICEARRIVAL, use the lParam to check if it is a DBT_DEVTYPE_VOLUME (i was getting the letter and the type of the drive here) or a DBT_DEVTYPE_DEVICEINTERFACE ( there you can use your wellcasted lParam to get the InstanceId from the input structure).
When you connect a drive your receive DEVINTERFACE first then the other.
I only give the main line beacause i did this long time ago and i don't have the code here, and also I had found a lot of code pieces on the net (long time ago so there should be more now ^^^) maybe msdn give a full code example to do that now.
If you read this and need more informations, i'll reply or make a full documented answer if many need it.
Hope it will help some of you.
I know it's years later but I had to do this and searching brought me here and #DanDan 's answer worked. In order to save future people a lot of work, I thought I'd give back a little and present the technique a bit more explicitly. You'll still have to write a bit of code, but the part I found difficult is below as code:
As DanDan mentioned, the idea is to use CreateFile and DeviceIoControl to get the Windows STORAGE_DEVICE_NUMBER for the disk associated with a file path, and then use the Setup API to enumerate disk devices until we find one whose device instance equals the SDN.
First, here's a summary of how you get the STORAGE_DEVICE_NUMBER from the path (e.g. c:\\users\\bob);
Strip the path to the root (e.g down to C:) and prepend it with \\\\.\\ so you have \\\\.\\C:
Open that path up using CreateFileW with to get metadata
Use DeviceIoControl with IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS to get the extents
Get the DiskNumber member from the first extent returned.
Close the file
Open up \\\\.\\PhysicalDrive<n> where <n> is the that DiskNumber from the first extent
Use DeviceIoControl with code IOCTL_STORAGE_GET_DEVICE_NUMBER to get make it fill out a STORAGE_DEVICE_NUMBER struct as output
Use SetupDiGetClassDevs with arguments &GUID_DEVCLASS_DISKDRIVE and DICGF_PRESENT to get all disks on the system
In a loop, use SetupDiEnumDeviceInfo to get a SP_DEVINFO_DATA repeatedly (on the device list returned by step #8 above) and a call the function below to determine which one, if any, matches the STORAGE_DEVICE_NUMBER for the give path.
(This is edited to remove custom utility classes of mine right on the SO web page so I might have introduced errors/typos)
bool DoesDeviceInstanceEqualStorageDeviceNumber(
const std::string& devInstance,
STORAGE_DEVICE_NUMBER sdn)
{
// Open up this device instance, specifying that we want the *interfaces*.
// The interfaces are key key because examining them will let us get a
// string we can use the Win32 CreateFile function.
const auto hDevInfo = SetupDiGetClassDevsA(
nullptr,
devInstance.c_str(),
nullptr,
DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE)
throws std::runtime_error("Unable to get disk devices");
DWORD dwSize = 0;
SP_DEVINFO_DATA did;
WCHAR buffer[4096];
did.cbSize = sizeof (did);
bool foundValidMatch = false;
int deviceNumber = 0;
// Iterate through all such devices, looking for one that has a storage device number that matches the given one.
while ( !foundValidMatch && SetupDiEnumDeviceInfo(hDevInfo, deviceNumber, &did))
{
deviceNumber++;
DEVPROPTYPE devPropType;
// We'll only bother comparing this one if it is fixed. Determine that.
const auto getPropResult = SetupDiGetDevicePropertyW (
hDevInfo,
&did,
&DEVPKEY_Device_RemovalPolicy, // Ask for the "removal policy"
&devPropType,
(BYTE*)buffer,
sizeof(buffer),
&dwSize,
0);
if (!getPropResult)
{
std::cerr << "Unable to to get removal policy for disk device: " << ::GetLastError() << std::endl;
continue;
}
/* This bit *would* skip removable disks, you wanted...
else if (buffer[0] != 1)
{
std::cerr << "Skipping removable disk device " << devInstance << std::endl;
continue;
}
*/
// OK this is a fixed disk so it might be the one we'll compare against
// 1. Get the very first disk interface from this particular disk device
// 2. Open a file on it
// 3. Query the resulting file for its device number.
// 4. Compare the device number to the one we determined above
// 5. If it matches ours, then we succeed. If not, continue
SP_DEVICE_INTERFACE_DATA devIntData;
devIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
// Get the disk interfaces
const auto result = SetupDiEnumDeviceInterfaces(
hDevInfo,
&did, //&did,
&GUID_DEVINTERFACE_DISK, // Get Disk Device Interface (from winioctl.h)
0, // We only need the very FIRST one. I think...
&devIntData);
if (!result)
continue;
DWORD dwRequiredSize = 0;
// Want to get the detail but don't yet know how much space we'll need
// Do a dummy call to find out
SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&devIntData,
nullptr,
0,
&dwRequiredSize,
nullptr);
if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
{
std::cerr << "Unable to get device interface Detail: " << ::GetLastError() << std::endl;;
}
else
{
// Get the detail data so we can get the device path and open a file.
std::vector<TCHAR> buf(dwRequiredSize);
auto pDidd = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buf.data());
// WARNING: HARD CODED HACK
// ------------------------
// https://stackoverflow.com/questions/10405193/vb-net-hid-setupdigetdeviceinterfacedetail-getlasterror-shows-1784-error-inv
//
// Don't ask. Just do what they tell you.
// -----------------------------------------------------------------
#ifdef BUILD_64
pDidd->cbSize = 8;
#else
pDidd->cbSize = 6;
#endif
// -----------------------------------------------------------------
if (!SetupDiGetDeviceInterfaceDetail(
hDevInfo,
&devIntData,
pDidd,
dwRequiredSize,
&dwRequiredSize,
nullptr))
{
std::cerr << "Cannot get interface detail: " << ::GetLastError());
}
else
{
// FINALLY: We now have a DevicePath that we can use to open up
// in a Win32 CreateFile() call. That will let us get the
// STORAGE_DEVICE_NUMBER and compare it to the one we were given.
const auto hFile = ::CreateFileW(pDidd->DevicePath, 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE != hFile)
{
std::cerr << "Unable to open logical volume: " + devicePath << std::endl;
continue;
}
STORAGE_DEVICE_NUMBER sdnTest;
ZeroMemory(&sdnTest, sizeof(STORAGE_DEVICE_NUMBER));
if (0 == DeviceIoControl(
hDevInfo
IOCTL_STORAGE_GET_DEVICE_NUMBER,
nullptr, // output only so not needed
0, // output only so not needed
&sdnTest,
sizeof(STORAGE_DEVICE_NUMBER),
nullptr,
nullptr))
{
std::cerr << "Unable to determine storage device number: " << ::GetLastError() << std::endl;);
}
else
{
// All this for a one-line test...
foundValidMatch = sdnTest.DeviceNumber == sdn.DeviceNumber;
}
}
}
}
SetupDiDestroyDeviceInfoList(hDevInfo);
return foundValidMatch;
}
I hope this saves someone a headache

Windows 7 - Taskbar - Pin or Unpin Program Links

As in title, is there any Win32 API to do that?
Don't do this.
I'm 99% sure there isn't an official API for it, for exactly the same reason that there wasn't programmatic access to the old Start Menu's pin list.
In short, most users don't want programs putting junk in their favorites, quick launch, taskbar, etc. so Windows doesn't support you doing as such.
I'm trying to implement a VirtuaWin (opensource virtual desktop software) plugin that allows me to pin different buttons to different virtual desktops. Completely valid reason to use this.
Found the way to pin/unpin it already:
Following code snippet is taken from Chromium shortcut.cc file, nearly unchanged, see also the ShellExecute function at the MSDN
bool TaskbarPinShortcutLink(const wchar_t* shortcut) {
int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarpin", shortcut,
NULL, NULL, 0));
return result > 32;
}
bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
shortcut, NULL, NULL, 0));
return result > 32;
}
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
Seems pretty straightforward if you know the shortcut. For me though this is not sufficient, I also need to iterate over existing buttons and unpin and repin them on different desktops.
In the comments of a Code Project article it says all you have to do is create a symbolic link in the folder "C:\Users\Username\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar".
But it appears to generally be unsociable practice, as the other comments here have noted.
You can pin/unpin apps via Windows Shell verbs:
http://blogs.technet.com/deploymentguys/archive/2009/04/08/pin-items-to-the-start-menu-or-windows-7-taskbar-via-script.aspx
For API, there is a script-friendly COM library for working with the Shell:
http://msdn.microsoft.com/en-us/library/bb776890%28VS.85%29.aspx
Here is an example written in JScript:
// Warning: untested and probably needs correction
var appFolder = "FOLDER CONTAINING THE APP/SHORTCUT";
var appToPin = "FILENAME OF APP/SHORTCUT";
var shell = new ActiveXObject("Shell.Application");
var folder = shell.NameSpace(appFolder);
var folderItem = folder.ParseName(appToPin);
var itemVerbs = folderItem.Verbs;
for(var i = 0; i < itemVerbs.Count; i++)
{
// You have to find the verb by name,
// so if you want to support multiple cultures,
// you have to match against the verb text for each culture.
if(itemVerbs[i].name.Replace(/&/, "") == "Pin to Start Menu")
{
itemVerbs[i].DoIt();
}
}
Just to put some links on the info as microsoft now offer an official documentation on "Taskbar Extensions" :
A small set of applications are pinned
by default for new installations.
Other than these, only the user can
pin further applications; programmatic
pinning by an application is not
permitted.
So Kevin Montrose answer is the correct one : DON'T.
It works, but not for all OS, e.g. Windows 10:
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
private static void PinUnpinTaskBar(string filePath, bool pin)
{
if (!File.Exists(filePath))
throw new FileNotFoundException(filePath + " not exists!");
int MAX_PATH = 255;
var actionIndex = pin ? 5386 : 5387; // 5386 is the DLL index for"Pin to Tas&kbar", ref. http://www.win7dll.info/shell32_dll.html
StringBuilder szPinToStartLocalized = new StringBuilder(MAX_PATH);
IntPtr hShell32 = LoadLibrary("Shell32.dll");
LoadString(hShell32, (uint)actionIndex, szPinToStartLocalized, MAX_PATH);
string localizedVerb = szPinToStartLocalized.ToString();
// create the shell application object
dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
dynamic directory = shellApplication.NameSpace(path);
dynamic link = directory.ParseName(fileName);
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if ((pin && verb.Name.Equals(localizedVerb)) || (!pin && verb.Name.Contains(localizedVerb)))
{
verb.DoIt();
break;
}
}
}
I found there is no offical API to do that, but someone has do it through VBScript.
http://blog.ananthonline.net/?p=37
Thanks.
this folder contains shortcut of pinned application
C:\Users\Your-User-Name\AppData\Roaming\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar

Resources