-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdxPreviewHandler.pas
More file actions
427 lines (379 loc) · 16.9 KB
/
dxPreviewHandler.pas
File metadata and controls
427 lines (379 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/// <summary>
/// Windows işletim sisteminin IPreviewHandler apisini kullanarak döküman önizleme yapılabilmesini sağlayan
/// bileşendir.
/// </summary>
/// <example>
/// Örnek kullanım için aşağıdaki kodu inceleyebilirsiniz
/// <code lang="Delphi">
/// unit Unit1;
///
/// interface
///
/// uses
/// Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
/// Dialogs, ComCtrls, ShellCtrls, ExtCtrls, StdCtrls, dxPreviewHandler, Vcl.FileCtrl, cxGraphics, cxControls,
/// cxLookAndFeels, cxLookAndFeelPainters, cxContainer, cxEdit, dxSkinsCore, dxSkinVS2010, cxGroupBox;
///
/// type
/// TForm1 = class(TForm)
/// Panel1 : TPanel;
/// Panel2 : TPanel;
/// DirectoryListBox1 : TDirectoryListBox;
/// FileListBox1 : TFileListBox;
/// DriveComboBox1 : TDriveComboBox;
/// Splitter1 : TSplitter;
/// Splitter2 : TSplitter;
/// dxPreviewHandler1 : TdxPreviewHandler;
/// procedure FormCreate(Sender: TObject);
/// procedure FileListBox1Click(Sender: TObject);
/// procedure Panel1Resize(Sender: TObject);
/// private
/// public
/// end;
///
/// var
/// Form1: TForm1;
///
/// implementation
///
/// {$R *.dfm}
///
/// procedure TForm1.FormCreate(Sender: TObject);
/// begin
/// DriveComboBox1.DirList := DirectoryListBox1;
/// DirectoryListBox1.FileList := FileListBox1;
/// dxPreviewHandler1.Control := Panel1;
/// end;
///
/// procedure TForm1.FileListBox1Click(Sender: TObject);
/// begin
/// if (FileExists(FileListBox1.FileName, True) = TRUE) then begin
/// dxPreviewHandler1.FileName := FileListBox1.FileName;
/// dxPreviewHandler1.Preview;
/// end;
/// end;
///
/// procedure TForm1.Panel1Resize(Sender: TObject);
/// begin
/// dxPreviewHandler1.Resize;
/// end;
///
/// end.
/// </code>
/// </example>
/// <remarks>
/// Copyright © Uğur PARLAYAN (01.05.2013) Lütfen kaynak belirterek kullanınız.
/// </remarks>
unit dxPreviewHandler;
interface
uses
StdCtrls,
Registry, ComObj, ActiveX, AxCtrls, ShlObj,
Windows, Classes, Controls, SysUtils, Dialogs;
type
/// <summary>
/// Detaylarına şu linkten ulaşabilirsiniz;
/// <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb761818(v=vs.85).aspx" />
/// </summary>
IInitializeWithFile = interface(IUnknown) ['{b7d14566-0509-4cce-a71f-0a554233bd9b}']
function Initialize(pszFilePath: LPWSTR; grfMode: DWORD):HRESULT; stdcall;
end;
/// <summary>
/// Detaylarına şu linkten ulaşabilirsiniz;
/// <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb761810(v=vs.85).aspx" />
/// </summary>
IInitializeWithStream = interface(IUnknown) ['{b824b49d-22ac-4161-ac8a-9916e8fa3f7f}']
function Initialize(pstream: IStream; grfMode: DWORD): HRESULT; stdcall;
end;
/// <summary>
/// Detaylarına şu linkten ulaşabilirsiniz;
/// <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/cc144143(v=vs.85).aspx" />
/// </summary>
IPreviewHandler = interface(IUnknown) ['{8895b1c6-b41f-4c1c-a562-0d564250836f}']
function SetWindow(hwnd: HWND; var RectangleRef: TRect): HRESULT; stdcall;
function SetRect(var RectangleRef: TRect): HRESULT; stdcall;
function DoPreview(): HRESULT; stdcall;
function Unload(): HRESULT; stdcall;
function SetFocus(): HRESULT; stdcall;
function QueryFocus(phwnd: HWND): HRESULT; stdcall;
function TranslateAccelerator(PointerToWindowMessage: MSG): HRESULT; stdcall;
end;
TdxPreviewHandler = class(TComponent)
private
FFileName : string; // Gösterilecek dosyanın yolu ve adı bu değişkende saklanacaktır.
FHandler : IPreviewHandler; // Bu arabirim aracılığıyla api çağırılacaktır.
FControl : TWinControl; // Bu nesne üzerinde önizleme yapılacaktır.
FClassID : String; // Bir TGUID değeri barındırmaktadır.
FKip : String; // PreviewFile prosedüründe kullanılan en son kipi tutar.
/// <summary>
/// PreviewFile prosedürünü tetikleyecek şekilde görüntülenmek istenen dosyanın tam yolunu ve adını vermemizi sağlar.
/// </summary>
procedure SetFileName(const Value: string);
/// <summary>
/// FControl üzerine binecek şekilde belgeyi görüntüler.
/// </summary>
procedure PreviewFile;
/// <summary>
/// Dosyanın uzantısını okur.
/// </summary>
function GetFileExt: String;
/// <summary>
/// Dosya uzantısının Registry'deki Preview Handler'inin sınıf kimliğini okur.
/// </summary>
function GetClassID: String;
protected
public
/// <summary>
/// Klasik Create yordamından farklı olarak nesnenin runtime anında bir preview alanına doğrudan bağlanarak
/// üretilmesini sağlar.
/// </summary>
/// <param name="aOwner">
/// TComponent türünden Parent nesne kastedilmektedir.
/// </param>
/// <param name="aControl">
/// TWinControl türünden bir nesne, mesela TPanel veya (DevEx'in) TcxGroupBox gibi nesneleri kastedilmektedir.
/// </param>
constructor Create(aOwner: TComponent; aControl: TWinControl); overload;
/// <summary>
/// Nesne yok edilirken kullandığı IPreviewHandler bağlantısını da yok etmelidir. IPreviewHandler.Unload
/// metoduyla birlikte nesne yok edilir.
/// </summary>
destructor Destroy; override;
/// <summary>
/// IPreviewHandler.Unload medorunu çağırır.
/// </summary>
procedure Bosalt;
/// <summary>
/// FControl nesnesinin üzerine binecek şekilde belgeyi ekranda görüntülemek için PreviewFile prosedürünü çağırır.
/// </summary>
procedure Preview;
/// <summary>
/// Bileşen dışından da referans azaltımını kullanabilmek için IPreviewHandler._Release metodunu çağırır.
/// </summary>
procedure _DelRef; deprecated; { Önerilmez... }
/// <summary>
/// Dökümanın gösterileceği bölgenin yeniden hesaplanmasında kullanılır.
/// </summary>
/// <remarks>
/// FControl nesnesinin resize olayı tetiklenirken bu prosedür çağırılmalıdır.
/// </remarks>
procedure Resize;
published
/// <summary>
/// Dosyanın uzantısını verir.
/// </summary>
property FileExt : String read GetFileExt;
/// <summary>
/// Dosyanın path'iyle birlikte tam adını verir. Örn: "C:\depo\test.pdf" gibi...
/// </summary>
property FileName : String read FFileName write SetFileName;
/// <summary>
/// Registry'de dosya uzantısını açacak olan Preview Handler nesnesinin sınıf kimliğini verir.
/// </summary>
property ClassID : String read FClassID; // Registry'den okuma işlemini GetClassID fonksiyonu gerçekleştirir. Burada değişken kullanılmasının sebebi Registry trafiğini azaltmak içindir.
/// <summary>
/// Belge bu nesnenin üzerinde görüntülenir.
/// </summary>
property Control : TWinControl read FControl write FControl;
end;
const
E_PREVIEWHANDLER_DRM_FAIL = $86420001;
E_PREVIEWHANDLER_NOAUTH = $86420002;
E_PREVIEWHANDLER_NOTFOUND = $86420003;
E_PREVIEWHANDLER_CORRUPT = $86420004;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('UpDevEx', [TdxPreviewHandler]);
end;
{ TdxPreviewHandler }
constructor TdxPreviewHandler.Create(aOwner: TComponent; aControl: TWinControl);
begin
inherited Create(aOwner);
Control := aControl;
end;
destructor TdxPreviewHandler.Destroy;
begin
{ FHandler interface tarafından halâ kullanıldığı için yok etme işlemi interface tarafından gerçekleştiriliyor... Ayrıca bir destroy, free ve nil sekansına girilmesine gerek yok... }
inherited Destroy;
end;
procedure TdxPreviewHandler.Bosalt;
begin
if (Assigned(FHandler) = TRUE) then begin
FHandler.Unload;
end;
end;
procedure TdxPreviewHandler._DelRef;
begin
if (Assigned(FHandler) = TRUE) then begin
FHandler._Release;
end;
end;
function TdxPreviewHandler.GetClassID: String;
var
aRegistry : TRegistry;
aExtens ,
aRegPath : String;
begin
Result := '';
aRegistry := TRegistry.Create();
try
aRegistry.RootKey := HKEY_CLASSES_ROOT;
aExtens := GetFileExt; { mesela '.pdf' veya '.docx' gibi... }
aRegPath := aExtens + '\shellex\{8895b1c6-b41f-4c1c-a562-0d564250836f}';
if (aRegistry.KeyExists(aRegPath) = TRUE) then begin
aRegistry.Access := KEY_READ; { 32 ve 64 bit windows işletim sistemlerinde registry'e erişim farklı kaynaklardan gerçekleşiyor... }
aRegistry.OpenKey(aRegPath, FALSE);
Result := aRegistry.ReadString(''); { Varsayılan değeri okur. Bu bileşen için bu kadarı yeterlidir... }
aRegistry.CloseKey;
end;
finally
FreeAndNil(aRegistry);
end;
FClassID := Result;
end;
function TdxPreviewHandler.GetFileExt: String;
begin
Result := ExtractFileExt(FFileName);
end;
procedure TdxPreviewHandler.PreviewFile;
var
aGUID : TGUID;
aRect : TRect;
FileInit : IInitializeWithFile;
StreamInit : IInitializeWithStream;
FS : TFileStream;
SA : IStream;
TamponBellek : TMemoryStream;
Mesaj : String;
X: Integer;
begin
FKip := ''; // Başlangıç değeridir.
{ Ön izlemenin yapılacağı konteyner bir nesne yoksa doğrudan çıkılır... }
if (Assigned(FControl) = False) then Exit;
FClassID := GetClassID;
{ Dosya uzantısını açacak bir preview handler ID'si yok demektir... }
if (FClassID = '') then Exit;
{
FHandler oluşturulmuşsa şu an bellekte bir dosya preview olarak gösteriliyor demektir. Bu durumda API 2 yoldan birini kendisi seçecektir;
1) Ya yeni bir FHandler arabirimi üretip yeni belgeyi ona yükleyecek,
2) Ya da mevcut FHandler arabirimindeki belgeyi bellekten kaldırıp yenisini yükleyecek...
IPreviewHandler.Unload metodu bize 2. seçeneği uygulama şansı veriyor. Böylece arka taraftaki Referans sayımlarında
mevcut referansı azaltmakla, yani IPreviewHandler._Release ile uşraşmak zorunda kalmıyoruz. Zira _Release'nin
RefCount değeri 0'a ulaştığında arabirim kendisini destroy ediyor dolayısıyla arabirimi yeniden üretmek
gerekebiliyor, çok karışık o nedenle buraya hiç girmemek en iyi çözüm bence...
Yani özetle unload metodu bellekteki IPreview arabirimini yok etmeden sadece dosya bağlantısını kesip ilgili
dosyanın bellekteki yerini sıfırlıyor ve yeni bir dosya yükleyebilmemize olanak tanıyor.
Bunun için BOSALT adlı bir prosedür ekledim.
}
if Assigned(FHandler) then FHandler.Unload;
aGUID := StringToGUID(FClassID);
FHandler := CreateComObject(aGUID) as IPreviewHandler;
{ İşletim sisteminde Dosya uzantısını açabilecek tanımlı bir Preview Handler "nesnesi" yok demektir... Dolayısıyla çıkıyoruz...
Bu durum daha çok kurulup kaldırıldıktan sonra registry'deki çöplerini temizlemeyen programlar nedeniyle ortaya çıkar... }
if (FHandler = nil) then Exit;
{ Bunu daha çok word ve excel kullanıyor... }
if (FHandler.QueryInterface(IInitializeWithFile, FileInit) = 0) then begin
FileInit.Initialize(StringToOleStr(FFileName), STGM_READ or STGM_SHARE_DENY_NONE);
FKip := 'fileinit';
end;
{ Bu noktada ELSE kullanılmamasının sebebi arabirimin her iki metodu da aynı anda destekleyebilecek şekilde tasarlanmasından kaynaklanıyor olabilir. Test edilmeli... UP }
{ Bunu da daha çok Acrobat Reader kullanıyor... }
if (FHandler.QueryInterface(IInitializeWithStream, StreamInit) = 0) then begin
{ Hem dosya, hem de bellek akışı kullanmamızın tek bir nedeni var. Dosya akışı kullandığımızda işletim sistemi
dosyayı (biz her ne kadar ShareDenyNone desek bile) bir sebepten dolayı işlem gören bir dosya olarak algılıyor,
Bu sorunu aşmanın en basit yolu dosyayı belleğe kopyalayıp bellek kopyası üzerinde işlem yapmak. O nedenle dosyayı
diskten okuduktan sonra doğruadn belleğe alıyoruz ve dosyayı kapatıyoruz. Bu sayede "Dosya başka bir işlem
tarafından kullanılıyor..." gibi bir hata ile muhatap olmuyoruz... Aksi durumda dosyayı kendimiz açtığımız
halde silememe gibi sorunlarla karşılaşırdık...
}
try
try
FS := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyNone); // Dosyayı oku, paylaşım için herhangi bir kilit koyma, diğer programlar da o dosyaya erişebilsin...
if (Assigned(FS) = TRUE) then begin
TamponBellek:= TMemoryStream.Create;
TamponBellek.Clear;
TamponBellek.Size := FS.Size;
TamponBellek.CopyFrom(FS, FS.Size);
end;
finally
FS.Free; { Nihayetinde dosyayı kapat... Dosya diskte dursun, zaten yerini biliyoruz... }
end;
finally
SA := TStreamAdapter.Create(TamponBellek, soOwned) as IStream;
StreamInit.Initialize(SA, STGM_READ or STGM_SHARE_DENY_NONE);
if (FKip <> '')
then FKip := format('%s + %s', [FKip, 'streaminit'])
else FKip := 'streaminit';
end;
end;
{
Acrobat ve MS Office arasında IPreviewHandler arabiriminin kullanımıyla ilgili temel bir farklılık var.
Ofis, yeni bir dosya ekleneceği zaman kendisini kapatıp yeni dosyayla birlikte belleğe yeniden yükleniyor.
Acrobat ise aynı durumda kendisini kapatmıyor, sadece eski dosyayı kapatıp yeni dosyayı yüklüyor.
O nedenle birisi fileinit'i diğeri ise FileStream'i kullanıyor. Yani normalde olması gereken ofisin yöntemi
fakat StreamInit if bloğundaki işlemi bazı programlar kendi içinde değil, bizim yapmamızı yeğliyor. Temel fark aslında bu...
}
ARect := Rect( 0, 0, FControl.Width, FControl.Height);
try
FHandler.SetWindow(FControl.Handle, aRect);
FHandler.SetRect(aRect);
case FHandler.DoPreview of
S_OK : Mesaj := ''; // 'The operation completed successfully.';
E_PREVIEWHANDLER_DRM_FAIL : Mesaj := 'Telif hakları gereği bu dosya dijital haklar yönetimi tarafından engellendi.'; //'Blocked by digital rights management.';
E_PREVIEWHANDLER_NOAUTH : Mesaj := 'Dosya izinleri engellendi.'; // 'Blocked by file permissions.';
E_PREVIEWHANDLER_NOTFOUND : Mesaj := 'Nesne bulunamadı.'; // 'Item was not found.';
E_PREVIEWHANDLER_CORRUPT : Mesaj := 'Önizleme yapan program düzgün yüklenmemiş veya ilgili fonksiyonu bozuk.'#13#10
+ 'Bu noktada ağ yöneticinizden yardım talep edebilirsiniz; Ağ yöneticiniz şunları yapmalıdır;'#13#10#13#10
+ ' 1) İlgili programı bilgisayarınızdan kaldırın (uninstall)'#13#10
+ ' 2) Kaldırma işlemi bittikten sonra Registry kayıtlarını temizleyen programlardan biriyle (CCleaner gibi) temizlik yapın'#13#10
+ ' 3) Kaldırdığınız programın eskiden kurulu olduğu Klasörleri silin (Genelde Program Files klasörünün altında olurlar)'#13#10
+ ' 4) 2. adımı tekrar yapın'#13#10
+ ' 5) Bilgisayarınızı yeniden başlatın'#13#10
+ ' 6) Kaldırdığınız programı sağlıklı bir şekilde yeniden kurun'#13#10
; // 'Item was corrupt.';
end;
if (Mesaj <> '') then begin
ShowMessage('DoPreview : ' + Mesaj);
end;
FHandler._AddRef; { Referans sayımını tetiklemek ve yeni bir dosyanın yüklendiğini IPreviewHandler arabirimine bildirmek için bu şart... }
except
FHandler.Unload;
end;
end;
procedure TdxPreviewHandler.Resize;
var
aRect: TRect;
begin
if (Assigned(FControl) = True) then begin
if (FControl.Visible = TRUE) then begin
if (Assigned(FHandler) = True) then begin
try
aRect := Rect( 0, 0, FControl.Width, FControl.Height);
FHandler.SetWindow(FControl.Handle, aRect);
FHandler.SetRect(aRect);
// FHandler.DoPreview; // gereksiz...
// FHandler.SetFocus; // gereksiz...
if pos('streaminit', FKip) > 0 then FHandler._AddRef; // Şart ! .... Referans sayımını tetiklemek ve yeni bir dosyanın yüklendiğini IPreviewHandler arabirimine bildirmek için bu şart...
except
on E: Exception do ShowMessage('Hata: ' + E.Message);
end;
end;
end;
end;
end;
procedure TdxPreviewHandler.Preview;
begin
PreviewFile;
end;
procedure TdxPreviewHandler.SetFileName(const Value: string);
begin
if not FileExists(Value, True) then exit; // dosya yoksa doğrudan çık...
if Trim(FFileName) = Trim(Value) then Exit; // Aynı şeyi arka arkaya yüklememek içindir...
FFileName := Value;
if (GetClassID <> '')
then PreviewFile // Destekleniyorsa belgeyi ekranda gösterir.
else Bosalt; // Desteklenmiyorsa ekranı ve belleği temizler.
end;
end.