Форум программистов, компьютерный форум, киберфорум
Visual Basic
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.80/41: Рейтинг темы: голосов - 41, средняя оценка - 4.80
Вопрощающий

Создание (+распаковка) CAB архива

11.10.2009, 19:26. Показов 10837. Ответов 130

Студворк — интернет-сервис помощи студентам
Обращаясь к cabinet.dll, без использования *.exe
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
11.10.2009, 19:26
Ответы с готовыми решениями:

Создание архива
Есть такая строка .AddAttachment "C:\logfiles.rar" При выполнении кода на ней ошибка. Как создать программно этот архив?

Программное создание архива.
Добрый день! Можноли программно сделать архив, например, ZIP или rar, не важно и добавить в него файлы? Заранее спасибо.

Создание архива с паролем
Private Declare Sub Sleep Lib "kernel32" (ByVal milliseconds As Long) Private Declare Function OpenProcess Lib "Kernel32.dll" (ByVal...

130
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
22.11.2024, 02:24
Студворк — интернет-сервис помощи студентам
В VBA cdecl будет работать только на кулебяках )
0
Модератор
10048 / 3894 / 883
Регистрация: 22.02.2013
Сообщений: 5,847
Записей в блоге: 79
22.11.2024, 10:53
testuser2, кстати на x64 должно работать, т.к. там только 1 соглашение вызова.
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
22.11.2024, 12:20
Надстройку же не подключишь в VBA

Добавлено через 37 минут
testuser2, а у fafalone вот так вот это кстати описано в его библиотеке для твинбейсика:

Visual Basic
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
Public Enum FdiFciTcompValues
    tcompMASK_TYPE = &H000F  ' Mask for compression type
    tcompTYPE_NONE = &H0000  ' No compression
    tcompTYPE_MSZIP = &H0001  ' MSZIP
    tcompTYPE_QUANTUM = &H0002  ' Quantum
    tcompTYPE_LZX = &H0003  ' LZX
    tcompBAD = &H000F  ' Unspecified compression type
    tcompMASK_LZX_WINDOW = &H1F00  ' Mask for LZX Compression Memory
    tcompLZX_WINDOW_LO = &H0F00  ' Lowest LZX Memory (15)
    tcompLZX_WINDOW_HI = &H1500  ' Highest LZX Memory (21)
    tcompSHIFT_LZX_WINDOW = 8  ' Amount to shift over to get int
    tcompMASK_QUANTUM_LEVEL = &H00F0  ' Mask for Quantum Compression Level
    tcompQUANTUM_LEVEL_LO = &H0010  ' Lowest Quantum Level (1)
    tcompQUANTUM_LEVEL_HI = &H0070  ' Highest Quantum Level (7)
    tcompSHIFT_QUANTUM_LEVEL = 4  ' Amount to shift over to get int
    tcompMASK_QUANTUM_MEM = &H1F00  ' Mask for Quantum Compression Memory
    tcompQUANTUM_MEM_LO = &H0A00  ' Lowest Quantum Memory (10)
    tcompQUANTUM_MEM_HI = &H1500  ' Highest Quantum Memory (21)
    tcompSHIFT_QUANTUM_MEM = 8  ' Amount to shift over to get int
    tcompMASK_RESERVED = &HE000&  ' Reserved bits (high 3 bits)
End Enum
 
' #define CompressionTypeFromTCOMP(tc) \
' ((tc) & tcompMASK_TYPE)
Public Function CompressionTypeFromTCOMP([TypeHint(FdiFciTcompValues)] ByVal tc As Integer) As Integer
    Return (tc) And CInt(tcompMASK_TYPE)
End Function
    
 
' #define CompressionLevelFromTCOMP(tc) \
' (((tc) & tcompMASK_QUANTUM_LEVEL) >> tcompSHIFT_QUANTUM_LEVEL)
Public Function CompressionLevelFromTCOMP([TypeHint(FdiFciTcompValues)] ByVal tc As Integer) As Integer
    Return (((tc) And CInt(tcompMASK_QUANTUM_LEVEL)) >> CInt(tcompSHIFT_QUANTUM_LEVEL))
End Function
    
' #define CompressionMemoryFromTCOMP(tc) \
' (((tc) & tcompMASK_QUANTUM_MEM) >> tcompSHIFT_QUANTUM_MEM)
Public Function CompressionMemoryFromTCOMP([TypeHint(FdiFciTcompValues)] ByVal tc As Integer) As Integer
   Return (((tc) And CInt(tcompMASK_QUANTUM_MEM)) >> CInt(tcompSHIFT_QUANTUM_MEM))
End Function
 
' #define TCOMPfromTypeLevelMemory(t,l,m)           \
' (((m) << tcompSHIFT_QUANTUM_MEM  ) |  \
 ' ((l) << tcompSHIFT_QUANTUM_LEVEL) |  \
 ' ( t                             ))
 Public Function TCOMPfromTypeLevelMemory(ByVal t As Integer, ByVal l As Integer, ByVal m As Integer) As Integer
    Return (((m) << CInt(tcompSHIFT_QUANTUM_MEM)) Or ((l) << CInt(tcompSHIFT_QUANTUM_LEVEL)) Or (t))
 End Function
 
' #define LZXCompressionWindowFromTCOMP(tc) \
' (((tc) & tcompMASK_LZX_WINDOW) >> tcompSHIFT_LZX_WINDOW)
Public Function LZXCompressionWindowFromTCOMP([TypeHint(FdiFciTcompValues)] ByVal tc As Integer) As Integer
    Return (((tc) And CInt(tcompMASK_LZX_WINDOW)) >> CInt(tcompSHIFT_LZX_WINDOW))
