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"
29 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
37 USHORT uEntrySelector
;
41 struct table_directory
43 char szTag
[4]; /* table name */
44 ULONG uCheckSum
; /* Check sum */
45 ULONG uOffset
; /* Offset from beginning of file */
46 ULONG uLength
; /* length of the table in bytes */
49 struct name_table_header
51 USHORT uFSelector
; /* format selector. Always 0 */
52 USHORT uNRCount
; /* Name Records count */
53 USHORT uStorageOffset
; /* Offset for strings storage from start of the table */
56 #define NAME_ID_FULL_FONT_NAME 4
57 #define NAME_ID_VERSION 5
66 USHORT uStringOffset
; /* from start of storage area */
69 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
70 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
73 * Code based off of code located here
74 * http://www.codeproject.com/gdi/fontnamefromfile.asp
76 static WCHAR
*load_ttf_name_id( MSIPACKAGE
*package
, const WCHAR
*filename
, DWORD id
)
78 struct table_directory tblDir
;
80 struct offset_table ttOffsetTable
;
81 struct name_table_header ttNTHeader
;
82 struct name_record ttRecord
;
89 handle
= msi_create_file( package
, filename
, GENERIC_READ
, 0, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
);
91 handle
= CreateFileW( filename
, GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, 0 );
92 if (handle
== INVALID_HANDLE_VALUE
)
94 ERR("Unable to open font file %s\n", debugstr_w(filename
));
98 if (!ReadFile(handle
,&ttOffsetTable
, sizeof(struct offset_table
),&dwRead
,NULL
))
101 ttOffsetTable
.uNumOfTables
= SWAPWORD(ttOffsetTable
.uNumOfTables
);
102 ttOffsetTable
.uMajorVersion
= SWAPWORD(ttOffsetTable
.uMajorVersion
);
103 ttOffsetTable
.uMinorVersion
= SWAPWORD(ttOffsetTable
.uMinorVersion
);
105 if ((ttOffsetTable
.uMajorVersion
!= 1 || ttOffsetTable
.uMinorVersion
!= 0) &&
106 (ttOffsetTable
.uMajorVersion
!= 0x4f54 || ttOffsetTable
.uMinorVersion
!= 0x544f))
109 for (i
=0; i
< ttOffsetTable
.uNumOfTables
; i
++)
111 if (!ReadFile(handle
, &tblDir
, sizeof(tblDir
), &dwRead
, NULL
))
113 if (memcmp(tblDir
.szTag
,"name",4)==0)
116 tblDir
.uLength
= SWAPLONG(tblDir
.uLength
);
117 tblDir
.uOffset
= SWAPLONG(tblDir
.uOffset
);
125 SetFilePointer(handle
, tblDir
.uOffset
, NULL
, FILE_BEGIN
);
126 if (!ReadFile(handle
, &ttNTHeader
, sizeof(ttNTHeader
), &dwRead
, NULL
))
129 ttNTHeader
.uNRCount
= SWAPWORD(ttNTHeader
.uNRCount
);
130 ttNTHeader
.uStorageOffset
= SWAPWORD(ttNTHeader
.uStorageOffset
);
131 for(i
=0; i
<ttNTHeader
.uNRCount
; i
++)
133 if (!ReadFile(handle
, &ttRecord
, sizeof(ttRecord
), &dwRead
, NULL
))
136 ttRecord
.uNameID
= SWAPWORD(ttRecord
.uNameID
);
137 ttRecord
.uPlatformID
= SWAPWORD(ttRecord
.uPlatformID
);
138 ttRecord
.uEncodingID
= SWAPWORD(ttRecord
.uEncodingID
);
139 if (ttRecord
.uNameID
== id
&& ttRecord
.uPlatformID
== 3 &&
140 (ttRecord
.uEncodingID
== 0 || ttRecord
.uEncodingID
== 1))
145 ttRecord
.uStringLength
= SWAPWORD(ttRecord
.uStringLength
);
146 ttRecord
.uStringOffset
= SWAPWORD(ttRecord
.uStringOffset
);
147 SetFilePointer(handle
, tblDir
.uOffset
+ ttRecord
.uStringOffset
+ ttNTHeader
.uStorageOffset
,
149 if (!(buf
= calloc(ttRecord
.uStringLength
, sizeof(WCHAR
)))) goto end
;
151 ReadFile(handle
, buf
, ttRecord
.uStringLength
, &dwRead
, NULL
);
152 if (dwRead
% sizeof(WCHAR
))
157 for (i
= 0; i
< dwRead
/ sizeof(WCHAR
); i
++) buf
[i
] = SWAPWORD(buf
[i
]);
169 static WCHAR
*font_name_from_file( MSIPACKAGE
*package
, const WCHAR
*filename
)
171 WCHAR
*name
, *ret
= NULL
;
173 if ((name
= load_ttf_name_id( package
, filename
, NAME_ID_FULL_FONT_NAME
)))
177 WARN("empty font name\n");
181 ret
= malloc( wcslen( name
) * sizeof(WCHAR
) + sizeof( L
" (TrueType)" ) );
182 lstrcpyW( ret
, name
);
183 lstrcatW( ret
, L
" (TrueType)" );
189 WCHAR
*msi_get_font_file_version( MSIPACKAGE
*package
, const WCHAR
*filename
)
191 WCHAR
*version
, *p
, *q
, *ret
= NULL
;
193 if ((version
= load_ttf_name_id( package
, filename
, NAME_ID_VERSION
)))
195 int len
, major
= 0, minor
= 0;
196 if ((p
= wcschr( version
, ';' ))) *p
= 0;
198 while (*p
&& !iswdigit( *p
)) p
++;
199 if ((q
= wcschr( p
, '.' )))
201 major
= wcstol( p
, NULL
, 10 );
203 while (*q
&& iswdigit( *q
)) q
++;
204 if (!*q
|| *q
== ' ') minor
= wcstol( p
, NULL
, 10 );
207 len
= lstrlenW( L
"%u.%u.0.0" ) + 20;
208 ret
= malloc( len
* sizeof(WCHAR
) );
209 swprintf( ret
, len
, L
"%u.%u.0.0", major
, minor
);
215 static UINT
ITERATE_RegisterFonts(MSIRECORD
*row
, LPVOID param
)
217 MSIPACKAGE
*package
= param
;
226 filename
= MSI_RecordGetString( row
, 1 );
227 file
= msi_get_loaded_file( package
, filename
);
230 WARN("unable to find file %s\n", debugstr_w(filename
));
231 return ERROR_SUCCESS
;
233 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
236 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
237 return ERROR_SUCCESS
;
239 comp
->Action
= msi_get_component_action( package
, comp
);
240 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
242 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
243 return ERROR_SUCCESS
;
246 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts" ,&hkey1
);
247 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2
);
249 if (MSI_RecordIsNull(row
,2))
250 name
= font_name_from_file( package
, file
->TargetPath
);
252 name
= msi_dup_record_field(row
,2);
256 msi_reg_set_val_str( hkey1
, name
, file
->TargetPath
);
257 msi_reg_set_val_str( hkey2
, name
, file
->TargetPath
);
265 uirow
= MSI_CreateRecord( 1 );
266 uipath
= wcsdup( file
->TargetPath
);
267 p
= wcsrchr(uipath
,'\\');
270 MSI_RecordSetStringW( uirow
, 1, p
);
271 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
272 msiobj_release( &uirow
->hdr
);
274 /* FIXME: call msi_ui_progress? */
276 return ERROR_SUCCESS
;
279 UINT
ACTION_RegisterFonts(MSIPACKAGE
*package
)
284 if (package
->script
== SCRIPT_NONE
)
285 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"RegisterFonts");
287 rc
= MSI_DatabaseOpenViewW(package
->db
, L
"SELECT * FROM `Font`", &view
);
288 if (rc
!= ERROR_SUCCESS
)
289 return ERROR_SUCCESS
;
291 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterFonts
, package
);
292 msiobj_release(&view
->hdr
);
296 static UINT
ITERATE_UnregisterFonts( MSIRECORD
*row
, LPVOID param
)
298 MSIPACKAGE
*package
= param
;
307 filename
= MSI_RecordGetString( row
, 1 );
308 file
= msi_get_loaded_file( package
, filename
);
311 WARN("unable to find file %s\n", debugstr_w(filename
));
312 return ERROR_SUCCESS
;
314 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
317 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
318 return ERROR_SUCCESS
;
320 comp
->Action
= msi_get_component_action( package
, comp
);
321 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
323 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
324 return ERROR_SUCCESS
;
327 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts", &hkey1
);
328 RegCreateKeyW( HKEY_LOCAL_MACHINE
, L
"Software\\Microsoft\\Windows\\CurrentVersion\\Fonts", &hkey2
);
330 if (MSI_RecordIsNull( row
, 2 ))
331 name
= font_name_from_file( package
, file
->TargetPath
);
333 name
= msi_dup_record_field( row
, 2 );
337 RegDeleteValueW( hkey1
, name
);
338 RegDeleteValueW( hkey2
, name
);
342 RegCloseKey( hkey1
);
343 RegCloseKey( hkey2
);
346 uirow
= MSI_CreateRecord( 1 );
347 uipath
= wcsdup( file
->TargetPath
);
348 p
= wcsrchr( uipath
,'\\' );
351 MSI_RecordSetStringW( uirow
, 1, p
);
352 MSI_ProcessMessage(package
, INSTALLMESSAGE_ACTIONDATA
, uirow
);
353 msiobj_release( &uirow
->hdr
);
355 /* FIXME: call msi_ui_progress? */
357 return ERROR_SUCCESS
;
360 UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
365 if (package
->script
== SCRIPT_NONE
)
366 return msi_schedule_action(package
, SCRIPT_INSTALL
, L
"UnregisterFonts");
368 r
= MSI_DatabaseOpenViewW( package
->db
, L
"SELECT * FROM `Font`", &view
);
369 if (r
!= ERROR_SUCCESS
)
370 return ERROR_SUCCESS
;
372 r
= MSI_IterateRecords( view
, NULL
, ITERATE_UnregisterFonts
, package
);
373 msiobj_release( &view
->hdr
);