How press "shift" + "command" + "3" simultaneously programmatically - macos

I am trying to capture a screenshot by posting this Keyboard event. I tried below way which doesn't work:
1]
CGEventRef event1, event2, event3, event4, event5, event6;
event1 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)56, true);
event2 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)55, true);
event3 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)85, true);//or 20
event4 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)85, false);// or 20
event5 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)55, false);
event6 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, event1);
CGEventPost(kCGSessionEventTap, event2);
CGEventPost(kCGSessionEventTap, event3);
CGEventPost(kCGSessionEventTap, event4);
CGEventPost(kCGSessionEventTap, event5);
CGEventPost(kCGSessionEventTap, event6);
CFRelease(event1);
CFRelease(event2);
CFRelease(event3);
CFRelease(event4);
CFRelease(event5);
CFRelease(event6);
2]
CGEventRef event7;
event7 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)85, true);//or 20
CGEventSetFlags(event7, kCGEventFlagMaskShift);
CGEventSetFlags(event7, kCGEventFlagMaskCommand);
CGEventPost(kCGSessionEventTap, event7);
CFRelease(event7);
Both are not working. Suggest me any good option.

1] doesn't work because you don't set the event flags. 2] doesn't work because you don't post the key-up event and CGEventSetFlags(event7, kCGEventFlagMaskCommand); replaces the kCGEventFlagMaskShift flag. Set both flags:
CGEventRef event7;
event7 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)85, true);//or 20
CGEventSetFlags(event7, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
CGEventPost(kCGSessionEventTap, event7);
CFRelease(event7);
event7 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)85, false);//or 20
CGEventSetFlags(event7, kCGEventFlagMaskShift | kCGEventFlagMaskCommand);
CGEventPost(kCGSessionEventTap, event7);
CFRelease(event7);

Related

Why ComboBox does not set and get ItemData in a loop?

I want to save a 'username' and 'ID' in a ComboBox in visual Studio 2010 and WIN32 (c++). Following code is for setting itemdata to combobox:
HWND hCB;
hCB = CreateWindowEx(0, L"COMBOBOX", 0x00,
CBS_DROPDOWNLIST | CBS_HASSTRINGS | CBS_NOINTEGRALHEIGHT | WS_CHILD | WS_VISIBLE | WS_VSCROLL,
10, 10, 200, 100,
hWnd, NULL, (HINSTANCE) GetWindowLong(hWnd, GWL_HINSTANCE), NULL);
WCHAR sTemp[256];
for (int i =0 ; i<4 ; i++)
{
wsprintf(sTemp, L"%s%d", L"User", i);
SendMessage(hCB, CB_ADDSTRING, 0, (LPARAM) sTemp);
wsprintf(sTemp, L"%s%d", L"ID", i);
SendMessage(hCB, CB_SETITEMDATA, (WPARAM)i, (LPARAM)sTemp);
}
And code for getting itemdata from combobox is:
WCHAR *sTemp;
for (int i=0; i < 4 ; i++)
{
sTemp = (WCHAR *)SendMessage(hCB, CB_GETITEMDATA, (WPARAM)i, 0);
MessageBox(NULL, sTemp, NULL, MB_OK);
}
The problem is, MessageBox does not show anything.
When I checked the first part of code by adding a CB_GETITEMDATA message for each combobox item, and a MessageBox to show result, I've found that all values of items' itemdata is "ID3", that is the last itemdata in the list.
I think the codes are straightforward, But I can't find what wrong is with my code?
You set the items' data to a pointer to the sTemp temporary string (the same for all items by the way).
When that variable goes out of scope, the pointer becomes invalid, and de-referencing it may cause a crash.
P.S. Just noticed your last comment. If you do it in the same scope, the sTemp contains the last text you wsprintf'ed to it, and every item points to it.
[added]
If you want to add string data to each item in your ComboBox, you need to allocate those strings. For example:
char* p = new char[some_buffer_length];
Then use that p in your call to set item data.
Don't forget to free the memory when you are done by calling:
delete [] p;
for each of your items.

WinAPI Cannot InsertMenuItem