End Function
' #define TCOMPfromLZXWindow(w)      \
' (((w) << tcompSHIFT_LZX_WINDOW ) |  \
 ' ( tcompTYPE_LZX ))
Public Function TCOMPfromLZXWindow(ByVal w As Integer) As Integer
    Return (((w) << CInt(tcompSHIFT_LZX_WINDOW)) Or (CInt(tcompTYPE_LZX)))
End Function
Посмотри на функцию кстати
Visual Basic
1
2
3
Public Function TCOMPfromLZXWindow(ByVal w As Integer) As Integer
    Return (((w) << CInt(tcompSHIFT_LZX_WINDOW)) Or (CInt(tcompTYPE_LZX)))
End Function
Но она всё равно только для Твина. Как и у тебя почти.
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
22.11.2024, 15:10
Я не пойму зачем у уважаемого fafalone столько скобок, и явных приведений к integer. Вообще если обрамлять скобками аргумент функции, то это какбы неявное приведение к ByVal, не знаю, правда, как это работает в выражениях, но я стараюсь просто всегда избегать лишних скобок. Небольшой тестик
Visual Basic
1
2
3
4
5
6
7
8
9
10
Sub fsdfsfa()
    Dim s$, v
    s = "ssfsf"
    v = s
    
    Debug.Print VarPtr(s); VarPtr((s))  '10610576  10610540
    Debug.Print StrPtr(s); StrPtr((s))  '447799780  447799780
    Debug.Print VarPtr(v); VarPtr((v))  '10610560  10610516
    Debug.Print StrPtr(v); StrPtr((v))  '449929380  449929380
End Sub
Как бы видно, что при добавлении скобок переменная меняется, но ссылка на данные остается прежней

Добавлено через 6 минут
Тут еще нюанс, что VarPtr работает как библиотечная функция, и там принцип передачи аргументов немного иной нежели в VB-функция. Если передать аргумент в скобках в VB-фнукцию будет полное копирование и переменной и данных

Добавлено через 50 минут
Скобки это привидение к варианту, вот это что, это также как cVar()
1
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
22.11.2024, 15:18
Цитата Сообщение от testuser2 Посмотреть сообщение
Я не пойму зачем у уважаемого fafalone столько скобок, и явных приведений к integer.
Я тоже этого не понимаю, если честно.

Добавлено через 28 секунд
testuser2, твой код мне больше понравился, чем фафалоновский, твоя функция лучше, мне больше нравится
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
22.11.2024, 15:22
Цитата Сообщение от HackerVlad Посмотреть сообщение
твоя функция лучше
Она просто чуть помощьнее, так практически тоже самое, поэтоу я не считаю ее лучше
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
22.11.2024, 15:30
Ну красивее же код у тебя, согласись, ну

Visual Basic
1
2
3
4
5
6
Private Const tcompSHIFT_LZX_WINDOW = 8
Private Const tcompTYPE_LZX  = 3
 
Private Sub TCOMPfromLZXWindow(w&)
    w = (w << tcompSHIFT_LZX_WINDOW) Or tcompTYPE_LZX
End Sub
Добавлено через 27 секунд
Хотя у тебя процедура, а не функция, у тебя немного по другому.
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
22.11.2024, 15:33
Это шутка конечно, еслиу у него какие-то свои заморочки то наверняка в этом какой-то смысл, а может быть это следы автопреобразования кода, может быть у него автопарсер из Си в VB
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
22.11.2024, 15:57
ааааа с функцией же легче работать, чем с процедурой, значит у фафалона всё-таки код лучше, хотя зачем так много скобок там и ещё и Integer тоже не понимаю этого совсем

Добавлено через 38 секунд
а давай проверим тогда в Твине, чем гадать

Добавлено через 8 минут
Короче проверил, его функция работает так же как и твоя процедура. Тоже самое значение возвращает. Попробовал так же убрать первую и последнею лишнею скобку тоже всё работает после этого прекрасно.

Добавлено через 2 минуты
Я вообще не понимаю зачем у него ТАК МНОГО скобок лишних, я их короче поубирал все, к чёртовой матери, вот теперь так:

Visual Basic
1
2
3
Public Function TCOMPfromLZXWindow2(ByVal w As Integer) As Integer
        Return w << CInt(tcompSHIFT_LZX_WINDOW) Or CInt(tcompTYPE_LZX)
    End Function
Фафалоновская функция переделанная мною тоже работает! Поудалял 500 скобок лишних.

Добавлено через 29 секунд
И вопрос зачем там CInt тоже не понимаю, если честно.

Добавлено через 24 секунды
Пожалуй, надо фафалону мозги покомпосировать по этому поводу)

Добавлено через 8 минут
Я проверил можно упростить даже вот так:

Visual Basic
1
2
3
Public Function TCOMPfromLZXWindow2(ByVal w As Long) As Long
        Return w << tcompSHIFT_LZX_WINDOW Or tcompTYPE_LZX
    End Function
Выставил претензию фафалону короче.

Добавлено через 2 минуты
testuser2, https://www.vbforums.com/showt... ost5663272
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
22.11.2024, 16:22
Цитата Сообщение от HackerVlad Посмотреть сообщение
Выставил претензию фафалону короче.
Ахах, докопался же ты до него ) Наверняка он парсит заголовочники, у меня тоже есть такая мысль, сделать парсер. Но если у fafalone есть все декларации в WinDevLib, то смысл такого парсера отпадает, поскольку можно все брать от туда. Вообще, заголовочники конечно дельная вещь в си. Я как-то смотрел обзорный видос о преимуществах С++ и среди прочего там выдвигалось - сравнительная простота портирования кода для разных битностей. Там ведь большую часть несостыковок типов x86/x64 решают заголовочники. Однакож обилие алиасов там просто пугающее..
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
22.11.2024, 16:28
Цитата Сообщение от testuser2 Посмотреть сообщение
Однакож обилие алиасов там просто пугающее..
это точно, vb6 всяко проще будет.
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 07:55
Мой алгоритм кстати не идеален, для создания CAB с несколькими файлами внутри что-то у меня там не так как надо работает. Вроде всё норм, в проводнике винды и в новом TotalCommander вроде нормально всё отображается, а вот в старой версии TotalCommander отображается неправильно уже содержимое архива, если несколько файлов, то оно почему на второй, третитй и т.д. файлы уже почему-то добавляет в самом начале файла плюсик (символ "+") хотя такого просто быть не должно... баг значит у меня где-то в загаловочниках может... ну такой полу-баг... в новых версиях-то всё работает...
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 07:59
Вот такой вот баг получается, что добавляет символ плюса зачем-то. И эти папки и/или файлы, с символом плюса, уже не читаются потом... В старой версии тотал коммандера, у меня. В новой всё норм отображается и всё читается как надо.
Миниатюры
Создание (+распаковка) CAB архива  
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 10:19
Где-то я накосячил в коде может. А самые новые версии умеют распознавать и игнорировать такие косяки...

