home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 June / Chip_2001-06_cd1.bin / zkuste / vbasic / Data / Utility / MSISDK15.msi / MsiLoc.cpp < prev    next >
C/C++ Source or Header  |  2000-10-05  |  136KB  |  3,582 lines

  1. #if 0  // makefile definitions
  2. DESCRIPTION = MSI Localization tool
  3. MODULENAME = msiloc
  4. SUBSYSTEM = console
  5. FILEVERSION = MSI
  6. LINKLIBS = OLE32.lib
  7. !include "..\TOOLS\MsiTool.mak"
  8. !if 0  #nmake skips the rest of this file
  9. #endif // end of makefile definitions
  10.  
  11. //+--------------------------------------------------------------------------------------------------+\\
  12. //                                                                                                    \\
  13. //  Microsoft Windows                                                                                 \\
  14. //                                                                                                    \\
  15. //  Copyright (C) Microsoft Corporation, 1999-2000                                                         \\
  16. //                                                                                                    \\
  17. //  File:       msiloc.cpp                                                                            \\
  18. //                                                                                                    \\
  19. //----------------------------------------------------------------------------------------------------\\ 
  20.  
  21. //-----------------------------------------------------------------------------------------
  22. //
  23. // BUILD Instructions
  24. //
  25. // notes:
  26. //    - SDK represents the full path to the install location of the
  27. //     Windows Installer SDK
  28. //
  29. // Using NMake:
  30. //        %vcbin%\nmake -f msiloc.cpp include="%include;SDK\Include" lib="%lib%;SDK\Lib"
  31. //
  32. // Using MsDev:
  33. //        1. Create a new Win32 Console Application project
  34. //      2. Add msiloc.cpp to the project
  35. //      3. Add SDK\Include and SDK\Lib directories on the Tools\Options Directories tab
  36. //      4. Add msi.lib to the library list in the Project Settings dialog
  37. //          (in addition to the standard libs included by MsDev)
  38. //
  39. //------------------------------------------------------------------------------------------
  40.  
  41. // Required headers
  42. #define WINDOWS_LEAN_AND_MEAN  // faster compile
  43. #include <windows.h>
  44.  
  45. #ifndef RC_INVOKED    // start of source code
  46.  
  47. #ifndef W32    // if W32 not defined
  48. #define W32
  49. #endif    // W32 defined
  50.  
  51. #ifndef MSI    // if MSI not defined
  52. #define MSI
  53. #endif    // MSI defined
  54.  
  55.  
  56. ///////////////////////////////////////////////////////////////////////////////
  57. // HEADERS
  58. #include "msiquery.h"
  59. #include "msidefs.h"
  60. #include <stdio.h>   // wprintf
  61. #include <stdlib.h>  // strtoul
  62. #include <tchar.h>   // define UNICODE=1 on nmake command line to build UNICODE
  63. #include <assert.h>  // assert
  64.  
  65. /////////////////////////////////////////////////////////////////////////////
  66. // CONSTANT STRINGS
  67. /*headers for resource file*/
  68. const TCHAR szWndwHdrFile[]      = TEXT("#include <windows.h>");
  69. const TCHAR szCommCtrlHdrFile[]  = TEXT("#include <commctrl.h>");
  70. /*tabs and carriage returns*/
  71. const TCHAR szCRLF[]             = TEXT("\r\n");
  72. const TCHAR szCommaTab[]         = TEXT(",\t");
  73. const TCHAR szTab[]              = TEXT("\t");
  74. const TCHAR szCurlyBeg[]         = TEXT("{");
  75. const TCHAR szCurlyEnd[]         = TEXT("}");
  76. const TCHAR szQuotes[]           = TEXT("\"");
  77. /*resource types or keywords WINDOWS*/
  78. const TCHAR resDialog[]          = TEXT("DIALOGEX");
  79. const TCHAR resPushButton[]      = TEXT("PUSHBUTTON");
  80. const TCHAR resCheckBox[]        = TEXT("CHECKBOX");
  81. const TCHAR resGroupBox[]        = TEXT("GROUPBOX");
  82. const TCHAR resRadioButton[]     = TEXT("RADIOBUTTON");
  83. const TCHAR resControl[]         = TEXT("CONTROL");
  84. const TCHAR resBitmap[]          = TEXT("BITMAP");
  85. const TCHAR resIcon[]            = TEXT("ICON");
  86. const TCHAR resJPEG[]            = TEXT("JPEG");
  87. const TCHAR resStringTable[]     = TEXT("STRINGTABLE");
  88. const TCHAR tokCaption[]         = TEXT("CAPTION");
  89. const TCHAR resSelTreeClass[]   = TEXT("WC_TREEVIEW");
  90. const TCHAR resButtonClass[]    = TEXT("BUTTON");
  91. const TCHAR resProgBar32Class[] = TEXT("PROGRESS_CLASS");
  92. const TCHAR resListViewClass[]  = TEXT("WC_LISTVIEW");
  93. const TCHAR resStaticClass[]    = TEXT("STATIC");
  94. const TCHAR resComboBoxClass[]  = TEXT("COMBOBOX");
  95. const TCHAR resEditClass[]      = TEXT("EDIT");
  96. const TCHAR resListBoxClass[]   = TEXT("LISTBOX");
  97. const TCHAR resRichEditClass[]  = TEXT("STATIC"); // TEXT("RICHEDIT") not working;
  98. /*control types INSTALLER*/
  99. const TCHAR* szMsiPushbutton =      TEXT("PushButton");
  100. const TCHAR* szMsiBillboard  =      TEXT("Billboard");
  101. const TCHAR* szMsiVolumeCostList =  TEXT("VolumeCostList");
  102. const TCHAR* szMsiScrollableText =  TEXT("ScrollableText");
  103. const TCHAR* szMsiMaskedEdit =      TEXT("MaskedEdit");
  104. const TCHAR* szMsiCheckBox =        TEXT("CheckBox");
  105. const TCHAR* szMsiGroupBox =        TEXT("GroupBox");
  106. const TCHAR* szMsiText =            TEXT("Text");
  107. const TCHAR* szMsiListBox =         TEXT("ListBox");
  108. const TCHAR* szMsiEdit =            TEXT("Edit");
  109. const TCHAR* szMsiPathEdit =        TEXT("PathEdit");
  110. const TCHAR* szMsiProgressBar =     TEXT("ProgressBar");
  111. const TCHAR* szMsiDirList =         TEXT("DirectoryList");
  112. const TCHAR* szMsiList =            TEXT("ListView");
  113. const TCHAR* szMsiComboBox =        TEXT("ComboBox");
  114. const TCHAR* szMsiDirCombo =        TEXT("DirectoryCombo");
  115. const TCHAR* szMsiVolSelCombo =     TEXT("VolumeSelectCombo");
  116. const TCHAR* szMsiRadioButtonGroup =TEXT("RadioButtonGroup");
  117. const TCHAR* szMsiRadioButton =     TEXT("RadioButton");
  118. const TCHAR* szMsiBitmap =          TEXT("Bitmap");
  119. const TCHAR* szMsiSelTree =         TEXT("SelectionTree");
  120. const TCHAR* szMsiIcon =            TEXT("Icon");
  121. const TCHAR* szMsiLine =            TEXT("Line");
  122. /*max sizes*/
  123. const int iMaxResStrLen          = 256;
  124. const TCHAR strOverLimit[]       = TEXT("!! STR OVER LIMIT !!");
  125. ////////////////////////////////////////////////////////////////////////
  126. // EXPORT SQL QUERIES
  127. /*particular column of strings from a particular table*/
  128. const TCHAR* sqlStrCol = TEXT("SELECT %s, `%s` FROM `%s`");
  129. const TCHAR sqlCreateStrMap[] = TEXT("CREATE TABLE `_RESStrings` (`Table` CHAR(72) NOT NULL, `Column` CHAR(72) NOT NULL, `Key` CHAR(0), `RCID` SHORT NOT NULL PRIMARY KEY `Table`, `Column`, `Key`)");
  130. const TCHAR* sqlSelMaxStrRcId = TEXT("SELECT `RCID` FROM `_RESStrings` WHERE `Table`='MAX_RESOURCE_ID' AND `Column`='MAX_RESOURCE_ID'");
  131. const TCHAR* sqlStrMark = TEXT("SELECT `Table`,`Column`, `Key`, `RCID` FROM `_RESStrings`");
  132. const TCHAR* sqlInsertStr = TEXT("SELECT `Table`,`Column`,`Key`, `RCID` FROM `_RESStrings`");
  133. const TCHAR* sqlFindStrResId  = TEXT("SELECT `RCID` FROM `_RESStrings` WHERE `Table`='%s' AND `Column`='%s' AND `Key`='%s'");
  134. /*binary table*/
  135. const TCHAR* sqlBinary = TEXT("SELECT `Name`,`Data` FROM `Binary`");
  136. const int ibcName = 1; // these constants must match query above
  137. const int ibcData = 2;
  138. /*dialog table*/
  139. const TCHAR* sqlCreateDlgMap = TEXT("CREATE TABLE `_RESDialogs` (`RCStr` CHAR(72) NOT NULL, `Dialog` CHAR(72) PRIMARY KEY `RCStr`)");
  140. const TCHAR* sqlDlgMap = TEXT("SELECT `RCStr`,`Dialog` FROM `_RESDialogs`");
  141. const TCHAR* sqlDialog = TEXT("SELECT `Dialog`,`HCentering`,`VCentering`,`Width`,`Height`,`Attributes`,`Title` FROM `Dialog`");
  142. const TCHAR* sqlDialogSpecific = TEXT("SELECT `Dialog`,`HCentering`,`VCentering`,`Width`,`Height`,`Attributes`,`Title` FROM `Dialog` WHERE `Dialog`=?");
  143. const int idcName   = 1; // these constants must match query above
  144. const int idcX      = 2;
  145. const int idcY      = 3;
  146. const int idcWd     = 4;
  147. const int idcHt     = 5;
  148. const int idcAttrib = 6;
  149. const int idcTitle  = 7;
  150. /*control table*/
  151. const TCHAR* sqlCreateCtrlMark = TEXT("CREATE TABLE `_RESControls` (`Dialog_` CHAR(72) NOT NULL, `Control_` CHAR(72) NOT NULL, `RCID` INT NOT NULL  PRIMARY KEY `Dialog_`, `Control_`)"); 
  152. const TCHAR* sqlCtrlMark = TEXT("SELECT `Dialog_`,`Control_`,`RCID` FROM `_RESControls`");
  153. const TCHAR* sqlSelMaxRcId = TEXT("SELECT `RCID` FROM `_RESControls` WHERE `Dialog_`='MAX_RESOURCE_ID' AND `Control_`='MAX_RESOURCE_ID'");
  154. const TCHAR* sqlInsertCtrl = TEXT("SELECT `Dialog_`,`Control_`,`RCID` FROM `_RESControls`");
  155. const TCHAR* sqlFindResId  = TEXT("SELECT `RCID` FROM `_RESControls` WHERE `Dialog_`='%s' AND `Control_`='%s'");
  156. const TCHAR* sqlControl = TEXT("SELECT `Control`,`Type`,`X`,`Y`,`Width`,`Height`,`Attributes`,`Text`,`Property` FROM `Control` WHERE `Dialog_`=?");
  157. const int iccName    = 1; // these constants must match query above
  158. const int iccType    = 2;
  159. const int iccX       = 3;
  160. const int iccY       = 4;
  161. const int iccWd      = 5;
  162. const int iccHt      = 6;
  163. const int iccAttrib  = 7;
  164. const int iccText    = 8;
  165. const int iccProperty= 9;
  166. /*radiobutton table*/
  167. const TCHAR* sqlRadioButton = TEXT("SELECT `Order`, `X`, `Y`, `Width`, `Height`, `Text` FROM `RadioButton` WHERE `Property`=?");
  168. const int irbcOrder = 1; // these constants must match query above
  169. const int irbcX     = 2;
  170. const int irbcY     = 3;
  171. const int irbcWd    = 4;
  172. const int irbcHt    = 5;
  173. const int irbcText  = 6;
  174. ////////////////////////////////////////////////////////////////////////
  175. // IMPORT SQL QUERIES
  176. /*dialog table*/
  177. const TCHAR* sqlDialogImport = TEXT("SELECT `HCentering`,`VCentering`,`Width`,`Height`,`Title` FROM `Dialog` WHERE `Dialog`=?");
  178. const int idiHCentering = 1; // these constants must match query above
  179. const int idiVCentering = 2;
  180. const int idiWidth      = 3;
  181. const int idiHeight     = 4;
  182. const int idiTitle      = 5;
  183. /*control table*/
  184. const TCHAR* sqlControlImport = TEXT("SELECT `X`,`Y`,`Width`,`Height`,`Text` FROM `Control` WHERE `Dialog_`=? AND `Control`=?");
  185. const int iciX          = 1; // these constants must match query above
  186. const int iciY          = 2;
  187. const int iciWidth      = 3;
  188. const int iciHeight     = 4;
  189. const int iciText       = 5;
  190. /*radiobutton table*/
  191. const TCHAR* sqlRadioButtonImport = TEXT("SELECT `Width`, `Height`, `Text` FROM `RadioButton` WHERE `Property`=? AND `Order`=?");
  192. const int irbiWidth     = 1; // these constants must match query above
  193. const int irbiHeight    = 2;
  194. const int irbiText      = 3;
  195. /*string table*/
  196. const TCHAR* sqlStringImport = TEXT("SELECT `%s` FROM `%s` WHERE ");
  197. const TCHAR* sqlStrTemp      = TEXT("SELECT * FROM `%s`");
  198. /*find Installer name*/
  199. const TCHAR sqlDialogInstallerName[] = TEXT("SELECT `Dialog` FROM `_RESDialogs` WHERE `RCStr`=?");
  200. const TCHAR sqlControlInstallerName[] = TEXT("SELECT `Dialog_`,`Control_` FROM `_RESControls` WHERE `RCID`=?");
  201. const TCHAR sqlStringInstallerName[] = TEXT("SELECT `Table`,`Column`,`Key` FROM `_RESStrings` WHERE `RCID`=? AND `Table`<>'MAX_RESOURCE_ID'");
  202. //////////////////////////////////////////////////////////////////////////
  203. // MISCELLANEOUS - CODEPAGE, etc.
  204. const TCHAR szTokenSeps[] = TEXT(":");
  205. const TCHAR* szCodepageFile = TEXT("codepage.idt");
  206. const TCHAR* szForceCodepage = TEXT("_ForceCodepage");
  207. const TCHAR* szLineFeed = TEXT("\r\n\r\n");
  208. const TCHAR* szCodepageExport = TEXT("_ForceCodepage.idt");
  209. //////////////////////////////////////////////////////////////////////////
  210. // COMMAND LINE PARSING
  211. /*mode*/
  212. const int iEXPORT_MSI     = 1 << 0;
  213. const int iIMPORT_RES     = 1 << 1;
  214. /*data type*/
  215. const int iDIALOGS = 1 << 2;
  216. const int iSTRINGS = 1 << 3;
  217. /*extra options*/
  218. const int iSKIP_BINARY = 1 << 4;
  219. const int iCREATE_NEW_DB = 1 << 5;
  220. /*max values*/
  221. const int MAX_DIALOGS = 32;
  222. const int MAX_STRINGS = 32;
  223. ////////////////////////////////////////////////////////////////////////////
  224. // ENUMS
  225. static enum bdtBinaryDataType
  226. {
  227.     bdtBitmap,        // bitmap
  228.     bdtJPEG,          // JPEG
  229.     bdtIcon,          // Icon
  230.     bdtEXE_DLL_SCRIPT // EXE, DLL, or SCRIPT
  231. };
  232. /////////////////////////////////////////////////////////////////////////////
  233. // FUNCTION PROTOTYPES
  234. BOOL __stdcall EnumDialogCallback(HINSTANCE hModule, const TCHAR* szType, TCHAR* szDialogName, long lParam);
  235. BOOL __stdcall EnumStringCallback(HINSTANCE hModule, const TCHAR* szType, TCHAR* szName, long lParam);
  236. BOOL __stdcall EnumLanguageCallback(HINSTANCE hModule, const TCHAR* szType, const TCHAR* szName, WORD wIDLanguage, long lParam);
  237. /////////////////////////////////////////////////////////////////////////////
  238. // GLOBAL VARIABLES - IMPORT 
  239. UINT g_uiCodePage;
  240. WORD g_wLangId = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL); // initialize to language neutral
  241.  
  242.  
  243. //__________________________________________________________________________________________
  244. //
  245. // CLASSES
  246. //__________________________________________________________________________________________
  247.  
  248. /////////////////////////////////////////////////////////////////////////////
  249. // class CGenerateRC -- handles creation of .rc file from .msi database
  250. //
  251. class CGenerateRC
  252. {
  253. public: // constructor and destructor
  254.     CGenerateRC(const TCHAR* szDatabase, const TCHAR* szSavedDatabase) : m_szDatabase(szSavedDatabase), m_szOrigDb(szDatabase),
  255.         m_hFile(0), m_hDatabase(0), m_iCtrlResID(0), m_fError(FALSE), m_cWriteFileErr(0), m_iStrResID(0), m_fWroteBinary(FALSE){};
  256.     ~CGenerateRC();
  257. public: // methods
  258.     UINT OutputDialogs(BOOL fBinary);
  259.     UINT OutputDialog(TCHAR* szDialog, BOOL fBinary);
  260.     UINT OutputString(TCHAR* szTable, TCHAR* szColumn);
  261.     BOOL IsInErrorState(){return m_fError;}
  262. private: // methods
  263.     UINT   Initialize();
  264.     UINT   CreateResourceFile();
  265.     BOOL   WriteDialogToRC(TCHAR* szDialog, TCHAR* szTitle, int x, int y, int wd, int ht, int attrib);
  266.     BOOL   PrintDimensions(int x, int y, int wd, int ht);
  267.     BOOL   OutputControls(TCHAR* szDialog);
  268.     UINT   OutputDialogInit(BOOL fBinary);
  269.     UINT   OutputDialogFinalize();
  270.     UINT   OutputStringInit();
  271.     BOOL   WriteBinaries();
  272.     BOOL   WriteRadioButtons(TCHAR* szDialog, TCHAR* szRBGroup, TCHAR* szProperty, int x, int y, int attrib);
  273.     BOOL   WriteControlToRC(TCHAR* szDialog, TCHAR* szCtrlName, TCHAR* szCtrlType, TCHAR* szCtrlText, TCHAR* szCtrlProperty, int x,
  274.                             int y, int wd, int ht, int attrib);
  275.     BOOL   WriteStdWinCtrl(int iResId, const TCHAR* resType, TCHAR* szCtrlText, int x, int y, int wd, int ht, int attrib);
  276.     BOOL   WriteWin32Ctrl(int iResId, const TCHAR* szClass, TCHAR* szCtrlText, TCHAR* szAttrib, int x, int y, int wd, int ht, int attrib);
  277.     UINT   VerifyDatabaseCodepage();
  278.     TCHAR* EscapeSlashAndQuoteForRC(TCHAR* szStr);
  279. private: // data
  280.     const TCHAR* m_szDatabase; // name of database to generate from
  281.     const TCHAR* m_szOrigDb;   // name of original database
  282.     HANDLE       m_hFile;      // handle to resource file
  283.     MSIHANDLE    m_hDatabase;  // handle to database
  284.     int          m_iCtrlResID; // current max resource Id used for controls
  285.     int          m_iStrResID;  // current max resource Id used for strings
  286.     BOOL         m_fError;     // store current error state
  287.     BOOL         m_fWroteBinary;// already wrote binary data to rc file
  288.     int          m_cWriteFileErr; // number of write file errors
  289. };
  290.  
  291. ///////////////////////////////////////////////////////////////////////////////
  292. // class CImportRes -- handles import of resource .dll file into .msi database
  293. //
  294. class CImportRes
  295. {
  296. public: // constructor and destructor
  297.     CImportRes(const TCHAR* szDatabase, const TCHAR* szSaveDatabase, const TCHAR* szDLLFile) : m_szDatabase(szSaveDatabase), m_szOrigDb(szDatabase), m_szDLLFile(szDLLFile), m_hDatabase(0), m_fError(FALSE),
  298.                 m_hControl(0), m_hDialog(0), m_hRadioButton(0), m_hInst(0), m_fSetCodepage(FALSE), m_fFoundLang(FALSE){};
  299.    ~CImportRes();
  300. public: // methods
  301.     UINT ImportDialogs();
  302.     UINT ImportStrings();
  303.     UINT ImportDialog(TCHAR* szDialog);
  304.     BOOL IsInErrorState(){return m_fError;}
  305. public: // but only for enumeration purposes
  306.     BOOL WasLanguagePreviouslyFound(){return m_fFoundLang;}
  307.     void SetFoundLang(BOOL fValue){ m_fFoundLang = fValue; }
  308.     BOOL LoadDialog(HINSTANCE hModule, const TCHAR* szType, TCHAR* szDialog);
  309.     BOOL LoadString(HINSTANCE hModule, const TCHAR* szType, TCHAR* szString);
  310.     void SetErrorState(BOOL fState) { m_fError = fState; }
  311.     BOOL SetCodePage(WORD wLang);
  312. private: // methods
  313.     UINT ImportDlgInit();
  314.     UINT Initialize();
  315.     UINT VerifyDatabaseCodepage();
  316. private: // data
  317.     const TCHAR* m_szOrigDb;   // orginal database for opening
  318.     const TCHAR* m_szDatabase; // name of database to save to, optional if you want to create a new Db
  319.     const TCHAR* m_szDLLFile;  // name of DLL file to import
  320.     MSIHANDLE    m_hDatabase;  // handle to database
  321.     MSIHANDLE    m_hControl;   // handle to control table
  322.     MSIHANDLE    m_hDialog;    // handle to dialog table
  323.     MSIHANDLE    m_hRadioButton;// handle to radiobutton table
  324.     BOOL         m_fError;     // store current error state
  325.     HINSTANCE    m_hInst;      // DLL (with localized resources)
  326.     BOOL         m_fSetCodepage; // whether codepage of database has been set
  327.     BOOL         m_fFoundLang;  // whether resource has already been found in previous language
  328. };
  329.  
  330. ///////////////////////////////////////////////////////////////////////////////////////
  331. // CDialogStream class -- used for walking DLGTEMPLATEEX and DLGITEMTEMPLATE in memory
  332. //
  333. class CDialogStream  
  334. {
  335. public:  
  336.     short  __stdcall GetInt16();
  337.     int    __stdcall GetInt32();
  338.     int    __stdcall GetInt8();
  339.     TCHAR* __stdcall GetStr();
  340.     BOOL   __stdcall Align16();
  341.     BOOL   __stdcall Align32();
  342.     BOOL   __stdcall Undo16();
  343.     BOOL   __stdcall Move(int cbBytes);
  344. public:  // constructor, destructor
  345.      CDialogStream(HGLOBAL hResource);
  346.     ~CDialogStream();
  347. private:
  348.     char*  m_pch;
  349. };
  350.  
  351. //_______________________________________________________________________________________
  352. //
  353. // CGENERATERC CLASS IMPLEMENTATION
  354. //_______________________________________________________________________________________
  355.  
  356. /////////////////////////////////////////////////////////////////////////////
  357. // CGenerateRC::~CGenerateRC
  358. // --  Handles destruction of necessary objects.
  359. // --  Commits database if no errors
  360. CGenerateRC::~CGenerateRC()
  361. {
  362.     UINT iStat;
  363.     if (m_hFile)
  364.         W32::CloseHandle(m_hFile);
  365.     if (m_hDatabase)
  366.     {
  367.         // commit database for internal tables
  368.         // only commit database if no errors
  369.         if (!m_fError && !m_cWriteFileErr)
  370.         {
  371.             if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseCommit(m_hDatabase)))
  372.                 _tprintf(TEXT("!! DATABASE COMMIT FAILED.  Error = %d\n"), iStat);
  373.         }
  374.         else
  375.             _tprintf(TEXT("!! NO CHANGES SAVED TO DATABASE. '%d' WriteFile errors occured. Error state = %s\n"), m_cWriteFileErr, m_fError ? TEXT("ERRORS OCCURED") : TEXT("NO ERRORS"));
  376.         MSI::MsiCloseHandle(m_hDatabase);
  377.     }
  378. }
  379.  
  380. /////////////////////////////////////////////////////////////////////////////
  381. // CGenerateRC::CreateResourceFile
  382. // -- Creates .rc file using base name from .msi file
  383. // -- Outputs required header files to .rc file
  384. UINT CGenerateRC::CreateResourceFile()
  385. {
  386.     // assumption of database with ".msi" extension
  387.     // resource file generated from the database we are saving to
  388.     // if m_szDatabase = NULL, then no output database specified, so use m_szOrigDb
  389.     // if m_szDatabase, then output database specified, so use m_szDatabase
  390.     int cLen = _tcsclen(m_szDatabase ? m_szDatabase : m_szOrigDb);
  391.     TCHAR* szFile = new TCHAR[cLen+1];
  392.     _tcscpy(szFile, m_szDatabase ? m_szDatabase : m_szOrigDb); // copy name over
  393.     assert(szFile[cLen-1] == TEXT('i') && szFile[cLen-2] == TEXT('s') && szFile[cLen-3] == TEXT('m'));
  394.  
  395.     // remove "msi" and change to "rc"
  396.     szFile[cLen-1] = TEXT('\0');
  397.     szFile[cLen-2] = TEXT('c');
  398.     szFile[cLen-3] = TEXT('r');
  399.  
  400.     if (m_szDatabase)
  401.         _tprintf(TEXT("LOG>> Original Database: %s, Saved Database: %s, Generated RC File: %s\n"), m_szOrigDb, m_szDatabase, szFile);
  402.     else
  403.         _tprintf(TEXT("LOG>> Database: %s, Generated RC file: %s\n"), m_szOrigDb , szFile);
  404.  
  405.     // attempt to create resource file
  406.     m_hFile = W32::CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_WRITE, 
  407.                                 0, CREATE_ALWAYS, 0, 0);
  408.     if (!m_hFile)
  409.     {
  410.         _tprintf(TEXT("Unable to create resource file: %s\n"), szFile);
  411.         return m_fError = TRUE, ERROR_FUNCTION_FAILED;
  412.     }
  413.  
  414.     // write required headers to resource file (needed for successful compilation)
  415.     DWORD dwBytesWritten; 
  416.     // need <windows.h> for most rc requirements
  417.     if (!W32::WriteFile(m_hFile, szWndwHdrFile, sizeof(szWndwHdrFile)-sizeof(TCHAR), &dwBytesWritten, 0))
  418.         m_cWriteFileErr++;
  419.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwBytesWritten, 0))
  420.         m_cWriteFileErr++;
  421.     // need <commctrl.h> for listview control
  422.     if (!W32::WriteFile(m_hFile, szCommCtrlHdrFile, sizeof(szCommCtrlHdrFile)-sizeof(TCHAR), &dwBytesWritten, 0))
  423.         m_cWriteFileErr++;
  424.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwBytesWritten, 0))
  425.         m_cWriteFileErr++;
  426.  
  427.     // whitespace output (required)
  428.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwBytesWritten, 0))
  429.         m_cWriteFileErr++;
  430.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwBytesWritten, 0))
  431.         m_cWriteFileErr++;
  432.  
  433.     // return success 
  434.     return m_cWriteFileErr ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS;
  435. }
  436.  
  437. /////////////////////////////////////////////////////////////////////////////
  438. // CGenerateRC::WriteBinaries
  439. BOOL CGenerateRC::WriteBinaries()
  440. {
  441.     m_fWroteBinary = TRUE;
  442.     MSICONDITION eCond = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("Binary"));
  443.     if (eCond == MSICONDITION_ERROR)
  444.     {
  445.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseIsTablePersisent(Binary)\n"));
  446.         return m_fError = TRUE, FALSE; // error - ABORT
  447.     }
  448.     if (eCond != MSICONDITION_TRUE)
  449.     {
  450.         _tprintf(TEXT("LOG>> Binary table does not exist or is not persistent\n"));
  451.         return TRUE;
  452.     }
  453.     
  454. #ifdef DEBUG
  455.     _tprintf(TEXT("LOG>>...BEGIN WRITING BINARY DATA TO RESOURCE FILE...\n"));
  456. #endif
  457.  
  458.     UINT iStat;
  459.     PMSIHANDLE hViewBinary = 0;
  460.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlBinary, &hViewBinary)))
  461.         return m_fError = TRUE, FALSE; // error - ABORT
  462.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewBinary, 0)))
  463.         return m_fError = TRUE, FALSE; // error - ABORT
  464.     PMSIHANDLE hRecBinary = 0;
  465.     bdtBinaryDataType bdt;
  466.     DWORD dwWritten;
  467.     while (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewBinary, &hRecBinary)))
  468.     {
  469.         TCHAR szFileBuf[2*MAX_PATH];
  470.         TCHAR szPathBuf[MAX_PATH+1];
  471.         DWORD dwLen;
  472.         MSI::MsiRecordGetString(hRecBinary, ibcName, TEXT(""), &dwLen);
  473.         TCHAR* szName = new TCHAR[++dwLen];
  474.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecBinary, ibcName, szName, &dwLen)))
  475.             return m_fError = TRUE, FALSE; // error - ABORT
  476.  
  477.         // find the temp directory to dump out the binary data into temp files
  478.         if (0 == W32::GetTempPath(MAX_PATH, szPathBuf))
  479.             return m_fError = TRUE, FALSE; // error - ABORT
  480.         // generate a temporary file name, prefix with IBD for Installer Binary Data
  481.         if (0 == W32::GetTempFileName(szPathBuf, TEXT("IBD"), 0, szFileBuf))
  482.             return m_fError = TRUE, FALSE; // error - ABORT
  483.         // create the file
  484.         HANDLE hBinFile = W32::CreateFile(szFileBuf, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);
  485.         // verify handle
  486.         if (hBinFile == INVALID_HANDLE_VALUE)
  487.             return m_fError = TRUE, FALSE; // error - ABORT
  488.  
  489. #ifdef DEBUG
  490.     _tprintf(TEXT("LOG>>Binary data temp file created: '%s'\n"), szFileBuf);
  491. #endif
  492.  
  493.         // read in stream of data and write to file
  494.         char szStream[1024];
  495.         DWORD cbBuf = sizeof(szStream);
  496.         BOOL fFirstRun = TRUE;
  497.         do 
  498.         {
  499.             if (MsiRecordReadStream(hRecBinary, ibcData, szStream, &cbBuf) !=ERROR_SUCCESS)
  500.                 break; /* error */
  501.             if (fFirstRun)
  502.             {
  503.                 // binary data prefixed with "BM" in stream
  504.                 if (szStream[0] == 'B' && szStream[1] == 'M')
  505.                     bdt = bdtBitmap;
  506.                 else if (szStream[0] == 0xFF && szStream[1] == 0xD8)
  507.                     bdt = bdtJPEG;
  508.                 else if (szStream[0] == 'M' && szStream[1] == 'Z')
  509.                     bdt = bdtEXE_DLL_SCRIPT; // 'MZ prefix with exe's and dll's
  510.                 else if (szStream[0] == 0x00 && szStream[1] == 0x00)
  511.                     bdt = bdtIcon;
  512.                 else bdt = bdtEXE_DLL_SCRIPT;
  513. #ifdef DEBUG
  514.             if (fFirstRun && bdt != bdtEXE_DLL_SCRIPT)
  515.                 _tprintf(TEXT("LOG>> Writing <%s> '%s'\n"), bdt == bdtIcon ? TEXT("ICON") : ((bdt == bdtJPEG) ? TEXT("JPEG") : TEXT("BITMAP")), szName);
  516. #endif // DEBUG
  517.                 fFirstRun = FALSE;
  518.             }
  519.             if (cbBuf && bdt != bdtEXE_DLL_SCRIPT)
  520.             {
  521.                 if (!WriteFile(hBinFile, szStream, cbBuf, &dwWritten, 0))
  522.                     return m_fError = TRUE, FALSE; // error - ABORT
  523.             }
  524.         }
  525.         while (cbBuf == sizeof(szStream) && bdt != bdtEXE_DLL_SCRIPT);
  526.         
  527.         // close file
  528.         if (!W32::CloseHandle(hBinFile))
  529.             return m_fError = TRUE, FALSE; // error - ABORT
  530.  
  531.         if (bdt == bdtEXE_DLL_SCRIPT)
  532.             continue; // skip over DLL and EXE binary data, not UI related
  533.  
  534.         // output to resource file
  535.         // escape chars in str
  536.         TCHAR* szEscTitle = EscapeSlashAndQuoteForRC(szFileBuf);
  537.         if (_tcsclen(szEscTitle) > iMaxResStrLen)
  538.         {
  539.             _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> BITMAP FILE: %s\n"), szName);
  540.             continue; // can't output this
  541.         }
  542.         /*NAMEID<tab>*/
  543.         if (!W32::WriteFile(m_hFile, szName, _tcsclen(szName)*sizeof(TCHAR), &dwWritten, 0))
  544.             m_cWriteFileErr++;
  545.         if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  546.             m_cWriteFileErr++;
  547.         switch (bdt)
  548.         {
  549.         case bdtBitmap: /*BITMAP*/
  550.             if (!W32::WriteFile(m_hFile, resBitmap, _tcsclen(resBitmap)*sizeof(TCHAR), &dwWritten, 0))
  551.                 m_cWriteFileErr++;
  552.             break;
  553.         case bdtJPEG:   /*JPEG*/
  554.             if (!W32::WriteFile(m_hFile, resJPEG, _tcsclen(resJPEG)*sizeof(TCHAR), &dwWritten, 0))
  555.                 m_cWriteFileErr++;
  556.             break;
  557.         case bdtIcon: /*ICON*/
  558.             if (!W32::WriteFile(m_hFile, resIcon, _tcsclen(resIcon)*sizeof(TCHAR), &dwWritten, 0))
  559.                 m_cWriteFileErr++;
  560.             break;
  561.         default:
  562.             return m_fError = TRUE, FALSE; // error - ABORT
  563.         }
  564.         /*<tab>"filename"*/
  565.         if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  566.             m_cWriteFileErr++;
  567.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  568.             m_cWriteFileErr++;
  569.         if (!W32::WriteFile(m_hFile, szEscTitle, _tcslen(szEscTitle)*sizeof(TCHAR), &dwWritten, 0))
  570.             m_cWriteFileErr++;
  571.         if (szEscTitle)
  572.             delete szEscTitle;
  573.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  574.             m_cWriteFileErr++;
  575.         if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  576.             m_cWriteFileErr++;
  577.  
  578.         if (szName)
  579.             delete [] szName;
  580.     }
  581.     if (ERROR_NO_MORE_ITEMS != iStat)
  582.         return m_fError = TRUE, FALSE; // error - ABORT
  583.  
  584.     // whitespace in .rc file for readability
  585.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  586.         m_cWriteFileErr++;
  587.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  588.         m_cWriteFileErr++;
  589.  
  590. #ifdef DEBUG
  591.     _tprintf(TEXT("LOG>>...END WRITING BINARY DATA TO RESOURCE FILE...\n"));
  592. #endif
  593.  
  594.     return TRUE;
  595. }
  596.  
  597. /////////////////////////////////////////////////////////////////////////////
  598. // CGenerateRC::OutputStringInit
  599. UINT CGenerateRC::OutputStringInit()
  600. {
  601.     /**********************************************************************************
  602.      create internal table for mapping string IDs to strings
  603.      TABLE: _RESStrings
  604.      COLUMNS: RCID (Short, Primary Key), Table (String), Column (String), Key (String) 
  605.     ***********************************************************************************/
  606.     UINT iStat;
  607.     // see if _RESStrings table is already there
  608.     MSICONDITION eCondition = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("_RESStrings"));
  609.     if (eCondition == MSICONDITION_TRUE)
  610.     {
  611.         // table persistent
  612.         // find the last resource Id
  613.         PMSIHANDLE hViewSelMaxRc = 0;
  614.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlSelMaxStrRcId, &hViewSelMaxRc)))
  615.             return m_fError = TRUE, iStat; // error - ABORT
  616.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewSelMaxRc, 0)))
  617.             return m_fError = TRUE, iStat; // error - ABORT
  618.         PMSIHANDLE hRecMaxRc = 0;
  619.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewSelMaxRc, &hRecMaxRc)))
  620.             return m_fError = TRUE, iStat; // error - ABORT
  621.         // update resource Id
  622.         m_iStrResID = MSI::MsiRecordGetInteger(hRecMaxRc, 1);
  623. #ifdef DEBUG
  624.     _tprintf(TEXT("LOG>> _RESStrings Table is Present. MAX RES ID = %d\n"), m_iStrResID);
  625. #endif
  626.     }
  627.     else if (eCondition == MSICONDITION_FALSE || eCondition == MSICONDITION_ERROR) // error or table temporary
  628.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  629.     else
  630.     {
  631.         // table not exist -- create it
  632.         PMSIHANDLE h_StrMarkingView = 0;
  633.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlCreateStrMap, &h_StrMarkingView)))
  634.             return m_fError = TRUE, iStat; // error - ABORT
  635.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(h_StrMarkingView, 0)))
  636.             return m_fError = TRUE, iStat; // error - ABORT
  637.     }
  638.     return ERROR_SUCCESS;
  639. }
  640.  
  641. /////////////////////////////////////////////////////////////////////////////
  642. // CGenerateRC::OutputString
  643. UINT CGenerateRC::OutputString(TCHAR* szTable, TCHAR* szColumn)
  644. {
  645.     UINT iStat;
  646.     if (ERROR_SUCCESS != (iStat = Initialize()))
  647.         return m_fError = TRUE, iStat; // error - ABORT
  648.     
  649.     // initialize _RESStrings marking table
  650.     if (ERROR_SUCCESS != (iStat = OutputStringInit()))
  651.         return m_fError = TRUE, iStat; // error - ABORT
  652.  
  653.     // verify table exists
  654.     MSICONDITION eCond = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, szTable);
  655.     switch (eCond)
  656.     {
  657.     case MSICONDITION_FALSE: // table is temporary
  658.     case MSICONDITION_NONE: // table does not exist
  659.         _tprintf(TEXT("LOG_ERROR>> TABLE: %s is temporary or does not exist.\n"), szTable);
  660.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  661.     case MSICONDITION_ERROR: // error occured
  662.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseIsTablePersistent\n"));
  663.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  664.     case MSICONDITION_TRUE: // table is persistent
  665.         break; 
  666.     default:
  667.         assert(0);
  668.     }
  669.  
  670.     // build query to open view on table
  671.     PMSIHANDLE hRecPrimaryKeys = 0;
  672.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseGetPrimaryKeys(m_hDatabase, szTable, &hRecPrimaryKeys)))
  673.         return m_fError = TRUE, iStat; // error - ABORT
  674.     int cPrimaryKeys = MSI::MsiRecordGetFieldCount(hRecPrimaryKeys);
  675.     TCHAR szKeyCols[720] = {0};
  676.     for (int i=1; i<=cPrimaryKeys;i++)
  677.     {
  678.         DWORD dwLen = 0;
  679.         MSI::MsiRecordGetString(hRecPrimaryKeys, i, TEXT(""), &dwLen);
  680.         TCHAR* szColName = new TCHAR[++dwLen];
  681.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecPrimaryKeys, i, szColName, &dwLen)))
  682.             return m_fError = TRUE, iStat; // error - abort
  683.         if (i != 1)
  684.             lstrcat(szKeyCols, TEXT(","));
  685.         lstrcat(szKeyCols, szColName);
  686.         if (szColName)
  687.             delete [] szColName;
  688.     }
  689.     TCHAR sql[2048];
  690.     _stprintf(sql, sqlStrCol, szKeyCols, szColumn, szTable);
  691.     PMSIHANDLE hViewStrCol = 0;
  692.  
  693.     // open view
  694.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sql, &hViewStrCol)))
  695.     {
  696.         if (ERROR_BAD_QUERY_SYNTAX == iStat)
  697.             _tprintf(TEXT("LOG_ERROR>> Query failed, probably because the column '%s' does not exist in table '%s'\n"), szColumn, szTable);
  698.         else
  699.             _tprintf(TEXT("LOG_ERROR>> MsiDatabaseOpenView(Column=%s, Table=%s)\n"), szColumn, szTable);
  700.         return m_fError = TRUE, iStat; // error - ABORT
  701.     }
  702.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewStrCol, 0)))
  703.         return m_fError = TRUE, iStat; // error - ABORT
  704.  
  705.     // verify column is localizable
  706.     PMSIHANDLE hRecColNames = 0;
  707.     PMSIHANDLE hRecColType = 0;
  708.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewGetColumnInfo(hViewStrCol, MSICOLINFO_NAMES, &hRecColNames)))
  709.         return m_fError = TRUE, iStat; // error - ABORT
  710.     int cCols = MSI::MsiRecordGetFieldCount(hRecColNames);
  711.     int iStrCol = 0;
  712.     for (int iFindCol = 1; iFindCol <= cCols; iFindCol++)
  713.     {
  714.         DWORD dwLen = 72;
  715.         TCHAR szColumnName[72];
  716.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecColNames, iFindCol, szColumnName, &dwLen)))
  717.             return m_fError = TRUE, iStat; // error - ABORT
  718.         if (_tcscmp(szColumnName, szColumn) == 0)
  719.         {
  720.             iStrCol = iFindCol;
  721.             break;
  722.         }
  723.     }
  724.     assert(iStrCol);
  725.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewGetColumnInfo(hViewStrCol, MSICOLINFO_TYPES, &hRecColType)))
  726.     {
  727.         _tprintf(TEXT("LOG_ERROR>> MsiViewGetColumnInfo\n"));
  728.         return m_fError = TRUE, iStat; // error - ABORT
  729.     }
  730.     DWORD cchColType = 0;
  731.     MSI::MsiRecordGetString(hRecColType, iStrCol, TEXT(""), &cchColType);
  732.     TCHAR* szColType = new TCHAR[++cchColType];
  733.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecColType, iStrCol, szColType, &cchColType)))
  734.         return m_fError = TRUE, iStat; // error - ABORT
  735.  
  736.     // for column to be localizable, first char in szColType must be an 'L' or an 'l'
  737.     if (*szColType != 'L' && *szColType != 'l')
  738.     {
  739.         _tprintf(TEXT("LOG_ERROR>> Column '%s' of Table '%s' is not localizable\n"), szColumn, szTable);
  740.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  741.     }
  742.  
  743.     if (szColType)
  744.         delete [] szColType;
  745.  
  746.     // write whitespace for readability
  747.     // whitespace in .rc file for readability
  748.     DWORD dwWritten;
  749.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  750.         m_cWriteFileErr++;
  751.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  752.         m_cWriteFileErr++;
  753.  
  754.     // output STRINGTABLE resource header
  755.     // format is:
  756.     //  STRINGTABLE  [[optional-statements]]  {      stringID string      . . .  }
  757.     /*STRINGTABLE*/
  758.     if (!W32::WriteFile(m_hFile, resStringTable, _tcsclen(resStringTable)*sizeof(TCHAR), &dwWritten, 0))
  759.         m_cWriteFileErr++;
  760.     /*<tab>{*/
  761.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  762.         m_cWriteFileErr++;
  763.     if (!W32::WriteFile(m_hFile, szCurlyBeg, sizeof(szCurlyBeg)-sizeof(TCHAR), &dwWritten, 0))
  764.         m_cWriteFileErr++;
  765.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  766.         m_cWriteFileErr++;
  767.     
  768.     // output all strings
  769.     PMSIHANDLE hRecStr = 0;
  770.     while (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewStrCol, &hRecStr)))
  771.     {
  772.         // string to localize is at cPrimaryKeys+1 in record hRecStr
  773.         // generate key identifier
  774.         TCHAR szKeyIdentifier[720] = {0};
  775.         for (int iKey = 1; iKey<=cPrimaryKeys;iKey++)
  776.         {
  777.             DWORD dwLen = 0;
  778.             MSI::MsiRecordGetString(hRecStr, iKey, TEXT(""), &dwLen);
  779.             TCHAR* szKey = new TCHAR[++dwLen];
  780.             if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecStr, iKey, szKey, &dwLen)))
  781.                 return m_fError = TRUE, iStat; // error - ABORT
  782.             if (iKey != 1)
  783.                 lstrcat(szKeyIdentifier, TEXT(":"));
  784.             lstrcat(szKeyIdentifier, szKey);
  785.             if (szKey)
  786.                 delete [] szKey;
  787.         }
  788.  
  789.         int iResId = 0;
  790.         // attempt to find resource ID if previously used
  791.         _stprintf(sql, sqlFindStrResId, szTable, szColumn, szKeyIdentifier);
  792.         PMSIHANDLE hViewFindRes = 0;
  793.         PMSIHANDLE hRecFindRes = 0;
  794.         UINT iStat;
  795.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sql, &hViewFindRes)))
  796.             return FALSE; // error - ABORT
  797.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewFindRes, 0)))
  798.             return FALSE; // error - ABORT
  799.         if (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewFindRes, &hRecFindRes)))
  800.         {
  801.             // grab res id
  802.             iResId = MSI::MsiRecordGetInteger(hRecFindRes, 1);
  803.         }
  804.         else if (ERROR_NO_MORE_ITEMS == iStat)
  805.         {
  806.             // use next available resource Id
  807.             iResId = ++m_iStrResID;
  808.         }
  809.         else
  810.             return m_fError = TRUE, iStat; // error - ABORT
  811.  
  812.         // output string to resource file
  813.         /*<tab>ID*/
  814.         if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  815.             m_cWriteFileErr++;
  816.         TCHAR szTempBuf[10];
  817.         wsprintf(szTempBuf, TEXT("%d"), iResId);
  818.         if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  819.             m_cWriteFileErr++;
  820.  
  821.         DWORD dwLenStr = 0;
  822.         MSI::MsiRecordGetString(hRecStr, cPrimaryKeys+1, TEXT(""), &dwLenStr);
  823.         TCHAR* szStr = new TCHAR[++dwLenStr];
  824.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecStr, cPrimaryKeys+1, szStr, &dwLenStr)))
  825.             return m_fError = TRUE, iStat; // error - ABORT
  826. #ifdef DEBUG
  827.         _tprintf(TEXT("LOG>> WRITING string '%s'. TABLE:%s COLUMN:%s KEY:%s\n"), szStr, szTable, szColumn, szKeyIdentifier);
  828. #endif
  829.         // escape chars in str
  830.         TCHAR* szEscTitle = EscapeSlashAndQuoteForRC(szStr);
  831.         if (_tcsclen(szEscTitle) > iMaxResStrLen)
  832.         {
  833.             _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> STRING: %s FROM TABLE: %s, COLUMN: %s, KEY: %s\n"), szStr, szTable, szColumn, szKeyIdentifier);
  834.             continue; // can't output this
  835.         }
  836.         if (szStr)
  837.             delete [] szStr;
  838.         /*,<tab>"str"*/
  839.         if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  840.             m_cWriteFileErr++;
  841.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  842.             m_cWriteFileErr++;
  843.         if (!W32::WriteFile(m_hFile, szEscTitle, _tcslen(szEscTitle)*sizeof(TCHAR), &dwWritten, 0))
  844.             m_cWriteFileErr++;
  845.         if (szEscTitle)
  846.             delete szEscTitle;
  847.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  848.             m_cWriteFileErr++;
  849.         if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  850.             m_cWriteFileErr++;
  851.  
  852.         // update resid in _RESStrings
  853.         PMSIHANDLE hViewRes = 0;
  854.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlInsertStr, &hViewRes)))
  855.             return m_fError = TRUE, iStat;
  856.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewRes, 0)))
  857.             return m_fError = TRUE, iStat;
  858.         PMSIHANDLE hRecInsertStr = MSI::MsiCreateRecord(4);
  859.         assert(hRecInsertStr);
  860.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetInteger(hRecInsertStr, 4, iResId)))
  861.             return m_fError = TRUE, iStat;
  862.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertStr, 1, szTable)))
  863.             return m_fError = TRUE, iStat;
  864.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertStr, 2, szColumn)))
  865.             return m_fError = TRUE, iStat;
  866.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertStr, 3, szKeyIdentifier)))
  867.             return m_fError = TRUE, iStat;
  868.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewRes, MSIMODIFY_ASSIGN, hRecInsertStr)))
  869.             return m_fError = TRUE, iStat;
  870.     }
  871.     if (iStat != ERROR_NO_MORE_ITEMS)
  872.         return m_fError = TRUE, iStat; // error - ABORT
  873.  
  874.     /*}*/
  875.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  876.         m_cWriteFileErr++;
  877.     if (!W32::WriteFile(m_hFile, szCurlyEnd, sizeof(szCurlyEnd)-sizeof(TCHAR), &dwWritten, 0))
  878.         m_cWriteFileErr++;
  879.  
  880.  
  881.     // update _RESStrings table with MAX_RESOURCE_ID
  882.     PMSIHANDLE hViewStrMark = 0;
  883.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlStrMark, &hViewStrMark)))
  884.         return m_fError = TRUE, iStat; // error - ABORT
  885.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewStrMark, 0)))
  886.         return m_fError = TRUE, iStat; // error - ABORT
  887.     PMSIHANDLE hRecMaxRcId = MSI::MsiCreateRecord(4);
  888.     assert(hRecMaxRcId);
  889.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecMaxRcId, 1, TEXT("MAX_RESOURCE_ID"))))
  890.         return m_fError = TRUE, iStat; // error - ABORT
  891.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecMaxRcId, 2, TEXT("MAX_RESOURCE_ID"))))
  892.         return m_fError = TRUE, iStat; // error - ABORT
  893.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecMaxRcId, 3, TEXT(""))))
  894.         return m_fError = TRUE, iStat; // error - ABORT
  895.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetInteger(hRecMaxRcId, 4, m_iStrResID)))
  896.         return m_fError = TRUE, iStat; // error - ABORT
  897.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewStrMark, MSIMODIFY_ASSIGN, hRecMaxRcId)))
  898.         return m_fError = TRUE, iStat; // error - ABORT
  899.  
  900.     return ERROR_SUCCESS;
  901. }
  902.  
  903. /////////////////////////////////////////////////////////////////////////////
  904. // CGenerateRC::Initialize
  905. UINT CGenerateRC::Initialize()
  906. {
  907.     UINT iStat;
  908.     // open database if not already open
  909.     assert(m_szOrigDb);
  910.     if (!m_hDatabase)
  911.     {
  912.         // if m_szDatabase is specified, then we use it for specifying an output database
  913.         iStat = MSI::MsiOpenDatabase(m_szOrigDb, m_szDatabase ? m_szDatabase : MSIDBOPEN_TRANSACT, &m_hDatabase);
  914.         if (ERROR_SUCCESS != iStat)
  915.         {
  916.             _tprintf(TEXT("LOG_ERROR>> Unable to open database %s\n"), m_szOrigDb);
  917.             return m_fError = TRUE, iStat; // error - ABORT
  918.         }
  919.     }
  920.  
  921.     // verify database codepage is NEUTRAL
  922.     if  (ERROR_SUCCESS != (iStat = VerifyDatabaseCodepage()))
  923.         return m_fError = TRUE, iStat; // error - ABORT
  924.  
  925.     // create resource file if not already created
  926.     if (!m_hFile && ERROR_SUCCESS != (iStat = CreateResourceFile()))
  927.     {
  928.         _tprintf(TEXT("LOG_ERROR>> Unable to create resource file.\n"));
  929.         return m_fError = TRUE, iStat; // error - ABORT
  930.     }
  931.  
  932.     return ERROR_SUCCESS;
  933. }
  934.  
  935. /////////////////////////////////////////////////////////////////////////////
  936. // CGenerateRC::OutputDialogFinalize
  937. UINT CGenerateRC::OutputDialogFinalize()
  938. {
  939.     // update _RESControls table with MAX_RESOURCE_ID
  940.     PMSIHANDLE hViewCtrlMark = 0;
  941.     UINT iStat;
  942.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlCtrlMark, &hViewCtrlMark)))
  943.         return m_fError = TRUE, iStat; // error - ABORT
  944.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewCtrlMark, 0)))
  945.         return m_fError = TRUE, iStat; // error - ABORT
  946.     PMSIHANDLE hRecMaxRcId = MSI::MsiCreateRecord(3);
  947.     assert(hRecMaxRcId);
  948.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecMaxRcId, 1, TEXT("MAX_RESOURCE_ID"))))
  949.         return m_fError = TRUE, iStat; // error - ABORT
  950.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecMaxRcId, 2, TEXT("MAX_RESOURCE_ID"))))
  951.         return m_fError = TRUE, iStat; // error - ABORT
  952.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetInteger(hRecMaxRcId, 3, m_iCtrlResID)))
  953.         return m_fError = TRUE, iStat; // error - ABORT
  954.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewCtrlMark, MSIMODIFY_ASSIGN, hRecMaxRcId)))
  955.         return m_fError = TRUE, iStat; // error - ABORT
  956.  
  957. #ifdef DEBUG
  958.     _tprintf(TEXT("LOG>>...END WRITING DIALOG DATA TO RESOURCE FILE...\n"));
  959. #endif
  960.  
  961.     return ERROR_SUCCESS;
  962. }
  963.  
  964. /////////////////////////////////////////////////////////////////////////////
  965. // CGenerateRC::VerifyDatabaseCodepage
  966. UINT CGenerateRC::VerifyDatabaseCodepage()
  967. {
  968.     UINT iStat;
  969.     // only output from language neutral database
  970.     TCHAR szTempPath[MAX_PATH];
  971.     W32::GetTempPath(MAX_PATH, szTempPath);
  972.     // export _ForceCodepage table so can verify codepage
  973.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseExport(m_hDatabase, TEXT("_ForceCodepage"), szTempPath, szCodepageExport)))
  974.     {
  975.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseExport(_ForceCodepage)\n"));
  976.         return m_fError = TRUE, iStat; // error - ABORT
  977.     }
  978.     // open _ForceCodepage.idt to read it
  979.     TCHAR szFullPath[MAX_PATH + 32];
  980.     wsprintf(szFullPath, TEXT("%s%s"), szTempPath, szCodepageExport);
  981.     HANDLE hFile = W32::CreateFile(szFullPath, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)0, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
  982.     if (hFile == NULL)
  983.     {
  984.         _tprintf(TEXT("LOG_ERROR>> W32::OpenFile(_ForceCodepage.idt)\n"));
  985.         return m_fError = TRUE, iStat; // error - ABORT
  986.     }
  987.     // read file for information
  988.     DWORD dwSize = W32::GetFileSize(hFile, NULL);
  989.     if (0xFFFFFFFF == dwSize)
  990.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  991.     char* szBuf = new char[dwSize+1];
  992.     DWORD dwRead;
  993.     if (!W32::ReadFile(hFile, (LPVOID)szBuf, dwSize, &dwRead, NULL))
  994.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  995.     // parse buffer
  996.     // format should be : blank line, blank line, "codepage<tab>_ForceCodepage"
  997.     assert(dwRead != 0);
  998.     char* pch = szBuf;
  999.     int cBlankLines = 0;
  1000.     while (dwRead && cBlankLines != 2)
  1001.     {
  1002.         if (*pch == '\n')
  1003.             cBlankLines++;
  1004.         pch++;
  1005.         dwRead--;
  1006.     }
  1007.     if (!dwRead || cBlankLines != 2)
  1008.     {
  1009.         _tprintf(TEXT("LOG_ERROR>> Invalid ForceCodepage idt format\n"));
  1010.         return m_fError = TRUE, ERROR_FUNCTION_FAILED;
  1011.     }
  1012.     // codepage is next
  1013.     char* pchCodepage = pch;
  1014.     while (dwRead && *pch != ' ' && *pch != '\t')
  1015.     {
  1016.         pch++;
  1017.         dwRead--;
  1018.     }
  1019.     assert(dwRead);
  1020.     *pch = '\0';
  1021.     // convert codepage to int
  1022.     UINT uiCodepage = strtoul(pchCodepage, NULL, 10);
  1023.     if (uiCodepage != 0) // 0 is language neutral
  1024.     {
  1025.         _tprintf(TEXT("LOG_ERROR>> DATABASE IS NOT LANGUAGE NEUTRAL. CANNOT EXPORT\n"));
  1026.         _tprintf(TEXT("LOG_ERROR>> CURRENT CODEPAGE IS %d\n"), uiCodepage);
  1027.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1028.     }
  1029.     if (!W32::CloseHandle(hFile))
  1030.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1031.  
  1032.     return ERROR_SUCCESS;
  1033. }
  1034.  
  1035. /////////////////////////////////////////////////////////////////////////////
  1036. // CGenerateRC::OutputDialogInit
  1037. UINT CGenerateRC::OutputDialogInit(BOOL fBinary)
  1038. {
  1039.     UINT iStat;
  1040.     if (ERROR_SUCCESS != (iStat = Initialize()))
  1041.         return m_fError = TRUE, iStat; // error - ABORT
  1042.  
  1043.     // only output if Dialog table exists
  1044.     MSICONDITION eCond = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("Dialog"));
  1045.     if (eCond == MSICONDITION_ERROR)
  1046.     {
  1047.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseIsTablePersistent(Dialog)\n"));
  1048.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1049.     }
  1050.     // require PERSISTENT tables
  1051.     if (eCond != MSICONDITION_TRUE)
  1052.     {
  1053.         _tprintf(TEXT("LOG>> Dialog table is not persistent or does not exist\n"));
  1054.         return ERROR_SUCCESS;
  1055.     }
  1056.         
  1057.     /**********************************************************************************
  1058.      create internal table for mapping DIALOGS to Dialogs (resource file stores
  1059.      strings IDs in ALL CAPS.  Installer is case-sensitive
  1060.      TABLE: _RESDialogs
  1061.      COLUMNS: RCStr (String, Primary Key), Dialog (String, Primary Key) 
  1062.     ***********************************************************************************/
  1063.     
  1064.     // see if _RESDialogs table is already there
  1065.     MSICONDITION eCondition = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("_RESDialogs"));
  1066.     if (eCondition == MSICONDITION_TRUE)
  1067.     {
  1068.         // table persistent
  1069. #ifdef DEBUG
  1070.         _tprintf(TEXT("LOG>> _RESDialogs Table is Present.\n"));
  1071. #endif
  1072.     }
  1073.     else if (eCondition == MSICONDITION_FALSE || eCondition == MSICONDITION_ERROR) // error or table temporary
  1074.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1075.     else
  1076.     {
  1077.         // table not exist -- create it
  1078.         PMSIHANDLE h_DlgMarkingView = 0;
  1079.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlCreateDlgMap, &h_DlgMarkingView)))
  1080.             return m_fError = TRUE, iStat; // error - ABORT
  1081.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(h_DlgMarkingView, 0)))
  1082.             return m_fError = TRUE, iStat; // error - ABORT
  1083.     }
  1084.  
  1085.     /**********************************************************************************
  1086.      create internal table for managing resource IDs of controls
  1087.      TABLE: _RESControls
  1088.      COLUMNS: Dialog_ (String, Primary Key), Control_ (String, Primary Key), RCID (Int)  
  1089.     ***********************************************************************************/
  1090.  
  1091.     // see if _RESControls table is already there
  1092.     eCondition = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("_RESControls"));
  1093.     if (eCondition == MSICONDITION_TRUE)
  1094.     {
  1095.         // table persistent
  1096.         // find the last resource Id
  1097.         PMSIHANDLE hViewSelMaxRc = 0;
  1098.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlSelMaxRcId, &hViewSelMaxRc)))
  1099.             return m_fError = TRUE, iStat; // error - ABORT
  1100.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewSelMaxRc, 0)))
  1101.             return m_fError = TRUE, iStat; // error - ABORT
  1102.         PMSIHANDLE hRecMaxRc = 0;
  1103.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewSelMaxRc, &hRecMaxRc)))
  1104.             return m_fError = TRUE, iStat; // error - ABORT
  1105.         // update resource Id
  1106.         m_iCtrlResID = MSI::MsiRecordGetInteger(hRecMaxRc, 1);
  1107. #ifdef DEBUG
  1108.     _tprintf(TEXT("LOG>> _RESControls Table is Present. MAX RES ID = %d\n"), m_iCtrlResID);
  1109. #endif
  1110.  
  1111.     }
  1112.     else if (eCondition == MSICONDITION_ERROR || eCondition == MSICONDITION_FALSE) // error or temporary
  1113.         return m_fError = TRUE, ERROR_FUNCTION_FAILED;
  1114.     else
  1115.     {
  1116.         // table not exist -- create it
  1117.         PMSIHANDLE h_CtrlMarkingView = 0;
  1118.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlCreateCtrlMark, &h_CtrlMarkingView)))
  1119.             return m_fError = TRUE, iStat; // error - ABORT
  1120.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(h_CtrlMarkingView, 0)))
  1121.             return m_fError = TRUE, iStat; // error - ABORT
  1122.     }
  1123. #ifdef DEBUG
  1124.     _tprintf(TEXT("LOG>>...BEGIN WRITING DIALOG DATA TO RESOURCE FILE...\n"));
  1125. #endif
  1126.  
  1127.     return ERROR_SUCCESS;
  1128. }
  1129.  
  1130. /////////////////////////////////////////////////////////////////////////////
  1131. // CGenerateRC::OutputDialogs
  1132. UINT CGenerateRC::OutputDialogs(BOOL fBinary)
  1133. {
  1134.     // write out each dialog in Dialog table
  1135.     // According to MSDN, new applications should use DIALOGEX resource instead of DIALOG
  1136.  
  1137.     UINT iStat;
  1138.     if (ERROR_SUCCESS != (iStat = OutputDialogInit(fBinary)))
  1139.         return m_fError = TRUE, iStat; // error - ABORT
  1140.  
  1141.         // write out the bitmaps and icons from the Binary table
  1142. #ifdef DEBUG
  1143.     if (!fBinary)
  1144.         _tprintf(TEXT("LOG>> SKIPPING Binary data export.\n"));
  1145. #endif
  1146.     if (fBinary && !m_fWroteBinary && !WriteBinaries())
  1147.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1148.  
  1149.     // prepare Dialog strResource --> Dialog mapping table
  1150.     PMSIHANDLE hViewDlgMap = 0;
  1151.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlDlgMap, &hViewDlgMap)))
  1152.         return m_fError = TRUE, iStat; // error - ABORT
  1153.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewDlgMap, 0)))
  1154.         return m_fError = TRUE, iStat; // error - ABORT
  1155.     PMSIHANDLE hRecDlgMap = MSI::MsiCreateRecord(2);
  1156.     assert(hRecDlgMap);
  1157.  
  1158.     // open view on Dialog table
  1159.     PMSIHANDLE hViewDialog = 0;
  1160.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlDialog, &hViewDialog)))
  1161.         return m_fError = TRUE, iStat; // error - ABORT
  1162.  
  1163.     // execute view
  1164.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewDialog, 0)))
  1165.         return m_fError = TRUE, iStat; // error - ABORT
  1166.  
  1167.     // fetch all row records of dialogs from Dialog table and output to .rc file
  1168.     PMSIHANDLE hRecDialog = 0;
  1169.     while (ERROR_NO_MORE_ITEMS != (iStat = MSI::MsiViewFetch(hViewDialog, &hRecDialog)))
  1170.     {
  1171.         if (ERROR_SUCCESS != iStat)
  1172.             return m_fError = TRUE, iStat; // error - ABORT
  1173.  
  1174.         // use dialog name as nameID
  1175.         // Reasoning: guarantees uniqueness since Dialog name is primary key of table
  1176.         // Potential caveat:  Dialog strId is stored in ALL CAPS. Installer is case-sensitive
  1177.         //   so we must store a mapping between this and original.  Could have case where we
  1178.         //   try Action1 and ACTion1 as two different strIds.  RC will fail on this
  1179.         
  1180.         // 1st call, obtain size needed
  1181.         // 2nd call, get string
  1182.         DWORD dwLen = 0;
  1183.         MSI::MsiRecordGetString(hRecDialog, idcName, TEXT(""), &dwLen);
  1184.         TCHAR* szDialog = new TCHAR[++dwLen];
  1185.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecDialog, idcName, szDialog, &dwLen)))
  1186.             return m_fError = TRUE, iStat; // error - ABORT
  1187.  
  1188.         // update Dialog Mapping table with information
  1189.         MSI::MsiRecordSetString(hRecDlgMap, 2, szDialog);
  1190.         TCHAR szTempDialog[72];
  1191.         _tcscpy(szTempDialog, szDialog);
  1192.         MSI::MsiRecordSetString(hRecDlgMap, 1, _tcsupr(szTempDialog)); // resource file format, ALL CAPS
  1193.         
  1194.         // update _RESDialogs table, note:  we will overwrite pre-existing. Rely on rc.exe to bail 
  1195.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewDlgMap, MSIMODIFY_ASSIGN, hRecDlgMap)))
  1196.             return m_fError = TRUE, iStat; // error - ABORT
  1197.  
  1198.         // get x, y, wd, ht, and attrib values
  1199.         int x,y,wd,ht,attrib;
  1200.         x      = MSI::MsiRecordGetInteger(hRecDialog, idcX);
  1201.         y      = MSI::MsiRecordGetInteger(hRecDialog, idcY);
  1202.         wd     = MSI::MsiRecordGetInteger(hRecDialog, idcWd);
  1203.         ht     = MSI::MsiRecordGetInteger(hRecDialog, idcHt);
  1204.         attrib = MSI::MsiRecordGetInteger(hRecDialog, idcAttrib);
  1205.  
  1206.  
  1207.         // obtain title of dialog
  1208.         MSI::MsiRecordGetString(hRecDialog, idcTitle, TEXT(""), &dwLen);
  1209.         TCHAR* szTitle = new TCHAR[++dwLen];
  1210.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecDialog, idcTitle, szTitle, &dwLen)))
  1211.             return m_fError = TRUE, iStat; // error - ABORT
  1212.  
  1213.         if (!WriteDialogToRC(szDialog, szTitle, x, y, wd, ht, attrib))
  1214.             return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1215.  
  1216.     }
  1217.  
  1218.     if (ERROR_SUCCESS != (iStat = OutputDialogFinalize()))
  1219.         return m_fError = TRUE, iStat; // error - ABORT
  1220.  
  1221.  
  1222.     // return success
  1223.     return ERROR_SUCCESS;
  1224. }
  1225.  
  1226. /////////////////////////////////////////////////////////////////////////////
  1227. // CGenerateRC::OutputDialog
  1228. UINT CGenerateRC::OutputDialog(TCHAR* szDialog, BOOL fBinary)
  1229. {
  1230.     // write out specified dialog in Dialog table
  1231.     // According to MSDN, new applications should use DIALOGEX resource instead of DIALOG
  1232.  
  1233.     UINT iStat;
  1234.     if (ERROR_SUCCESS != (iStat = OutputDialogInit(fBinary)))
  1235.         return m_fError = TRUE, iStat; // error - ABORT
  1236.  
  1237.     // prepare Dialog strResource --> Dialog mapping table
  1238.     PMSIHANDLE hViewDlgMap = 0;
  1239.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlDlgMap, &hViewDlgMap)))
  1240.         return m_fError = TRUE, iStat; // error - ABORT
  1241.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewDlgMap, 0)))
  1242.         return m_fError = TRUE, iStat; // error - ABORT
  1243.     PMSIHANDLE hRecDlgMap = MSI::MsiCreateRecord(2);
  1244.     assert(hRecDlgMap);
  1245.  
  1246.     // open view on Dialog table
  1247.     PMSIHANDLE hViewDialog = 0;
  1248.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlDialogSpecific, &hViewDialog)))
  1249.         return m_fError = TRUE, iStat; // error - ABORT
  1250.  
  1251.     // execute view
  1252.     PMSIHANDLE hRecFindDlg = MSI::MsiCreateRecord(1);
  1253.     assert(hRecFindDlg);
  1254.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecFindDlg, 1, szDialog)))
  1255.         return m_fError = TRUE, iStat; // error - ABORT
  1256.  
  1257.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewDialog, hRecFindDlg)))
  1258.         return m_fError = TRUE, iStat; // error - ABORT
  1259.  
  1260.     // fetch specified Dialog from Dialog table and output to .rc file
  1261.     PMSIHANDLE hRecDialog = 0;
  1262.     if (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewDialog, &hRecDialog)))
  1263.     {
  1264.         // write out the bitmaps and icons from the Binary table
  1265. #ifdef DEBUG
  1266.         if (!fBinary)
  1267.             _tprintf(TEXT("LOG>> SKIPPING Binary data export.\n"));
  1268. #endif
  1269.         if (fBinary && !m_fWroteBinary && !WriteBinaries())
  1270.             return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1271.         // use dialog name as nameID
  1272.         // Reasoning: guarantees uniqueness since Dialog name is primary key of table
  1273.         // Potential caveat:  Dialog strId is stored in ALL CAPS. Installer is case-sensitive
  1274.         //   so we must store a mapping between this and original.  Could have case where we
  1275.         //   try Action1 and ACTion1 as two different strIds.  RC will fail on this
  1276.         
  1277.         // 1st call, obtain size needed
  1278.         // 2nd call, get string
  1279.         DWORD dwLen = 0;
  1280.         MSI::MsiRecordGetString(hRecDialog, idcName, TEXT(""), &dwLen);
  1281.         TCHAR* szDialog = new TCHAR[++dwLen];
  1282.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecDialog, idcName, szDialog, &dwLen)))
  1283.             return m_fError = TRUE, iStat; // error - ABORT
  1284.  
  1285.         // update Dialog Mapping table with information
  1286.         MSI::MsiRecordSetString(hRecDlgMap, 2, szDialog);
  1287.         TCHAR szTempDialog[72];
  1288.         _tcscpy(szTempDialog, szDialog);
  1289.         MSI::MsiRecordSetString(hRecDlgMap, 1, _tcsupr(szTempDialog)); // resource file format, ALL CAPS
  1290.         
  1291.         // update _RESDialogs table, note:  we will overwrite pre-existing. Rely on rc.exe to bail 
  1292.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewDlgMap, MSIMODIFY_ASSIGN, hRecDlgMap)))
  1293.             return m_fError = TRUE, iStat; // error - ABORT
  1294.  
  1295.         // get x, y, wd, ht, and attrib values
  1296.         int x,y,wd,ht,attrib;
  1297.         x      = MSI::MsiRecordGetInteger(hRecDialog, idcX);
  1298.         y      = MSI::MsiRecordGetInteger(hRecDialog, idcY);
  1299.         wd     = MSI::MsiRecordGetInteger(hRecDialog, idcWd);
  1300.         ht     = MSI::MsiRecordGetInteger(hRecDialog, idcHt);
  1301.         attrib = MSI::MsiRecordGetInteger(hRecDialog, idcAttrib);
  1302.  
  1303.  
  1304.         // obtain title of dialog
  1305.         MSI::MsiRecordGetString(hRecDialog, idcTitle, TEXT(""), &dwLen);
  1306.         TCHAR* szTitle = new TCHAR[++dwLen];
  1307.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecDialog, idcTitle, szTitle, &dwLen)))
  1308.             return m_fError = TRUE, iStat; // error - ABORT
  1309.  
  1310.         if (!WriteDialogToRC(szDialog, szTitle, x, y, wd, ht, attrib))
  1311.             return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  1312.  
  1313.     }
  1314.     else if (ERROR_NO_MORE_ITEMS == iStat)
  1315.     {
  1316.         _tprintf(TEXT("LOG_ERROR>> Dialog '%s' not found in Dialog table\n"), szDialog);
  1317.         if (ERROR_SUCCESS != (iStat = OutputDialogFinalize()))
  1318.             return m_fError = TRUE, iStat;
  1319.         return ERROR_SUCCESS; // error, but not fatal...keep processing
  1320.     }
  1321.     else
  1322.     {
  1323.         _tprintf(TEXT("LOG_ERROR>> MsiViewFetch(specific dialog)\n"));
  1324.         return iStat;
  1325.     }
  1326.  
  1327.     if (ERROR_SUCCESS != (iStat = OutputDialogFinalize()))
  1328.         return m_fError = TRUE, iStat; // error - ABORT
  1329.  
  1330.     return ERROR_SUCCESS;
  1331. }
  1332.  
  1333. /////////////////////////////////////////////////////////////////////////////
  1334. // CGenerateRC::EscapeSlashAndQuteForRc
  1335. TCHAR* CGenerateRC::EscapeSlashAndQuoteForRC(TCHAR* szStr)
  1336. {
  1337.     TCHAR* szNewStr = 0;
  1338.     
  1339.     // check for NULL str
  1340.     if (szStr == 0)
  1341.         return szNewStr;
  1342.  
  1343.     // determine if str contains any esc char
  1344.     int cEscChar = 0;
  1345.     TCHAR* pch = szStr;
  1346.     while (*pch != 0)
  1347.     {
  1348.         if (*pch == TEXT('\\') || *pch == TEXT('"'))
  1349.             cEscChar++;
  1350.         pch++;
  1351.     }
  1352.  
  1353.     if (cEscChar == 0)
  1354.     {
  1355.         int iLen = _tcsclen(szStr) + 1; // for null
  1356.         szNewStr = new TCHAR[iLen];
  1357.         _tcscpy(szNewStr, szStr);
  1358.     }
  1359.     else
  1360.     {
  1361.         int iLen = _tcsclen(szStr) + 1 + cEscChar;
  1362.         szNewStr = new TCHAR[iLen];
  1363.         pch = szStr;
  1364.         TCHAR* pchNew = szNewStr;
  1365.         while (*pch != 0)
  1366.         {
  1367.             if (*pch == TEXT('\\'))
  1368.                 *pchNew++ = TEXT('\\');
  1369.             else if (*pch == TEXT('"'))
  1370.                 *pchNew++ = TEXT('"');
  1371.             *pchNew++ = *pch++;
  1372.         }
  1373.         *pchNew = TEXT('\0');
  1374.     }
  1375.     
  1376.     return szNewStr;
  1377. }
  1378.  
  1379. /////////////////////////////////////////////////////////////////////////////
  1380. // CGenerateRC::WriteDialogToRC
  1381. BOOL CGenerateRC::WriteDialogToRC(TCHAR* szDialog, TCHAR* szTitle, int x, int y, int wd, int ht, int attrib)
  1382. {
  1383. #ifdef DEBUG
  1384.     _tprintf(TEXT("LOG>> Writing <%s> Dialog\n"), szDialog);
  1385. #endif
  1386.     // Format for DIALOGEX is:
  1387.     // nameID DIALOGEX x, y, width, height [ , helpID]]  [[ optional-statements]]  {control-statements}
  1388.  
  1389.     // write out to file
  1390.  
  1391.     DWORD dwWritten;
  1392.     /*nameId*/
  1393.     if (!W32::WriteFile(m_hFile, szDialog, _tcsclen(szDialog)*sizeof(TCHAR), &dwWritten, 0))
  1394.         m_cWriteFileErr++;
  1395.     /*<tab>DIALOGEX*/
  1396.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1397.         m_cWriteFileErr++;
  1398.     if (!W32::WriteFile(m_hFile, resDialog, sizeof(resDialog)-sizeof(TCHAR), &dwWritten, 0))
  1399.         m_cWriteFileErr++;
  1400.     /*x, y, wd, ht DIMENSIONS*/
  1401.     if (!PrintDimensions(x, y, wd, ht))
  1402.         return m_fError = TRUE, FALSE; // error - ABORT
  1403.     //TODO??: HelpId
  1404.     /*<tab>CAPTION<tab>"str"*/
  1405.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1406.         m_cWriteFileErr++;
  1407.     if (!W32::WriteFile(m_hFile, tokCaption, sizeof(tokCaption)-sizeof(TCHAR), &dwWritten, 0))
  1408.         m_cWriteFileErr++;
  1409.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1410.         m_cWriteFileErr++;
  1411.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  1412.         m_cWriteFileErr++;
  1413.     // escape chars in str
  1414.     TCHAR* szEscTitle = EscapeSlashAndQuoteForRC(szTitle);
  1415.     if (_tcsclen(szEscTitle) > iMaxResStrLen)
  1416.     {
  1417.         _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> DIALOG: %s\n"), szDialog);
  1418.         _tcscpy(szEscTitle, strOverLimit);
  1419.     }
  1420.     if (!W32::WriteFile(m_hFile, szEscTitle, _tcslen(szEscTitle)*sizeof(TCHAR), &dwWritten, 0))
  1421.         m_cWriteFileErr++;
  1422.     if (szEscTitle)
  1423.         delete szEscTitle;
  1424.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  1425.         m_cWriteFileErr++;
  1426.     //attributes are ignored at this time (would require conversion from Installer to Windows and masking of Installer specific)
  1427.     /*<tab>{*/
  1428.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1429.         m_cWriteFileErr++;
  1430.     if (!W32::WriteFile(m_hFile, szCurlyBeg, sizeof(szCurlyBeg)-sizeof(TCHAR), &dwWritten, 0))
  1431.         m_cWriteFileErr++;
  1432.  
  1433.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  1434.         m_cWriteFileErr++;
  1435.     // Select all controls associated with said dialog and output
  1436.     if (!OutputControls(szDialog))
  1437.         return m_fError = TRUE, FALSE; // error - ABORT
  1438.     if (!W32::WriteFile(m_hFile, szCurlyEnd, sizeof(szCurlyEnd)-sizeof(TCHAR), &dwWritten, 0))
  1439.         m_cWriteFileErr++;
  1440.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  1441.         m_cWriteFileErr++;
  1442.  
  1443.     // whitespace in .rc file for readability
  1444.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  1445.         m_cWriteFileErr++;
  1446.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  1447.         m_cWriteFileErr++;
  1448.  
  1449.  
  1450.     // return success
  1451.     return TRUE;
  1452. }
  1453.  
  1454. /////////////////////////////////////////////////////////////////////////////
  1455. // CGenerateRC::PrintDimensions
  1456. BOOL CGenerateRC::PrintDimensions(int x, int y, int wd, int ht)
  1457. {
  1458.     // TODO: may require special coordinate computation (system units versus Installer dialog units)
  1459.     TCHAR szTempBuf[64];
  1460.     DWORD dwWritten;
  1461.  
  1462.     /*<tab>x*/
  1463.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1464.         m_cWriteFileErr++;
  1465.     wsprintf(szTempBuf, TEXT("%d"), x);
  1466.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcsclen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  1467.         m_cWriteFileErr++;
  1468.     /*,<tab>y*/
  1469.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1470.         m_cWriteFileErr++;
  1471.     wsprintf(szTempBuf, TEXT("%d"), y);
  1472.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcsclen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  1473.         m_cWriteFileErr++;
  1474.     /*,<tab>wd*/
  1475.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1476.         m_cWriteFileErr++;
  1477.     wsprintf(szTempBuf, TEXT("%d"), wd);
  1478.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcsclen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  1479.         m_cWriteFileErr++;
  1480.     /*,<tab>ht*/
  1481.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1482.         m_cWriteFileErr++;
  1483.     wsprintf(szTempBuf, TEXT("%d"), ht);
  1484.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcsclen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  1485.         m_cWriteFileErr++;
  1486.  
  1487.     // return success
  1488.     return TRUE;
  1489. }
  1490.  
  1491. /////////////////////////////////////////////////////////////////////////////
  1492. // CGenerateRC::OutputControls
  1493. BOOL CGenerateRC::OutputControls(TCHAR* szDialog)
  1494. {
  1495.     //PUNTED: output in tab order
  1496.     //REASON: extremely difficult to get right, very difficult if changes are made when importing
  1497.  
  1498.     // TODO: Handle output in correct tab order.  Fairly easy for export if you remember to output
  1499.     // all non tab order controls first.  The Control_Next column of the Control table would be
  1500.     // used as well as the Control_First column of the Dialog table.  Control_First is the start
  1501.     // of the tab order.  Control_Next is the next control in the tab order.  It is difficult to
  1502.     // handle the tab order when importing if you allow the tab order to change between export
  1503.     // and import.
  1504.     UINT iStat;
  1505.  
  1506.     // only have controls to write if Control table exists
  1507.     MSICONDITION eCond = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("Control"));
  1508.     if (eCond == MSICONDITION_ERROR)
  1509.     {
  1510.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseIsTablePersistent(Control)\n"));
  1511.         return m_fError = TRUE, FALSE; // error - ABORT
  1512.     }
  1513.     // require Control table to be persistent
  1514.     if (eCond != MSICONDITION_TRUE)
  1515.     {
  1516.         _tprintf(TEXT("LOG: Control table is not persistent or present\n"));
  1517.         return TRUE;
  1518.     }
  1519.  
  1520.     // open view on Control table
  1521.     PMSIHANDLE hViewControl = 0;
  1522.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlControl, &hViewControl)))
  1523.         return m_fError = TRUE, FALSE; // error - ABORT
  1524.  
  1525.     // create Dialog execution record
  1526.     PMSIHANDLE hRec = MSI::MsiCreateRecord(1);
  1527.     assert(hRec);
  1528.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRec, 1, szDialog)))
  1529.         return m_fError = TRUE, FALSE; // error - ABORT
  1530.  
  1531.     // execute sql query
  1532.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewControl, hRec)))
  1533.         return m_fError = TRUE, FALSE; // error - ABORT
  1534.  
  1535.     // begin fetching rows from Control table
  1536.     PMSIHANDLE hRecControl = 0;
  1537.     while (ERROR_NO_MORE_ITEMS != (iStat = MSI::MsiViewFetch(hViewControl, &hRecControl)))
  1538.     {
  1539.         if (ERROR_SUCCESS != iStat)
  1540.             return m_fError = TRUE, FALSE; // error - ABORT
  1541.  
  1542.  
  1543.         /**********************************************
  1544.          obtain all values for control
  1545.         ***********************************************/
  1546.         // control's type determines how control is output to resource file
  1547.         // 1st call, obtain size needed
  1548.         // 2nd call, get string
  1549.         DWORD dwLen = 0;
  1550.         MSI::MsiRecordGetString(hRecControl, iccType, TEXT(""), &dwLen);
  1551.         TCHAR* szCtrlType = new TCHAR[++dwLen];
  1552.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecControl, iccType, szCtrlType, &dwLen)))
  1553.             return m_fError = TRUE, FALSE; // error - ABORT
  1554.         dwLen = 0;
  1555.         MSI::MsiRecordGetString(hRecControl, iccName, TEXT(""), &dwLen);
  1556.         TCHAR* szCtrlName = new TCHAR[++dwLen];
  1557.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecControl, iccName, szCtrlName, &dwLen)))
  1558.             return m_fError = TRUE, FALSE; // error - ABORT
  1559.         dwLen = 0;
  1560.         MSI::MsiRecordGetString(hRecControl, iccText, TEXT(""), &dwLen);
  1561.         TCHAR* szCtrlText = new TCHAR[++dwLen];
  1562.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecControl, iccText, szCtrlText, &dwLen)))
  1563.             return m_fError = TRUE, FALSE; // error - ABORT
  1564.         dwLen = 0;
  1565.         MSI::MsiRecordGetString(hRecControl, iccProperty, TEXT(""), &dwLen);
  1566.         TCHAR* szCtrlProperty = new TCHAR[++dwLen];
  1567.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecControl, iccProperty, szCtrlProperty, &dwLen)))
  1568.             return m_fError = TRUE, FALSE; // error - ABORT
  1569.  
  1570.         // get x, y, wd, ht, and attrib values
  1571.         int x,y,wd,ht,attrib;
  1572.         x      = MSI::MsiRecordGetInteger(hRecControl, iccX);
  1573.         y      = MSI::MsiRecordGetInteger(hRecControl, iccY);
  1574.         wd     = MSI::MsiRecordGetInteger(hRecControl, iccWd);
  1575.         ht     = MSI::MsiRecordGetInteger(hRecControl, iccHt);
  1576.         attrib = MSI::MsiRecordGetInteger(hRecControl, iccAttrib);
  1577.  
  1578. #ifdef DEBUG
  1579.     _tprintf(TEXT("LOG>>\tWriting control <%s>\n"), szCtrlName);
  1580. #endif
  1581.  
  1582.         if (!WriteControlToRC(szDialog, szCtrlName, szCtrlType, szCtrlText, szCtrlProperty, x, y, wd, ht, attrib))
  1583.             return m_fError = TRUE, FALSE;
  1584.     }
  1585.     
  1586.     // return success
  1587.     return TRUE;
  1588. }
  1589.  
  1590. //////////////////////////////////////////////////////////////////////////////////////
  1591. // NOTES:
  1592. // 1.)Output control to RC file according to control type
  1593. // 2.)For simplicity, controls are classified into two types:
  1594. //                StdWinCtrl and Win32Ctrl.  
  1595. //        StdWinCtrl = PushButtons, RadioButtons, ComboBoxes, and ListBoxes
  1596. //        Win32Ctrl  = ListView, ComboBox, etc.
  1597. //
  1598. // 3.)Some controls used in the installer are simply StdWinCtrls with special
  1599. //      attributes set
  1600. // 
  1601. // 4.)Bitmaps and Icons should be output to prevent improper resizing of dialogs.
  1602. //      If a dialog were shrunk, the display area could be reduced to the point where
  1603. //      the bitmap or icon would not be shown correctly
  1604. ///////////////////////////////////////////////////////////////////////////////////////
  1605.  
  1606. /////////////////////////////////////////////////////////////////////////////
  1607. // CGenerateRC::WriteControlToRC
  1608. BOOL CGenerateRC::WriteControlToRC(TCHAR* szDialog, TCHAR* szCtrlName, TCHAR* szCtrlType, TCHAR* szCtrlText, TCHAR* szCtrlProperty, int x,
  1609.                                    int y, int wd, int ht, int attrib)
  1610. {
  1611.     assert(szCtrlType != NULL);
  1612.  
  1613.     int iResId = 0;
  1614.     // attempt to find resource ID if previously used
  1615.     TCHAR sqlTempBuf[512];
  1616.     wsprintf(sqlTempBuf, sqlFindResId, szDialog, szCtrlName);
  1617.     PMSIHANDLE hViewFindRes = 0;
  1618.     PMSIHANDLE hRecFindRes = 0;
  1619.     UINT iStat;
  1620.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlTempBuf, &hViewFindRes)))
  1621.         return FALSE; // error - ABORT
  1622.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewFindRes, 0)))
  1623.         return FALSE; // error - ABORT
  1624.     if (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewFindRes, &hRecFindRes)))
  1625.     {
  1626.         // grab res id
  1627.         iResId = MSI::MsiRecordGetInteger(hRecFindRes, 1);
  1628.     }
  1629.     else if (ERROR_NO_MORE_ITEMS == iStat)
  1630.     {
  1631.         // use next available resource Id
  1632.         iResId = ++m_iCtrlResID;
  1633.     }
  1634.     else
  1635.         return FALSE; // error - ABORT
  1636.  
  1637.  
  1638.     if (0 == _tcscmp(szCtrlType, szMsiPushbutton))
  1639.     {
  1640.         // StdWin
  1641.         if (!WriteStdWinCtrl(iResId, resPushButton, szCtrlText, x, y, wd, ht, attrib))
  1642.             return FALSE; // error - ABORT
  1643.     }
  1644.     else if (0 == _tcscmp(szCtrlType, szMsiText))
  1645.     {
  1646.         // Win32
  1647.         if (!WriteWin32Ctrl(iResId, resStaticClass, szCtrlText, TEXT("SS_LEFT"), x, y, wd, ht, attrib))
  1648.             return FALSE; // error - ABORT
  1649.     }
  1650.     else if (0 == _tcscmp(szCtrlType, szMsiBillboard))
  1651.     {
  1652.         // StdWin
  1653.         // output GROUPBOX for placeholder for billboard
  1654.         //PUNTED: output Billboard separate using Billboard and BBControl tables
  1655.         //REASON: would have to somehow connect changes in Billboard size back and forth with Dialog displayed on
  1656.         if (!WriteStdWinCtrl(iResId, resGroupBox, szCtrlText, x, y, wd, ht, attrib))
  1657.             return FALSE;
  1658.     }
  1659.     else if (0 == _tcscmp(szCtrlType, szMsiVolumeCostList))
  1660.     {
  1661.         // Win32
  1662.         if (!WriteWin32Ctrl(iResId, resListViewClass, szCtrlText, TEXT("WS_GROUP"), x, y, wd, ht, attrib))
  1663.             return FALSE; // error - ABORT
  1664.     }
  1665.     else if (0 == _tcscmp(szCtrlType, szMsiCheckBox))
  1666.     {
  1667.         // StdWin
  1668.         if (!WriteStdWinCtrl(iResId, resCheckBox, szCtrlText, x, y, wd, ht, attrib))
  1669.             return FALSE; // error - ABORT
  1670.     }
  1671.     else if (0 == _tcscmp(szCtrlType, szMsiGroupBox))
  1672.     {
  1673.         // StdWin
  1674.         if (!WriteStdWinCtrl(iResId, resGroupBox, szCtrlText, x, y, wd, ht, attrib))
  1675.             return FALSE; // error - ABORT
  1676.     }
  1677.     else if (0 == _tcscmp(szCtrlType, szMsiRadioButtonGroup))
  1678.     {
  1679.         // StdWin
  1680.         if (!WriteStdWinCtrl(iResId, resGroupBox, szCtrlText, x, y, wd, ht, attrib))
  1681.             return FALSE; // error - ABORT
  1682.         // write out radiobuttons
  1683.         if (!WriteRadioButtons(szDialog, szCtrlName, szCtrlProperty, x, y, attrib))
  1684.             return FALSE; // error - ABORT
  1685.     }
  1686.     else if (0 == _tcscmp(szCtrlType, szMsiListBox))
  1687.     {
  1688.         // Win32
  1689.         //TODO: handling of extra attributes like LBS_STANDARD or LBS_NOTIFY
  1690.         if (!WriteWin32Ctrl(iResId, resListBoxClass, szCtrlText, TEXT("LBS_STANDARD"), x, y, wd, ht, attrib))
  1691.             return FALSE; // error - ABORT
  1692.     }
  1693.     else if ((0 == _tcscmp(szCtrlType, szMsiEdit))
  1694.     || (0 == _tcscmp(szCtrlType, szMsiPathEdit))
  1695.     || (0 == _tcscmp(szCtrlType, szMsiMaskedEdit)))
  1696.     {
  1697.         // Win32
  1698.         if (!WriteWin32Ctrl(iResId, resEditClass, szCtrlText, TEXT("0x000"), x, y, wd, ht, attrib))
  1699.             return FALSE; // error - ABORT
  1700.     }
  1701.     else if (0 == _tcscmp(szCtrlType, szMsiProgressBar))
  1702.     {
  1703.         // Win32
  1704.         //TODO: handling of extra attributes like BS_OWNERDRAW
  1705.         if (!WriteWin32Ctrl(iResId, resProgBar32Class, szCtrlText, TEXT("0x000"), x, y, wd, ht, attrib))
  1706.             return FALSE; // error - ABORT
  1707.     }
  1708.     else if (0 == _tcscmp(szCtrlType, szMsiDirList))
  1709.     {
  1710.         // Win32
  1711.         if (!WriteWin32Ctrl(iResId, resListViewClass, szCtrlText, TEXT("WS_BORDER"), x, y, wd, ht, attrib))
  1712.             return FALSE; // error - ABORT
  1713.     }
  1714.     else if (0 == _tcscmp(szCtrlType, szMsiList))
  1715.     {
  1716.         // Win32
  1717.         if (!WriteWin32Ctrl(iResId, resListViewClass, szCtrlText, TEXT("WS_BORDER"), x, y, wd, ht, attrib))
  1718.             return FALSE; // error - ABORT
  1719.     }
  1720.     else if (0 == _tcscmp(szCtrlType, szMsiComboBox))
  1721.     {
  1722.         // Win32
  1723.         if (!WriteWin32Ctrl(iResId, resComboBoxClass, szCtrlText, TEXT("CBS_AUTOHSCROLL"), x, y, wd, ht, attrib))
  1724.             return FALSE; // error - ABORT
  1725.     }
  1726.     else if (0 == _tcscmp(szCtrlType, szMsiDirCombo))
  1727.     {
  1728.         // Win32
  1729.         if (!WriteWin32Ctrl(iResId, resComboBoxClass, szCtrlText, TEXT("WS_VSCROLL"), x, y, wd, ht, attrib))
  1730.             return FALSE; // error - ABORT
  1731.     }
  1732.     else if (0 == _tcscmp(szCtrlType, szMsiVolSelCombo))
  1733.     {
  1734.         // Win32
  1735.         if (!WriteWin32Ctrl(iResId, resComboBoxClass, szCtrlText, TEXT("WS_VSCROLL"), x, y, wd, ht, attrib))
  1736.             return FALSE; // error - ABORT
  1737.     }
  1738.     else if (0 == _tcscmp(szCtrlType, szMsiBitmap))
  1739.     {
  1740.         // Win32
  1741.         if (!WriteWin32Ctrl(iResId, resStaticClass, szCtrlText, TEXT("SS_BITMAP | SS_CENTERIMAGE"), x, y, wd, ht, attrib))
  1742.             return FALSE; // error - ABORT
  1743.     }
  1744.     else if (0 == _tcscmp(szCtrlType, szMsiIcon))
  1745.     {
  1746.         // Win32
  1747.         if (!WriteWin32Ctrl(iResId, resStaticClass, szCtrlText, TEXT("SS_ICON | SS_CENTERIMAGE"), x, y, wd, ht, attrib))
  1748.             return FALSE; // error - ABORT
  1749.     }
  1750.     else if (0 == _tcscmp(szCtrlType, szMsiSelTree))
  1751.     {
  1752.         // Win32
  1753.         if (!WriteWin32Ctrl(iResId, resSelTreeClass, szCtrlText, TEXT("WS_BORDER"), x, y, wd, ht, attrib))
  1754.             return FALSE; // error - ABORT
  1755.     }
  1756.     else if (0 == _tcscmp(szCtrlType, szMsiLine))
  1757.     {
  1758.         // Win32
  1759.         if (!WriteWin32Ctrl(iResId, resStaticClass, szCtrlText, TEXT("SS_ETCHEDHORZ | SS_SUNKEN"), x, y, wd, ht, attrib))
  1760.             return FALSE; // error - ABORT
  1761.     }
  1762.     else if (0 == _tcscmp(szCtrlType, szMsiScrollableText))
  1763.     {
  1764.         // Win32
  1765.         if (!WriteWin32Ctrl(iResId, resRichEditClass, szCtrlText, TEXT("WS_GROUP"), x, y, wd, ht, attrib))
  1766.             return FALSE; // error - ABORT
  1767.         return TRUE;
  1768.     }
  1769.     else
  1770.     {
  1771.         // unsupported control type
  1772.         _tprintf(TEXT("!! >> Control Type: '%s' is unsupported\n"), szCtrlType);
  1773.         return FALSE;
  1774.     }
  1775.  
  1776.     // update _RESControls table with new info
  1777.     PMSIHANDLE hViewRes = 0;
  1778.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlInsertCtrl, &hViewRes)))
  1779.     {
  1780.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1781.         return FALSE;
  1782.     }
  1783.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewRes, 0)))
  1784.     {
  1785.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1786.         return FALSE;
  1787.     }
  1788.     PMSIHANDLE hRecInsertCtrl = MSI::MsiCreateRecord(3);
  1789.     assert(hRecInsertCtrl);
  1790.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertCtrl, 1, szDialog)))
  1791.     {
  1792.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1793.         return FALSE;
  1794.     }
  1795.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertCtrl, 2, szCtrlName)))
  1796.     {
  1797.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1798.         return FALSE;
  1799.     }
  1800.     if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetInteger(hRecInsertCtrl, 3, iResId)))
  1801.     {
  1802.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1803.         return FALSE;
  1804.     }
  1805.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewRes, MSIMODIFY_ASSIGN, hRecInsertCtrl)))
  1806.     {
  1807.         _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1808.         return FALSE;
  1809.     }
  1810.  
  1811.     // return success
  1812.     return TRUE;
  1813. }
  1814.  
  1815. /////////////////////////////////////////////////////////////////////////////
  1816. // CGenerateRC::WriteRadioButtons
  1817. BOOL CGenerateRC::WriteRadioButtons(TCHAR* szDialog, TCHAR* szRBGroup, TCHAR* szProperty, int x, int y, int attrib)
  1818. {
  1819.     // Format for RadioButton is
  1820.     //  RADIOBUTTON<tab>"str",<tab>RESID,<tab>x,<tab>y,<tab>wd,<tab>ht[[,<tab> STYLE ]]
  1821.     // radiobuttons come from the RadioButton table based on the Property of the RBGroup in the Control table
  1822.     // radiobutton dimensions (X and Y) are local to the radiobutton group
  1823.     // because you can have multiple radiobutton groups with the same property in a dialog, we have to use the following
  1824.     // scheme when updating the _RESControls table:
  1825.     // Dialog<tab>RadioButtonGroup:Property:Order<tab>RESID
  1826.  
  1827.     // make sure radiobutton table exists
  1828.     MSICONDITION eCond = MSI::MsiDatabaseIsTablePersistent(m_hDatabase, TEXT("RadioButton"));
  1829.     switch (eCond)
  1830.     {
  1831.     case MSICONDITION_ERROR:
  1832.     case MSICONDITION_NONE:
  1833.     case MSICONDITION_FALSE:
  1834.         _tprintf(TEXT("LOG_ERROR>> RadioButton table does not exist, is not persistent, or an error occured.\n"));
  1835.         return m_fError = TRUE, FALSE; // error - ABORT
  1836.     case MSICONDITION_TRUE:
  1837.         break;
  1838.     default:
  1839.         assert(0);
  1840.     }
  1841.     int iResId = 0;
  1842.  
  1843.     // open view on RadioButton table
  1844.     PMSIHANDLE hViewRB = 0;
  1845.     if (ERROR_SUCCESS != MSI::MsiDatabaseOpenView(m_hDatabase, sqlRadioButton, &hViewRB))
  1846.     {
  1847.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseOpenView(RadioButton)\n"));
  1848.         return m_fError = TRUE, FALSE; // error - ABORT
  1849.     }
  1850.     PMSIHANDLE hRecExec = MSI::MsiCreateRecord(1);
  1851.     assert(hRecExec);
  1852.     if (ERROR_SUCCESS != MSI::MsiRecordSetString(hRecExec, 1, szProperty))
  1853.     {
  1854.         _tprintf(TEXT("LOG_ERROR>> MsiRecordSetString(property)\n"));
  1855.         return m_fError = TRUE, FALSE; // error - ABORT
  1856.     }
  1857.     // execute view
  1858.     if (ERROR_SUCCESS != MSI::MsiViewExecute(hViewRB, hRecExec))
  1859.     {
  1860.         _tprintf(TEXT("LOG_ERROR>> MsiViewExecute\n"));
  1861.         return m_fError = TRUE, FALSE; // error - ABORT
  1862.     }
  1863.     // fetch record
  1864.     PMSIHANDLE hRecRB = 0;
  1865.     UINT iStat;
  1866.     while (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewRB, &hRecRB)))
  1867.     {
  1868.         DWORD dwLen = 0;
  1869.         MSI::MsiRecordGetString(hRecRB, irbcText, TEXT(""), &dwLen);
  1870.         TCHAR* szRBText = new TCHAR[++dwLen];
  1871.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecRB, irbcText, szRBText, &dwLen)))
  1872.         {
  1873.             _tprintf(TEXT("LOG_ERROR>> MsiRecordGetString(radio button text).  %d\n"), iStat);
  1874.             return m_fError = TRUE, FALSE; // error - ABORT
  1875.         }
  1876.         int iRBX = MSI::MsiRecordGetInteger(hRecRB, irbcX);
  1877.         int iRBY = MSI::MsiRecordGetInteger(hRecRB, irbcY);
  1878.         int iRBWd = MSI::MsiRecordGetInteger(hRecRB, irbcWd);
  1879.         int iRBHt = MSI::MsiRecordGetInteger(hRecRB, irbcHt);
  1880.  
  1881.         // grab order value
  1882.         int iOrder = MSI::MsiRecordGetInteger(hRecRB, irbcOrder);
  1883.         // attempt to find resource ID if previously used
  1884.         TCHAR szGeneratedName[255];
  1885.         wsprintf(szGeneratedName, TEXT("%s:%s:%d"), szRBGroup, szProperty, iOrder); 
  1886.         TCHAR sqlTempBuf[512];
  1887.         wsprintf(sqlTempBuf, sqlFindResId, szDialog, szGeneratedName);
  1888.         PMSIHANDLE hViewFindRes = 0;
  1889.         PMSIHANDLE hRecFindRes = 0;
  1890.         UINT iStat;
  1891.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlTempBuf, &hViewFindRes)))
  1892.             return FALSE; // error - ABORT
  1893.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewFindRes, 0)))
  1894.             return FALSE; // error - ABORT
  1895.         if (ERROR_SUCCESS == (iStat = MSI::MsiViewFetch(hViewFindRes, &hRecFindRes)))
  1896.         {
  1897.             // grab res id
  1898.             iResId = MSI::MsiRecordGetInteger(hRecFindRes, 1);
  1899.         }
  1900.         else if (ERROR_NO_MORE_ITEMS == iStat)
  1901.         {
  1902.             // use next available resource Id
  1903.             iResId = ++m_iCtrlResID;
  1904.         }
  1905.         else
  1906.             return FALSE; // error - ABORT
  1907.  
  1908.         TCHAR szTempBuf[64];
  1909.         DWORD dwWritten;
  1910.         /*KEYWORD*/
  1911.         if (!W32::WriteFile(m_hFile, resRadioButton, _tcsclen(resRadioButton)*sizeof(TCHAR), &dwWritten, 0))
  1912.             m_cWriteFileErr++;
  1913.         /*<tab>"str",*/
  1914.         if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1915.             m_cWriteFileErr++;
  1916.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  1917.             m_cWriteFileErr++;
  1918.         // escape chars in str
  1919.         TCHAR* szEscText = EscapeSlashAndQuoteForRC(szRBText);
  1920.         if (_tcsclen(szEscText) > iMaxResStrLen)
  1921.         {
  1922.             _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> CONTROL ID: %d\n"), iResId);
  1923.             _tcscpy(szEscText, strOverLimit);
  1924.         }
  1925.         if (!W32::WriteFile(m_hFile, szEscText, _tcslen(szEscText)*sizeof(TCHAR), &dwWritten, 0))
  1926.             m_cWriteFileErr++;
  1927.         if (szEscText)
  1928.             delete szEscText;
  1929.         if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  1930.             m_cWriteFileErr++;
  1931.         if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1932.             m_cWriteFileErr++;
  1933.         /*<tab>RESId,*/
  1934.         if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  1935.             m_cWriteFileErr++;
  1936.         wsprintf(szTempBuf, TEXT("%d"), iResId);
  1937.         if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  1938.             m_cWriteFileErr++;
  1939.         if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1940.             m_cWriteFileErr++;
  1941.         /*x, y, wd, ht DIMENSIONS*/
  1942.         // for RB, X & Y dimensions are local to group, so must add in group's x and y
  1943.         if (!PrintDimensions(x+iRBX, y+iRBY, iRBWd, iRBHt))
  1944.             return FALSE; // error - ABORT
  1945.         //attributes are ignored at this time (would require conversion from Installer to Windows and masking of Installer specific)
  1946.         if (attrib & msidbControlAttributesBitmap)
  1947.         {
  1948.             // control with bitmap picture -- want to prevent localization of picture property names
  1949.             /*,<tab>BS_BITMAP*/
  1950.             if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1951.                 m_cWriteFileErr++;
  1952.             wsprintf(szTempBuf, TEXT("BS_BITMAP"));
  1953.             if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten,0))
  1954.                 m_cWriteFileErr++;
  1955.         }
  1956.         else if (attrib & msidbControlAttributesIcon)
  1957.         {
  1958.             // control with icon picture
  1959.             /*<tab>BS_ICON*/
  1960.             if(!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  1961.                 m_cWriteFileErr++;
  1962.             wsprintf(szTempBuf, TEXT("BS_BITMAP"));
  1963.             if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten,0))
  1964.                 m_cWriteFileErr++;
  1965.         }
  1966.         if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  1967.             m_cWriteFileErr++;
  1968.  
  1969.         // update _RESControls table with new info
  1970.         PMSIHANDLE hViewRes = 0;
  1971.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlInsertCtrl, &hViewRes)))
  1972.         {
  1973.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1974.             return FALSE;
  1975.         }
  1976.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewRes, 0)))
  1977.         {
  1978.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1979.             return FALSE;
  1980.         }
  1981.         PMSIHANDLE hRecInsertCtrl = MSI::MsiCreateRecord(3);
  1982.         assert(hRecInsertCtrl);
  1983.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertCtrl, 1, szDialog)))
  1984.         {
  1985.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1986.             return FALSE;
  1987.         }
  1988.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecInsertCtrl, 2, szGeneratedName)))
  1989.         {
  1990.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1991.             return FALSE;
  1992.         }
  1993.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetInteger(hRecInsertCtrl, 3, iResId)))
  1994.         {
  1995.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  1996.             return FALSE;
  1997.         }
  1998.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewRes, MSIMODIFY_ASSIGN, hRecInsertCtrl)))
  1999.         {
  2000.             _tprintf(TEXT("!! >> Unable to update _RESControls table\n"));
  2001.             return FALSE;
  2002.         }
  2003.     }
  2004.     if (ERROR_NO_MORE_ITEMS != iStat)// doesn't catch where property never in RadioButton table
  2005.     {
  2006.         _tprintf(TEXT("LOG_ERROR>> MsiViewFetch(RadioButton table)\n"));
  2007.         return m_fError = TRUE, FALSE; // error - ABORT
  2008.     }
  2009.  
  2010.     // return success
  2011.     return TRUE;
  2012. }
  2013.  
  2014. /////////////////////////////////////////////////////////////////////////////
  2015. // CGenerateRC::WriteStdWinCtrl
  2016. BOOL CGenerateRC::WriteStdWinCtrl(int iResId, const TCHAR* resType, TCHAR* szCtrlText, int x, int y, int wd, int ht, int attrib)
  2017. {
  2018.     // Format for StdWinCtrl is
  2019.     //   KEYWORD<tab>"str",<tab>RESID,<tab>x,<tab>y,<tab>wd,<tab>ht[[,<tab> STYLE ]]
  2020.     // Keyword can be one of PUSHBUTTON, CHECKBOX, GROUPBOX
  2021.  
  2022.     TCHAR szTempBuf[64];
  2023.     DWORD dwWritten;
  2024.     /*KEYWORD*/
  2025.     if (!W32::WriteFile(m_hFile, resType, _tcsclen(resType)*sizeof(TCHAR), &dwWritten, 0))
  2026.         m_cWriteFileErr++;
  2027.     /*<tab>"str",*/
  2028.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  2029.         m_cWriteFileErr++;
  2030.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  2031.         m_cWriteFileErr++;
  2032.     // escape chars in str
  2033.     TCHAR* szEscText = EscapeSlashAndQuoteForRC(szCtrlText);
  2034.     if (_tcsclen(szEscText) > iMaxResStrLen)
  2035.     {
  2036.         _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> CONTROL ID: %d\n"), iResId);
  2037.         _tcscpy(szEscText, strOverLimit);
  2038.     }
  2039.     if (!W32::WriteFile(m_hFile, szEscText, _tcslen(szEscText)*sizeof(TCHAR), &dwWritten, 0))
  2040.         m_cWriteFileErr++;
  2041.     if (szEscText)
  2042.         delete szEscText;
  2043.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  2044.         m_cWriteFileErr++;
  2045.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2046.         m_cWriteFileErr++;
  2047.     /*<tab>RESId,*/
  2048.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  2049.         m_cWriteFileErr++;
  2050.     wsprintf(szTempBuf, TEXT("%d"), iResId);
  2051.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  2052.         m_cWriteFileErr++;
  2053.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2054.         m_cWriteFileErr++;
  2055.     /*x, y, wd, ht DIMENSIONS*/
  2056.     if (!PrintDimensions(x, y, wd, ht))
  2057.         return FALSE; // error - ABORT
  2058.     //attributes are ignored at this time (would require conversion from Installer to Windows and masking of Installer specific)
  2059.     // valid picture controls are CheckBox, PushButton, and RadioButtons
  2060.     if (0 != _tcscmp(resType, szMsiGroupBox))
  2061.     {
  2062.         if (attrib & msidbControlAttributesBitmap)
  2063.         {
  2064.             // control with bitmap picture -- want to prevent localization of picture property names
  2065.             /*,<tab>BS_BITMAP*/
  2066.             if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2067.                 m_cWriteFileErr++;
  2068.             wsprintf(szTempBuf, TEXT("BS_BITMAP"));
  2069.             if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten,0))
  2070.                 m_cWriteFileErr++;
  2071.         }
  2072.         else if (attrib & msidbControlAttributesIcon)
  2073.         {
  2074.             // control with icon picture
  2075.             /*<tab>BS_ICON*/
  2076.             if(!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2077.                 m_cWriteFileErr++;
  2078.             wsprintf(szTempBuf, TEXT("BS_BITMAP"));
  2079.             if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten,0))
  2080.                 m_cWriteFileErr++;
  2081.         }
  2082.     }
  2083.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  2084.         m_cWriteFileErr++;
  2085.  
  2086.     // return success
  2087.     return TRUE;
  2088. }
  2089.  
  2090. /////////////////////////////////////////////////////////////////////////////
  2091. // CGenerateRC::WriteWin32Ctrl
  2092. BOOL CGenerateRC::WriteWin32Ctrl(int iResId, const TCHAR* szClass, TCHAR* szCtrlText, TCHAR* szAttrib, int x, int y, int wd, int ht, int attrib)
  2093. {
  2094.     // Format for StdWinCtrl is
  2095.     //   CONTROL<tab>"str",<tab>RESID,<tab>class,<tab>attrib,<tab>x,<tab>y,<tab>wd,<tab>ht[[,<tab> STYLE ]]
  2096.  
  2097.     TCHAR szTempBuf[64];
  2098.     DWORD dwWritten;
  2099.     /*CONTROL*/
  2100.     if (!W32::WriteFile(m_hFile, resControl, _tcsclen(resControl)*sizeof(TCHAR), &dwWritten, 0))
  2101.         m_cWriteFileErr++;
  2102.     /*<tab>"str",*/
  2103.     if (!W32::WriteFile(m_hFile, szTab, sizeof(szTab)-sizeof(TCHAR), &dwWritten, 0))
  2104.         m_cWriteFileErr++;
  2105.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  2106.         m_cWriteFileErr++;
  2107.     // escape chars in str
  2108.     TCHAR* szEscText = EscapeSlashAndQuoteForRC(szCtrlText);
  2109.     if (_tcsclen(szEscText) > iMaxResStrLen)
  2110.     {
  2111.         _tprintf(TEXT("!! >> STR TOO LONG FOR RC FILE >> CONTROL ID:%d\n"), iResId);
  2112.         _tcscpy(szEscText, strOverLimit);
  2113.     }
  2114.     if (!W32::WriteFile(m_hFile, szEscText, _tcslen(szEscText)*sizeof(TCHAR), &dwWritten, 0))
  2115.         m_cWriteFileErr++;
  2116.     if (szEscText)
  2117.         delete szEscText;
  2118.     if (!W32::WriteFile(m_hFile, szQuotes, sizeof(szQuotes)-sizeof(TCHAR), &dwWritten, 0))
  2119.         m_cWriteFileErr++;
  2120.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2121.         m_cWriteFileErr++;
  2122.     /*<tab>RESId,*/
  2123.     wsprintf(szTempBuf, TEXT("%d"), iResId);
  2124.     if (!W32::WriteFile(m_hFile, szTempBuf, _tcslen(szTempBuf)*sizeof(TCHAR), &dwWritten, 0))
  2125.         m_cWriteFileErr++;
  2126.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2127.         m_cWriteFileErr++;
  2128.     /*<tab>class,*/
  2129.     if (!W32::WriteFile(m_hFile, szClass, _tcslen(szClass)*sizeof(TCHAR), &dwWritten, 0))
  2130.         m_cWriteFileErr++;
  2131.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2132.         m_cWriteFileErr++;
  2133.     /*<tab>attrib,*/
  2134.     if (!W32::WriteFile(m_hFile, szAttrib, _tcslen(szAttrib)*sizeof(TCHAR), &dwWritten, 0))
  2135.         m_cWriteFileErr++;
  2136.     if (!W32::WriteFile(m_hFile, szCommaTab, sizeof(szCommaTab)-sizeof(TCHAR), &dwWritten, 0))
  2137.         m_cWriteFileErr++;
  2138.     /*x, y, wd, ht DIMENSIONS*/
  2139.     if (!PrintDimensions(x, y, wd, ht))
  2140.         return FALSE; // error - ABORT
  2141.     //attributes are ignored at this time (would require conversion from Installer to Windows and masking of Installer specific)
  2142.     if (!W32::WriteFile(m_hFile, szCRLF, sizeof(szCRLF)-sizeof(TCHAR), &dwWritten, 0))
  2143.         m_cWriteFileErr++;
  2144.  
  2145.     // return success
  2146.     return TRUE;
  2147. }
  2148.  
  2149.  
  2150. //_______________________________________________________________________________________
  2151. //
  2152. // CIMPORTRES CLASS IMPLEMENTATION
  2153. //_______________________________________________________________________________________
  2154.  
  2155.  
  2156.  
  2157. /////////////////////////////////////////////////////////////////////////////
  2158. // CImportRes::~CImportRes
  2159. // --  Handles destruction of necessary objects.
  2160. // --  Commits database if no errors
  2161. CImportRes::~CImportRes()
  2162. {
  2163.     UINT iStat;
  2164.     if (m_hDatabase)
  2165.     {
  2166.         // only commit database if no errors
  2167.         if (!m_fError)
  2168.         {
  2169.             if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseCommit(m_hDatabase)))
  2170.                 _tprintf(TEXT("!! DATABASE COMMIT FAILED. Error = %d\n"), iStat);
  2171.         }
  2172.         else
  2173.             _tprintf(TEXT("NO CHANGES SAVED TO DATABASE DUE TO ERROR\n"));
  2174.         MSI::MsiCloseHandle(m_hDatabase);
  2175.     }
  2176.     if (m_hControl)
  2177.         MSI::MsiCloseHandle(m_hControl);
  2178.     if (m_hDialog)
  2179.         MSI::MsiCloseHandle(m_hDialog);
  2180.     if (m_hRadioButton)
  2181.         MSI::MsiCloseHandle(m_hRadioButton);
  2182.     if (m_hInst)
  2183.         W32::FreeLibrary(m_hInst);
  2184.  
  2185. }
  2186.  
  2187. /////////////////////////////////////////////////////////////////////////////
  2188. // Notes:
  2189. // 1.)We will only update vcenter, hcenter, width, height and title
  2190. //         of Dialog.
  2191. //
  2192. // 2.)We will only update x, y, width, height, and text of Control.
  2193. // 3.)We will only update width, height, and text of RadioButton.
  2194. //       RadioButtons can be used multiply in different dialogs and multiple
  2195. //       RadioButtonGroups using same properties can be wired on the same dialog
  2196. //       Plus, RadioButtons are local to the GroupBox that contains them therefore
  2197. //       it would require maintaining state data of the GroupBox's X and Y dimensions
  2198.  
  2199. //////////////////////////////////////////////////////////////////////////////
  2200. // VerifyDatabaseCodepage
  2201. UINT CImportRes::VerifyDatabaseCodepage()
  2202. {
  2203.     UINT iStat;
  2204.     // only output from language neutral database
  2205.     TCHAR szTempPath[MAX_PATH];
  2206.     W32::GetTempPath(MAX_PATH, szTempPath);
  2207.     // export _ForceCodepage table so can verify codepage
  2208.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseExport(m_hDatabase, TEXT("_ForceCodepage"), szTempPath, szCodepageExport)))
  2209.     {
  2210.         _tprintf(TEXT("LOG_ERROR>> MsiDatabaseExport(_ForceCodepage)\n"));
  2211.         return m_fError = TRUE, iStat; // error - ABORT
  2212.     }
  2213.     // open _ForceCodepage.idt to read it
  2214.     TCHAR szFullPath[MAX_PATH + 32];
  2215.     wsprintf(szFullPath, TEXT("%s%s"), szTempPath, szCodepageExport);
  2216.     HANDLE hFile = W32::CreateFile(szFullPath, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)0, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
  2217.     if (hFile == NULL)
  2218.     {
  2219.         _tprintf(TEXT("LOG_ERROR>> W32::OpenFile(_ForceCodepage.idt)\n"));
  2220.         return m_fError = TRUE, iStat; // error - ABORT
  2221.     }
  2222.     // read file for information
  2223.     DWORD dwSize = W32::GetFileSize(hFile, NULL);
  2224.     if (0xFFFFFFFF == dwSize)
  2225.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2226.     char* szBuf = new char[dwSize+1];
  2227.     DWORD dwRead;
  2228.     if (!W32::ReadFile(hFile, (LPVOID)szBuf, dwSize, &dwRead, NULL))
  2229.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2230.     // parse buffer
  2231.     // format should be : blank line, blank line, "codepage<tab>_ForceCodepage"
  2232.     assert(dwRead != 0);
  2233.     char* pch = szBuf;
  2234.     int cBlankLines = 0;
  2235.     while (dwRead && cBlankLines != 2)
  2236.     {
  2237.         if (*pch == '\n')
  2238.             cBlankLines++;
  2239.         pch++;
  2240.         dwRead--;
  2241.     }
  2242.     if (!dwRead || cBlankLines != 2)
  2243.     {
  2244.         _tprintf(TEXT("LOG_ERROR>> Invalid ForceCodepage idt format\n"));
  2245.         return m_fError = TRUE, ERROR_FUNCTION_FAILED;
  2246.     }
  2247.     // codepage is next
  2248.     char* pchCodepage = pch;
  2249.     while (dwRead && *pch != ' ' && *pch != '\t')
  2250.     {
  2251.         pch++;
  2252.         dwRead--;
  2253.     }
  2254.     assert(dwRead);
  2255.     *pch = '\0';
  2256.     // convert codepage to int
  2257.     UINT uiCodepage = strtoul(pchCodepage, NULL, 10);
  2258.     if (uiCodepage != 0 && uiCodepage != g_uiCodePage) // 0 is language neutral
  2259.     {
  2260.         _tprintf(TEXT("LOG_ERROR>> DATABASE IS NOT LANGUAGE NEUTRAL OR OF SAME CODEPAGE AS RESOURCE STRINGS. CANNOT IMPORT\n"));
  2261.         _tprintf(TEXT("LOG_ERROR>> DATABASE CODEPAGE= %d\n"), uiCodepage);
  2262.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2263.     }
  2264.     if (!W32::CloseHandle(hFile))
  2265.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2266.  
  2267.     return ERROR_SUCCESS;
  2268. }
  2269.  
  2270. /////////////////////////////////////////////////////////////////////////////
  2271. // CImportRes::ImportDialog
  2272. UINT CImportRes::ImportDialog(TCHAR* szDialog)
  2273. {
  2274.     UINT iStat = Initialize();
  2275.     if (ERROR_SUCCESS != iStat)
  2276.         return m_fError = TRUE, iStat; // error - ABORT
  2277.  
  2278.     if (ERROR_SUCCESS != (iStat = ImportDlgInit()))
  2279.         return m_fError = TRUE, iStat; // error - ABORT
  2280.  
  2281.     // convert dialog name to resource identifier (all upper case)
  2282.     _tcsupr(szDialog);
  2283.  
  2284. #ifdef DEBUG
  2285.     _tprintf(TEXT("LOG>>...BEGIN SEARCH FOR DIALOG <%s>...\n"), szDialog);
  2286. #endif
  2287.  
  2288.     // load dialog
  2289.     if (!LoadDialog(m_hInst, RT_DIALOG, szDialog))
  2290.     {
  2291.         _tprintf(TEXT("LOG_ERROR>> UNABLE LOAD DIALOG: %s\n"), szDialog);
  2292.         return ERROR_FUNCTION_FAILED; // fatal error state set in LoadDialog
  2293.     }
  2294.  
  2295.     return ERROR_SUCCESS;
  2296. }
  2297.  
  2298. /////////////////////////////////////////////////////////////////////////////
  2299. // CImportRes::ImportDlgInit
  2300. UINT CImportRes::ImportDlgInit()
  2301. {
  2302.     // open up view on Dialog table
  2303.     UINT iStat;
  2304.     if (!m_hDialog)
  2305.     {
  2306.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlDialogImport, &m_hDialog)))
  2307.             return m_fError = TRUE, iStat; // error - ABORT
  2308.     }
  2309.     // open up view on Control table
  2310.     if (!m_hControl)
  2311.     {
  2312.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlControlImport, &m_hControl)))
  2313.         {
  2314.             if (ERROR_BAD_QUERY_SYNTAX != iStat)
  2315.                 return m_fError = TRUE, iStat; // error - ABORT
  2316.         }
  2317.     }
  2318.     // open up view on RadioButton table
  2319.     if (!m_hRadioButton)
  2320.     {
  2321.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlRadioButtonImport, &m_hRadioButton)))
  2322.         {
  2323.             if (ERROR_BAD_QUERY_SYNTAX != iStat)
  2324.                 return ERROR_SUCCESS; // they just don't have a radiobutton table
  2325.             else
  2326.                 return m_fError = TRUE, iStat; // error - ABORT
  2327.         }
  2328.     }
  2329.  
  2330.     return ERROR_SUCCESS;
  2331. }
  2332.  
  2333. ///////////////////////////////////////////////////////////////////////////////
  2334. // CImportRes::Initialize
  2335. UINT CImportRes::Initialize()
  2336. {
  2337.     // attempt to load the DLL in memory
  2338.     if (!m_hInst)
  2339.     {
  2340.         m_hInst = W32::LoadLibrary(m_szDLLFile);
  2341.         if (NULL == m_hInst)
  2342.         {
  2343.             _tprintf(TEXT("LOG_ERROR>> Unable to load DLL '%s'\n"), m_szDLLFile);
  2344.             return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2345.         }
  2346.     }
  2347.  
  2348.     // open up database in TRANSACT mode so we can update
  2349.     if (!m_hDatabase)
  2350.     {
  2351.         assert(m_szOrigDb);
  2352.         // open up existing database in transacted mode, or specify a new database for creation
  2353.         UINT iStat;
  2354.         if (ERROR_SUCCESS != (iStat = MSI::MsiOpenDatabase(m_szOrigDb, m_szDatabase ? m_szDatabase : MSIDBOPEN_TRANSACT, &m_hDatabase)))
  2355.         {
  2356.             _tprintf(TEXT("LOG_ERROR>> Unable to open database '%s'\n"), m_szOrigDb);
  2357.             return m_fError = TRUE, iStat; // error - ABORT
  2358.         }
  2359.         _tprintf(TEXT("LOG>> Database opened from-->%s, Database saving to-->%s\n"),m_szOrigDb, m_szDatabase ? m_szDatabase : m_szOrigDb);
  2360.     }
  2361.     return ERROR_SUCCESS;
  2362. }
  2363.  
  2364. /////////////////////////////////////////////////////////////////////////////
  2365. // CImportRes::ImportStrings
  2366. UINT CImportRes::ImportStrings()
  2367. {
  2368.     UINT iStat = Initialize();
  2369.     if (ERROR_SUCCESS != iStat)
  2370.         return m_fError = TRUE, iStat; // error - ABORT
  2371.  
  2372. #ifdef DEBUG
  2373.     _tprintf(TEXT("LOG>>...BEGIN STRING RESOURCE ENUMERATION...\n"));
  2374. #endif
  2375.  
  2376.     // enumerate through string resources
  2377.     BOOL fOK = W32::EnumResourceNames(m_hInst, RT_STRING, EnumStringCallback, (long)this);
  2378.     if (!fOK)
  2379.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2380.  
  2381. #ifdef DEBUG
  2382.     _tprintf(TEXT("LOG>>...END STRING RESOURCE ENUMERATION...\n"));
  2383. #endif
  2384.  
  2385.     return ERROR_SUCCESS;
  2386. }
  2387.  
  2388. /////////////////////////////////////////////////////////////////////////////
  2389. // CImportRes::ImportDialogs
  2390. UINT CImportRes::ImportDialogs()
  2391. {
  2392.     UINT iStat = Initialize();
  2393.     if (ERROR_SUCCESS != iStat)
  2394.         return m_fError = TRUE, iStat; // error - ABORT
  2395.     if (ERROR_SUCCESS != (iStat = ImportDlgInit()))
  2396.         return m_fError = TRUE, iStat; // error - ABORT
  2397.  
  2398. #ifdef DEBUG
  2399.     _tprintf(TEXT("LOG>>...BEGIN DIALOG RESOURCE ENUMERATION...\n"));
  2400. #endif
  2401.  
  2402.     // enumerate through dialog resources
  2403.     BOOL fOK = W32::EnumResourceNames(m_hInst, RT_DIALOG, EnumDialogCallback, (long)this);
  2404.     if (!fOK)
  2405.         return m_fError = TRUE, ERROR_FUNCTION_FAILED; // error - ABORT
  2406.  
  2407. #ifdef DEBUG
  2408.     _tprintf(TEXT("LOG>>...END DIALOG RESOURCE ENUMERATION...\n"));
  2409. #endif
  2410.  
  2411.     return ERROR_SUCCESS;
  2412. }
  2413.  
  2414. /////////////////////////////////////////////////////////////////////////////
  2415. // CImportRes::LoadString
  2416. BOOL CImportRes::LoadString(HINSTANCE hModule, const TCHAR* szType, TCHAR* szStringName)
  2417. {
  2418.     PMSIHANDLE hViewStrId = 0;
  2419.     PMSIHANDLE hRecStrId = 0;
  2420.     UINT iStat;
  2421.     if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlStringInstallerName, &hViewStrId)))
  2422.     {
  2423.         _tprintf(TEXT("LOG_ERROR>> _RESStrings table is missing from database.\n"));
  2424.         return m_fError = TRUE, FALSE;
  2425.     }
  2426.  
  2427.     TCHAR sql[3000];
  2428.     DWORD dwSqlSize = sizeof(sql)/sizeof(TCHAR);
  2429.     int iFirst = (int(szStringName) - 1) * 16;
  2430.     TCHAR rgchBuffer[512];
  2431.     DWORD cchBuffer = sizeof(rgchBuffer)/sizeof(TCHAR);
  2432.     for (int i = 0; i < 16; i++) 
  2433.     {
  2434.         int cchWritten = W32::LoadString(hModule, iFirst + i, rgchBuffer, cchBuffer);
  2435.         if (cchWritten == 0)
  2436.             continue; // null string
  2437.  
  2438.         if (_tcscmp(rgchBuffer, strOverLimit) == 0)
  2439.             continue; // string greater than limit, leave alone
  2440.  
  2441.         // use id of string to find table, column, and row it belongs to
  2442.         PMSIHANDLE hRecExec = MSI::MsiCreateRecord(1);
  2443.         MSI::MsiRecordSetInteger(hRecExec, 1, iFirst + i);
  2444.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewStrId, hRecExec)))
  2445.             return m_fError = TRUE, FALSE;
  2446.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewStrId, &hRecStrId)))
  2447.             return m_fError = TRUE, FALSE;
  2448.  
  2449.  
  2450.         // we now have the table and column and row
  2451.         // want to SELECT `column` from `table` WHERE row matches
  2452.         DWORD dwLen = 0;
  2453.         MSI::MsiRecordGetString(hRecStrId, 1, TEXT(""), &dwLen);
  2454.         TCHAR* szTable = new TCHAR[++dwLen];
  2455.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecStrId, 1, szTable, &dwLen)))
  2456.             return m_fError = TRUE, FALSE;
  2457.         dwLen = 0;
  2458.         MSI::MsiRecordGetString(hRecStrId, 2, TEXT(""), &dwLen);
  2459.         TCHAR* szColumn = new TCHAR[++dwLen];
  2460.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecStrId, 2, szColumn, &dwLen)))
  2461.             return m_fError = TRUE, FALSE;
  2462.         dwLen = 0;
  2463.         MSI::MsiRecordGetString(hRecStrId, 3, TEXT(""), &dwLen);
  2464.         TCHAR* szKey = new TCHAR[++dwLen];
  2465.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecStrId, 3, szKey, &dwLen)))
  2466.             return m_fError = TRUE, FALSE;
  2467.  
  2468.         assert(szTable && szColumn && szKey);
  2469.         _stprintf(sql, sqlStringImport, szColumn, szTable);
  2470.  
  2471.         // need to determine how big to make WHERE clause, i.e. # of primary keys
  2472.         PMSIHANDLE hRecPrimaryKeys = 0;
  2473.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseGetPrimaryKeys(m_hDatabase, szTable, &hRecPrimaryKeys)))
  2474.             return m_fError = TRUE, FALSE;
  2475.         int cKeys = MSI::MsiRecordGetFieldCount(hRecPrimaryKeys);
  2476.  
  2477.         // determine number of keys in "szKey" by counting # of ':'
  2478.         TCHAR* pch = szKey;
  2479.         int cKeyFromTable = 1;
  2480.         while (pch != 0 && *pch != '\0')
  2481.         {
  2482.             if (*pch++ == ':')
  2483.                 cKeyFromTable++;
  2484.         }
  2485.  
  2486.         assert(cKeyFromTable == cKeys);
  2487.  
  2488.         // need to get column types
  2489.         TCHAR sqlTemp[255] = {0};
  2490.         _stprintf(sqlTemp, sqlStrTemp, szTable);
  2491.         PMSIHANDLE hViewTemp = 0;
  2492.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sqlTemp, &hViewTemp)))
  2493.             return m_fError = TRUE, FALSE;
  2494.         PMSIHANDLE hRecColInfo = 0;
  2495.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewGetColumnInfo(hViewTemp, MSICOLINFO_TYPES, &hRecColInfo)))
  2496.             return m_fError = TRUE, FALSE;
  2497.  
  2498.         // for each primary key, add to WHERE clause
  2499.         TCHAR szColType[10];
  2500.         DWORD cchColType = sizeof(szColType)/sizeof(TCHAR);
  2501.         for (int iKey = 1; iKey <= cKeys; iKey++)
  2502.         {
  2503.             DWORD dwLenKey = 0;
  2504.             MSI::MsiRecordGetString(hRecPrimaryKeys, iKey, TEXT(""), &dwLenKey);
  2505.             TCHAR* szPrimaryKeyCol = new TCHAR[++dwLenKey];
  2506.             if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecPrimaryKeys, iKey, szPrimaryKeyCol, &dwLenKey)))
  2507.                 return m_fError = TRUE, FALSE;
  2508.  
  2509.             if (ERROR_SUCCESS != (iStat = MSI::MsiRecordGetString(hRecColInfo, iKey, szColType, &cchColType)))
  2510.                 return m_fError = TRUE, FALSE;
  2511.             assert(szColType && szPrimaryKeyCol);
  2512.  
  2513.             cchColType = sizeof(szColType)/sizeof(TCHAR); // reset
  2514.  
  2515.             TCHAR* szKeyValue;
  2516.             if (iKey == 1)
  2517.                 szKeyValue = _tcstok(szKey, szTokenSeps);
  2518.             else
  2519.                 szKeyValue = _tcstok(NULL, szTokenSeps);
  2520.             assert(szKeyValue);
  2521.             // make sure buffer can hold what we are adding
  2522.             // we add <space>AND<space>Col=Val
  2523.             int iAdd = _tcsclen(TEXT(" AND ")) + _tcsclen(sql) + _tcsclen(TEXT("='")) +_tcsclen(TEXT("'")) + _tcsclen(szKeyValue);
  2524.             assert(dwSqlSize > iAdd);
  2525.             if (iKey != 1)
  2526.                 lstrcat(sql, TEXT(" AND "));
  2527.             lstrcat(sql, szPrimaryKeyCol);
  2528.             if ((*szColType | 0x20) == 'i') // integer
  2529.             {
  2530.                 lstrcat(sql, TEXT("="));
  2531.                 lstrcat(sql, szKeyValue);
  2532.             }
  2533.             else if ((*szColType | 0x20) == 's' || (*szColType | 0x20) == 'l')
  2534.             {
  2535.                 // string constants must be enclosed in 'str'
  2536.                 lstrcat(sql, TEXT("='"));
  2537.                 lstrcat(sql, szKeyValue);
  2538.                 lstrcat(sql, TEXT("'"));
  2539.             }
  2540.             else
  2541.                 assert(0); // unexpected column type
  2542.             if (szPrimaryKeyCol)
  2543.                 delete [] szPrimaryKeyCol;
  2544.         }
  2545.  
  2546.         // now grab row from table
  2547.         PMSIHANDLE hViewRow = 0;
  2548.         PMSIHANDLE hRecRow = 0;
  2549.         if (ERROR_SUCCESS != (iStat = MSI::MsiDatabaseOpenView(m_hDatabase, sql, &hViewRow)))
  2550.             return m_fError = TRUE, FALSE;
  2551.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewExecute(hViewRow, 0)))
  2552.             return m_fError = TRUE, FALSE;
  2553.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewRow, &hRecRow)))
  2554.         {
  2555.             _tprintf(TEXT("LOG_ERROR>> ROW: %s is Missing From Table: %s\n"), szKey, szTable);
  2556.             return m_fError = TRUE, FALSE;
  2557.         }
  2558.  
  2559.         // update string with localized string
  2560.         if (ERROR_SUCCESS != (iStat = MSI::MsiRecordSetString(hRecRow, 1, rgchBuffer)))
  2561.             return m_fError = TRUE, FALSE;
  2562.  
  2563.         // update row
  2564.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(hViewRow, MSIMODIFY_UPDATE, hRecRow)))
  2565.             return m_fError = TRUE, FALSE;
  2566.  
  2567.         // clean-up
  2568.         if (szTable)
  2569.             delete [] szTable;
  2570.         if (szColumn)
  2571.             delete [] szColumn;
  2572.         if (szKey)
  2573.             delete [] szKey;
  2574.     }//For block of 16 string table strings
  2575.  
  2576.     return TRUE;
  2577. }
  2578.  
  2579. /////////////////////////////////////////////////////////////////////////////
  2580. // CImportRes::LoadDialog
  2581. BOOL CImportRes::LoadDialog(HINSTANCE hModule, const TCHAR* szType, TCHAR* szDialog)
  2582. {
  2583.     // Find dialog resource into memory
  2584.     HRSRC hrsrc = W32::FindResource(hModule, szDialog, szType);
  2585.     if (hrsrc == NULL)
  2586.     {
  2587.         _tprintf(TEXT("LOG_ERROR>> DIALOG RESOURCE: %s NOT FOUND!\n"));
  2588.         // not set error state here we could be attempting to load individual dlg resources and we want to continue
  2589.         return FALSE;
  2590.     }
  2591.  
  2592.     // Load resource
  2593.     HGLOBAL hResource = W32::LoadResource(hModule, hrsrc);
  2594.     if (hResource == NULL)
  2595.         return m_fError = TRUE, FALSE; // error - ABORT
  2596.  
  2597.     // Create stream object to read from resource in memory
  2598.     CDialogStream DialogRes(W32::LockResource(hResource));
  2599.  
  2600.     ////////////////////////////////////////////////////////////////
  2601.     //                  Dialog Information                        //
  2602.     //                                                            //
  2603.     // stored as DLGTEMPLATEEX (should not have DLGTEMPLATE)      //
  2604.             /*    typedef struct {  
  2605.                 WORD      dlgVer; 
  2606.                 WORD      signature; 
  2607.                 DWORD     helpID; 
  2608.                 DWORD     exStyle; 
  2609.                 DWORD     style; 
  2610.                 WORD      cDlgItems; 
  2611.                 short     x; 
  2612.                 short     y; 
  2613.                 short     cx; 
  2614.                 short     cy; 
  2615.                 sz_Or_Ord menu; 
  2616.                 sz_Or_Ord windowClass; 
  2617.                 WCHAR     title[titleLen]; 
  2618.             // The following members exist only if the style member is 
  2619.             // set to DS_SETFONT or DS_SHELLFONT.
  2620.                 short     pointsize; 
  2621.                 short     weight; 
  2622.                 short     italic; 
  2623.                 WCHAR     typeface[stringLen];  
  2624.             } DLGTEMPLATEEX; */
  2625.     ////////////////////////////////////////////////////////////////
  2626.  
  2627.     ////////////////////////////////////////////////////////////////////
  2628.     // DLGTEMPLATE structure does not have dlgVer, signature, helpID,
  2629.     //             weight, or italic members
  2630.     //
  2631.  
  2632.     /* dlgVer */
  2633.     int iDlgVer = DialogRes.GetInt16();
  2634.     BOOL fOldVersion = FALSE;
  2635.     if (iDlgVer != 1)
  2636.     {
  2637.         // we have the old style -- DLGTEMPLATE + DLGITEMTEMPLATE
  2638.         fOldVersion = TRUE;
  2639.         DialogRes.Undo16();
  2640.     }
  2641.     if (!fOldVersion)
  2642.     {
  2643.         /* signature */
  2644.         DialogRes.GetInt16();
  2645.         /* helpID */
  2646.         DialogRes.GetInt32();
  2647.     }
  2648.     /* Extended Style + Style */
  2649.     int iDlgStyle = DialogRes.GetInt32() | DialogRes.GetInt32();
  2650.     /* Number of Controls on Dialog */
  2651.     int iNumCtrl  = DialogRes.GetInt16();
  2652.     /* X-coord (maps to HCentering value) */
  2653.     int iDlgXDim  = DialogRes.GetInt16();
  2654.     /* Y-coord (maps to VCentering value) */
  2655.     int iDlgYDim  = DialogRes.GetInt16();
  2656.     /* width */
  2657.     int iDlgWdDim = DialogRes.GetInt16();
  2658.     /* height */
  2659.     int iDlgHtDim = DialogRes.GetInt16();
  2660.     /* menu, aligned on WORD boundary */
  2661.     DialogRes.Align16();
  2662.     int iDlgMenu = DialogRes.GetInt16();
  2663.     if (iDlgMenu == 0xFFFF)
  2664.         DialogRes.GetInt16(); // ordinal menu value
  2665.     else if (iDlgMenu != 0x0000)
  2666.     {
  2667.         TCHAR* szMenu = DialogRes.GetStr();
  2668.         if (szMenu)
  2669.             delete [] szMenu;
  2670.     }
  2671.     /* class, aligned on WORD boundary */
  2672.     DialogRes.Align16();
  2673.     int iDlgClass = DialogRes.GetInt16();
  2674.     if (iDlgClass == 0xFFFF)
  2675.         DialogRes.GetInt16(); // ordinal window class value
  2676.     else if (iDlgClass != 0x0000)
  2677.     {
  2678.         TCHAR* szClass = DialogRes.GetStr();
  2679.         if (szClass)
  2680.             delete [] szClass;
  2681.     }
  2682.     /* title, aligned on WORD boundary */
  2683.     DialogRes.Align16();
  2684.     TCHAR* szDlgTitle = DialogRes.GetStr();
  2685.     /* font */
  2686.     if (iDlgStyle & DS_SETFONT)
  2687.     {
  2688.         /* font point size */
  2689.         int iDlgPtSize = DialogRes.GetInt16();
  2690.         if (!fOldVersion)
  2691.         {
  2692.             /* font weight */
  2693.             int iDlgFontWt = DialogRes.GetInt16();
  2694.             /* font italic */
  2695.             int iDlgFontItalic = DialogRes.GetInt16();
  2696.         }
  2697.         /* font typeface, aligned on WORD boundary */
  2698.         DialogRes.Align16();
  2699.         TCHAR* szFont = DialogRes.GetStr();
  2700.         if (szFont)
  2701.             delete [] szFont;
  2702.     }
  2703.  
  2704. #ifdef DEBUG
  2705.     _tprintf(TEXT("LOG>> DIALOG '%s' with '%d' controls at x=%d,y=%d,wd=%d,ht=%d. Title = \"%s\"\n"),
  2706.                 szDialog,iNumCtrl,iDlgXDim,iDlgYDim,iDlgWdDim,iDlgHtDim,szDlgTitle);
  2707. #endif
  2708.  
  2709.     /* find dialog in Dialog table, if fail, we ignore */
  2710.     //UNSUPPORTED: additional dialogs
  2711.  
  2712.     // first find match to dialog in _RESDialogs table
  2713.     PMSIHANDLE hViewFindDlgInstlrName = 0;
  2714.     PMSIHANDLE hRecDlgInstlrName = 0;
  2715.     PMSIHANDLE hRecDlgRESName = MSI::MsiCreateRecord(1);
  2716.     assert(hRecDlgRESName);
  2717.     UINT iStat;
  2718.     if (ERROR_SUCCESS != MSI::MsiRecordSetString(hRecDlgRESName, 1, szDialog))
  2719.         return m_fError = TRUE, TRUE;
  2720.     if (ERROR_SUCCESS != MSI::MsiDatabaseOpenView(m_hDatabase, sqlDialogInstallerName, &hViewFindDlgInstlrName))
  2721.     {
  2722.         _tprintf(TEXT("LOG_ERROR>> _RESDialogs table is missing from database.\n"));
  2723.         return m_fError = TRUE, TRUE;
  2724.     }
  2725.     if (ERROR_SUCCESS != MSI::MsiViewExecute(hViewFindDlgInstlrName, hRecDlgRESName))
  2726.         return m_fError = TRUE, TRUE;
  2727.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewFindDlgInstlrName, &hRecDlgInstlrName)))
  2728.     {
  2729.         // probably a new dialog
  2730.         _tprintf(TEXT("ALERT!, new dialog '%s' added. Unsupported feature!\n"), szDialog);
  2731.         return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing through enumeration
  2732.     }
  2733.  
  2734.     // execute query for search for dialog in Dialog table
  2735.     if (ERROR_SUCCESS != MSI::MsiViewExecute(m_hDialog, hRecDlgInstlrName))
  2736.         return m_fError = TRUE, TRUE;
  2737.  
  2738.     // fetch dialog for update
  2739.     PMSIHANDLE hRecDlg = 0;
  2740.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(m_hDialog, &hRecDlg)))
  2741.     {
  2742.         assert(iStat == ERROR_NO_MORE_ITEMS);
  2743.         // could be ERROR_NO_MORE_ITEMS -- someone removed the dialog
  2744.         _tprintf(TEXT("LOG_ERROR>> Dialog '%s' not found in database '%s'. New Dialogs are not supported.\n"), szDialog, m_szDatabase);
  2745.         return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing
  2746.     }
  2747.  
  2748.     // update dialog
  2749.     if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecDlg, idiHCentering, iDlgXDim))
  2750.         return m_fError = TRUE, TRUE;
  2751.     if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecDlg, idiVCentering, iDlgYDim))
  2752.         return m_fError = TRUE, TRUE;
  2753.     if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecDlg, idiWidth, iDlgWdDim))
  2754.         return m_fError = TRUE, TRUE;
  2755.     if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecDlg, idiHeight, iDlgHtDim))
  2756.         return m_fError = TRUE, TRUE;
  2757.     if (_tcscmp(strOverLimit, szDlgTitle) != 0) // don't update if "!! STR OVER LIMIT !!"
  2758.     {
  2759.         if (ERROR_SUCCESS != MSI::MsiRecordSetString(hRecDlg, idiTitle, szDlgTitle))
  2760.             return m_fError = TRUE, TRUE;
  2761.     }
  2762.  
  2763.     if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(m_hDialog, MSIMODIFY_UPDATE, hRecDlg)))
  2764.     {
  2765.         _tprintf(TEXT("LOG_ERROR>>Failed to update Dialog '%s'.\n"), szDialog);
  2766.         return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue with other Dialogs
  2767.     }
  2768.  
  2769.     ///////////////////////////////////////////////////////////////////////
  2770.     //                  Control Information                              //
  2771.     //                                                                   //
  2772.     // stored as DLGITEMTEMPLATEEX (should not have DLGITEMTEMPLATE)     //
  2773.     /*    typedef struct { 
  2774.             DWORD  helpID; 
  2775.             DWORD  exStyle; 
  2776.             DWORD  style; 
  2777.             short  x; 
  2778.             short  y; 
  2779.             short  cx; 
  2780.             short  cy; 
  2781.             WORD   id; 
  2782.             sz_Or_Ord windowClass; 
  2783.             sz_Or_Ord title; 
  2784.             WORD   extraCount; 
  2785.         } DLGITEMTEMPLATEEX; */
  2786.     /////////////////////////////////////////////////////////////////////////
  2787.  
  2788.     /////////////////////////////////////////////////////////////////////////
  2789.     // DLGITEMTEMPLATE does not have helpID
  2790.     //
  2791.     if (iNumCtrl > 0 && !m_hControl)
  2792.     {
  2793.         _tprintf(TEXT("LOG_ERROR>> Unable to update controls.  Control table does not exist\n"));
  2794.         return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing
  2795.     }
  2796.     PMSIHANDLE hViewCtrlInstallerName = 0;
  2797.     PMSIHANDLE hRecCtrl = 0;
  2798.     PMSIHANDLE hRecRadioButton = MSI::MsiCreateRecord(2);
  2799.     PMSIHANDLE hRecCtrlResId = MSI::MsiCreateRecord(1);
  2800.     assert(hRecCtrlResId);
  2801.     if (ERROR_SUCCESS != MSI::MsiDatabaseOpenView(m_hDatabase, sqlControlInstallerName, &hViewCtrlInstallerName))
  2802.     {
  2803.         _tprintf(TEXT("LOG_ERROR>> _RESControls table is missing from database\n"));
  2804.         return m_fError = TRUE, TRUE;
  2805.     }
  2806.     bool fRadioButton = false;
  2807.     // cycle through the controls
  2808.     for (int i = 1; i <= iNumCtrl; i++)
  2809.     {
  2810.         /* DLGITEMTEMPLATEEX is alligned on a DWORD boundary */
  2811.         DialogRes.Align32(); 
  2812.  
  2813.         fRadioButton = false;
  2814.  
  2815.         if (!fOldVersion)
  2816.         {
  2817.             /* helpID */
  2818.             DialogRes.GetInt32();
  2819.         }
  2820.         /* exStyle | style */
  2821.         int iCtrlAttrib = DialogRes.GetInt32() | DialogRes.GetInt32();
  2822.         /* x */
  2823.         int iCtrlXDim = DialogRes.GetInt16();
  2824.         /* y */
  2825.         int iCtrlYDim = DialogRes.GetInt16();
  2826.         /* cx */
  2827.         int iCtrlWdDim = DialogRes.GetInt16();
  2828.         /* cy */
  2829.         int iCtrlHtDim = DialogRes.GetInt16();
  2830.         /* id */
  2831.         int iCtrlId = DialogRes.GetInt16();
  2832.         /* windowClass -- aligned on word boundary*/
  2833.         if (!fOldVersion)
  2834.             DialogRes.GetInt16(); //!! don't appear to be aligned on word boundary, instead have extra 16 though ??
  2835.         else
  2836.             DialogRes.Align16();
  2837.         TCHAR* szCtrlType = 0;
  2838.         int iWndwClass = DialogRes.GetInt16();
  2839.         if (iWndwClass == 0xFFFF)
  2840.         {
  2841.             // pre-defined window class
  2842.             short iCtrlWindowClass = DialogRes.GetInt16();
  2843.             switch (iCtrlWindowClass)
  2844.             {
  2845.             case 0x0080: // button
  2846.                 if (iCtrlAttrib & BS_RADIOBUTTON)
  2847.                     fRadioButton = true;
  2848.                 break;
  2849.             case 0x0081: // edit
  2850.                 break;
  2851.             case 0x0082: // static
  2852.                 break;
  2853.             case 0x0083: // list box
  2854.                 break;
  2855.             case 0x0084: // scroll bar
  2856.                 break;
  2857.             case 0x0085: // combo box
  2858.                 break;
  2859.             default: assert(0);
  2860.                 break;
  2861.             }
  2862.         }
  2863.         else
  2864.         {
  2865.             // custom window class, stored as str
  2866.             DialogRes.Undo16();
  2867.             szCtrlType = DialogRes.GetStr();
  2868.         }
  2869.         /* title -- aligned on word boundary*/
  2870.         DialogRes.Align16();
  2871.         TCHAR* szCtrlText = 0;
  2872.         if (DialogRes.GetInt16() == 0xFFFF)
  2873.         {
  2874.             // ordinal
  2875.             DialogRes.GetInt16();
  2876.         }
  2877.         else
  2878.         {
  2879.             // str title
  2880.             DialogRes.Undo16();
  2881.             szCtrlText = DialogRes.GetStr();
  2882.         }
  2883.         /* extra count */
  2884.         int iCtrlCreationData = DialogRes.GetInt16();
  2885.         if (iCtrlCreationData > 0)
  2886.         {
  2887.             DialogRes.Align16(); // data begins at next WORD boundary
  2888.             DialogRes.Move(iCtrlCreationData);
  2889.         }
  2890.  
  2891.         // find control's real name (for use with Installer)
  2892.         if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecCtrlResId, 1, iCtrlId))
  2893.             return m_fError = TRUE, TRUE;
  2894.         if (ERROR_SUCCESS != MSI::MsiViewExecute(hViewCtrlInstallerName, hRecCtrlResId))
  2895.             return m_fError = TRUE, TRUE;
  2896.  
  2897.         // fetch control's real name
  2898.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(hViewCtrlInstallerName, &hRecCtrl)))
  2899.         {
  2900.             if (ERROR_NO_MORE_ITEMS == iStat)
  2901.             {
  2902.                 // new control, unsupported feature
  2903.                 _tprintf(TEXT("LOG_ERROR>>\t Control with ID '%d' not found. New Controls are not supported.\n"), iCtrlId);
  2904.                 return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing
  2905.             }
  2906.             assert(ERROR_SUCCESS != iStat);
  2907.         }
  2908.  
  2909.         DWORD dwName = 0;
  2910.         MSI::MsiRecordGetString(hRecCtrl, 2, TEXT(""), &dwName);
  2911.         TCHAR* szCtrlName = new TCHAR[++dwName];
  2912.         if (ERROR_SUCCESS != MSI::MsiRecordGetString(hRecCtrl, 2, szCtrlName, &dwName))
  2913.             return m_fError = TRUE, TRUE;
  2914. #ifdef DEBUG
  2915.         _tprintf(TEXT("LOG>>\tCONTROL '%d' ('%s') at x=%d,y=%d,wd=%d,ht=%d. Text = \"%s\"\n"),
  2916.             iCtrlId,szCtrlName,iCtrlXDim,iCtrlYDim,iCtrlWdDim,iCtrlHtDim,szCtrlText);
  2917. #endif
  2918.  
  2919.         // fetch control's info for update
  2920.         // for radiobuttons, we have to get it from the radiobutton table
  2921.         // we also have to parse the szCtrlName string to get out the property and order keys used in the radiobutton table
  2922.         // assumes that radiobuttons follow group:property:order syntax
  2923.         if (fRadioButton && !_tcschr(szCtrlName, ':'))
  2924.             fRadioButton = false; // not really a radiobutton, just the group encapsulating them
  2925.  
  2926.         PMSIHANDLE hRecRBExec = 0;
  2927.         if (fRadioButton)
  2928.         {
  2929.             if (!m_hRadioButton)
  2930.             {
  2931.                 _tprintf(TEXT("LOG_ERROR>> RadioButtons found, but no RadioButton table exists in the database\n"));
  2932.                 return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing
  2933.             }
  2934.             // parse name RadioButtonGroup:Property:Order
  2935.             TCHAR* szRBGroup = _tcstok(szCtrlName, szTokenSeps);
  2936.             assert(szRBGroup);
  2937.             TCHAR* szRBProperty = _tcstok(NULL, szTokenSeps);
  2938.             assert(szRBProperty);
  2939.             TCHAR* szRBOrder = _tcstok(NULL, szTokenSeps);
  2940.             assert(szRBOrder);
  2941.             int iRBOrder = _ttoi(szRBOrder);
  2942. #ifdef DEBUG
  2943.             _tprintf(TEXT("LOG>> RadioButton belongs to RBGroup: %s, Property: %s, and has Order=%d"), szRBGroup, szRBProperty, iRBOrder);
  2944. #endif
  2945.             hRecRBExec = MSI::MsiCreateRecord(2);
  2946.             if (ERROR_SUCCESS != MSI::MsiRecordSetString(hRecRBExec, 1, szRBProperty))
  2947.                 return m_fError = TRUE, TRUE;
  2948.             if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecRBExec, 2, iRBOrder))
  2949.                 return m_fError = TRUE, TRUE;
  2950.         }
  2951.         if (ERROR_SUCCESS != MSI::MsiViewExecute(fRadioButton ? m_hRadioButton : m_hControl, fRadioButton ? hRecRBExec : hRecCtrl))
  2952.             return m_fError = TRUE, TRUE;
  2953.         PMSIHANDLE hRecCtrlUpdate = 0;
  2954.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewFetch(fRadioButton ? m_hRadioButton : m_hControl, &hRecCtrlUpdate)))
  2955.         {
  2956.             if (ERROR_NO_MORE_ITEMS == iStat)
  2957.             {
  2958.                 // control has been removed from database
  2959.                 _tprintf(TEXT("LOG_ERROR>>\t Control with ID '%d' not found in database.\n"), iCtrlId);
  2960.                 return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue processing
  2961.             }
  2962.             assert(ERROR_SUCCESS != iStat);
  2963.         }
  2964.  
  2965.         // update info
  2966.         if (!fRadioButton)
  2967.         {
  2968.             if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecCtrlUpdate, iciX, iCtrlXDim))
  2969.                 return m_fError = TRUE, TRUE;
  2970.             if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecCtrlUpdate, iciY, iCtrlYDim))
  2971.                 return m_fError = TRUE, TRUE;
  2972.         }
  2973.         if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecCtrlUpdate, fRadioButton ? irbiWidth : iciWidth, iCtrlWdDim))
  2974.             return m_fError = TRUE, TRUE;
  2975.         if (ERROR_SUCCESS != MSI::MsiRecordSetInteger(hRecCtrlUpdate, fRadioButton ? irbiHeight : iciHeight, iCtrlHtDim))
  2976.             return m_fError = TRUE, TRUE;
  2977.         if (0 != _tcscmp(strOverLimit, szCtrlText)) // don't update if "!! STR OVER LIMIT !!"
  2978.         {
  2979.             if (ERROR_SUCCESS != MSI::MsiRecordSetString(hRecCtrlUpdate, fRadioButton ? irbiText : iciText, szCtrlText))
  2980.                 return m_fError = TRUE, TRUE;
  2981.         }
  2982.         if (ERROR_SUCCESS != (iStat = MSI::MsiViewModify(fRadioButton ? m_hRadioButton : m_hControl, MSIMODIFY_UPDATE, hRecCtrlUpdate)))
  2983.         {
  2984.             _tprintf(TEXT("LOG_ERROR>>Failed to update Control '%d'.\n"), iCtrlId);
  2985.             return m_fError = TRUE, TRUE; // error - ABORT, but TRUE to continue with other Dialogs
  2986.         }
  2987.  
  2988.         if (szCtrlType)
  2989.             delete [] szCtrlType;
  2990.         if (szCtrlText)
  2991.             delete [] szCtrlText;
  2992.         if (ERROR_SUCCESS != MSI::MsiViewClose(hViewCtrlInstallerName)) // for re-execute
  2993.             return m_fError = TRUE, TRUE;
  2994.     }
  2995.  
  2996.     return TRUE;
  2997. }
  2998.  
  2999.  
  3000. //////////////////////////////////////////////////////////////////////////////
  3001. // CImportRes::SetCodePage
  3002. BOOL CImportRes::SetCodePage(WORD wLang)
  3003. {
  3004.     // if we have already set the codepage, we don't need to do it again
  3005.     if (m_fSetCodepage)
  3006.         return TRUE;
  3007.  
  3008.     DWORD dwLocale = MAKELCID(wLang, SORT_DEFAULT);
  3009.     TCHAR szLocaleBuf[7]; // from MSDN, max # char allowed is 6
  3010.     int cch = W32::GetLocaleInfo(dwLocale, LOCALE_IDEFAULTANSICODEPAGE, szLocaleBuf, sizeof(szLocaleBuf)/sizeof(TCHAR));
  3011.     assert(cch != 0);
  3012.  
  3013.     // GetLocaleInfo always returns information in text format
  3014.     // Numeric data is written in decimal format
  3015.     // Expect numeric data because ask for CodePage...need to convert to int
  3016.     TCHAR* szStop;
  3017.     g_uiCodePage = _tcstoul(szLocaleBuf, &szStop, 0);
  3018.  
  3019.     //verify database's codepage
  3020.     // database's codepage must either be language NEUTRAL or match g_uiCodepage
  3021.     if (ERROR_SUCCESS != VerifyDatabaseCodepage())
  3022.         return m_fError = TRUE, FALSE;
  3023.  
  3024.     // verify codepage is available on system
  3025.     // A code page is considered valid only if it is installed in the system. 
  3026.     if (!IsValidCodePage(g_uiCodePage))
  3027.         return m_fError = TRUE, FALSE; // codepage not valid for this system
  3028.  
  3029.     // set codepage in database using _ForceCodepage table
  3030.     // find temp directory
  3031.     TCHAR szTempPath[MAX_PATH];
  3032.     W32::GetTempPath(MAX_PATH, szTempPath);
  3033.  
  3034.     // create full path (TEMP directory already has backslash)
  3035.     TCHAR szFileFullPath[2*MAX_PATH + 2];
  3036.     wsprintf(szFileFullPath, TEXT("%s%s"), szTempPath, szCodepageFile);
  3037.     DWORD dwWritten;
  3038.     HANDLE hFile = W32::CreateFile(szFileFullPath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, 0, 0);
  3039.     if (!hFile)
  3040.     {
  3041.         _tprintf(TEXT("!! Unable to set codepage of database.\n"));
  3042.         return m_fError = TRUE, FALSE;
  3043.     }
  3044.  
  3045.     /*********************************
  3046.      FORMAT FOR FORCING CODEPAGE
  3047.     **********************************/
  3048.     // blank line
  3049.     // blank line
  3050.     // codepage<tab>_ForceCodepage
  3051.     TCHAR szCodepage[MAX_PATH];
  3052.     DWORD dw = wsprintf(szCodepage, TEXT("\r\n\r\n%s\t%s\r\n"), szLocaleBuf, szForceCodepage);
  3053. #ifdef UNICODE
  3054.     assert(_tcsclen(szCodepage) + 1 < MAX_PATH/2);
  3055.     char szBuf[MAX_PATH];
  3056.     if (!W32::WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, szCodepage, _tcsclen(szCodepage), szBuf, MAX_PATH, NULL, NULL))
  3057.         return m_fError = TRUE, FALSE;
  3058.     if (!WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, 0))
  3059.         return m_fError = TRUE, FALSE;
  3060. #else
  3061.     if (!WriteFile(hFile, szCodepage, dw, &dwWritten, 0))
  3062.         return m_fError = TRUE, FALSE;
  3063. #endif
  3064.     if (!W32::CloseHandle(hFile))
  3065.         return m_fError = TRUE, FALSE;
  3066.  
  3067.     // set codepage of database
  3068.     UINT iStat = MSI::MsiDatabaseImport(m_hDatabase, szTempPath, szCodepageFile);
  3069.     if (iStat != ERROR_SUCCESS)
  3070.     {
  3071.         _tprintf(TEXT("!! Unable to set codepage of database. Error = %d\n"), iStat);
  3072.         return m_fError = TRUE, FALSE;
  3073.     }
  3074.     
  3075.     // attempt to delete the file we created for clean-up
  3076. //    if (!DeleteFile(szCodepageFile))
  3077. //        return m_fError = TRUE, FALSE;
  3078.  
  3079.     // update status flage
  3080.     m_fSetCodepage = TRUE;
  3081.  
  3082.     return TRUE;
  3083. }
  3084.  
  3085. /////////////////////////////////////////////////////////////////////////////
  3086. // EnumDialogCallback 
  3087. BOOL __stdcall EnumDialogCallback(HINSTANCE hModule, const TCHAR* szType, TCHAR* szDialogName, long lParam)
  3088. {
  3089.     // determine codepage required.  steps, enumerate languages in resource file (better be no more than two)
  3090.     // possibilities are NEUTRAL (non-localized) + other lang
  3091.  
  3092.     ((CImportRes*)lParam)->SetFoundLang(FALSE); // init to FALSE
  3093.     if (!W32::EnumResourceLanguages(hModule, szType, szDialogName, EnumLanguageCallback, lParam))
  3094.         return FALSE;
  3095.     
  3096. #ifdef DEBUG
  3097.     _tprintf(TEXT("LOG>> DIALOG: '%s' FOUND\n"),szDialogName);
  3098. #endif
  3099.  
  3100.     BOOL fOK = ((CImportRes*)lParam)->LoadDialog(hModule, szType, szDialogName);
  3101.     if (!fOK)
  3102.     {
  3103.         // error occured
  3104.         ((CImportRes*)lParam)->SetErrorState(TRUE);
  3105.         return FALSE;
  3106.     }
  3107.     return TRUE;
  3108. }
  3109.  
  3110. /////////////////////////////////////////////////////////////////////////////
  3111. // EnumStringCallback
  3112. BOOL __stdcall EnumStringCallback(HINSTANCE hModule, const TCHAR* szType, TCHAR* szName, long lParam)
  3113. {
  3114.     ((CImportRes*)lParam)->SetFoundLang(FALSE); // init to FALSE
  3115.  
  3116.     // determine codepage required.  steps, enumerate languages in resource file (better be no more than two)
  3117.     // possibilities are NEUTRAL (non-localized) + other lang
  3118.     if (!W32::EnumResourceLanguages(hModule, szType, szName, EnumLanguageCallback, lParam))
  3119.         return FALSE;
  3120.  
  3121.  
  3122.     BOOL fOK = ((CImportRes*)lParam)->LoadString(hModule, szType, szName);
  3123.     if (!fOK)
  3124.     {
  3125.         // error occured
  3126.         ((CImportRes*)lParam)->SetErrorState(TRUE);
  3127.         return FALSE;
  3128.     }
  3129.     return TRUE;
  3130. }
  3131.  
  3132. /////////////////////////////////////////////////////////////////////////////
  3133. // EnumLanguageCallback
  3134. BOOL __stdcall EnumLanguageCallback(HINSTANCE hModule, const TCHAR* szType, const TCHAR* szName, WORD wIDLanguage, long lParam)
  3135. {
  3136.     /*************************************************************************************
  3137.     RESTRICTIONS:
  3138.     1.) ONLY 1 language per resource
  3139.     2.) UP To 2 languages per resource file (but 1 must be LANG_NEUTRAL)
  3140.     3.) On Win9x we must be on a system matching the required codepage
  3141.     4.) We should only be able to update a language neutral database or
  3142.         a database set with the required code page
  3143.     5.) Database can only have one code page (Although _SummaryInformation stream
  3144.         can have a code page different from database as _SummaryInformation is considered
  3145.         to be different
  3146.     **************************************************************************************/
  3147.     
  3148.     if (((CImportRes*)lParam)->WasLanguagePreviouslyFound())
  3149.     {
  3150.         // ERROR -- more than 1 language per dialog
  3151.         _tprintf(TEXT("!! STRING RESOURCE IS IN MORE THAN ONE LANGUAGE IN RESOURCE FILE\n"));
  3152.         ((CImportRes*)lParam)->SetErrorState(TRUE);
  3153.         return FALSE;
  3154.     }
  3155.  
  3156.     // if languages match we are good to go
  3157.     if (g_wLangId != wIDLanguage)
  3158.     {
  3159.         // languages don't match
  3160.         // 2 valid scenarios
  3161.  
  3162.         // valid SCENARIO 1: g_wLangId is NEUTRAL, wIDLanguage new language
  3163.         // valid SCENARIO 2: g_wLangId is language, wIDLanguage is NEUTRAL
  3164.         // all other scenarios invalid
  3165.         if (g_wLangId == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
  3166.         {
  3167.             // set codepage, update g_uiCodePage
  3168.             if (!((CImportRes*)lParam)->SetCodePage(wIDLanguage))
  3169.                 return ((CImportRes*)lParam)->SetErrorState(TRUE), FALSE;
  3170.             g_wLangId = wIDLanguage;
  3171.         }
  3172.         else if (wIDLanguage != MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
  3173.         {
  3174.             // invalid, 2 different languages, neither one NEUTRAL
  3175.             _tprintf(TEXT("!! Resource file contains more than one language. Not Supported. Lang1 = %d, Lang2 = %d\n"), g_wLangId, wIDLanguage);
  3176.             return ((CImportRes*)lParam)->SetErrorState(TRUE), FALSE;
  3177.         }
  3178.     }
  3179.     
  3180.     ((CImportRes*)lParam)->SetFoundLang(TRUE); // language found for resource
  3181.     return TRUE;
  3182. }
  3183.  
  3184. //_______________________________________________________________________________________
  3185. //
  3186. // CDIALOGSTREAM CLASS IMPLEMENTATION
  3187. //_______________________________________________________________________________________
  3188.  
  3189. /////////////////////////////////////////////////////////////////////////////
  3190. // CDialogStream::GetInt16 -- returns a 16 bit integer, moves internal ptr 16
  3191. short CDialogStream::GetInt16()
  3192. {
  3193.     short i = *(unsigned short*)m_pch;
  3194.     m_pch += sizeof(unsigned short);
  3195.     return i;
  3196. }
  3197.  
  3198. /////////////////////////////////////////////////////////////////////////////
  3199. // CDialogStream::GetInt32 -- returns a 32 bit integer, moves internal ptr 32
  3200. int CDialogStream::GetInt32()
  3201. {
  3202.     int i = *(int*)m_pch;
  3203.     m_pch += sizeof(int);
  3204.     return i;
  3205. }
  3206.  
  3207. /////////////////////////////////////////////////////////////////////////////
  3208. // CDialogStream::GetInt8 -- returns a 8 bit integer, moves internal ptr 8
  3209. int CDialogStream::GetInt8()
  3210. {
  3211.     int i = *(unsigned char*)m_pch;
  3212.     m_pch += sizeof(unsigned char);
  3213.     return i;
  3214. }
  3215.  
  3216. /////////////////////////////////////////////////////////////////////////////
  3217. // CDialogStream::GetStr -- returns a null terminated str from memory.
  3218. //   Handles DBCS, Unicode str storage.  Moves ptr length of str.
  3219. //   Resource strings stored as unicode
  3220. TCHAR* CDialogStream::GetStr()
  3221. {
  3222.     TCHAR* sz;
  3223. #ifdef UNICODE
  3224.     int cchwide = lstrlenW((wchar_t*)m_pch);
  3225.     sz = new TCHAR[cchwide + 1];
  3226.     lstrcpyW(sz, (wchar_t*)m_pch);
  3227. #else
  3228.     // what codepage to use to translate?
  3229.     int cb = W32::WideCharToMultiByte(CP_ACP, 0, (wchar_t*)m_pch, -1, 0, 0, 0, 0);
  3230.     int cchwide = lstrlenW((wchar_t*)m_pch);
  3231.     sz = new TCHAR[cb+1];
  3232.     BOOL fUsedDefault;
  3233.     W32::WideCharToMultiByte(CP_ACP, 0, (wchar_t*)m_pch, -1, sz, cb, 0, &fUsedDefault);
  3234. #endif // UNICODE
  3235.     
  3236.     m_pch += 2*(cchwide+1);
  3237.     return sz;
  3238. }
  3239.  
  3240. /////////////////////////////////////////////////////////////////////////////
  3241. // CDialogStream::Align32-- moves pointer to DWORD boundary
  3242. BOOL CDialogStream::Align32()
  3243. {
  3244.     m_pch = (char*)(int(m_pch) + 3 & ~ 3);
  3245.     return TRUE;
  3246. }
  3247.  
  3248. /////////////////////////////////////////////////////////////////////////////
  3249. // CDialogStream::Align16-- moves pointer to WORD boundary
  3250. BOOL CDialogStream::Align16()
  3251. {
  3252.     m_pch = (char*)(int(m_pch) + 1 & ~ 1);
  3253.     return TRUE;
  3254. }
  3255.  
  3256. /////////////////////////////////////////////////////////////////////////////
  3257. // CDialogStream::Undo16 -- moves ptr back 16
  3258. BOOL CDialogStream::Undo16()
  3259. {
  3260.     m_pch -= sizeof(unsigned short);
  3261.     return TRUE;
  3262. }
  3263.  
  3264. /////////////////////////////////////////////////////////////////////////////
  3265. // CDialogStream::Move -- moves pointer cbBytes
  3266. BOOL CDialogStream::Move(int cbBytes)
  3267. {
  3268.     m_pch += cbBytes;
  3269.     return TRUE;
  3270. }
  3271.  
  3272. CDialogStream::CDialogStream(HGLOBAL hResource)
  3273.     : m_pch((char*)hResource)
  3274. {
  3275.     // Constructor
  3276. }
  3277.  
  3278. CDialogStream::~CDialogStream()
  3279. {
  3280.     // Destructor
  3281. }
  3282.  
  3283.  
  3284. //__________________________________________________________________________________________
  3285. //
  3286. // MAIN + HELPER FUNCTIONS
  3287. //__________________________________________________________________________________________
  3288.  
  3289. ///////////////////////////////////////////////////////////
  3290. // usage
  3291. void Usage()
  3292. {
  3293.     _tprintf(
  3294.             TEXT("MSILOC.EXE -- Copyright (C) Microsoft Corporation, 2000.  All rights reserved\n")
  3295.             TEXT("\t*Generates a resource file from the UI in the installation package\n")
  3296.             TEXT("\t*Imports a localized resource DLL into an installation package\n")
  3297.             TEXT("\n")
  3298.             TEXT("SYNTAX -->EXPORT MSI TO RC (creates a resource file):\n")
  3299.             TEXT("  msiloc -e {database} {option 1}{option 2}...\n")
  3300.             TEXT("SYNTAX -->IMPORT (RES)DLL TO MSI:\n")
  3301.             TEXT("  msiloc -i {database} {resource DLL} {option 1}{option 2}...\n")
  3302.             TEXT("OPTIONS:\n")
  3303.             TEXT("    -d * all dialogs\n")
  3304.             TEXT("    -d {Dialog1} specific dialog\n")
  3305.             TEXT("    -s {Table Column} specific column of strings (EXPORT ONLY)\n")
  3306.             TEXT("    -s * * all strings (IMPORT ONLY)\n")
  3307.             TEXT("    -x option to not export binary data (bitmaps, icons, jpegs) (EXPORT ONLY)\n")
  3308.             TEXT("    -c {database} option to save to a new database\n")
  3309.             TEXT("\n")
  3310.             TEXT("CREATING A .RES FILE:\n")
  3311.             TEXT("    rc.exe {resource file}\n")
  3312.             TEXT("CREATING A .DLL FILE:\n")
  3313.             TEXT("    link.exe /DLL /NOENTRY /NODEFAULTLIB /MACHINE:iX86\n")
  3314.             TEXT("         /OUT:{resource DLL} {compiled res file}\n")
  3315.             );
  3316. }
  3317.  
  3318. ///////////////////////////////////////////////////////////
  3319. // SkipWhiteSpace
  3320. TCHAR SkipWhiteSpace(TCHAR*& rpch)
  3321. {
  3322.     TCHAR ch;
  3323.     for (; (ch = *rpch) == TEXT(' ') || ch == TEXT('\t'); rpch++)
  3324.         ;
  3325.     return ch;
  3326. }
  3327.  
  3328. ///////////////////////////////////////////////////////////
  3329. // SkipValue
  3330. BOOL SkipValue(TCHAR*& rpch)
  3331. {
  3332.     TCHAR ch = *rpch;
  3333.     if (ch == 0 || ch == TEXT('/') || ch == TEXT('-'))
  3334.         return FALSE;   // no value present
  3335.     for (; (ch = *rpch) != TEXT(' ') && ch != TEXT('\t') && ch != 0; rpch++)
  3336.     {    
  3337.         if (*rpch == TEXT('"'))
  3338.         {
  3339.             rpch++; // for '"'
  3340.             for (; (ch = *rpch) != TEXT('"') && ch != TEXT('/') && ch != TEXT('-') && ch != 0; rpch++)
  3341.                 ;
  3342.             ch = *(++rpch);
  3343.             break;
  3344.         }
  3345.     }
  3346.  
  3347.     if (ch != 0)
  3348.         *rpch++ = 0;
  3349.     return TRUE;
  3350. }
  3351.  
  3352. ///////////////////////////////////////////////////////////
  3353. // Error
  3354. void Error(TCHAR* szMsg)
  3355. {
  3356.     _tprintf(TEXT("MSILOC ERROR: %s\n"), szMsg);
  3357.     throw 1;
  3358. }
  3359.  
  3360. ///////////////////////////////////////////////////////////
  3361. // ErrorIf
  3362. void ErrorIf(BOOL fError, TCHAR* szMsg, BOOL fThrow)
  3363. {
  3364.     if (fError)
  3365.     {
  3366.         _tprintf(TEXT("MSILOC ERROR: %s\n"), szMsg);
  3367.         if (fThrow)
  3368.             throw 1;
  3369.     }
  3370. }
  3371.  
  3372. ///////////////////////////////////////////////////////////
  3373. // _tmain
  3374. extern "C" int __cdecl _tmain(int argc, TCHAR* argv[])
  3375. {    
  3376.     // WE WANT UNICODE ON NT/Windows2000
  3377.     // ?? ANSI on WIN9x
  3378.  
  3379.     try
  3380.     {
  3381.         TCHAR* szCmdLine = W32::GetCommandLine();
  3382.         TCHAR* pch = szCmdLine;
  3383.         SkipValue(pch); // skip module name
  3384.         TCHAR chCmdNext;
  3385.  
  3386.         TCHAR* rgszTables[MAX_STRINGS];
  3387.         TCHAR* rgszColumns[MAX_STRINGS];
  3388.         TCHAR* rgszDialogs[MAX_DIALOGS];
  3389.         TCHAR* szDb = 0;
  3390.         TCHAR* szRESDLL = 0;
  3391.         TCHAR* szSaveDatabase = 0;
  3392.         int cStr = 0;
  3393.         int cDlg = 0;
  3394.         int iMode = 0;
  3395.         while ((chCmdNext = SkipWhiteSpace(pch)) != 0)
  3396.         {
  3397.             if (chCmdNext == TEXT('/') || chCmdNext == TEXT('-'))
  3398.             {
  3399.                 TCHAR* szCmdOption = pch++;
  3400.                 TCHAR chOption = (TCHAR)(*pch++ | 0x20);
  3401.                 chCmdNext = SkipWhiteSpace(pch);
  3402.                 TCHAR* szCmdData = pch;
  3403.                 switch (chOption)
  3404.                 {
  3405.                 case TEXT('i'):
  3406.                     iMode |= iIMPORT_RES;
  3407.                     if (!SkipValue(pch))
  3408.                         Error(TEXT("Missing Option Data (option = I)\n"));
  3409.                     szDb = szCmdData;
  3410.                     szCmdData = pch;
  3411.                     if (!SkipValue(pch))
  3412.                         Error(TEXT("Missing Option Data (option = I)\n"));
  3413.                     szRESDLL = szCmdData;
  3414.                     break;
  3415.                 case TEXT('e'):
  3416.                     iMode |= iEXPORT_MSI;
  3417.                     if (!SkipValue(pch))
  3418.                         Error(TEXT("Missing Option Data (option = E)\n"));
  3419.                     szDb = szCmdData;
  3420.                     break;
  3421.                 case TEXT('d'):
  3422.                     iMode |= iDIALOGS;
  3423.                     if (!SkipValue(pch))
  3424.                         Error(TEXT("Missing Option Data (option = D)\n"));
  3425.                     if (cDlg == MAX_DIALOGS)
  3426.                         Error(TEXT("Too Many Dialogs On Command Line\n"));
  3427.                     rgszDialogs[cDlg++] = szCmdData;
  3428.                     break;
  3429.                 case TEXT('s'):
  3430.                     iMode |= iSTRINGS;
  3431.                     if (!SkipValue(pch))
  3432.                         Error(TEXT("Missing Option Data (option = S)\n"));
  3433.                     if (cStr == MAX_STRINGS)
  3434.                         Error(TEXT("Too Many Table:Column Pairs On Command Line\n"));
  3435.                     rgszTables[cStr] = szCmdData;
  3436.                     szCmdData = pch;
  3437.                     if (!SkipValue(pch))
  3438.                         Error(TEXT("Missing Option Data (option = S)\n"));
  3439.                     rgszColumns[cStr++] = szCmdData;
  3440.                     break;
  3441.                 case TEXT('x'):
  3442.                     iMode |= iSKIP_BINARY;
  3443.                     break;
  3444.                 case TEXT('c'):
  3445.                     iMode |= iCREATE_NEW_DB;
  3446.                     if (!SkipValue(pch))
  3447.                         Error(TEXT("Missing Option Data (option = C)\n"));
  3448.                     szSaveDatabase = szCmdData;
  3449.                     break;
  3450.                 case TEXT('?'):
  3451.                     Usage();
  3452.                     return 0;
  3453.                 default:
  3454.                     Usage();
  3455.                     return 1;
  3456.                 }
  3457.             }
  3458.             else
  3459.             {
  3460.                 Usage();
  3461.                 return 1;
  3462.             }
  3463.         }
  3464.  
  3465.         // must specify either EXPORT or IMPORT, but not both
  3466.         if (iMode == 0 || (iMode & (iEXPORT_MSI | iIMPORT_RES)) == (iEXPORT_MSI | iIMPORT_RES) ||
  3467.             (iMode & ~(iEXPORT_MSI | iIMPORT_RES)) == 0)
  3468.         {
  3469.             Usage();
  3470.             throw 1;
  3471.         }
  3472.  
  3473.         if ((iMode & iCREATE_NEW_DB) && !szDb)
  3474.         {
  3475.             Usage();
  3476.             throw 1;
  3477.         }
  3478.  
  3479.         if ((iMode & iEXPORT_MSI) && szDb)
  3480.         {
  3481.             // export MSI to RESOURCE file
  3482.             CGenerateRC genRC(szDb, (iMode & iCREATE_NEW_DB) ? szSaveDatabase : NULL);
  3483.             if (iMode & iDIALOGS)
  3484.             {
  3485.                 // export DIALOGS
  3486.                 BOOL fBinary = (iMode & iSKIP_BINARY) ? FALSE : TRUE;
  3487.                 if (1 == cDlg && 0 == _tcscmp(TEXT("*"), rgszDialogs[0]))
  3488.                 {
  3489.                     // export all dialogs
  3490.                     ErrorIf(ERROR_SUCCESS != genRC.OutputDialogs(fBinary), TEXT("Failed to Export Dialogs To Resource File"), true);
  3491.                 }
  3492.                 else
  3493.                 {
  3494.                     // export specified dialogs only
  3495.                     // we'll try every dialog listed so we won't through the error
  3496.                     for (int i = 0; i < cDlg; i++)
  3497.                         ErrorIf(ERROR_SUCCESS != genRC.OutputDialog(rgszDialogs[i], fBinary), TEXT("Failed to Export Dialog To Resource File"), false);
  3498.                     ErrorIf(genRC.IsInErrorState(), TEXT("EXPORT failed"), true);
  3499.                 }
  3500.             }
  3501.             if (iMode & iSTRINGS)
  3502.             {
  3503.                 // export STRINGS
  3504.                 if (1 == cStr && 0 == _tcscmp(TEXT("*"), rgszTables[0]) && 0 == _tcscmp(TEXT("*"), rgszColumns[0]))
  3505.                 {
  3506.                     // export all strings
  3507.                     // NOT SUPPORTED
  3508.                     _tprintf(TEXT("EXPORT ALL STRINGS OPTION is not supported\n"));
  3509.                     Usage();
  3510.                     throw 1;
  3511.                 }
  3512.                 for (int i = 0; i < cStr; i++)
  3513.                     ErrorIf(ERROR_SUCCESS != genRC.OutputString(rgszTables[i], rgszColumns[i]), TEXT("Failed to Export Strings"), false);
  3514.                 ErrorIf(genRC.IsInErrorState(), TEXT("EXPORT STRINGS failed"), true);
  3515.             }
  3516.         }
  3517.         else if ((iMode & iIMPORT_RES) && szDb && szRESDLL)
  3518.         {
  3519.             // import RESOURCE DLL into MSI
  3520.             CImportRes importRes(szDb, (iMode & iCREATE_NEW_DB) ? szSaveDatabase  : NULL, szRESDLL);
  3521.             if (iMode & iDIALOGS)
  3522.             {
  3523.                 // import DIALOGS
  3524.                 if (1 == cDlg && 0 == _tcscmp(TEXT("*"), rgszDialogs[0]))
  3525.                 {
  3526.                     // import all dialogs
  3527.                     ErrorIf(ERROR_SUCCESS != importRes.ImportDialogs(), TEXT("Failed to Import Dialogs Into Database"), true);
  3528.                 }
  3529.                 else
  3530.                 {
  3531.                     // import specified dialogs only
  3532.                     // we'll try every dialog listed so we won't through the error
  3533.                     for (int i = 0; i < cDlg; i++)
  3534.                         ErrorIf(ERROR_SUCCESS != importRes.ImportDialog(rgszDialogs[i]), TEXT("Failed to Import Dialog Into Database"), false);
  3535.                     ErrorIf(importRes.IsInErrorState(), TEXT("IMPORT failed"), true);
  3536.                 }
  3537.             }
  3538.             if (iMode & iSTRINGS)
  3539.             {
  3540.                 // import STRINGS
  3541.                 if (1 == cStr && 0 == _tcscmp(TEXT("*"), rgszTables[0]) && 0 == _tcscmp(TEXT("*"), rgszColumns[0]))
  3542.                 {
  3543.                     // import all strings
  3544.                     ErrorIf(importRes.ImportStrings(), TEXT("IMPORT STRINGS failed"), true);
  3545.                 }
  3546.                 else
  3547.                 {
  3548.                     // import specific strings only
  3549.                     // UNSUPPORTED option
  3550.                     _tprintf(TEXT("IMPORT SPECIFIC STRINGS option is not supported\n"));
  3551.                     Usage();
  3552.                     throw 1;
  3553.                 }
  3554.             }
  3555.         }
  3556.         else
  3557.         {
  3558.             Usage();
  3559.             throw 1;
  3560.         }
  3561.         return 0;
  3562.     }
  3563.     catch (int i)
  3564.     {
  3565.         i;
  3566.         return 1;
  3567.     }
  3568.     catch (...)
  3569.     {
  3570.         _tprintf(TEXT("\n MSILOC: unhandled exception.\n"));
  3571.         return 2;
  3572.     }
  3573.  
  3574. }    // end of main
  3575.  
  3576. #else // RC_INVOKED, end of source code, start of resources
  3577. // resource definition go here
  3578. #endif // RC_INVOKED
  3579. #if 0 
  3580. !endif // makefile terminator
  3581. #endif
  3582.