I'm creating a menu in Win32 App. I want to create 2 items for submenu, but it shows only one.
BOOL foo;
hSubMenu = CreatePopupMenu();
if (!hSubMenu)
return NULL;
InsertMenu(hMenu, 0, MF_BYCOMMAND | MF_STRING | MF_POPUP, (UINT)hSubMenu, L"1 sub");
HBITMAP hBitmap = (HBITMAP)LoadImage(hInst, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 16, 16, LR_DEFAULTCOLOR);
MENUITEMINFO mii;
ZeroMemory(&mii, sizeof(MENUITEMINFO));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_CHECKMARKS | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
mii.fType = MFT_STRING;
mii.fState = MFS_DEFAULT;
mii.wID = ID_FILE_QUIT;
mii.hbmpChecked = hBitmap;
mii.hbmpUnchecked = hBitmap;
mii.dwTypeData = L"Item";
mii.cch = 4;
foo = InsertMenuItem(hSubMenu, 0, TRUE, &mii);
ZeroMemory(&mii, sizeof(MENUITEMINFO));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_CHECKMARKS | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
mii.fType = MFT_STRING;
mii.fState = MFS_DEFAULT;
mii.wID = ID_FILE_QUIT1;
mii.hbmpChecked = hBitmap;
mii.hbmpUnchecked = hBitmap;
mii.dwTypeData = L"Item abc";
mii.cch = 4;
foo = InsertMenuItem(hSubMenu, 1, TRUE, &mii);
TCHAR buff[256];
if (!foo)
{
DWORD i = GetLastError();
if (i)
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, i, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buff, 256, NULL); // it says "The parameter is incorrect"
}
}
I don't know where's the problem. Could anyone help me? Your suggestion will be apprreciated!
You can have only one default menu item. But, you specified MFS_DEFAULT for both. Try changing one of them to MFS_ENABLED.
P.S. It happens when you copy/paste your code :-)

What does Windows Installer itself actually do and why do I never see an msi made without third party tools?