Добавлено через 1 час 35 минут
Ура! Я нашёл где косяк. Чтобы кэбы читались везде-везде хорошо, нужно по другому определять изначальные значения структуры ccab для FCICreate. За основу я теперь взял уже пример из MSDN: https://learn.microsoft.com/ru... -a-cabinet
Лучше всего всегда переписывать из примеров от Microsoft конечно же, чтобы избегать косяков... А до этого я переписывал у немцев с Delphi отсюда: https://www.viathinksoft.com/codelib/206
Вообще я и был вдохновлён, этой статьёй у немцев, этим готовым кодом на Delphi, а оказывается косяки там...
Так же я был вдохновлён статьёй где рассказывалось как обманывать технологию сжатия/распаковки не в файлы а в IStream в память, вот здесь: https://habr.com/ru/articles/314832/

Добавлено через 8 минут
Кто бы мог подумать, у немцев ошибка в коде была, поэтому когда я переписывал функцию с Delphi то и у меня тоже такой же косяк поэтому получился.

Добавлено через 1 минуту
А всего-то они забыли объявить cbFolderThresh =
И только из-за этого в старых версиях архиваторов CAB не читалось как надо, и было с глюками

Добавлено через 3 минуты
Вот я переписывал этот немецкий код с Delphi:

Delphi
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
procedure CabinetAddFiles(Cabinet: AnsiString; Files: TStrings);
var
  fci: THandle;
  erf: TERF;
  ccab: TCCAB;
  i: Integer;
begin
  ZeroMemory(@erf, sizeof(erf));
  ZeroMemory(@ccab, sizeof(ccab));
 
  ccab.cb := $7FFFFFFF {2GB}; // "the maximum size, in bytes, of a cabinet created by FCI"
  ccab.iDisk := 1;
  {$IFDEF USE_ANSISTRINGS}AnsiStrings.{$ENDIF}StrPCopy(ccab.szDisk, PAnsiChar(AnsiString('DISK1')));
  {$IFDEF USE_ANSISTRINGS}AnsiStrings.{$ENDIF}StrPCopy(ccab.szCab, PAnsiChar(AnsiString(ExtractFileName(Cabinet))));
  {$IFDEF USE_ANSISTRINGS}AnsiStrings.{$ENDIF}StrPCopy(ccab.szCabPath, PAnsiChar(AnsiString(ExtractFilePath(Cabinet))));
 
  // use a callback function to build the context
  fci := FCICreate(erf, @fnFilePlaced, @fnAlloc, @fnFree,
    @fnOpen, @fnRead, @fnWrite, @fnClose, @fnSeek, @fnDelete,
    @fnFciGTF, ccab, nil);
  if fci <> 0 then
  try
    for i := 0 to Files.Count-1 do
    begin                                                    
      if not FCIAddFile(fci, PAnsiChar(AnsiString(Files[i])), PAnsiChar(AnsiString(ExtractFileName(Files[i]))), FALSE{Execute},
        @fnGetNextCabinet, @fnStatus, @fnOpenInfo, Ord(tcompTYPE_MSZIP)) then
      begin
        raise Exception.CreateFmt('FCIAddFile %d', [erf.erfOper]);
      end;
    end;
 
    if FCIFlushCabinet(fci, FALSE, @fnGetNextCabinet, @fnStatus) = FALSE then
    begin
      raise Exception.CreateFmt('FCIFlushCabinet %d', [erf.erfOper]);
    end;
  finally
    // dispose of used context
    FCIDestroy(fci);
  end;
end;
Обратите внимание на то, что здесь нет в структуре ccab присвоения cbFolderThresh. И только из-за этого и был косяк.

Добавлено через 5 минут
Чтобы это исправить, нужно просто в определении структуры добавить:

Visual Basic
1
ccab.cbFolderThresh = &H7FFFFFFF
Добавлено через 17 минут
Я исправил ошибки в коде!

Добавлено через 7 минут
А немцы переписывали у китайцев, этот код, кстати, так что изначально могли ошибку допустить китайцы... А теперь я русский переписал код у немцев, которые переписали у китайцев... А лучше всего конечно всего переписывать из MSDN...
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
23.11.2024, 10:20
Цитата Сообщение от HackerVlad Посмотреть сообщение
А немцы переписывали у китайцев
Как все хитро запутано )
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 10:22
Цитата Сообщение от testuser2 Посмотреть сообщение
Как все хитро запутано )
Ну ни говори) Все друг у друга слизывают)

Добавлено через 12 секунд
И ошибки тоже...
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 10:35
Лучший ответ Сообщение было отмечено HackerVlad как решение

Решение

Теперь вот новая версия модуля, без ошибок уже:

Visual Basic
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
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
Option Explicit
'////////////////////////////////////////////
'// Модуль упаковки CAB-архивов            //
'// Copyright (c) 23.11.2024 by HackerVlad //
'// e-mail: vladislavpeshkov@yandex.ru     //
'// Версия 1.3                             //
'////////////////////////////////////////////
 
