2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/debug.h"
28 #include "wine/unicode.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
32 typedef struct _tagTT_OFFSET_TABLE
{
37 USHORT uEntrySelector
;
41 typedef struct _tagTT_TABLE_DIRECTORY
{
42 char szTag
[4]; /* table name */
43 ULONG uCheckSum
; /* Check sum */
44 ULONG uOffset
; /* Offset from beginning of file */
45 ULONG uLength
; /* length of the table in bytes */
48 typedef struct _tagTT_NAME_TABLE_HEADER
{
49 USHORT uFSelector
; /* format selector. Always 0 */
50 USHORT uNRCount
; /* Name Records count */
51 USHORT uStorageOffset
; /* Offset for strings storage,
52 * from start of the table */
53 } TT_NAME_TABLE_HEADER
;
55 typedef struct _tagTT_NAME_RECORD
{
61 USHORT uStringOffset
; /* from start of storage area */
64 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
65 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
67 static const WCHAR szRegisterFonts
[] =
68 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
69 static const WCHAR szUnregisterFonts
[] =
70 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
72 static const WCHAR regfont1
[] =
73 {'S','o','f','t','w','a','r','e','\\',
74 'M','i','c','r','o','s','o','f','t','\\',
75 'W','i','n','d','o','w','s',' ','N','T','\\',
76 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
77 'F','o','n','t','s',0};
78 static const WCHAR regfont2
[] =
79 {'S','o','f','t','w','a','r','e','\\',
80 'M','i','c','r','o','s','o','f','t','\\',
81 'W','i','n','d','o','w','s','\\',
82 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
83 'F','o','n','t','s',0};
86 * Code based off of code located here
87 * http://www.codeproject.com/gdi/fontnamefromfile.asp
89 * Using string index 4 (full font name) instead of 1 (family name)
91 static LPWSTR
load_ttfname_from(LPCWSTR filename
)
93 TT_TABLE_DIRECTORY tblDir
;
95 TT_OFFSET_TABLE ttOffsetTable
;
96 TT_NAME_TABLE_HEADER ttNTHeader
;
97 TT_NAME_RECORD ttRecord
;
103 handle
= CreateFileW(filename
,GENERIC_READ
, 0, NULL
, OPEN_EXISTING
,
104 FILE_ATTRIBUTE_NORMAL
, 0 );
105 if (handle
== INVALID_HANDLE_VALUE
)
107 ERR("Unable to open font file %s\n", debugstr_w(filename
));
111 if (!ReadFile(handle
,&ttOffsetTable
, sizeof(TT_OFFSET_TABLE
),&dwRead
,NULL
))
114 ttOffsetTable
.uNumOfTables
= SWAPWORD(ttOffsetTable
.uNumOfTables
);
115 ttOffsetTable
.uMajorVersion
= SWAPWORD(ttOffsetTable
.uMajorVersion
);
116 ttOffsetTable
.uMinorVersion
= SWAPWORD(ttOffsetTable
.uMinorVersion
);
118 if (ttOffsetTable
.uMajorVersion
!= 1 || ttOffsetTable
.uMinorVersion
!= 0)
121 for (i
=0; i
< ttOffsetTable
.uNumOfTables
; i
++)
123 if (!ReadFile(handle
,&tblDir
, sizeof(TT_TABLE_DIRECTORY
),&dwRead
,NULL
))
125 if (memcmp(tblDir
.szTag
,"name",4)==0)
128 tblDir
.uLength
= SWAPLONG(tblDir
.uLength
);
129 tblDir
.uOffset
= SWAPLONG(tblDir
.uOffset
);
137 SetFilePointer(handle
, tblDir
.uOffset
, NULL
, FILE_BEGIN
);
138 if (!ReadFile(handle
,&ttNTHeader
, sizeof(TT_NAME_TABLE_HEADER
), &dwRead
,NULL
))
141 ttNTHeader
.uNRCount
= SWAPWORD(ttNTHeader
.uNRCount
);
142 ttNTHeader
.uStorageOffset
= SWAPWORD(ttNTHeader
.uStorageOffset
);
144 for(i
=0; i
<ttNTHeader
.uNRCount
; i
++)
146 if (!ReadFile(handle
,&ttRecord
, sizeof(TT_NAME_RECORD
),&dwRead
,NULL
))
149 ttRecord
.uNameID
= SWAPWORD(ttRecord
.uNameID
);
150 /* 4 is the Full Font Name */
151 if(ttRecord
.uNameID
== 4)
155 static const char tt
[] = " (TrueType)";
157 ttRecord
.uStringLength
= SWAPWORD(ttRecord
.uStringLength
);
158 ttRecord
.uStringOffset
= SWAPWORD(ttRecord
.uStringOffset
);
159 nPos
= SetFilePointer(handle
, 0, NULL
, FILE_CURRENT
);
160 SetFilePointer(handle
, tblDir
.uOffset
+
161 ttRecord
.uStringOffset
+
162 ttNTHeader
.uStorageOffset
,
164 buf
= msi_alloc_zero( ttRecord
.uStringLength
+ 1 + strlen(tt
) );
165 ReadFile(handle
, buf
, ttRecord
.uStringLength
, &dwRead
, NULL
);
169 ret
= strdupAtoW(buf
);
175 SetFilePointer(handle
,nPos
, NULL
, FILE_BEGIN
);
182 TRACE("Returning fontname %s\n",debugstr_w(ret
));
186 static UINT
ITERATE_RegisterFonts(MSIRECORD
*row
, LPVOID param
)
188 MSIPACKAGE
*package
= param
;
196 filename
= MSI_RecordGetString( row
, 1 );
197 file
= get_loaded_file( package
, filename
);
200 ERR("Unable to load file\n");
201 return ERROR_SUCCESS
;
204 /* check to make sure that component is installed */
205 if (!ACTION_VerifyComponentForAction( file
->Component
, INSTALLSTATE_LOCAL
))
207 TRACE("Skipping: Component not scheduled for install\n");
208 return ERROR_SUCCESS
;
211 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont1
,&hkey1
);
212 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont2
,&hkey2
);
214 if (MSI_RecordIsNull(row
,2))
215 name
= load_ttfname_from( file
->TargetPath
);
217 name
= msi_dup_record_field(row
,2);
221 msi_reg_set_val_str( hkey1
, name
, file
->TargetPath
);
222 msi_reg_set_val_str( hkey2
, name
, file
->TargetPath
);
230 uirow
= MSI_CreateRecord( 1 );
231 uipath
= strdupW( file
->TargetPath
);
232 p
= strrchrW(uipath
,'\\');
235 MSI_RecordSetStringW( uirow
, 1, p
);
236 ui_actiondata( package
, szRegisterFonts
, uirow
);
237 msiobj_release( &uirow
->hdr
);
239 /* FIXME: call ui_progress? */
241 return ERROR_SUCCESS
;
244 UINT
ACTION_RegisterFonts(MSIPACKAGE
*package
)
248 static const WCHAR ExecSeqQuery
[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','F','o','n','t','`',0};
252 rc
= MSI_DatabaseOpenViewW(package
->db
, ExecSeqQuery
, &view
);
253 if (rc
!= ERROR_SUCCESS
)
255 TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc
);
256 return ERROR_SUCCESS
;
259 MSI_IterateRecords(view
, NULL
, ITERATE_RegisterFonts
, package
);
260 msiobj_release(&view
->hdr
);
262 return ERROR_SUCCESS
;
265 static UINT
ITERATE_UnregisterFonts( MSIRECORD
*row
, LPVOID param
)
267 MSIPACKAGE
*package
= param
;
275 filename
= MSI_RecordGetString( row
, 1 );
276 file
= get_loaded_file( package
, filename
);
279 ERR("Unable to load file\n");
280 return ERROR_SUCCESS
;
283 /* check to make sure that component is uninstalled */
284 if (!ACTION_VerifyComponentForAction( file
->Component
, INSTALLSTATE_ABSENT
))
286 TRACE("Skipping: Component not scheduled for uninstall\n");
287 return ERROR_SUCCESS
;
290 RegCreateKeyW( HKEY_LOCAL_MACHINE
, regfont1
, &hkey1
);
291 RegCreateKeyW( HKEY_LOCAL_MACHINE
, regfont2
, &hkey2
);
293 if (MSI_RecordIsNull( row
, 2 ))
294 name
= load_ttfname_from( file
->TargetPath
);
296 name
= msi_dup_record_field( row
, 2 );
300 RegDeleteValueW( hkey1
, name
);
301 RegDeleteValueW( hkey2
, name
);
305 RegCloseKey( hkey1
);
306 RegCloseKey( hkey2
);
309 uirow
= MSI_CreateRecord( 1 );
310 uipath
= strdupW( file
->TargetPath
);
311 p
= strrchrW( uipath
,'\\' );
314 MSI_RecordSetStringW( uirow
, 1, p
);
315 ui_actiondata( package
, szUnregisterFonts
, uirow
);
316 msiobj_release( &uirow
->hdr
);
318 /* FIXME: call ui_progress? */
320 return ERROR_SUCCESS
;
323 UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
327 static const WCHAR query
[] =
328 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
329 '`','F','o','n','t','`',0};
331 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
332 if (r
!= ERROR_SUCCESS
)
334 TRACE("MSI_DatabaseOpenViewW failed: %u\n", r
);
335 return ERROR_SUCCESS
;
338 MSI_IterateRecords( view
, NULL
, ITERATE_UnregisterFonts
, package
);
339 msiobj_release( &view
->hdr
);
341 return ERROR_SUCCESS
;