So I have used a number of tools to create msi installers for my stuff, including things like WiX and a couple of the many GUI's around.
However one thing that I have never really worked out is what part does Windows Installer itself actually do, and where do these tools start and end? For that matter, what exactly is the msi technically, and why is it no one (I couldn't even find information on how it could be done in theory, like if its actually just some kind of DLL type thing that implements a simple interface) create an msi themselves, without using one of these tools to make it for them?
Some years ago I asked me myself the questions like "What is MSI file?", "How one can create or decode it?", "Why MSI database structure looks so strange?". So I answered for me on the questions. If you have an interest I can share the knowledge with you.
About the history. Windows Installer technology was introduced by Microsoft Office installer team during developing of Office 2000 setup. Before that Office 97 setup was STF based. The STF file consist from two tables: one with general information which can be compared with Properties table of MSI and another table which described the order of execution of different installation steps. Three main starting modes was supported: Installation (Administrative installation is sub-mode), Remove and Maintains. Office Software was going more and more complex and Microsoft wanted to make the setups more stable. See here for some more information.
Last years of the 20-century was the COM and COM+ time at Microsoft. The format of WinWord documents was COM Structured Storage too. So the format of the MSI file was chosen also as the structured storage. In general one wanted only to save some separate information like tables in one file. It's important that some tables will be modified during the installation. So one want be sure that the whole MSI file will be not corrupted in case of failed installation. The structured storage provided minimal support for the case, so the format will used since the time. The modified MSI files will be saved in the %SystemRoot%\Installer folder.
If you open MSI file with respect of Orca tool and export all tables in the files you will have exact the same information set which you have in MSI. You can modify the text tiles and then import the files back. If you would get some empty MSI file and import the tables you will create new Windows Installer setup.
Windows SDK has a list of Scripts which you can find in the C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\sysmgmt\msi\scripts folder. You can use the scripts to create empty MSI and to import the tables in MSI. So you can use the scripts instead of WiX. The WiX uses XML format which makes the input information more readable as idt-files (tables exported by Orca) and more easy to maintain.
For better understanding I wrote some years ago some small utilities which create the empty MSI files without Windows Installer API and which just used COM Structured Storage API. Additionally I created utility which decodes full information from MSI files on the low-level (also without usage of Windows Installer API). So I am sure that MSI files are really not more as I described above.
I see that you are C/C++ developer. If it would be interesting for you you can play with the C program which create empty MSI.
#define STRICT
#define _WIN32_WINNT 0x501
#define COBJMACROS
#include <stdio.h>
#include <windows.h>
#pragma warning (disable: 4201)
#include <ShLwApi.h> // for wnsprintf
#pragma warning (default: 4201)
#include <malloc.h> // for _alloca
#include <lmerr.h>
#include <tchar.h>
#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
#define CONST_STR_LEN(s) (ARRAY_SIZE(s) - 1)
#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "ShLwApi.lib")
#define MIDL_DEFINE_GUID(type,name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) const type name = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
MIDL_DEFINE_GUID (CLSID, CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); //.mst
MIDL_DEFINE_GUID (CLSID, CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); //.msi, .msm
MIDL_DEFINE_GUID (CLSID, CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); //.msp
// This function do almost the same as Base64 encoding used for example in MIME (see 6.8 in http://www.ietf.org/rfc/rfc2045.txt).
// Base64 convert codes from 0 till 63 (0x3F) to the corresponding character from the array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
// This function convert it to the corresponding character from the another array '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._'
static BYTE MsiBase64Encode (BYTE x)
{
// 0-0x3F converted to '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._'
// all other values higher as 0x3F converted also to '_'
if (x < 10)
return x + '0'; // 0-9 (0x0-0x9) -> '0123456789'
else if (x < (10+26))
return x - 10 + 'A'; // 10-35 (0xA-0x23) -> 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
else if (x < (10+26+26))
return x - 10 - 26 + 'a'; // 36-61 (0x24-0x3D) -> 'abcdefghijklmnopqrstuvwxyz'
else if (x == (10+26+26)) // 62 (0x3E) -> '.'
return '.';
else
return '_'; // 63-0xffffffff (0x3F-0xFFFFFFFF) -> '_'
}
#pragma warning (disable: 4706)
static UINT DecodeStreamName (LPWSTR pszInStreamName, LPWSTR pszOutStreamName)
{
WCHAR ch;
DWORD count = 0;
while ((ch = *pszInStreamName++)) {
if ((ch >= 0x3800) && (ch < 0x4840)) {
// a part of Unicode charecterd used with CJK Unified Ideographs Extension A. (added with Unicode 3.0) used by
// Windows Installer for encoding one or two ANSI characters. This subset of Unicode characters are not currently
// used nether in "MS PMincho" or "MS PGothic" font nor in "Arial Unicode MS"
if (ch >= 0x4800) // 0x4800 - 0x483F
// only one charecter can be decoded
ch = (WCHAR) MsiBase64Encode ((BYTE)(ch - 0x4800));
else { // 0x3800 - 0x383F
// the value contains two characters
ch -= 0x3800;
*pszOutStreamName++ = (WCHAR) MsiBase64Encode ((BYTE)(ch & 0x3f));
count++;
ch = (WCHAR) MsiBase64Encode ((BYTE)((ch >> 6) & 0x3f));
}
}
// all characters lower as 0x3800 or higher as 0x4840 will be saved without changes
*pszOutStreamName++ = ch;
count++;
}
*pszOutStreamName = L'\0';
return count;
}
#pragma warning (default: 4706)
#define INVALID_DECODING_RESULT ((BYTE)(-1))
// This function do almost the same as Base64 decoding used for example in MIME (see 6.8 in http://www.ietf.org/rfc/rfc2045.txt).
// Base64 convert character from the array 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' to the corresponding codes from 0 till 63 (0x3F)
// This function convert character from the another array '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' to it to 0 till 63 (0x3F)
static BYTE MsiBase64Decode (BYTE ch)
// returns values 0 till 0x3F or 0xFF in the case of an error
{
// only '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' are allowed and converted to 0-0x3F
if ((ch>=L'0') && (ch<=L'9')) // '0123456789' -> 0-9 (0x0-0x9)
return ch-L'0';
else if ((ch>=L'A') && (ch<=L'Z')) // 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -> 10-35 (26 chars) - (0xA-0x23)
return ch-'A'+10;
else if ((ch>=L'a') && (ch<=L'z')) // 'abcdefghijklmnopqrstuvwxyz' -> 36-61 (26 chars) - (0x24-0x3D)
return ch-L'a'+10+26;
else if (ch==L'.')
return 10+26+26; // '.' -> 62 (0x3E)
else if (ch==L'_')
return 10+26+26+1; // '_' -> 63 (0x3F) - 6 bits
else
return INVALID_DECODING_RESULT; // other -> -1 (0xFF)
}
#define MAX_STREAM_NAME 0x1f
static void EncodeStreamName (BOOL bTable, LPCWSTR pszInStreamName, LPWSTR pszOutStreamName, UINT cchOutStreamName)
{
LPWSTR pszCurrentOut = pszOutStreamName;
if (bTable) {
*pszCurrentOut++ = 0x4840;
cchOutStreamName--;
}
while (cchOutStreamName--) {
WCHAR ch = *pszInStreamName++;
if (ch && (ch < 0x80) && (MsiBase64Decode((BYTE)ch) <= 0x3F)) {
WCHAR chNext = *pszInStreamName;
// MsiBase64Decode() convert any "standard" character '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz._' to 0-0x3F.
// One can pack two charecters together in 0-0xFFF. To do so, one needs convert the first one with respect of MsiBase64Decode(),
// convert the next character also with respect MsiBase64Decode() and shift it 6 bits on the left. Two characters together
// produce a value from 0 till 0xFFF. We add 0x3800 to the result. We receive a value between 0x3800 and 0x47FF
if (chNext && (chNext < 0x80) && (MsiBase64Decode((BYTE)chNext) <= 0x3F)) {
ch = (WCHAR)(MsiBase64Decode((BYTE)ch) + 0x3800 + (MsiBase64Decode((BYTE)chNext)<<6));
pszInStreamName++;
}
else
ch = MsiBase64Decode((BYTE)ch) + 0x4800;
}
*pszCurrentOut++ = ch;
if (!ch)
break;
}
}
enum tagStringIds {
IDS_PROPERTY = 1, // Property
IDS_VALUE, // Value
IDS_MANUFACTURER, // Manufacturer
IDS_MANUFACTURER_VALUE, // "OK soft GmbH"
IDS_PRODUCT_LANGUAGE, // ProductLanguage
IDS_PRODUCT_LANGUAGE_VALUE, // 1033
IDS_PRODUCT_VERSION, // ProductVersion
IDS_PRODUCT_VERSION_VALUE, // 1.0
IDS_PRODUCT_NAME, // ProductName
IDS_PRODUCT_NAME_VALUE, // "Trust to User (T2U) Service"
IDS_PRODUCT_CODE, // ProductCode
IDS_PRODUCT_CODE_VALUE, // {B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}
IDS_UPGRADE_CODE, // UpgradeCode
IDS_UPGRADE_CODE_VALUE // {EE115A5D-D05A-465F-B077-F28CCDB20ECB}
};
//struct _StringPool {
// WORD wLength;
// WORD wRefcnt;
//} *pStringPool = NULL;
struct StrintgTable {
UINT uId;
UINT cRefcnt;
} g_StrintgTable[] = {
{IDS_PROPERTY, 4},
{IDS_VALUE, 1},
{IDS_MANUFACTURER, 1},
{IDS_MANUFACTURER_VALUE, 1},
{IDS_PRODUCT_LANGUAGE, 1},
{IDS_PRODUCT_LANGUAGE_VALUE, 1},
{IDS_PRODUCT_VERSION, 1},
{IDS_PRODUCT_VERSION_VALUE, 1},
{IDS_PRODUCT_NAME, 1},
{IDS_PRODUCT_NAME_VALUE, 1},
{IDS_PRODUCT_CODE, 1},
{IDS_PRODUCT_CODE_VALUE, 1},
{IDS_UPGRADE_CODE, 1},
{IDS_UPGRADE_CODE_VALUE, 1}
};
//Id: 13 Refcnt: 4 String: Property
//Id: 1 Refcnt: 1 String: Value
//Id: 2 Refcnt: 1 String: {EE115A5D-D05A-465F-B077-F28CCDB20ECB}
//Id: 3 Refcnt: 1 String: ProductLanguage
//Id: 4 Refcnt: 1 String: UpgradeCode
//Id: 5 Refcnt: 1 String: {B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}
//Id: 6 Refcnt: 1 String: 1.0
//Id: 7 Refcnt: 1 String: ProductCode
//Id: 8 Refcnt: 1 String: ProductVersion
//Id: 9 Refcnt: 1 String: OK soft GmbH
//Id: 10 Refcnt: 1 String: Trust to User (T2U) Service
//Id: 11 Refcnt: 1 String: Manufacturer
//Id: 12 Refcnt: 1 String: ProductName
//Id: 14 Refcnt: 1 String: 1033
UINT g_Tabeles[] = {IDS_PROPERTY};
//uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("Manufacturer"), TEXT("OK soft GmbH"));
//uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductLanguage"), TEXT("1033"));
//uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductVersion"), TEXT("1.0"));
//uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductCode"), TEXT("{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}"));
//uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductName"), TEXT("Trust to User (T2U) Service"));
int main()
{
HRESULT hr;
LPCWSTR pszFilename = L"Empty.msi";
IStorage *pStg = NULL;
IStream *pStm = NULL;
IPropertySetStorage *pPropSetStg = NULL;
IPropertyStorage *pPropStg = NULL;
WCHAR szOutStreamName[64];
WORD wCodePage, wStringIdSize;
ULONG cbWritten;
PROPSPEC rgpspec[8] = {
{PRSPEC_PROPID, PIDSI_TITLE},
{PRSPEC_PROPID, PIDSI_SUBJECT},
{PRSPEC_PROPID, PIDSI_AUTHOR},
{PRSPEC_PROPID, PIDSI_KEYWORDS},
{PRSPEC_PROPID, PIDSI_TEMPLATE},
{PRSPEC_PROPID, PIDSI_REVNUMBER},
{PRSPEC_PROPID, PIDSI_PAGECOUNT},
{PRSPEC_PROPID, PIDSI_WORDCOUNT}
};
PROPVARIANT rgpropvar[8];
PROPSPEC pspec;
PROPVARIANT propvar = {0};
hr = StgCreateStorageEx (pszFilename,
STGM_CREATE | STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
STGFMT_DOCFILE,
0,
NULL,
NULL,
&IID_IStorage,
&pStg);
if (FAILED(hr))
return hr;
hr = IStorage_SetClass (pStg, &CLSID_MsiDatabase);
// file has 1536 bytes (512*3)
hr = IStorage_QueryInterface (pStg, &IID_IPropertySetStorage, &pPropSetStg);
hr = IPropertySetStorage_Create (pPropSetStg, &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg);
pspec.propid = PRSPEC_PROPID;
pspec.ulKind = PID_CODEPAGE;
PropVariantInit (&propvar);
propvar.vt = VT_I2;
propvar.iVal = 1252;
hr = IPropertyStorage_WriteMultiple (pPropStg, 1, &pspec, &propvar, 0);
PropVariantInit (&rgpropvar[0]);
rgpropvar[0].vt = VT_LPSTR;
rgpropvar[0].pszVal = "Installation Database";
PropVariantInit (&rgpropvar[1]);
rgpropvar[1].vt = VT_LPSTR;
rgpropvar[1].pszVal = "Trust To User (T2U) Service";
PropVariantInit (&rgpropvar[2]);
rgpropvar[2].vt = VT_LPSTR;
rgpropvar[2].pszVal = "OK soft GmbH";
PropVariantInit (&rgpropvar[3]);
rgpropvar[3].vt = VT_LPSTR;
rgpropvar[3].pszVal = "Installer,MSI,Database";
PropVariantInit (&rgpropvar[4]);
rgpropvar[4].vt = VT_LPSTR;
rgpropvar[4].pszVal = "Intel;1033";
PropVariantInit (&rgpropvar[5]);
rgpropvar[5].vt = VT_LPSTR;
rgpropvar[5].pszVal = "{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}";
PropVariantInit (&rgpropvar[6]);
rgpropvar[6].vt = VT_I4;
rgpropvar[6].lVal = 110;
PropVariantInit (&rgpropvar[7]);
rgpropvar[7].vt = VT_I4;
rgpropvar[7].pszVal = 0;
hr = IPropertyStorage_WriteMultiple (pPropStg, ARRAY_SIZE(rgpspec), rgpspec, rgpropvar, PIDSI_TITLE); // PID_FIRST_USABLE
hr = IPropertyStorage_Commit (pPropStg, 0);
EncodeStreamName (TRUE, L"_Tables", szOutStreamName, ARRAY_SIZE(szOutStreamName));
hr = IStorage_CreateStream (pStg, szOutStreamName,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0, 0, &pStm);
for (i=0; i<ARRAY_SIZE(g_Tabeles); i++) {
WORD w = g_Tabeles[i];
hr = IStream_Write (pStm, (LPCVOID)&w, sizeof(WORD), &cbWritten);
}
IStream_Release (pStm);
// file has 1536 bytes (512*3)
EncodeStreamName (TRUE, L"_StringData", szOutStreamName, ARRAY_SIZE(szOutStreamName));
hr = IStorage_CreateStream (pStg, szOutStreamName,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0, 0, &pStm);
IStream_Release (pStm);
EncodeStreamName (TRUE, L"_StringPool", szOutStreamName, ARRAY_SIZE(szOutStreamName));
hr = IStorage_CreateStream (pStg, szOutStreamName,
STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
0, 0, &pStm);
wCodePage = 1252;
wStringIdSize = 0; // 2 bytes
hr = IStream_Write (pStm, (LPCVOID)&wCodePage, sizeof(WORD), &cbWritten);
hr = IStream_Write (pStm, (LPCVOID)&wStringIdSize, sizeof(WORD), &cbWritten);
IStream_Release (pStm);
// 2560 bytes (512*5)
IPropertyStorage_Release (pPropStg);
IPropertySetStorage_Release (pPropSetStg);
IStorage_Release (pStg);
return hr;
}
The code of the program which dumps MSI is more long and I don't see that it is really needed for you.
The utility which use Windows Installer API and create an empty MSI is below. It create more full valid MSI in sense of MSI-Validation :
#include <windows.h>
#include <Msi.h>
#include <MsiQuery.h>
#pragma warning (disable: 4201)
#include <ShLwApi.h>
#pragma warning (default: 4201)
#pragma comment (lib, "Msi.lib")
#pragma comment (lib, "ShLwApi.lib")
#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
#define CONST_STR_LEN(s) (ARRAY_SIZE(s) - 1)
UINT ExecuteSimpleMsiQuery (MSIHANDLE hDatabase, LPCTSTR pszQuery)
{
UINT uStatus = ERROR_INVALID_DATA;
MSIHANDLE hView = (MSIHANDLE)0;
__try {
uStatus = MsiDatabaseOpenView (hDatabase, pszQuery, &hView);
if (uStatus != NO_ERROR) __leave;
uStatus = MsiViewExecute (hView, (MSIHANDLE)0);
if (uStatus != NO_ERROR) __leave;
uStatus = MsiViewClose(hView);
}
__finally {
if (hView != (MSIHANDLE)0)
MsiCloseHandle (hView);
}
return uStatus;
}
UINT ExecuteQueryWirhTwoStringParameters (MSIHANDLE hView, LPCTSTR pszStr1, LPCTSTR pszStr2)
{
UINT uStatus = ERROR_INVALID_DATA;
MSIHANDLE hRec = (MSIHANDLE)0;
__try {
hRec = MsiCreateRecord(2);
MsiRecordSetString (hRec, 1, pszStr1);
MsiRecordSetString (hRec, 2, pszStr2);
uStatus = MsiViewExecute (hView, hRec);
// prepair for the next call of MsiViewExecute
uStatus = MsiViewClose(hView);
}
__finally {
if (hRec != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hRec);
}
return uStatus;
}
void main()
{
LPCTSTR pszMsiName = TEXT("Empty.msi");
MSIHANDLE hDatabase = (MSIHANDLE)0, hSummaryInfo = (MSIHANDLE)0, hView = (MSIHANDLE)0;
UINT uiUpdateCount;
UINT uStatus;
HANDLE hFile = INVALID_HANDLE_VALUE;
BOOL bSuccess;
char szPropertyValues[] =
"Property\tValue\r\n"
"s72\tl0\r\n"
"Property\tProperty\r\n"
"Manufacturer\tOK soft GmbH\r\n"
"ProductCode\t{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}\r\n"
"ProductLanguage\t1031\r\n"
"ProductName\tTrust to User (T2U) Service\r\n"
"ProductVersion\t1.0\r\n"
"UpgradeCode\t{EE115A5D-D05A-465F-B077-F28CCDB20ECB}\r\n";
DWORD cbNumberOfBytesWritten;
char szBuffer[128];
__try {
UINT i, cMaxProperty=0xFFFFFF;
// Create empty database. Inspite of it is empty MSI file has 2560 bytes
uStatus = MsiOpenDatabase (pszMsiName, MSIDBOPEN_CREATE, &hDatabase);
if (uStatus != NO_ERROR) __leave;
uiUpdateCount = 9;
uStatus = MsiGetSummaryInformation (hDatabase, NULL, uiUpdateCount, &hSummaryInfo);
if (uStatus != NO_ERROR) __leave;
// PID_CODEPAGE is optional
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PID_CODEPAGE, VT_I2, 1252, NULL, NULL);
//C:\Oleg\Win32.new\MSI\CreateEmptyMsi>msiinfo C:\Oleg\Win32.new\MSI\CreateEmptyMsi\Empty.msi
//
//Class Id for the MSI storage is {000C1084-0000-0000-C000-000000000046}
//
//Error 1627. Unable to display summary information. System does not support the codepage of the Summary Information Stream (codepage = '1200')
// VT_LPSTR MUST be used and not VT_LPWSTR.
// If one use MsiSummaryInfoSetPropertyW(..., VT_LPWSTR, 9, NULL, L"string"); one receive 1629 ERROR - ERROR_DATATYPE_MISMATCH
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_TITLE, VT_LPSTR, 0, NULL, L"Installation Database");
// PIDSI_SUBJECT and PIDSI_AUTHOR are optional
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_SUBJECT, VT_LPSTR, 0, NULL, L"Trust To User (T2U) Service");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_AUTHOR, VT_LPSTR, 0, NULL, L"OK soft GmbH");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_KEYWORDS, VT_LPSTR, 0, NULL, L"Installer,MSI,Database");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_TEMPLATE, VT_LPSTR, 0, NULL, L"Intel;1033");
uStatus = MsiSummaryInfoSetPropertyW (hSummaryInfo, PIDSI_REVNUMBER, VT_LPSTR, 0, NULL, L"{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}");
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PIDSI_PAGECOUNT, VT_I4, 110, NULL, NULL);
uStatus = MsiSummaryInfoSetProperty (hSummaryInfo, PIDSI_WORDCOUNT, VT_I4, 0, NULL, NULL);
uStatus = MsiSummaryInfoPersist (hSummaryInfo);
// if we commit database here we receive 3072 large MSI file
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("CREATE TABLE `Property` (`Property` CHAR(72) NOT NULL, `Value` CHAR(0) NOT NULL LOCALIZABLE PRIMARY KEY `Property`)"));
if (uStatus != NO_ERROR) __leave;
uStatus = MsiDatabaseOpenView (hDatabase, TEXT("INSERT INTO `Property` (`Property`, `Value`) VALUES (?, ?)"), &hView);
if (uStatus != NO_ERROR) __leave;
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("Manufacturer"), TEXT("OK soft GmbH"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductLanguage"), TEXT("1033"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductVersion"), TEXT("1.0"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductCode"), TEXT("{B2F99E7E-6C86-4D60-A6D1-F81CAC15B0F7}"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("ProductName"), TEXT("Trust to User (T2U) Service"));
uStatus = ExecuteQueryWirhTwoStringParameters (hView, TEXT("UpgradeCode"), TEXT("{EE115A5D-D05A-465F-B077-F28CCDB20ECB}"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("CREATE TABLE `_Validation` (`Table` CHAR(32) NOT NULL, `Column` CHAR(32) NOT NULL, `Nullable` CHAR(4) NOT NULL, `MinValue` LONG, `MaxValue` LONG, `KeyTable` CHAR(255), `KeyColumn` INT, `Category` CHAR(32), `Set` CHAR(255), `Description` CHAR(255) PRIMARY KEY `Table`, `Column`)"));
if (uStatus != NO_ERROR) __leave;
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Category', 'Y', NULL, NULL, NULL, NULL, NULL, 'Text;Formatted;Template;Condition;Guid;Path;Version;Language;Identifier;Binary;UpperCase;LowerCase;Filename;Paths;AnyPath;WildCardFilename;RegPath;KeyFormatted;CustomSource;Property;Cabinet;Shortcut;URL', 'String category')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Column', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of column')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Description', 'Y', NULL, NULL, NULL, NULL, 'Text', NULL, 'Description of column')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'KeyColumn', 'Y', 1, 32, NULL, NULL, NULL, NULL, 'Column to which foreign key connects')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'KeyTable', 'Y', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'For foreign key, Name of table to which data must link')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'MaxValue', 'Y', -2147483647, 2147483647, NULL, NULL, NULL, NULL, 'Maximum value allowed')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'MinValue', 'Y', -2147483647, 2147483647, NULL, NULL, NULL, NULL, 'Minimum value allowed')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Nullable', 'N', NULL, NULL, NULL, NULL, NULL, 'Y;N;#', 'Whether the column is nullable')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Set', 'Y', NULL, NULL, NULL, NULL, 'Text', NULL, 'Set of values that are permitted')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'_Validation', 'Table', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of table')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'Property', 'Property', 'N', NULL, NULL, NULL, NULL, 'Identifier', NULL, 'Name of property, uppercase if settable by launcher or loader.')"));
uStatus = ExecuteSimpleMsiQuery (hDatabase, TEXT("INSERT INTO `_Validation` (`Table`, `Column`, `Nullable`, `MinValue`, `MaxValue`, `KeyTable`, `KeyColumn`, `Category`, `Set`, `Description`) VALUES (")
TEXT("'Property', 'Value', 'N', NULL, NULL, NULL, NULL, 'Text', NULL, 'String value for property. Never null or empty.')"));
uStatus = MsiDatabaseCommit (hDatabase);
// now we have MSI file which has 4608 Bytes 2560 -> it is 2048 bytes larger as an empty MSI
}
__finally {
if (hFile != INVALID_HANDLE_VALUE)
CloseHandle (hFile);
if (hView != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hView);
if (hSummaryInfo != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hSummaryInfo);
if (hDatabase != (MSIHANDLE)0)
uStatus = MsiCloseHandle (hDatabase);
}
}
MSI's are database files. They have tables with instructions that the Microsoft Installer interprets and contain the files that will be copied to the filesystem.
You can manually edit these files with the Orca tool from Microsoft.
You might want to start here:
Windows Installer
Windows Installer is an Microsoft Windows Platform service and associated SDK. The SDK contains tools such as Orca to edit MSI databases. The platform service exposes a specification for the database and a Win32 and COM Automation interface for interacting with it. The Windows Installer team was not asked with creating full blown authoring tools. Instead, for the most part authoring tools was left to the industry to create by building applications on top of that API and database specification. My understanding was that this was an olive branch to several company such as InstallShield and Wise who already had their own frameworks for authoring installers and was an attempt to consolidate the technology without alienating these companies.
Since then Microsoft has published the Windows Installer XML open source project which is an authoring tool in it's own right. Also the Visual Studio team had Setup and Deployment Projects ( defunct in the next release of Visual Studio ).