' Декларации API ...
Private Declare Function FCICreate CDecl Lib "cabinet.dll" (perf As TERF, ByVal fnFilePlaced As Long, ByVal fnAlloc As Long, ByVal fnFree As Long, ByVal fnOpen As Long, ByVal fnRead As Long, ByVal fnWrite As Long, ByVal fnClose As Long, ByVal fnSeek As Long, ByVal fnDelete As Long, ByVal fnFciGTF As Long, ByVal ccab As Long, Optional ByVal pv As Long) As Long
Private Declare Function FCIAddFile CDecl Lib "cabinet.dll" (ByVal hfci As Long, ByVal pszSourceFile As Long, ByVal pszFileName As Long, ByVal fExecute As BOOL, ByVal pfnGetNextCabinet As Long, ByVal pfnProgress As Long, ByVal pfnOpenInfo As Long, ByVal typeCompress As Long) As Long
Private Declare Function FCIFlushCabinet CDecl Lib "cabinet.dll" (ByVal hfci As Long, ByVal fGetNextCab As BOOL, ByVal pfnfcignc As Long, ByVal pfnfcis As Long) As BOOL
Private Declare Function FCIDestroy CDecl Lib "cabinet.dll" (ByVal hfci As Long) As BOOL
Private Declare Function SHCreateMemStream Lib "shlwapi.dll" Alias "#12" (ByVal pInit As Long, ByVal cbInit As Long) As Long
Private Declare Function PathStripPathW Lib "shlwapi.dll" (ByVal pszPath As Long) As Long
Private Declare Function PathRemoveFileSpecW Lib "shlwapi.dll" (ByVal pszPath As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)
Private Declare Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Private Declare Function GlobalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetFileAttributes Lib "kernel32" Alias "GetFileAttributesA" (ByVal lpFileName As Long) As Long
Private Declare Function GetFileTime Lib "kernel32" (ByVal hFile As Long, lpCreationTime As FILETIME, lpLastAccessTime As FILETIME, lpLastWriteTime As FILETIME) As Long
Private Declare Function FileTimeToLocalFileTime Lib "kernel32" (lpFileTime As FILETIME, lpLocalFileTime As FILETIME) As Long
Private Declare Function FileTimeToDosDateTime Lib "kernel32" (lpFileTime As FILETIME, lpFatDate As Integer, lpFatTime As Integer) As Long
Private Declare Function SetFilePointer Lib "kernel32" (ByVal hFile As Long, ByVal lDistanceToMove As Long, lpDistanceToMoveHigh As Long, ByVal dwMoveMethod As Long) As Long
Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As BOOL
Private Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As Long, ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long, ByVal lpOverlapped As Any) As BOOL
Private Declare Function CreateFileA Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function GetMem4 Lib "msvbvm60" (ByVal Addr As Long, ByRef dstValue As Long) As Long
 
' Константы ...
Private Const CB_MAX_DISK_NAME = 256
Private Const CB_MAX_CABINET_NAME = 256
Private Const CB_MAX_CAB_PATH = 256
Private Const OFS_MAXPATHNAME = 128
Private Const GENERIC_READ As Long = &H80000000
Private Const GENERIC_WRITE As Long = &H40000000
Private Const FILE_SHARE_READ = &H1
Private Const OPEN_EXISTING As Long = 3
Private Const FILE_ATTRIBUTE_ARCHIVE = &H20
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Const CREATE_ALWAYS = 2
Private Const MAX_PATH As Long = 260
Private Const tcompTYPE_MSZIP = &H1&
Private Const tcompTYPE_LZX = &H3& ' 0x0003
Private Const tcompLZX_WINDOW_LO = &HF00& ' 0x0F00
Private Const tcompLZX_WINDOW_HI = &H1500& ' 0x1500
 
' Типы ...
Private Type TCCAB
    cb As Long ' size available for cabinet on this media
    cbFolderThresh As Long ' Thresshold for forcing a new Folder
    cbReserveCFHeader As Long ' Space to reserve in CFHEADER
    cbReserveCFFolder As Long ' Space to reserve in CFFOLDER
    cbReserveCFData As Long ' Space to reserve in CFDATA
    iCab As Long ' sequential numbers for cabinets
    iDisk As Long ' Disk number
    fFailOnIncompressible As Long ' TRUE => Fail if a block is incompressible
    setID As Integer ' Cabinet set ID
    szDisk(0 To (CB_MAX_DISK_NAME - 1)) As Byte ' current disk name
    szCab(0 To (CB_MAX_CABINET_NAME - 1)) As Byte ' current cabinet name
    szCabPath(0 To (CB_MAX_CAB_PATH - 1)) As Byte ' path for creating cabinet
End Type
 
Private Type TERF
    erfOper As Long
    erfType As Long
    fError As Byte
End Type
 
Private Type OFSTRUCT
    cBytes As Byte
    fFixedDisk As Byte
    nErrCode As Integer
    Reserved1 As Integer
    Reserved2 As Integer
    szPathName(0 To (OFS_MAXPATHNAME - 1)) As Byte
End Type
 
Private Type FILETIME
  dwLowDateTime As Long
  dwHighDateTime As Long
End Type
 
' Переменные для временного хранения данных ...
Dim fh As Long
Dim fh_cab As Long
Dim cabFileName As String
 
' Енумы ...
Private Enum BOOL
    cFalse
    cTrue
End Enum
 
Private Enum Stream_Seek
    STREAM_SEEK_SET
    STREAM_SEEK_CUR
    STREAM_SEEK_END
End Enum
 
Public Enum cabCompressionMethod
    cm_MSZIP = tcompTYPE_MSZIP
    cm_LZX15 = tcompTYPE_LZX Or tcompLZX_WINDOW_LO
    cm_LZX16 = &H1003&
    cm_LZX17 = &H1103&
    cm_LZX18 = &H1203&
    cm_LZX19 = &H1303&
    cm_LZX20 = &H1403&
    cm_LZX21 = tcompTYPE_LZX Or tcompLZX_WINDOW_HI
End Enum
 
' Для совместимости с TwinBasic и VBA7
#If (VBA7 <> 0) Or (TWINBASIC <> 0) Then
    Private Declare PtrSafe Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As LongPtr, ByVal lCc As Long, ByVal vtReturn As VbVarType, ByVal cActuals As Long, prgVt As Any, prgpVarg As Any, pvargResult As Variant) As Long
#Else
    Private Declare Function DispCallFunc Lib "oleaut32" (ByVal pvInstance As LongPtr, ByVal oVft As LongPtr, ByVal lCc As Long, ByVal vtReturn As VbVarType, ByVal cActuals As Long, prgVt As Any, prgpVarg As Any, pvargResult As Variant) As Long
    
    Private Enum LongPtr
        [_]
    End Enum
#End If
 
Private Function DispCallByVtbl(ByVal pUnk As LongPtr, ByVal lIndex As Long, ParamArray A() As Variant) As Variant
    Const CC_STDCALL    As Long = 4
#If Win64 Then
    Const PTR_SIZE      As Long = 8
#Else
    Const PTR_SIZE      As Long = 4
#End If
    Dim lIdx            As Long
    Dim vParam()        As Variant
    Dim vType(0 To 63)  As Integer
    Dim vPtr(0 To 63)   As LongPtr
    Dim hResult         As Long
    
    vParam = A
    For lIdx = 0 To UBound(vParam)
        vType(lIdx) = VarType(vParam(lIdx))
        vPtr(lIdx) = VarPtr(vParam(lIdx))
    Next
    hResult = DispCallFunc(pUnk, lIndex * PTR_SIZE, CC_STDCALL, vbLong, lIdx, vType(0), vPtr(0), DispCallByVtbl)
    If hResult < 0 Then
        Err.Raise hResult, "DispCallFunc"
    End If
End Function
 
Private Function IStream_Read(ByVal ptrIStream As Long, ByVal pv As Long, ByVal BytesRead As Long) As Long
    Dim BytesReaded As Long
    
    DispCallByVtbl ptrIStream, 3, pv, BytesRead, VarPtr(BytesReaded)
    IStream_Read = BytesReaded
End Function
 
Private Function IStream_Write(ByVal ptrIStream As Long, ByVal pv As Long, ByVal BytesWrite As Long) As Long
    Dim BytesWritten As Long
    
    DispCallByVtbl ptrIStream, 4, pv, BytesWrite, VarPtr(BytesWritten)
    IStream_Write = BytesWritten
End Function
 
Private Function IStream_Seek(ByVal ptrIStream As Long, ByVal Offset As Currency, ByVal Origin As Stream_Seek) As Long
    Dim NewPosition As Currency
    
    DispCallByVtbl ptrIStream, 5, Offset, Origin, VarPtr(NewPosition)
    IStream_Seek = NewPosition * 10000@
End Function
 
Private Sub IStream_Release(ByVal ptrIStream As Long)
    DispCallByVtbl ptrIStream, 2
End Sub
 
' +++ FCICreate CallBack's +++
 
' 1. Выделение памяти
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnalloc
' Delphi: function fnAlloc(Size: ULONG): Pointer; cdecl;
Private Function fnAlloc CDecl(ByVal lngSize As Long) As Long
    fnAlloc = GlobalAlloc(0, lngSize)
End Function
 
' 2. Создание временного файла (потока)
' Delphi: function fnFciGTF(pszTempName: PAnsiChar; cbTempName: Integer; pv: Pointer): BOOL; cdecl;
Private Function fnFciGTF CDecl(ByRef pszTempName As Long, ByVal cbTempName As Long, ByVal pv As Long) As BOOL
    ' Специальный хак:
    ' Обманываем операционную систему, которая будет "думать", что работает с TMP-файлами на диске
    ' Вместо временного файла на диске, мы будем создавать поток IStream в оперативной памяти своего процесса
    Dim hStream As Long
    
    hStream = SHCreateMemStream(0, 0) ' Создать новый поток IStream для временного файла
    pszTempName = hStream ' Здесь мы будем использовать хак: засовываем в переменную String значение Long
    fnFciGTF = 1
End Function
 
' 3. Открытие файла (потока)
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnopen
' Delphi: function fnOpen(pszFile: PAnsiChar; oflag: Integer; pmode: Integer; err: PInteger; pv: Pointer): Integer; cdecl;
Private Function fnOpen CDecl(ByRef pszFile As Long, ByVal oFlag As Long, ByVal pMode As Long, ByRef ErrNo As Long, ByVal pv As Long) As Long
    If oFlag <> &H8302& Then ' Хак
        fnOpen = pszFile
    Else
        fh_cab = CreateFileW(StrPtr(cabFileName), GENERIC_WRITE, FILE_SHARE_READ, ByVal 0&, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, 0)
        
        If fh_cab <> INVALID_HANDLE_VALUE Then
            ErrNo = Err.LastDllError
            fnOpen = fh_cab
        Else
            ErrNo = Err.LastDllError
            fnOpen = -1
        End If
    End If
End Function
 
' 4. Чтение данных
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnread
' Delphi: function fnRead(hf: Integer; memory: Pointer; cb: UINT; err: PInteger; pv: Pointer): UINT; cdecl;
Private Function fnRead CDecl(ByVal hf As Long, ByVal hMemory As Long, ByVal cbSize As Long, ByRef ErrNo As Long, ByVal pv As Long) As Long
    Dim dwBytesRead As Long
    
    If hf = fh Then ' Если открывается на чтение файл, который добавляется в архив
        If ReadFile(fh, hMemory, cbSize, dwBytesRead, ByVal 0&) = cFalse Then
            ErrNo = Err.LastDllError
            fnRead = -1
            Exit Function
        End If
    Else ' Если открывается на чтение временный поток IStream
        dwBytesRead = IStream_Read(hf, hMemory, cbSize)
    End If
    
    fnRead = dwBytesRead
End Function
 
' 5. Запись данных
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnwrite
' Delphi: function fnWrite(hf: Integer; memory: Pointer; cb: UINT; err: PInteger; pv: Pointer): UINT; cdecl;
Private Function fnWrite CDecl(ByVal hf As Long, ByVal hMemory As Long, ByVal cbSize As Long, ByRef ErrNo As Long, ByVal pv As Long) As Long
    Dim dwBytesWritten As Long
    
    If hf = fh_cab Then ' Если открывается на запись файл архива
        If WriteFile(fh_cab, hMemory, cbSize, dwBytesWritten, ByVal 0&) = cFalse Then
            ErrNo = Err.LastDllError
            fnWrite = -1
            Exit Function
        End If
    Else ' Если открывается на запись временный поток IStream
        dwBytesWritten = IStream_Write(hf, hMemory, cbSize)
    End If
    
    fnWrite = dwBytesWritten