Creating context menu for win32 API

I am trying to create context menu for win32 application using
case WM_RBUTTONDOWN:
{
HMENU hPopupMenu = CreatePopupMenu();
InsertMenu(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_CLOSE, (LPCWSTR)"Exit");
InsertMenu(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_EXIT, (LPCWSTR)"Play");
SetForegroundWindow(hWnd);
TrackPopupMenu(hPopupMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, 0, 0, 0, hWnd, NULL);
}
But I always get context menu as shown below
alt text http://img191.imageshack.us/img191/866/70219076.png
I want text exit and play to be displayed in the menu
You can't convert a string literal to wide by casting, you have to declare it as a wide char string. The casting just defeats the compiler warning, it doesn't change the content of the string.
change this
(LPCWSTR)"Exit"
(LPCWSTR)"Play"
to this
_T("Exit")
_T("Play")
or this
L"Exit"
L"Play"
Are you specifying the encoding in the API function definition? I ran into that problem recently and removing the specification fixed the problem.
Following worked for me
case WM_RBUTTONDOWN:
{
HMENU hPopupMenu = CreatePopupMenu();
InsertMenu(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_CLOSE, L"Exit");
InsertMenu(hPopupMenu, 0, MF_BYPOSITION | MF_STRING, ID_EXIT, L"Play");
SetForegroundWindow(hWnd);
TrackPopupMenu(hPopupMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, 0, 0, 0, hWnd, NULL);
}