End Function
 
' 6. Освобождение памяти
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnfree
' Delphi: procedure fnFree(memory: Pointer); cdecl;
Private Sub fnFree CDecl(ByVal lngMemory As Long)
    GlobalFree lngMemory
End Sub
 
' 7. Позиционирование указателя
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnseek
' Delphi: function fnSeek(hf: Integer; dist: Longint; seektype: Integer; err: PInteger; pv: Pointer): Longint; cdecl;
Private Function fnSeek CDecl(ByVal hf As Long, ByVal dist As Long, ByVal seektype As Long, ByRef ErrNo As Long, pv As Long) As Long
    Dim newPos As Long
    
    If hf = fh Or hf = fh_cab Then ' Если открывается на позиционирование файл, который добавляется в архив, либо сам файл архива
        newPos = SetFilePointer(hf, dist, ByVal 0&, seektype)
        ErrNo = Err.LastDllError
    Else ' Позиционирование "временного файла" то есть потока
        newPos = IStream_Seek(hf, dist / 10000@, seektype)
    End If
    
    fnSeek = newPos
End Function
 
' 8. Закрытие файла (потока)
' Описание макроса: https://learn.microsoft.com/en-us/windows/win32/api/fdi/nf-fdi-fnclose
' Delphi: function fnClose(hf: Integer; err, pv: Pointer): Integer; cdecl;
Private Function fnClose CDecl(ByVal hf As Long, ErrNo As Long, pv As Long) As Long
    If hf = fh Or hf = fh_cab Then ' Если закрывать нужно файл, который добавляется в архив, либо нужно закрывать сам файл архива
        CloseHandle hf
    End If
    
    fnClose = 0
End Function
 
' 9. Удаление временного файла (потока)
' Delphi: function fnDelete(pszFile: PAnsiChar; err: PInteger; pv: Pointer): Integer; cdecl;
Private Function fnDelete CDecl(ByRef pszFile As Long, ErrNo As Long, pv As Long) As Long
    IStream_Release pszFile
    fnDelete = 0
End Function
 
' 10. Вызывается каждый раз при добавлении нового файла в архив
' Delphi: function fnFilePlaced(var ccab: TCCAB; pszFile: PAnsiChar; cbFile: Longint; fContinuation: BOOL; pv: Pointer): THandle; cdecl;
Private Function fnFilePlaced CDecl(ccab As TCCAB, ByVal pszFile As String, ByVal FileSize As Long, ByVal fContinuation As BOOL, ByVal pv As Long) As Long
    ' Здесь можно получить полезные данные:
    ' 1. FileSize
    ' 2. StrConv(ccab.szCabPath, vbUnicode)
    ' 3. StrConv(ccab.szCab, vbUnicode)
    
    fnFilePlaced = 0
End Function
 
' --- FCICreate CallBack's ---
 
' +++ FCIAddFile CallBack's +++
 
' 11. Устанавливаем атрибуты файла
' Delphi: function fnOpenInfo(pszName: PAnsiChar; var pDate: WORD; var pTime: WORD; var pAttrib: WORD; err: PInteger; pv: Pointer): Integer; cdecl;
' Syntax C++
' ;;    void FNFCIGETOPENINFO(
' ;;      [in]  LPSTR pszName,
' ;;      USHORT *pdate,
' ;;      USHORT *ptime,
' ;;      USHORT *pattribs,
' ;;      int FAR *err,
' ;;      void FAR *pv
' ;;    );
Private Function fnOpenInfo CDecl(ByVal pszName As String, pDate As Integer, pTime As Integer, pAttribs As Integer, ErrNo As Long, ByVal pv As Long) As Long
    Dim LocalTime As FILETIME
    Dim CreationTime As FILETIME
    Dim LastAccessTime As FILETIME
    Dim LastWriteTime As FILETIME
    
    pAttribs = GetFileAttributes(StrPtr(pszName))
    fh = CreateFileA(StrPtr(pszName), GENERIC_READ, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
    
    If fh <> INVALID_HANDLE_VALUE Then
        GetFileTime fh, CreationTime, LastAccessTime, LastWriteTime
        FileTimeToLocalFileTime LastWriteTime, LocalTime
        FileTimeToDosDateTime LocalTime, pDate, pTime
        
        fnOpenInfo = fh
    Else
        ErrNo = Err.LastDllError
        fnOpenInfo = -1
    End If
End Function
 
' 12. Вызывается на нескольких этапах обработки файла: сжатие блока, добавление сжатого блока и запись архива
' Delphi: function fnStatus(typeStatus: UINT; cb1, cb2: ULONG; pv: Pointer): Longint; cdecl;
Private Function fnStatus CDecl(ByVal typeStatus As Long, ByVal cb1 As Long, ByVal cb2 As Long, ByVal pv As Long) As Long
    fnStatus = 0
End Function
 
' 13. Вызывается перед созданием нового тома архива
' Delphi: function fnGetNextCabinet(var ccab: TCCAB; cbPrevCab: ULONG; pv: Pointer): BOOL; cdecl;
Private Function fnGetNextCabinet CDecl(ccab As TCCAB, ByVal cbPrevCab As Long, ByVal pv As Long) As BOOL
    fnGetNextCabinet = 0
End Function
 
' --- FCIAddFile CallBack's ---
 
' Упаковать файлы в архив CAB
' Функция принимает в качестве параметров SourceFullFileNames, DestFileNames строку (если файл один) либо массив строк (список файлов)
' DestFileNames - это необязательный параметр, это путь и имя файла внутри архива CAB
Public Function CabinetAddFiles(ByVal CabinetFullFileName As String, SourceFullFileNames As Variant, Optional DestFileNames As Variant, Optional CompressionMethod As cabCompressionMethod = cm_LZX21) As Boolean
    Dim ccab As TCCAB
    Dim erf As TERF
    Dim fci As Long
    Dim CabinetName As String
    Dim CabinetPath As String
    Dim AnsiSourceFileName As String
    Dim AnsiExtractFileName As String
    Dim AnsiDestinationFileName As String
    Dim AnsiSourceFullFileNames() As String
    Dim AnsiDestFileNames() As String
    Dim DestFileNamesArrayInitialized As Boolean
    Dim i As Long
    
    If Len(CabinetFullFileName) = 0 Then Exit Function
    
    If IsArray(SourceFullFileNames) Then ' Если это массив
        If CabinetIsArrayInitialized(SourceFullFileNames) = True Then ' Если массив инициализирован
            For i = 0 To UBound(SourceFullFileNames)
                AnsiSourceFileName = StrConv(SourceFullFileNames(i), vbFromUnicode) ' Преобразовать в ANSI
                
                If InStrB(1, AnsiSourceFileName, ChrB(&H3F)) > 0 Then
                    ' Cabinet.dll не поддерживает юникодные имена файлов для упаковки
                    Exit Function
                End If
                
                ' Копируем массив, только в результатирующем массиве будут имена файлов в кодировке ANSI
                CabinetInsertArrayString AnsiSourceFullFileNames, AnsiSourceFileName
            Next
        Else
            Exit Function
        End If
    Else
        If VarType(SourceFullFileNames) = vbString Then
            If SourceFullFileNames <> vbNullString Then
                AnsiSourceFileName = StrConv(SourceFullFileNames, vbFromUnicode) ' Преобразовать в ANSI
                
                If InStrB(1, AnsiSourceFileName, ChrB(&H3F)) > 0 Then
                    ' Cabinet.dll не поддерживает юникодные имена файлов для упаковки
                    Exit Function
                End If
                
                CabinetInsertArrayString AnsiSourceFullFileNames, AnsiSourceFileName ' В массиве будет только одна строка
            Else ' Строка не должна быть пустой
                Exit Function
            End If
        Else ' Ошибка типа данных (не массив и не строка)
            Exit Function
        End If
    End If
    
    If IsArray(DestFileNames) Then
        If CabinetIsArrayInitialized(SourceFullFileNames) = True Then ' Если массив инициализирован
            If UBound(SourceFullFileNames) <> UBound(DestFileNames) Then Exit Function ' Не совпадают границы массивов
            
            For i = 0 To UBound(DestFileNames)
                AnsiDestinationFileName = StrConv(DestFileNames(i), vbFromUnicode) ' Преобразовать в ANSI
                
                If InStrB(1, AnsiDestinationFileName, ChrB(&H3F)) > 0 Then
                    ' Cabinet.dll не поддерживает юникодные имена файлов для упаковки
                    Exit Function
                End If
                
                ' Копируем массив, только в результатирующем массиве будут имена файлов в кодировке ANSI
                CabinetInsertArrayString AnsiDestFileNames, AnsiDestinationFileName
            Next
            
            DestFileNamesArrayInitialized = True
        End If
    Else
        If VarType(DestFileNames) = vbString Then
            If DestFileNames <> vbNullString Then
                AnsiDestinationFileName = StrConv(DestFileNames, vbFromUnicode) ' Преобразовать в ANSI
                
                If InStrB(1, AnsiDestinationFileName, ChrB(&H3F)) > 0 Then
                    ' Cabinet.dll не поддерживает юникодные имена файлов для упаковки
                    Exit Function
                End If
                
                CabinetInsertArrayString AnsiDestFileNames, AnsiDestinationFileName ' В массиве будет только одна строка
                DestFileNamesArrayInitialized = True
            End If
        End If
    End If
    
    ' Прежде всего нужно взять FullFileName будущего архива и извлечь из него путь к папке и имя файла
    CabinetName = StrConv(CabinetExtractFileName(CabinetFullFileName), vbFromUnicode) ' Преобразовать в ANSI
    CabinetPath = StrConv(CabinetExtractFilePath(CabinetFullFileName), vbFromUnicode) ' Преобразовать в ANSI
    
    ' Определить значения структуры (переписываем из MSDN: https://learn.microsoft.com/ru-ru/windows/win32/devnotes/creating-a-cabinet)
    ccab.cb = &H7FFFFFFF ' Максимальный размер создаваемого архива будет 2 Гб (увеличим на максимум по сравнению с примером из MSDN)
    ccab.cbFolderThresh = &H7FFFFFFF ' Важно! Если этого не написать, то старые версии архиваторов CAB будут неправильно читать архив
    ccab.setID = 555
    ccab.iCab = 1
    ccab.iDisk = 0
    
    CopyMemory VarPtr(ccab.setID) + 2 + 256, StrPtr(CabinetName), LenB(CabinetName) ' ccab.szCab = CabinetName
    CopyMemory VarPtr(ccab.setID) + 2 + 512, StrPtr(CabinetPath), LenB(CabinetPath) ' ccab.szCabPath = CabinetPath
    cabFileName = CabinetFullFileName ' Запомнить FileName будущего архива
    
    fci = FCICreate(erf, AddressOf fnFilePlaced, AddressOf fnAlloc, AddressOf fnFree, AddressOf fnOpen, AddressOf fnRead, AddressOf fnWrite, AddressOf fnClose, AddressOf fnSeek, AddressOf fnDelete, AddressOf fnFciGTF, VarPtr(ccab))
    
    If fci <> 0 Then
        For i = 0 To UBound(AnsiSourceFullFileNames)
            AnsiSourceFileName = AnsiSourceFullFileNames(i)
            If DestFileNamesArrayInitialized = True Then
                AnsiExtractFileName = AnsiDestFileNames(i)
            Else
                If IsArray(SourceFullFileNames) Then ' Если это массив
                    AnsiExtractFileName = StrConv(CabinetExtractFileName(SourceFullFileNames(i)), vbFromUnicode) ' Преобразовать в ANSI
                Else
                    AnsiExtractFileName = StrConv(CabinetExtractFileName(SourceFullFileNames), vbFromUnicode) ' Преобразовать в ANSI
                End If
            End If
            
            FCIAddFile fci, StrPtr(AnsiSourceFileName), StrPtr(AnsiExtractFileName), 0, AddressOf fnGetNextCabinet, AddressOf fnStatus, AddressOf fnOpenInfo, CompressionMethod
        Next
        
        If FCIFlushCabinet(fci, cFalse, AddressOf fnGetNextCabinet, AddressOf fnStatus) = cTrue Then
            CabinetAddFiles = True
        End If
        
        FCIDestroy fci
    End If
    
    cabFileName = vbNullString
End Function
 
' Преобразовать полный путь кабинета в имя файла
Public Function CabinetExtractFileName(ByVal FileName As String) As String
    Dim lNullPos As Long
    Dim pszPath As String
    
    pszPath = FileName
    PathStripPathW StrPtr(pszPath)
    
    lNullPos = InStr(1, pszPath, vbNullChar)
    If lNullPos Then
        CabinetExtractFileName = Left$(pszPath, lNullPos - 1)
    Else
        CabinetExtractFileName = FileName
    End If
End Function
 
' Преобразовать полный путь кабинета в путь к папке (всегда возвращает на конце "\")
Public Function CabinetExtractFilePath(ByVal FileName As String) As String
    Dim lNullPos As Long
    Dim pszPath As String
    
    pszPath = FileName
    PathRemoveFileSpecW StrPtr(pszPath)
    
    lNullPos = InStr(1, pszPath, vbNullChar)
    If lNullPos Then
        pszPath = Left$(pszPath, lNullPos - 1)
        If Right$(pszPath, 1) <> "\" Then pszPath = pszPath & "\"
        CabinetExtractFilePath = pszPath
    Else
        CabinetExtractFilePath = FileName
    End If
End Function
 
' Добавить строку в массив, в не зависимости от того, был ли он инициализирован
Public Sub CabinetInsertArrayString(ByRef strArr() As String, ByVal InsertString As String)
    Dim NewIndex As Long
    
    If CabinetIsArrayInitialized(strArr) = False Then
        ReDim strArr(0)
        strArr(0) = InsertString
    Else
        NewIndex = UBound(strArr) + 1
        
        ReDim Preserve strArr(NewIndex)
        strArr(NewIndex) = InsertString
    End If
End Sub
 
' Инициализирован ли массив
Public Function CabinetIsArrayInitialized(arr) As Boolean
    Dim saAddress As Long
    
    GetMem4 VarPtr(arr) + 8, saAddress
    GetMem4 saAddress, saAddress
    CabinetIsArrayInitialized = (saAddress <> 0)
    If CabinetIsArrayInitialized Then CabinetIsArrayInitialized = UBound(arr) >= LBound(arr)
End Function
1
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
23.11.2024, 22:03
testuser2, кстати fafalone выложил 64-битную версию модуля, как раз для тебя))) ты же любишь 64))) я его даже не просил он сам решил написать 64 битно-совместимую версию) Вот здесь посмотри: https://www.vbforums.com/showt... ost5663417