CGEventPost - possible bug when simulating keyboard events?

I have a very simple chunk of code that is designed to simulate keyboard events. The simple example below should type "Cz" - the shift key goes down, the c key goes down, c comes up and shift comes up. Then the z key goes down and up.
It seems that sometimes the order gets muddled though. When I create a timer to call this routine every second, the output should be CzCzCzCz.... But here's what I get:
CZcZCZCzczCzczCzczCZCZCzCz
I'll run it again:
CzCzCzCzCZCzCZCzCZCzCZCzCZCzCzCz
Different. And equally wrong.
The code:
e1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, true);
CGEventPost(kCGSessionEventTap, e1);
CFRelease(e1);
e2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, true);
CGEventPost(kCGSessionEventTap, e2);
CFRelease(e2);
e3 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)8, false);
CGEventPost(kCGSessionEventTap, e3);
CFRelease(e3);
e4 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e4);
CFRelease(e4);
e7 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);
CGEventPost(kCGSessionEventTap, e7);
CFRelease(e7);
e8 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventPost(kCGSessionEventTap, e8);
CFRelease(e8);
Is there something I'm missing in how to implement the keydown and keyup for the shift key? I think this might be a bug - where would I report it?
I have found a reliable way to post modified keyboard events - it does not follow the example in Apple's documentation (which doesn't work) but seems to make sense, and most importantly, WORKS.
Rather than sending 'shift key down' and 'shift key up' messages (as instructed in the docs), you need to set a modifier flag on the keypress. Here's how to output an uppercase Z.
CGEventRef event1, event2;
event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);//'z' keydown event
CGEventSetFlags(event1, kCGEventFlagMaskShift);//set shift key down for above event
CGEventPost(kCGSessionEventTap, event1);//post event
I'm then releasing the 'z' key for completeness (also setting the shift-flag on, though not sure if this is correct).
event2 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, false);
CGEventSetFlags(event2, kCGEventFlagMaskShift);
CGEventPost(kCGSessionEventTap, event2);
Finally (and bizarrely) you DO need to send the 'key up' event for the shift key:
e5 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)56, false);
CGEventPost(kCGSessionEventTap, e5);
Don't forget to release your events once you're done with them.
I hope this is useful to someone - it took me a long time to get this to work.
The cleanest way for this is bitwise OR'ing the current modifier flags with the flag of your desired modifier(s) , e.g.:
CGEventFlags flags = kCGEventFlagMaskShift;
CGEventRef ev;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
//press down
ev = CGEventCreateKeyboardEvent (source, keyCode, true);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
//press up
ev = CGEventCreateKeyboardEvent (source, keyCode, false);
CGEventSetFlags(ev,flags | CGEventGetFlags(ev)); //combine flags
CGEventPost(kCGHIDEventTap,ev);
CFRelease(ev);
CFRelease(source);
Like another comment referring to other solutions here, after using uppercase with the Shift mask, a successive call would cause any further intended non-shifted character to be turned into an shifted characters. I figured the call to CGEventCreateKeyboardEvent was somehow saving previous masks, so I purposefully clear the mask of the modifier:
CGEventRef event1, event2;
CGEventSourceRef source = CGEventSourceCreate (kCGEventSourceStateCombinedSessionState);
CGEventFlags flags;
event1 = CGEventCreateKeyboardEvent (source, keyCode, true);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event1);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event1);
CGEventSetFlags(event1, flags);
CGEventPost(kCGHIDEventTap,event1);
event2 = CGEventCreateKeyboardEvent (source, keyCode, false);
if (upper)
flags = kCGEventFlagMaskShift | CGEventGetFlags(event2);
else
flags = ~kCGEventFlagMaskShift & CGEventGetFlags(event2);
CGEventSetFlags(event2, flags);
CGEventPost(kCGHIDEventTap,event2);
CFRelease(event1);
CFRelease(event2);
CFRelease(source);
I had a similar problem recently. Instead of passing NULL as the first argument of CGEventCreateKeyboardEvent, create an event source like this:
CGEventSourceRef eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
and use it while you are creating and posting events. CFRelease it when you are done with it.
Any relation to this guy's bug?
reliable workaround:
+(void)runScript:(NSString*)scriptText
{
NSDictionary *error = nil;
NSAppleEventDescriptor *appleEventDescriptor;
NSAppleScript *appleScript;
appleScript = [[NSAppleScript alloc] initWithSource:scriptText];
appleEventDescriptor = [appleScript executeAndReturnError:&error];
}
[self runScript:#"tell application \"System Events\" to key code "c" using shift down"];
[self runScript:#"tell application \"System Events\" to key code "z"];

Resources