Особенно забавно какие комментарии он оставил к моему модулю, мне больше всего понравилась фраза "Некоторые из использованных вами хаков немного свели меня с ума..." ахахахахах

Добавлено через 3 минуты
Будет ли только в VBA7 работать это большой вопрос конечно, проверять надо. Особенно непонятно как там будет себя чувствовать CDecl.
0
1386 / 842 / 91
Регистрация: 08.02.2017
Сообщений: 3,579
Записей в блоге: 1
24.11.2024, 05:56
Цитата Сообщение от HackerVlad Посмотреть сообщение
выложил 64-битную версию модуля, как раз для тебя)))
Это не для меня, 64 бит это актуальная сейчас платформа, все десктопы сейчас 64битные.
Цитата Сообщение от HackerVlad Посмотреть сообщение
Будет ли только в VBA7 работать
Нет конечно эта строка "#If VBA7" вообще не актуальна. Да и зачем на VBA CAB. CAB это установочные файлы для программ, это чисто для компилируемых программ. Хотя не знаю, вроде на Access как-то компилят. Там ошибка кстати в секции VBA7, кстати
Visual Basic
1
Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long)
Length должна быть LongPtr
Хотя я замечал в Твин-бейские такую странность там в vbaCopyBytes размер именно Long, хотя по сути, в среде x64 это урезает его возможности, больше 2 гигов не скопируешь
Visual Basic
1
Public DeclareWide PtrSafe Function vbaCopyBytes Lib "<hiddenmodule>" Alias "#16" (ByVal length As Long, ByVal dest As LongPtr, ByVal src As LongPtr) As LongPtr
0
Испарился
 Аватар для HackerVlad
1741 / 637 / 45
Регистрация: 10.09.2021
Сообщений: 2,769
24.11.2024, 12:19
Цитата Сообщение от testuser2 Посмотреть сообщение
Там ошибка кстати в секции VBA7, кстати
Код же писал фафалон, а не я. Мне так всё равно вообще на ваши 64 бита.

Добавлено через 1 минуту
CAB всё равно не поддерживает файлы больше 2 Гб, так что нафига там копировать LongPtr таких больших данных быть не может просто
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
24.11.2024, 12:19
Помогаю со студенческими работами здесь

Создание установочного архива для своих любимых программ
Многие новички в программировании рано или поздно создают программы или игры, которыми хотят поделиться с другими пользователями или своими...

Распаковка архива rar без сохранения файла во временную папку
В архиве rar хранится сжатый (с паролем) файл txt, как его распаковать (пароль известен) чтобы файл не записывался на диск, а содержимому...

Создание архива с паролем средствами PB. Возможно?
Есть ли какая то библиотека что позволяет запихнуть PB кодом файлы в архив с паролем? Ну и соответственно извлекать из архива. Спасибо.

Создание cab архива
Здравствуйте! Помогите реализовать код создания cab архива с помощью CabinetAPI Код из msdn не получается скомпилировать, не понимаю...

Создание папки с датой в имени и распаковка в неё архива
Есть папка на диске С:\Arhiv в нем есть архивы по датам! надо что бы брал самый последний архив по дате и распаковывал в корень С:\Arhiv\...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
SDL3 для Android: Загрузка PNG с альфа-каналом с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru