I have a C library that is badly documented. With PEview, I can see the exports, but not the number (and type) of arguments. So I googled a bit, and it seems consensus is "you'll have to disassemble it".
The odd thing is that my linker knows exactly the type and number of arguments - judging from the error messages.
Example:
UnzipFile LABEL NEAR
call UnzipFile
Gets assembled & linked, so the linker recognises the existence of UnzipFile
UnzipFile PROTO
call UnzipFile
Linker error unresolved external symbol '_UnzipFile@0'
UnzipFile PROTO :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
invoke UnzipFile, 1, 2, 3, 4, 5
Linker error unresolved external symbol '_UnzipFile@20'
So how does the linker recognise that definitions in the assembler source don't match those in the library? And can we get the info from the library?
C calling convention? "UnzipFile PROTO C ..."
Quote from: qWord on May 21, 2014, 11:04:52 PM
C calling convention? "UnzipFile PROTO C ..."
That makes the _ and @nn disappear, but the linker still does not recognise it... I tried 4 and 6 args, too, no success.
The source is Pascal (from here (http://sources.ru/pascal/archives/unziptmt.htm)), and calls it like this:
rc := unzipfile ( thename, buf, r.headeroffset, 0,
{$ifdef Windows}vk_escape{$else}27{$endif} ); {Escape interrupts}
IF rc = unzip_ok THEN
writeln ( 'Ok' )
Example:
FUNCTION UnzipFile conv arg_stdcall
( in_name : pchar;out_name : pchar;offset : longint;hFileAction : word;cm_index : integer ) : integer;
{$ENDIF}
{usage:
in_name: name of zip file with full path
out_name: desired name for out file
offset: header position of desired file in zipfile
hFileAction: handle to dialog box showing advance of decompression (optional)
cm_index: notification code sent in a wm_command message to the dialog
to update percent-bar
Return value: one of the above unzip_xxx codes
I can get this to assemble & link & run: CloseZipFile PROTO SYSCALL :DWORD
invoke CloseZipFile, 1
... but
only for Polink. MS Link complains with
fatal error LNK1115: /MACHINE option required, in spite of the fact that I do supply /MACHINE:X86
If I change that to :X86
J, MS Link suddenly acknowledges that a /MACHINE option was indeed specified, and throws
LINKV9 : warning LNK4012: invalid value 'X86J', must be 'ARM, EBC, IA64, MIPS, MIPS16, MIPSFPU, MIPSFPU16, SH4, THUMB, X64, or X86'; option ignored
LINKV9 : fatal error LNK1115: /MACHINE option required
Which looks really stupid (greetings to Richmond).
SYSCALL seems to work, though:
include \masm32\include\masm32rt.inc
includelib unzipw.lib
.code
start:
mov ebx, esp
sub ebx, esp
CloseZipFile PROTO SYSCALL :DWORD
; int 3
push 0
invoke CloseZipFile, esp
pop ecx
print str$(eax), " retval", 13, 10
print str$(ebx), " stack difference"
exit
end start
deleted
Seems like that something is wrong with import library - I would simple build a new one using polib...
have a look at msvcrt.inc....
c_msvcrt typedef PROTO C :VARARG
externdef _imp__printf:PTR c_msvcrt
crt_printf equ <_imp__printf>
Quote from: nidud on May 22, 2014, 12:03:42 AM
UnzipFile PROTO PASCAL :DWORD, :DWORD, :DWORD, :DWORD, :DWORD
No success...
Quote from: dedndave on May 22, 2014, 02:08:46 AM
have a look at msvcrt.inc....
That's all VARARG and C, while the unzip functions are apparently stdcall and not C.
I found two workarounds - SYSCALL with polink is the only solution that works:
includelib LiteUnZip.lib
UnzipOpenFileA PROTO SYSCALL :DWORD, :DWORD, :DWORD
.code
LiteUnzip proc filename
LOCAL hLite
mov esi, esp
if 1
push 0 ; works fine
push filename
lea eax, hLite
push eax
call UnzipOpenFileA
else
invoke UnzipOpenFileA, addr hLite, filename, 0 ; works fine, too
sub esp, 3*4 ; stack needs "negative balancing" here!
endif
sub esi, esp
deb 4, "Result & stack difference", eax, hLite, esi
ret
LiteUnzip endp@qWord: Thanks, but this lib is indeed built with polib...
i see a couple possibilities
1) have a look at Erol's Dll2Inc readme files and examples
i tried that, and it creates an import library and an INC :t
2) doubtful, but COM interface ?
Hi Jochen,
Analyzing the import library :
\masm32\bin\dumpbin.exe /EXPORTS unzipw.lib
ChfUnzip_Init
CloseZipFile
FileUnzip
FileUnzipEx
GetFirstInZip
GetNextInZip
GetSupportedMethods
GetUnzipDllVersion
IsZip
SetNoRecurseDirs
SetUnzipQuestionProc
SetUnzipReportProc
UnzipFile
UnzipSize
ViewZip
No any decoration information. My solution is to use a custom invoke macro not checking the number of parameters :
include \masm32\include\masm32rt.inc
include invoke.inc
includelib unzipw.lib
ChfUnzip_Init PROTO SYSCALL
CloseZipFile PROTO SYSCALL
FileUnzip PROTO SYSCALL
FileUnzipEx PROTO SYSCALL
GetFirstInZip PROTO SYSCALL
GetNextInZip PROTO SYSCALL
GetSupportedMethods PROTO SYSCALL
GetUnzipDllVersion PROTO SYSCALL
IsZip PROTO SYSCALL
SetNoRecurseDirs PROTO SYSCALL
SetUnzipQuestionProc PROTO SYSCALL
SetUnzipReportProc PROTO SYSCALL
UnzipFile PROTO SYSCALL
UnzipSize PROTO SYSCALL
ViewZip PROTO SYSCALL
.data
test1 db 'test',0
.data?
.code
start:
invoke ExitProcess,0
_invoke SetUnzipQuestionProc,1,2,3
_invoke SetUnzipReportProc,1,ADDR test1,ADDR test1,4
_invoke FileUnzip
_invoke IsZip,1
END start
As I don't know the prototypes of the zip functions, I wrote some dummy function calls above.
Jochen, Erol,
Quote from: Vortex on May 22, 2014, 04:22:46 AM
As I don't know the prototypes of the zip functions, I wrote some dummy function calls above.
That's quite a fumbling. What about building it again?
Gunther
Hi Jochen,
Reading the document in :
http://sources.ru/pascal/archives/unziptmt.htm
Here is my attempt to create a .def and .lib file :
UnZipDll.def :
LIBRARY UnZipDll
EXPORTS
"_FileUnzip@20"
"_FileUnzipEx@12"
"_ViewZip@12"
"_SetUnZipReportProc@4"
"_SetUnZipQuestionProc@4"
"_UnzipSize@8"
"_ChfUnzip_Init@0"
"_SetNoRecurseDirs@4"
"_UnzipFile@20"
"_GetFirstInZip@8"
"_GetNextInZip@4"
"_IsZip@4"
"_CloseZipFile@4"
"_GetSupportedMethods@0"
\masm32\bin\polib /OUT:UnZipDll.lib /DEF:UnZipDll.def /MACHINE:x86
Could you try this one?
Hi Jochen,
Checking this project :
http://www.codeproject.com/Articles/13370/LiteZip-and-LiteUnzip
Here is my include file :
; include file generated by lib2inc V2.2
UnzipClose PROTO :DWORD
UnzipFindItemA PROTO :DWORD,:DWORD,:DWORD
UnzipFindItem equ <UnzipFindItemA>
UnzipFindItemW PROTO :DWORD,:DWORD,:DWORD
UnzipFormatMessageA PROTO :DWORD,:DWORD,:DWORD
UnzipFormatMessage equ <UnzipFormatMessageA>
UnzipFormatMessageW PROTO :DWORD,:DWORD,:DWORD
UnzipGetItemA PROTO :DWORD,:DWORD
UnzipGetItem equ <UnzipGetItemA>
UnzipGetItemW PROTO :DWORD,:DWORD
UnzipItemToBuffer PROTO :DWORD,:DWORD,:DWORD,:DWORD
UnzipItemToFileA PROTO :DWORD,:DWORD,:DWORD
UnzipItemToFile equ <UnzipItemToFileA>
UnzipItemToFileW PROTO :DWORD,:DWORD,:DWORD
UnzipItemToHandle PROTO :DWORD,:DWORD,:DWORD
UnzipOpenBuffer PROTO :DWORD,:DWORD,:DWORD,:DWORD
UnzipOpenBufferRaw PROTO :DWORD,:DWORD,:DWORD,:DWORD
UnzipOpenFileA PROTO :DWORD,:DWORD,:DWORD
UnzipOpenFile equ <UnzipOpenFileA>
UnzipOpenFileRawA PROTO :DWORD,:DWORD,:DWORD
UnzipOpenFileRaw equ <UnzipOpenFileRawA>
UnzipOpenFileRawW PROTO :DWORD,:DWORD,:DWORD
UnzipOpenFileW PROTO :DWORD,:DWORD,:DWORD
UnzipOpenHandle PROTO :DWORD,:DWORD,:DWORD
UnzipOpenHandleRaw PROTO :DWORD,:DWORD,:DWORD
UnzipSetBaseDirA PROTO :DWORD,:DWORD
UnzipSetBaseDir equ <UnzipSetBaseDirA>
UnzipSetBaseDirW PROTO :DWORD,:DWORD
i don't think that last one jives, Erol
different library, perhaps ?
the one Jochen is working with is (C) fPrint UK Ltd, 1998
is that info-zip, Jochen ????
Hi Dave,
If I am not wrong, UnzipOpenFileA is a member of the LiteUnzip library.
Thanks a lot, Erol and Dave :icon14:
It is actually Jeff Glatt's LiteZip library (http://www.codeproject.com/Articles/13370/LiteZip-and-LiteUnzip):
QuoteThis project is largely based upon work by Lucian Wischik, who in turn based his work on gzip 1.1.4, zlib, and info-zip which are by Jean-Loup Gailly and Mark Adler. Lucian's code has been reworked to be written in plain C, using only the Win32 API, and packaged into 2 DLLs. (Also some improvements to error-checking, some added functionality, and code-reduction/stream-lining was accomplished.)
With your help I found a way:
- \masm32\bin\dll2inc liteunzip.dll
- edit LiteUnZip.inc: delete all leading underscores (dll2inc has no options?)
- \masm32\bin\polib /OUT:LiteUnzip.lib /DEF:LiteUnZip.def /MACHINE:x86 /NOUND
Works like a charm :t
I've seen some HLL examples that use IShellFolder to create ZIP files. (http://blog.airesoft.co.uk/2012/10/dropping-like-files-zipping-without-libraries/)
Not sure how compatible they would be with WinZip, WinRAR or 7zip but might be worth a look.
Quote from: peter_asm on May 22, 2014, 09:06:13 AM
I've seen some HLL examples that use IShellFolder to create ZIP files. (http://blog.airesoft.co.uk/2012/10/dropping-like-files-zipping-without-libraries/)
Not sure how compatible they would be with WinZip, WinRAR or 7zip but might be worth a look.
The only comment says "The sad thing is of course that the zip compatibility is crap" - but I can't open it because you need an account there.
Besides, it looks pretty complicated. A simpler solution seems Quickly unzipping a file (http://www.codeproject.com/Tips/714387/Quickly-unzipping-a-file) by imagiro, and I've tried my luck in assembler (see attachment, RTF format), but currently I'm stuck with hilariously sick stuff like
(shldisp.h)
#ifndef __Folder_FWD_DEFINED__
#define __Folder_FWD_DEFINED__
typedef interface Folder Folder;
#endif /* __Folder_FWD_DEFINED__ */... and I won't have time in the coming days to dig into this can of worms :(
deleted
Hi Jochen,
Here's what I wrote for simple zip-unzip operations - not perfect and not pure ( I forgot about releasing the object after all the work is done :redface: ) but it works fine here
include \masm32\include\masm32rt.inc
__UNICODE__ EQU
__DEBUG__ EQU 0
IDispatch_Invoke PROTO :DWORD,:DWORD,:DWORD,:DWORD
IDispatch_GetIDsOfNames PROTO :DWORD,:DWORD
SetParams PROTO :DWORD
VARIANT STRUCT
dw1 dd 0CCh
dw2 dd 0CCh
dw3 dd 0CCh
dw4 dd 0CCh
VARIANT ENDS
DISPPARAMS STRUCT
rgvarg dd ?
rgdispidNamedArgs dd ?
cArgs dd ?
cNamedArgs dd ?
DISPPARAMS ENDS
IFERROR MACRO arg
.if eax!=S_OK
printf("%s FAILED with hr 0x%08X\n",arg,eax)
print LastError$(),13,10,0
print "Exiting ...",13,10,0
jmp _exit
.endif
ENDM
IFDEBUG MACRO arg
IFDEF __DEBUG__
printf("%s : SUCCESS\n",arg)
ENDIF
ENDM
FEEDBACK MACRO arg
IFERROR arg
IFDEBUG arg
ENDM
.data
IID_IDispatch GUID <00020400h,0000h,0000h,<0C0h,0h,0h,0h,0h,0h,0h,46h>>
IID_NULL GUID <00000000h,0000h,0000h,<0h,0h,0h,0h,0h,0h,0h,0h>>
dpar DISPPARAMS <>
varg VARIANT <>
vres VARIANT <>
.data?
DISPID dd ?,?
pclsid dd ?
ppvShA dd ?
ppvFld dd ?
.code
start:
call main
inkey
exit
main proc
mov eax, uc$("Shell.Application")
invoke CLSIDFromProgID, eax, addr pclsid
FEEDBACK "CLSIDFromProgID"
invoke CoInitialize, NULL
invoke CoCreateInstance, addr pclsid,
NULL,
CLSCTX_INPROC_SERVER,
addr IID_IDispatch,
addr ppvShA
FEEDBACK "CoCreateInstance"
; Define the DESTINATION in the third parameter "path"
; Note that it must be an already EXISTING ZIP file (for zip operation)
; or folder (for unzip operation)
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; Below is an example of zipping of "C:\masm32\projects\TST\ZIPIT" folder
; to "C:\masm32\projects\TST\NEW\NEW.ZIP" zip archive
; ! REPLACE THEM with your own paths !
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
invoke IDispatch_Invoke, ppvShA,uc$("NameSpace"),uc$("C:\masm32\projects\TST\NEW\NEW.ZIP"),addr vres
m2m ppvFld, vres.dw3
; Define the SOURCE file or folder in the third parameter "path"
; Note that NO WILDCARDS ALLOWED for zip operation
; Though "C:\folder\new.zip\*" seems to work for unzip operation
invoke IDispatch_Invoke, ppvFld,uc$("CopyHere"),uc$("C:\masm32\projects\TST\ZIPIT"),NULL
_exit::
invoke CoUninitialize
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
; PROCEDURES
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
IDispatch_Invoke proc ppv:DWORD,member:DWORD,path:DWORD,result:DWORD
invoke IDispatch_GetIDsOfNames,ppv,member
invoke SetParams,path
push NULL
push NULL
push result
push offset dpar
push 1 ; DISPATCH_METHOD
push 409h
push offset IID_NULL
push DISPID
mov edx, ppv
mov ecx, [edx]
push edx
call dword ptr [ecx+24] ; offset of INVOKE
FEEDBACK "IDispatch_Invoke"
ret
IDispatch_Invoke endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
; WORKER SUBROUTINES
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
IDispatch_GetIDsOfNames proc ppv:DWORD, mname:DWORD
printf("GetIDsOfNames %s\n ",mname)
push mname
mov eax,esp
push offset DISPID
push 409h
push 1
push eax
push offset IID_NULL
mov edx, ppv
mov ecx, [edx]
push edx
call dword ptr [ecx+20] ; offset of GetIDsOfNames
pop edx
FEEDBACK "GetIDsOfNames"
ret
IDispatch_GetIDsOfNames endp
SetParams proc wstr:DWORD
mov eax,wstr
mov varg.dw1,8
mov varg.dw2,0
mov varg.dw3,eax
mov varg.dw4,0
mov dpar.rgvarg, offset varg
mov dpar.rgdispidNamedArgs,0
mov dpar.cArgs,1
mov dpar.cNamedArgs,0
ret
SetParams endp
end start
CONSOLE ASSEMBLE & LINK in QEDITOR
Hi Vertograd,
On Win7-32, it crashes (with an existing zip file and an existing folder):
CLSIDFromProgID : SUCCESS
CoCreateInstance : SUCCESS
GetIDsOfNames NameSpace
GetIDsOfNames : SUCCESS
IDispatch_Invoke FAILED with hr 0x80020009
桔灯牥瑡潩潣灭敬整畳捣獥晳汵祬മ
Exiting ...
@nidud: Thanks, I will have a look asap.
0x80020009 error code is DISP_E_EXCEPTION
It's working fine here
Honestly I don't know what is wrong :(
I use ML 6.14
LOCALE ID = 409
[EDIT]: Jochen, did you create that empty(?) zip file by built-in windows means?
Could you try this (http://masm32.com/board/index.php?topic=2825.msg29822#msg29822) in powershell with your paths ? (just to make sure that the problem is in my code)
I used a zip file that matches the specs at http://www.codeproject.com/Tips/257193/Easily-zip-unzip-files-using-Windows-Shell
'1) Lets create an empty Zip File .
'The following data represents an empty zip file .
Dim startBuffer() As Byte = {80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, _
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
I am off for a few days now, sorry. See you back on Saturday 8)
when you right-click, New, you can create a new "Compressed Folder"
which we all know is a file
right-click/New can use templates (premade files that exist in user folders),
or it can use data from the registry
under my version of XP.....
[HKEY_CLASSES_ROOT\.zip\CompressedFolder\ShellNew]
"Data"=hex:50,4b,05,06,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
this format might be different for newer OS's
you can read the registry and either locate the template file or get the raw binary data to create a new (empty) ZIP file
that way, it will always be compatible with the shell functions you are about to use to put things in it
when i try to run Vertograd's code...
my guess is.....
ppv means "pointer to a pointer"
you want to pass the address of the pv variable, not the contents
or....
if the pointer is not filled when you create the interface (some sort of error creating interface)
you pass a 0, and it tries to access [00000000]
that would seem to be the case, as EDX = 0
...
Using the Windows zip/unzip interface is probably a bad idea.
120MB zip file (140 uncompressed) takes less than 2 seconds to unzip with WinRAR but at least 20 seconds with Explorer.
Quote from: sinsi on May 25, 2014, 05:44:10 PM
Using the Windows zip/unzip interface is probably a bad idea.
120MB zip file (140 uncompressed) takes less than 2 seconds to unzip with WinRAR but at least 20 seconds with Explorer.
Yes, it's lousy, and it would be limited to files compressed with the built-in zipper, which most likely nobody uses.
I found a MUCH better solution in the meantime, but it requires digging into the zip file specs - which is what I'll be doing the next few days ;-)
@vertograd: my condolencies for your drive, and thanks anyway for posting this very personal code :icon14:
Quote from: sinsi on May 25, 2014, 05:44:10 PM
Using the Windows zip/unzip interface is probably a bad idea.
120MB zip file (140 uncompressed) takes less than 2 seconds to unzip with WinRAR but at least 20 seconds with Explorer.
Maybe it's so when it comes to system-wide backups ... and what if you need something like FILE HISTORY for your source code ? I suppose that no one of your actively developed MASM projects is SO big .
I'm just learning to use built-in features ;)
@DAVE:
I've tested it on Windows XP . It crashes with access violation at 401190 :t
The reason most likely is in " m2m ppvFld, vres.dw3" line of code
Could you try this:
Quote
invoke IDispatch_Invoke, ppvShA,uc$("NameSpace"),uc$("C:\masm32\projects\TST\NEW\NEW.ZIP"),addr vres
printf("vres.dw3 = %08X\n", vres.dw3)
m2m ppvFld, vres.dw3
printf("ppvFld = %08X\n", ppvFld)
; Define the SOURCE file or folder in the third parameter "path"
; Note that NO WILDCARDS ALLOWED for zip operation
; Though "C:\folder\new.zip\*" seems to work for unzip operation
Now I'll continue work on enhancing the debug output :t
[EDIT]: All is good with that line of code here . Going to find out where that pointer is lost ...
...
Hi vertograd,
Quote from: vertograd on May 25, 2014, 07:00:29 PM
Your condolences are accepted but my poor hard drive isn't dead yet . It's in coma :biggrin:
Copying of 150MB of MASM folder with all the stuff took about 3 hours . That's what I call slow
oh yes, that's indeed a bit slow.
Gunther
well - part of the problem was that i did not have the proper file and folder structure
i created the folders
\masm32\projects
\masm32\projects\TST
\masm32\projects\TST\NEW
\masm32\projects\TST\ZIPIT
i then created an empty zip file (NEW.ZIP) in the \masm32\projects\TST\NEW folder
and, i put the assembler source file in the \masm32\projects\TST\ZIPIT folder (something to zip)
now, when i run the program, it says SUCCESS on all steps
at the end, we get the "Press Any Key To Continue..." prompt
THEN, Olly pops up :redface:
a weird state i have never seen before
the NEW.ZIP file is still empty
things are not properly released, but that shouldn't cause an empty zip file :(
Dave, thanks again
Exactly the same behavior here - SUCCESS on every step , final crash and still empty ZIP .
It looks like ZIP folder is not registered as DROP TARGET on XP - drag'n'drop operation doesn't add files to ZIP
Maybe it os version specific feature
I ran ASSOC command and it showed that ZIP folder exists as SendToTarget :
QuoteASSOC | FIND "ZF"
I created an empty file with .ZFSendToTarget extension on the Desktop and tried to drop files on it. OS asked me if I want to chose ZIP FOLDER as a default program for zip archives and only then it worked ... once :biggrin: Next time I drop file onto it - next message box with the same question.
I'll check that final crash and try to fix it .
The lack of wanted functionality requires additional reading and testing .
For now thank you all for your feedback
welllll.....
after doing some reading, i am not sure that this is a very good method to zip files, anyways
it seems that, under XP, there is no way to get rid of the progress dialog box
i remember someone using zlib a while back, and creating an INC for it
worse comes to worse, PK zip is not that difficult of an algorithm to code :P
that having been said, we should still be able to get it to work - lol
i found some very interesting reading in the Community Additions section of CopyHere
http://msdn.microsoft.com/en-us/library/windows/desktop/bb787866%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/windows/desktop/bb787866%28v=vs.85%29.aspx)
MSDN seems to have gotten away from Community Additions - it's a shame - i have learned a lot from them
also, i seem to recall we did something similar a while ago
i wrote an interface using qWord's METHOD macro and all that
Japheth came in and made a few changes or something....
http://masm32.com/board/index.php?topic=2834.0 (http://masm32.com/board/index.php?topic=2834.0)
one more note....
when working with API functions like CreateFile, UNICODE paths must be prepended with '\\?\'
that rule may not apply to COM, however :P
Dave,
Does DRAG'N'DROP work on ZIP folder for you?
As to whether this built-in method to zip files is good or not - It's BUILT-IN , the DEFAULT , ready for use OUT OF THE BOX and in this role it must not be perfect.
Somewhere I read that option 4 ( hide the progress dialog ) doesn't work in scripts etc.
yes, i can drag-n-drop to zips
according to what i've read, the 4 value works on vista/win7, just not XP
the windows shell method probably has a couple advantages
the obvious being, it's built-in
and - it will handle special locations like "My Documents" for you
also - it will do the recursion for you
on the other hand, disadvantages...
you get the dialog under xp
you may have UAC issues under vista or win7/8
it probably uses the windows shell copy algo, which doesn't always copy everything
(files like desktop.ini files that are marked do not copy or zero-length files, etc)
The final crash is fixed now!
Simply comment out "inkey" macro call . That's all.
Still have no idea why it doesn't zip files ...
Quote from: dedndave on May 25, 2014, 10:34:26 PM
on the other hand, disadvantages...
you get the dialog under xp
you may have UAC issues under vista or win7/8
it probably uses the windows shell copy algo, which doesn't always copy everything
(files like desktop.ini files that are marked do not copy or zero-length files, etc)
It also runs on a separate, unwaitable and unstoppable thread, which is a plus or minus depending on your perception of things. At least in the Win7 version (which is the only one I've really looked at), there is no way to disable the progress dialog. You only see it sometimes because it's an IProgressDialog, which runs the actual dialog on another thread. If the zipping finishes before the dialog's done it's initializing, you'll never see it.
The separate zipping thread is probably the reason for the 0 length zips. You'd have to wait for it to finish before calling ExitProcess, but as zipfldr uses SHCreateThread which doesn't give you a thread handle or the thread id, it can't even wait for the zipping to finish, never mind us mortals on the outside.
Let's just say that it's not the most robust piece of multi-threaded programming ever seen.
Oh and hi :) I wrote the necessarily hacky monstrosity that peter_asm linked to in post 16.
EDIT:
Totally forgot, the preceeding applies to dragging and dropping, whether from explorer or simulated. The IStorage/IStream interfaces don't use a separate thread, but I can't ever get them to work. I'll have a look at what underlying method the scripting interface uses.
Quote from: adeyblue on May 26, 2014, 12:49:36 AM
Oh and hi :) I wrote the necessarily hacky monstrosity that peter_asm linked to in post 16.
You mean IShellFolder to create ZIP files (http://blog.airesoft.co.uk/2012/10/dropping-like-files-zipping-without-libraries/) in post #15? Looks pretty advanced, you know your COM, also at (dis)assembly level ;-)
Welcome to the Forum :icon14:
Hi adeyblue,
Quote from: adeyblue on May 26, 2014, 12:49:36 AM
Oh and hi :) I wrote the necessarily hacky monstrosity that peter_asm linked to in post 16.
Hats off. :t And welcome to the forum.
Gunther
Quote
You mean IShellFolder to create ZIP files in post #15?
Yep, that's the one. Shame I can't count. It is a monstrostity though, going through the IContextMenu and simulating pasting the files on the zip is a lot easier and smaller than setting up custom dataobjects and droptargets (at least when using certain c++ features anyway - http://pastebin.com/pNjfLjWn).
Anyway, the IShellDispatch method of zipping simulates a drag and drop, so it follows what I said above. I checked the XP SP0 version of zipfldr.dll and that uses CreateThread instead of SHCreateThread. Any hope of it waiting for zipping to complete dies there though, as it promptly ignores the thread handle and leaks it.
Hello and welcome , adeyblue
Your article was an answer to my question in this thread (http://masm32.com/board/index.php?topic=2825.msg29815#msg29815)
Thanks for your explanations :icon14:
hi adeyblue :t
well - i get up to the point where i have to assign one of the ID'ed namespaces to an object
then, it gets a little fuzzy - lol
it looks easy in C, but
1) there is a lot of material to read about Shell.Application
2) i can't really find the specific docs i am looking for
Dave,
Now I can do drag'n'drop on zips too ... so the Windows XP knows how to do it BUT WHY WE DON'T?
Adeyblue's approach is a bit complicated to try it in MASM
well - the idea seems simple enough
i don't know what language this is - python or something - lol
Set sa = CreateObject("Shell.Application")
Set zip= sa.NameSpace(zipFile)
Set Fol=sa.NameSpace(FolderToZip)
zip.CopyHere(Fol.Items)
but - setting a folder is a 2 step process
1) get the id
2) set the property (i guess it's a property)
i can get the id, fine
and - CopyHere seems easy
it's that "set the folder" part that i don't understand
it seems to be done with IDispatch::Invoke
i wish i had a working C++ source and EXE to look at :(
in fact, i think this would work
CoCreateInstance
IDispatch::GetIDsOfNames ;source folder
IDispatch::Invoke ;set source folder - this is the part i can't get going
IDispatch::GetIDsOfNames ;target folder
IDispatch::CopyHere(idTarget)
IDispatch::Release
Yes , it's simple : 3 lines of code in powershell
That's why I've implemented it in MASM and it worked JUST FINE on my machine .
The code you posted looks like VisualBasic script and you may try to run it with WSCRIPT
Here (http://masm32.com/board/index.php?topic=2825.msg29755#msg29755) is a link to an advanced VBasic Script:
http://stackoverflow.com/questions/30211/can-windows-built-in-zip-compression-be-scripted
I'll try it on my Windows XP . I want to know if this simple method works in VBasic Script and powershell on XP .
scripts don't help us, much
i need a working C++ source and EXE :t
Quote from: dedndave on May 27, 2014, 04:12:15 AM
in fact, i think this would work
CoCreateInstance
IDispatch::GetIDsOfNames ;source folder
IDispatch::Invoke ;set source folder - this is the part i can't get going
IDispatch::GetIDsOfNames ;target folder
IDispatch::CopyHere(idTarget)
IDispatch::Release
You're wrong , Dave
The first call to GetIDsOfNames obtains Namespace member and IT'S THE TARGET FOLDER
My code is alright except that I don't call Release method
[EDIT]: Oh, I understood now - we're obtaining folder interface for both source and target . Worth a try . Usually I try scripts first .
Quote from: dedndave on May 27, 2014, 04:14:58 AM
scripts don't help us, much
i need a working C++ source and EXE :t
They can help us - if we will know that this "simple method" works in scripts on XP then we may be sure that the reason is in my code and not in OS
well - that may not be the way your example works
but, it seems logical - and i seem to recall seeing code like that browsing around the web
the point of having an EXE (with source helps) is that we can see the generated code
we can't do that with scripts
...
thanks :t
this is my code, so far
if i get it working, i will add error reports (or sooner, if needed - lol)
and, i even have a way to wait for it to finish writing the zip :biggrin:
notice, i used different paths for the test files
CLSIDFromProgID: Success
CoCreateInstance: Success
IDispatch::GetIDsOfNames(Source): Success
IDispatch::Release: Success
Press any key to continue ...
Dave , could you post the essential parts of your code in "CODE" tags , please - I don't have an opportunity to download files here :(
If you still need it, here's a C++ exe and src (http://blog.airesoft.co.uk/code/zipstuff.zip). It uses IShellDispatch and the Folder interfaces directly since basic IDispatch and me aren't friends. I'll rework it to use those if you need to see what that looks like.
thanks, adeyblue :t
vertograd...
the biggest difference between my code and yours is that i use qWord's METHOD macro
it simplifies the IDispatch calls :biggrin:
;***********************************************************************************************
;METHOD macro by qWord
METHOD MACRO name,args:VARARG
LOCAL _type1,_type2
_type1 TYPEDEF PROTO args
_type2 TYPEDEF PTR _type1
EXITM <name _type2 ?>
ENDM
;###############################################################################################
;IDispatch Interface
IDispatch STRUCT
METHOD(QueryInterface, _this:LPVOID,riid:LPVOID,ppvObj:LPVOID)
METHOD(AddRef, _this:LPVOID)
METHOD(Release, _this:LPVOID)
METHOD(GetTypeInfoCount, _this:LPVOID,pctinfo:UINT)
METHOD(GetTypeInfo, _this:LPVOID,iTInfo:UINT,lcid:LCID,ppTInfo:LPVOID)
METHOD(GetIDsOfNames, _this:LPVOID,riid:LPVOID,rgszNames:LPOLESTR,cNames:UINT,lcid:LCID,rgDispId:LPVOID)
METHOD(dInvoke, _this:LPVOID,dispIdMember:DWORD,riid:LPVOID,lcid:LCID,wFlags:DWORD,pDispParams:LPVOID,pVarResult:LPVOID,pExcepInfo:LPVOID,puArgErr:UINT)
IDispatch ENDS
then, the calls are easy...
print offset sz0001
INVOKE CLSIDFromProgID,offset szShell,offset pClsId
.if eax==S_OK
print offset szSuccess
print offset sz0002
INVOKE CoCreateInstance,offset pClsId,NULL,CLSCTX_INPROC_SERVER,offset IID_IDispatch,offset pvShell
.if eax==S_OK
print offset szSuccess
print offset sz0003
mov edx,pvShell
mov ecx,[edx]
INVOKE IDispatch.GetIDsOfNames[ecx],edx,offset IID_NULL,offset adwObjSource,1,409h,offset adwDispIdSource
.if eax==S_OK
print offset szSuccess
; print offset sz0004
.endif
print offset sz0009
mov edx,pvShell
mov ecx,[edx]
INVOKE IDispatch.Release[ecx],edx
.if eax==S_OK
print offset szSuccess
.endif
.endif
.endif
Thanks, Dave
[EDIT]: I've detected the first mistake in my code: the call to CopyHere doesn't return status SUCCESS because as stated here (http://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx) :
QuoteReturn value
This method does not return a value.
Remarks
No notification is given to the calling program to indicate that the copy has completed.
This VBScript is zipping the file / folder only if I call the MessageBox after CopyHere :
SET o=CreateObject("Shell.Application")
SET n=o.Namespace("C:\new.zip")
n.CopyHere("C:\myfolder")
Wscript.Echo "Please, wait"
My code requires major fixes ... I'll be back in a day or two ;)
i think i am starting to understand the interface a little bit :P
i can see that you were right - CopyHere wants the source
something like this
CLSIDFromProgID
CoCreateInstance
IDispatch::GetIDsOfNames(szTarget)
IDispatch::Invoke(Shell.NameSpace(idTarget))
IDispatch::GetIDsOfNames(szSource)
IDispatch::Invoke(Folder.CopyHere(idSource))
(wait for it to finish)
IDispatch::Release
i'm getting closer, at least :biggrin:
i was thinking how to wait on thread termination and ExecNotificationQueryAsync might work, see vbscript for basic example
option explicit
Dim sink, end_query, wmi, shell, zip, completed
Set wmi = GetObject("WINMGMTS:\\.\root\cimv2")
Set sink = WScript.CreateObject ("WbemScripting.SWbemSink", "SINK_")
' query to monitor for deletion of threads
end_query = "SELECT * FROM __InstanceDeletionEvent " & _
"WITHIN 1 " & _
"WHERE TargetInstance ISA 'Win32_Thread'"
completed = False
wmi.ExecNotificationQueryAsync sink, end_query
set shell=CreateObject("Shell.Application")
set zip=shell.Namespace("C:\users\pete\desktop\test.zip")
' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx
' ===============================================================
' 4 = do not display a progress box
' 16 = Respond with "Yes to All" for any dialog box that is displayed.
' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified.
' 256 = Display a progress dialog box but do not show the file names.
' 2048 = Version 4.71. Do not copy the security attributes of the file.
' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories.
' even though ignore is specified, a dialog box still gets displayed
Call zip.CopyHere("C:\test_folder", 4)
WScript.StdOut.Write "Please wait"
Do While Not completed
WScript.Sleep 500
WScript.StdOut.Write "."
Loop
WScript.StdOut.WriteLine vbCrLf & "Complete"
' notifys when thread is ending
Sub SINK_OnObjectReady(ByVal objWbemObject, ByVal objWbemAsyncContext)
completed = True
End Sub
If i try using wildcards, it just complains about file being too long.
pity MS don't have a built in wait feature for this. i suppose because it's part of explorer and explorer always running, no need.
implementing this solution as C++ would mean inheriting from IWbemObjectSink and I've no idea how that would be done using assembly so it's probably more pain than it's worth.
found another script example : https://github.com/ynkdir/winscript/blob/master/bin/zip.bat
my thinking was...
if i can open the zip file for write, it must be done :biggrin:
windows will put a lock on the zip file while processing
Quote from: dedndave on May 28, 2014, 12:43:23 AM
something like this
CLSIDFromProgID
CoCreateInstance
IDispatch::GetIDsOfNames(szTarget)
IDispatch::Invoke(Shell.NameSpace(idTarget))
IDispatch::GetIDsOfNames(szSource)
IDispatch::Invoke(Folder.CopyHere(idSource))
(wait for it to finish)
IDispatch::Release
i'm getting closer, at least :biggrin:
One remark : GetIDsOfNames takes a IDispatch member name as a parameter - "Namespace" or "CopyHere" and returns its DISPID .
For closure, I got my head around the workngs of the zipfldr's IStorage / IStream interfaces (which don't create a separate thread nor any UI when zipping) and they're bugged :(
They create a temporary directory fine, then an IBindCtx that'll make the shell create the file if it doesn't exist, all good so far. Unfortunately they then blunder transforming the filesystem path to a pidl: they call SHILCreateFromPath, which requires the path to already exist, instead of SHSimpleIDListFromPath which doesn't. They then call the function that uses the IBindCtx and would create the file, but since the transfomation fails they never get that far.
Shame really since it's not that much more code to use them.
Quote
if i can open the zip file for write, it must be done
windows will put a lock on the zip file while processing
You'll have to make sure it's already opened the file before you try to, otherwise it'll probably fail (it uses FILE_SHARE_READ when calling CreateFile).
i think you misunderstand
you start the ZIP thread
perhaps, wait a few hundred microseconds (give it a chance to get started)
then, you attempt to open the ZIP file (probably with FILE_SHARE_WRITE flag)
if it fails, it means the ZIP thread still has the file locked (wait a few hundred microseconds, try again)
when you can successfully open the ZIP file,
it means the ZIP thread has released it - you close the handle and exit
there's probably some cleaner way to test the file lock flag (more COM stuff ::) )
Quote from: vertograd on May 28, 2014, 03:16:54 AM
One remark : GetIDsOfNames takes a IDispatch member name as a parameter
"Namespace" or "CopyHere" and returns its DISPID .
thanks vertograd
what i am trying to figure out, is how to determine what "named methods or classes" are available for a specific type of object
i found a COM book online - a lot of material to read
eventually, i'll work my way through it
for now, i just want to read enough to get past this one thing - lol
Would be neat to see way using COM API to monitor when the thread ends.
normally, we could use GetExitCodeThread
but - that requires a thread handle
don't guess you have that - it may be possible to get it (difficult with no thread id)
it might be easier to get a thread count
might be something for that in the TEB
just a thought...
GetProcessHandleCount
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683214%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683214%28v=vs.85%29.aspx)
afaik WaitForSingleObject can take a process handle, too.
Quote from: dedndave on May 28, 2014, 10:48:43 AM
thanks vertograd
what i am trying to figure out, is how to determine what "named methods or classes" are available for a specific type of object
i found a COM book online - a lot of material to read
eventually, i'll work my way through it
for now, i just want to read enough to get past this one thing - lol
All is simple - search on msdn or dig into headers in Windows SDK
NameSpace is a member of Shell object (http://msdn.microsoft.com/en-us/library/windows/desktop/bb774094(v=vs.85).aspx)
CopyHere is a member of Folder object (http://msdn.microsoft.com/en-us/library/windows/desktop/bb787868(v=vs.85).aspx) (you can get there following the link in NameSpace description)
Another way is to use powershell . It's completely object-oriented
To create Shell object:
$shApp=New-Object -ComObject Shell.Application
To dump its members:
$shApp | Get-Member
To create Folder object:
$oFolder=$shApp.Namespace("C:\")
To dump its members:
$oFolder | Get-Member
and so on
You may learn COM with powershell :t
Hi vertograd,
Quote from: vertograd on May 28, 2014, 04:44:37 PM
Another way is to use powershell . It's completely object-oriented
To create Shell object:
$shApp=New-Object -ComObject Shell.Application
To dump its members:
$shApp | Get-Member
To create Folder object:
$oFolder=$shApp.Namespace("C:\")
To dump its members:
$oFolder | Get-Member
and so on
You may learn COM with powershell :t
Wow. I've done it a wide berth around powershell in the past. Was that an error?
Gunther
Quote from: Gunther on May 28, 2014, 05:06:17 PM
...
Wow. I've done it a wide berth around powershell in the past. Was that an error?
...
Hi Gunther,
I don't think so . It's a matter of personal preferences, your free time and professional interests.
@Dave
FIRST SUCCESS HERE !
On my P3 with 128MB RAM I must give zipfldr some time to finish its work :
Quote
invoke Sleep,10000
_exit::
Only 10 seconds and posted above asm source was zipped :biggrin:
"Sleep" is not the best solution though
I'll think about thread / file lock monitoring ...
nice job :t
PowerShell is very cool
however, the user may not have it installed
Quote from: dedndave on May 28, 2014, 09:10:45 PM
nice job :t
PowerShell is very cool
however, the user may not have it installed
Powershell is included in Windowws 7 and can be downloaded for Windows XP from here (http://support.microsoft.com/kb/968929)
For detailed instructions read this (http://blogs.technet.com/b/heyscriptingguy/archive/2011/01/28/install-powershell-on-windows-xp-and-copying-files.aspx)
Remarks:
Powershell requires appropriate .NET Framework pack
yah - i have it installed and .NET 1.0, 1.1, 2.0, 3.0, 3.5 i think
i can't install 4.0+ because it is incompatible with XP Media Center Edition :(
later today, i may do some more COM reading
Folks,
I am watching with fascination and some envy your efforts to tame the COM beast :t
In the meantime, I've given up and went, as far as un-zipping is concerned, for a third party library provided by the fabulous Jørgen Ibsen (http://ibsensoftware.com/download.html) - one or two k extra, fast and comfortable ;-)
Here is a demo from \Masm32\MasmBasic\MbGuide.rtf, using UnzipInit, UnzipFile() and UnzipExit:
include \masm32\MasmBasic\MasmBasic.inc ; version 28 May 2014 required (http://masm32.com/board/index.php?topic=94.0)
Init
UnzipInit "test.zip" ; expects a filename, returns a comment (if present); edx has #records
.if Sign?
Print eax ; print an error message
.else
push eax ; UnzipInit returns a comment or an empty string
For_ ecx=0 To edx-1 ; #files returned in edx
mov esi, Files$(ecx)
PrintLine Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, esi
.if Instr_(esi, ".asm", 1) ; assembler source, plain text?
PrintLine "##First 60 chars: [", Left$(UnzipFile(ecx), 60), "]"
.endif
Next
pop eax
Print "Zipfile comment: [", eax, "]"
UnzipExit
.endif
Exit
end start
Rem
- decompresses files in zip archive to a buffer returned by UnzipFile(ecx)
- you may save the file as FileWrite Cat$("\SomeFolder\"+Files$(ecx)), UnzipFile(ecx)
Sample output:
20222 19.04.2011 14:33:14 Irvine32Mb.lib
9451 02.12.2011 15:26:26 IrvineMacsMb.asm
##First 60 chars: [.NOLIST
COMMENT ! ** for use with MB - nothing adapted here]
3318 10.12.2012 09:40:28 IrvineMasm32.asc
16274 02.12.2011 15:18:26 IrvineSmallWinMb.inc
When your COM version is ready, we can start the timings thread in the Laboratory.
we're close, Jochen
i may work on it, if i have some time later today
but Vertograd may get one going, first - lol
Quote from: jj2007 on May 28, 2014, 03:20:53 PM
afaik WaitForSingleObject can take a process handle, too.
Could enumerate threads with Thread32First/Thread32Next (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686728%28v=vs.85%29.aspx) then call WaitForMultipleObjects()
Or enumerate all threads before calling CopyHere() and then after, using WaitForSingleObject() on new handle.
I find it strange there isn't some way to wait on thread termination through COM API..
i am sure COM provides a way - we just don't know what it is yet - lol
have been reading a bit
they have something called "thread apartments"
not sure that applies here, but it looks about right (haven't finished reading that part)
if you want to see some COM code, i suspect you could disassemble Explorer and some of the DLL's to see how it's done
...
Quote from: vertograd on May 29, 2014, 06:31:29 PMThe Windows , ohh, it's huge , it's like the ocean . The ocean never meets fast brooks ;)
Indeed. I have timed the unzipping of the 1.7->17MB database in my spreadsheet viewer (http://masm32.com/board/index.php?topic=3231.0), and it's around 340 ms on my trusty old Celeron. I doubt that Explorer can compete :biggrin:
i unzipped a fairly large archive the other day
333 Mb zipped, 640 Mb unzipped
took forever - lol
well - i have to wonder if the algorithm used for zipping wasn't kind of old
Quote from: peter_asm on May 28, 2014, 02:00:24 AM
i was thinking how to wait on thread termination and ExecNotificationQueryAsync might work, see vbscript for basic example
...
implementing this solution as C++ would mean inheriting from IWbemObjectSink and I've no idea how that would be done using assembly so it's probably more pain than it's worth.
...
Thanks peter_asm,
your suggestion to use WMI IWbemServices interface method ExecNotificationQueryAsync (http://msdn.microsoft.com/en-us/library/aa392106(v=vs.85).aspxhttp://msdn.microsoft.com/en-us/library/aa392106(v=vs.85).aspx) seems to be the reasonable way to monitor threads .
I've found COM API for WMI (http://msdn.microsoft.com/en-us/library/aa389276(v=vs.85).aspx) reference and C++ Example: Receiving Event Notifications Through WMI (http://msdn.microsoft.com/en-us/library/aa390425(v=vs.85).aspx)
That's all I need for now
So ... my path lies to WMI :biggrin:
As soon as I restore my development machine I'll dive into WMI coding in MASM
P.S.: WaitForSingleObject / WaitForMultipleObjects require thread(s) handle(s) in parameters . Enumerating threads with Thread32First/Thread32Next functions we get only thread IDs . However we can count the total number of threads with their help. I've already have this code written but can not re-assemble it because it wants modified kernel32.lib which I've lost with my HD .
Quote from: dedndave on May 29, 2014, 09:46:04 PM
well - i have to wonder if the algorithm used for zipping wasn't kind of old
The older versions of zipfldr have various 'Dynazip' strings in them, so it looks like they licensed their code rather than writing it from scratch. That product had been alive since 1996-ish so yeah, I'd guess explorer's zip algorithm is older than it is!
Quote from: vertograd
As soon as I restore my development machine I'll dive into WMI coding in MASM
I don't envy that :) I think it's painful and long enough in C++, it must be like War & Peace in assembly.
I tried two other notification methods of various complexity. You can register for Shell change notifications on the zip file with SHChangeNotifyRegister (http://msdn.microsoft.com/en-gb/library/windows/desktop/bb762120(v=vs.85).aspx), downside is it requires creating a window and a message loop.
There's also the WinEvent hooks (http://msdn.microsoft.com/en-us/library/windows/desktop/dd373640(v=vs.85).aspx) and their window creation/deletion callback. Set one for the current process, determine when the compressing dialog has been created, then wait the reciprocal delete where you signal that it's finished and exit the app. Downside is it requires Microsoft not to change the window properties of the compressing dialog
...
adeyblue, just added function to monitor thread termination using Thread32First/WaitForMultipleObjects but it's just a hack to show how it might work. Would cause problems where process threads > MAXIMUM_WAIT_OBJECTS (64) or lots of other thread activity going on..unreliable.
Also looked inside zipfldr.dll to see how it works there but still unsure.
Seems to be based on window notifications posted to explorer.
i've been reading up a little on COM....
it would seem that the shell is a "dual interface"
that is, it may be accessed via IDispatch, or via the shell functions
from what i gather, the IDispatch methods target "variable type" languages, such as scripts
VB scripts and java scripts use IDispatch
so, it would seem that adeyblue has the right idea for what we want
however - that's not to say that it can't be done via IDispatch
a good analogy might be using DialogBoxParam and a bunch of structures to create a dialog box,
as opposed to using a resource template, and letting the OS do it
still playing with IDispatch - i want to see if i can figure it out - lol
I was poking through zipfldr.dll again and noticed various objects used to work with ZIP files inherit from ITransferHelper, ITransferAdviseSink and ITransferSource2 which is undocumented
seen this too : http://blog.airesoft.co.uk/2012/10/dropping-like-files-zipping-without-libraries/
Quote from: peter_asm on May 30, 2014, 07:45:04 AMseen this too : http://blog.airesoft.co.uk/2012/10/dropping-like-files-zipping-without-libraries/
That is adeyblue's blog, see reply #40 (http://masm32.com/board/index.php?topic=3215.msg33750#msg33750). Does anybody have a Microsoft account?
QuoteThe sad thing is of course that the zip compatibility is crap; http://blogs.msdn.com/b/michkap/archive/2012/01/04/10252916.aspx etc
i checked it the other day
it's a dead link
you can create a hotmail acct to sign into MS - it's free
it's Michael Kaplan
you can google and find that they are refering to zipping UNICODE filenames
the locale identifier may cause issues (zip in one locale, unzip in another)
seems to me, you could set the locale id to 0 :P
Here is a sample for testing...
I can handle it with the new UnzipFile macro, but it's a bit clumsy. Curious to see if Windows knows about Unicode ;)
i had a little time to play, today
as i mentioned before - there is a lot of documentation on creating a COM interface
it almost seems easier than using one - lol
but, one of the things i noticed was that IDispatch interfaces aren't necessarily limited to the 7 functions
the first 3 are IUnknown, which is a subset of IDispatch, which is a subset.... and so on
IDispatch STRUCT
METHOD(QueryInterface, _this:LPVOID,riid:LPVOID,ppvObj:LPVOID)
METHOD(AddRef, _this:LPVOID)
METHOD(Release, _this:LPVOID)
METHOD(GetTypeInfoCount, _this:LPVOID,pctinfo:UINT)
METHOD(GetTypeInfo, _this:LPVOID,iTInfo:UINT,lcid:LCID,ppTInfo:LPVOID)
METHOD(GetIDsOfNames, _this:LPVOID,riid:LPVOID,rgszNames:LPOLESTR,cNames:UINT,lcid:LCID,rgDispId:LPVOID)
METHOD(dInvoke, _this:LPVOID,dispIdMember:DWORD,riid:LPVOID,lcid:LCID,wFlags:DWORD,pDispParams:LPVOID,pVarResult:LPVOID,pExcepInfo:LPVOID,puArgErr:UINT)
IDispatch ENDS
what i learned was, because we are using "Shell.Application",
what we are actually getting is an IShellDispatch interface vTable
and - with different versions of OS, service pack, CommonCtrls, and .NET, you get different versions of IShellDispatch
using XP SP3, .NET 3.5, and without referencing CommCtrls 6.0, i get an IShellDispatch4 vTable
these can be acquired directly with
IID_IShellDispatch GUID <0D8F015C0h,0C278h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
IID_IShellDispatch2 GUID <0A4C6892Ch,3BA9h,11D2h,<9Dh,0EAh,0,0C0h,4Fh,0B1h,61h,62h>>
IID_IShellDispatch3 GUID <177160CAh,0BB5Ah,411Ch,<84h,1Dh,0BDh,38h,0FAh,0CDh,0EAh,0A0h>>
IID_IShellDispatch4 GUID <0EFD84B2Dh,4BCFh,4298h,<0BEh,25h,0EBh,54h,2Ah,59h,0FBh,0DAh>>
IID_IShellDispatch5 GUID <866738B9h,6CF2h,4DE8h,<87h,67h,0F7h,94h,0EBh,0E7h,4Fh,4Eh>>
IID_IShellDispatch6 GUID <286E6F1Bh,7113h,4355h,<95h,62h,96h,0B7h,0E9h,0D6h,4Ch,54h>>
and, the vTables progress like this...
;IUnknown methods (3)
AddRef
QueryInterface
Release
;IDispatch methods (4)
GetIDsOfNames
GetTypeInfo
GetTypeInfoCount
Invoke
;IShellDispatch properties (2)
Application
Parent
;IShellDispatch methods (21)
NameSpace
BrowseForFolder
Windows
Open
Explore
MinimizeAll
UndoMinimizeALL
FileRun
CascadeWindows
TileVertically
TileHorizontally
ShutdownWindows
Suspend
EjectPC
SetTime
TrayProperties
Help
FindFiles
FindComputer
RefreshMenu
ControlPanelItem
;IShellDispatch2 methods (9)
IsRestricted
ShellExecute
FindPrinter
GetSystemInformation
ServiceStart
ServiceStop
IsServiceRunning
CanStartStopService
ShowBrowserBar
;IShellDispatch3 method (1)
AddToRecent
;IShellDispatch4 methods (4) (visible in XP SP3 without Common Controls 6.0)
WindowsSecurity
ToggleDesktop
ExplorerPolicy
GetSetting
IShellDispatch5 method (1) (suspect visible with Common Controls 6.0 and/or some level of .NET)
WindowSwitcher
IShellDispatch6 method (1) (suspect visible with Windows 8 and/or some level of .NET)
SearchCommand
i'm not really sure what's required to get the later versions
but, it doesn't matter, because we're primarily interested in NameSpace
the other thing i learned today was how to build a C++ program using Visual C++ 2005 Express
not being much on compilers, that's a big step for me :P
tomorrow, i'll be able to play some more
this works, but it seems like a lot of code :P
it's a starting point
the empty zip file and folders must exist
int _tmain(int argc, _TCHAR* argv[])
{
DWORD strlen = 0;
char szFrom[] = "C:\\ZipTest\\ZipIt",
szTo[] = "C:\\ZipTest\\New\\New.zip";
HRESULT hResult;
IShellDispatch *pISD;
Folder *pToFolder = NULL;
VARIANT vDir, vFile, vOpt;
BSTR strptr1, strptr2;
CoInitialize(NULL);
hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);
if (SUCCEEDED(hResult) && pISD != NULL)
{
strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
strptr1 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);
VariantInit(&vDir);
vDir.vt = VT_BSTR;
vDir.bstrVal = strptr1;
hResult = pISD->NameSpace(vDir, &pToFolder);
if (SUCCEEDED(hResult))
{
strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
strptr2 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);
VariantInit(&vFile);
vFile.vt = VT_BSTR;
vFile.bstrVal = strptr2;
VariantInit(&vOpt);
vOpt.vt = VT_I4;
vOpt.lVal = 4; // Do not display a progress dialog box
hResult = NULL;
printf("Copying %s to %s ...\n", szFrom, szTo);
hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
/*
* 1) Enumerate current threads in the process using Thread32First/Thread32Next
* 2) Start the operation
* 3) Enumerate the threads again
* 4) Wait for any new threads using WaitForMultipleObjects
*
* Of course, if the operation creates any new threads that don't exit, then you have a problem.
*/
if (hResult == S_OK) {
//NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
HANDLE hThrd[5];
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL ,0); //TH32CS_SNAPMODULE, 0);
DWORD NUM_THREADS = 0;
if (h != INVALID_HANDLE_VALUE) {
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te)) {
do {
if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
//only enumerate threads that are called by this process and not the main thread
if((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId()) ){
//printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
hThrd[NUM_THREADS] = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
NUM_THREADS++;
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h, &te));
}
CloseHandle(h);
printf("waiting for all threads to exit...\n");
//Wait for all threads to exit
WaitForMultipleObjects(NUM_THREADS, hThrd , TRUE , INFINITE);
//Close All handles
for ( DWORD i = 0; i < NUM_THREADS ; i++ ){
CloseHandle( hThrd[i] );
}
} //if invalid handle
} //if CopyHere() hResult is S_OK
SysFreeString(strptr2);
pToFolder->Release();
}
SysFreeString(strptr1);
pISD->Release();
}
CoUninitialize();
printf ("Press ENTER to exit\n");
getchar();
return 0;
}
while reading, i came across this function that looked interesting
the descriptions are sometimes misleading, though
SHGetInstanceExplorer
http://msdn.microsoft.com/en-us/library/windows/desktop/bb762186%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762186%28v=vs.85%29.aspx)
I think that SHGetInstanceExplorer is called from the shell extension / component's side .
I didn't know that we can get thread handle from its ID and thus can use WaitForMultipleObjects function.
MSDN (http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms687025(v=vs.85).aspx) has a warning about its usage in Remarks:
QuoteUse caution when calling the wait functions and code that directly or indirectly creates windows. If a thread creates any windows, it must process messages. Message broadcasts are sent to all windows in the system. A thread that uses a wait function with no time-out interval may cause the system to become deadlocked.
CopyHere method doesn't return any value so it's our job to check if the copy operation was successful .
I don't understand this line of code:
Quoteif (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
Could anybody explain its meaning?
Quote from: vertograd on June 02, 2014, 10:00:31 PMI don't understand this line of code:
Quoteif (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
Could anybody explain its meaning?
Quote from: msdnThe calling application must set the dwSize member of THREADENTRY32 to the size, in bytes, of the structure. Thread32First (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686728%28v=vs.85%29.aspx) changes dwSize to the number of bytes written to the structure. This will never be greater than the initial value of dwSize, but it may be smaller. If the value is smaller, do not rely on the values of any members whose offsets are greater than this value.
I'm really curious under which conditions the structure is only partially filled, even it seems like some backward compatibility issue...
qWord, thank you
Now it makes sense .
Quote from: qWord on June 02, 2014, 10:49:26 PM
Quote from: msdnThe calling application must set the dwSize member of THREADENTRY32 to the size, in bytes, of the structure. Thread32First (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686728%28v=vs.85%29.aspx) changes dwSize to the number of bytes written to the structure. This will never be greater than the initial value of dwSize, but it may be smaller. If the value is smaller, do not rely on the values of any members whose offsets are greater than this value.
I'm really curious under which conditions the structure is only partially filled, even it seems like some backward compatibility issue...
There are none (so far). The struct has been the same size from Win95 to 8.1, and all the thread funcs do is basically open a memory mapped file and memcpy the relevant struct.
Quote from: dedndave
hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);
If you don't mind Microsoft's ominous, but so far untrue words (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762124(v=vs.85).aspx) (it still exists and works fine in 8.1). You can skip hitting the registry and all of ole32's funkiness with:
hResult = SHCoCreateInstance(NULL, &CLSID_Shell, NULL, IID_IShellDispatch, (void **)&pISD);
you can create it directly
IFNDEF CLSCTX_INPROC_SERVER
CLSCTX_INPROC_SERVER EQU 1
ENDIF
.DATA
CLSID_Shell GUID <13709620h,0C279h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
IID_IShellDispatch GUID <0D8F015C0h,0C278h,11CEh,<0A4h,9Eh,44h,45h,53h,54h,0,0>>
.DATA?
pvShell LPVOID ?
.CODE
INVOKE CoInitialize,NULL
INVOKE CoCreateInstance,offset CLSID_Shell,NULL,CLSCTX_INPROC_SERVER,offset IID_IShellDispatch,offset pvShell
once you have it, you just need some proper vTable structures :biggrin:
let's start with qWord's METHOD macro
;METHOD macro by qWord
METHOD MACRO name,args:VARARG
LOCAL _type1,_type2
_type1 TYPEDEF PROTO args
_type2 TYPEDEF PTR _type1
EXITM <name _type2 ?>
ENDM
then, IUnknown and IDispatch
;IUnknown interface vTable (structure)
IUnknown STRUCT
METHOD(QueryInterface, _this:LPVOID,riid:LPVOID,ppvObj:LPVOID)
METHOD(AddRef, _this:LPVOID)
METHOD(Release, _this:LPVOID)
IUnknown ENDS
;IDispatch interface vTable (structure)
IDispatch STRUCT
IUnknown <>
METHOD(GetTypeInfoCount, _this:LPVOID,pctinfo:LPVOID)
METHOD(GetTypeInfo, _this:LPVOID,iTInfo:UINT,lcid:LCID,ppTInfo:LPVOID)
METHOD(GetIDsOfNames, _this:LPVOID,riid:LPVOID,rgszNames:LPOLESTR,cNames:UINT,lcid:LCID,rgDispId:LPVOID)
METHOD(dInvoke, _this:LPVOID,dispIdMember:DWORD,riid:LPVOID,lcid:LCID,wFlags:DWORD,pDispParams:LPVOID,pVarResult:LPVOID,pExcepInfo:LPVOID,puArgErr:LPVOID)
IDispatch ENDS
because we created an IShellDispatch interface...
;IShellDispatch interface vTable (structure)
; vDir VARIANT struct = vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD
;vRootFolder VARIANT struct = vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD
IShellDispatch STRUCT
IDispatch <>
METHOD(Application, _this:LPVOID,ppid:LPVOID)
METHOD(Parent, _this:LPVOID,ppid:LPVOID)
METHOD(NameSpace, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD,ppsdf:LPVOID)
METHOD(BrowseForFolder, _this:LPVOID,Hwnd:DWORD,lpTitle:LPVOID,Options:DWORD,vtRoot:DWORD,vR0:DWORD,vstrRoot:LPVOID,vRV0:DWORD,ppsdf:LPVOID)
METHOD(Windows, _this:LPVOID,ppid:LPVOID)
METHOD(Open, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
METHOD(Explore, _this:LPVOID,vtDir:DWORD,vD0:DWORD,vstrDir:LPVOID,vDV0:DWORD)
METHOD(MinimizeAll, _this:LPVOID)
METHOD(UndoMinimizeALL, _this:LPVOID)
METHOD(FileRun, _this:LPVOID)
METHOD(CascadeWindows, _this:LPVOID)
METHOD(TileVertically, _this:LPVOID)
METHOD(TileHorizontally, _this:LPVOID)
METHOD(ShutdownWindows, _this:LPVOID)
METHOD(Suspend, _this:LPVOID)
METHOD(EjectPC, _this:LPVOID)
METHOD(SetTime, _this:LPVOID)
METHOD(TrayProperties, _this:LPVOID)
METHOD(Help, _this:LPVOID)
METHOD(FindFiles, _this:LPVOID)
METHOD(FindComputer, _this:LPVOID)
METHOD(RefreshMenu, _this:LPVOID)
METHOD(ControlPanelItem, _this:LPVOID,bstrDir:LPVOID)
IShellDispatch ENDS
when you call the IShellDispatch::NameSpace method, it returns a "folder object".
the folder object is another IDispatch interface - from which we can call the CopyHere method
;IShellDispatch::NameSpace Folder object interface vTable (structure)
; vItem VARIANT struct = vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD
;vOptions VARIANT struct = vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD
Folder STRUCT
IDispatch <>
METHOD(getTitle, _this:LPVOID,pbs:LPVOID)
METHOD(Application, _this:LPVOID,ppid:LPVOID)
METHOD(Parent, _this:LPVOID,ppid:LPVOID)
METHOD(ParentFolder, _this:LPVOID,ppsf:LPVOID)
METHOD(Items, _this:LPVOID,ppid:LPVOID)
METHOD(ParseName, _this:LPVOID,bstrName:LPVOID,ppid:LPVOID)
METHOD(NewFolder, _this:LPVOID,bstrName:LPVOID,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(MoveHere, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(CopyHere, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,vtOpt:DWORD,vO0:DWORD,vdwOpt:LPVOID,vOV0:DWORD)
METHOD(GetDetailsOf, _this:LPVOID,vtItem:DWORD,vI0:DWORD,vdwItem:DWORD,vIV0:DWORD,iColumn:DWORD,pbs:LPVOID)
Folder ENDS
now that we have proper vTable definitions, we just need a little work with VARIANT structures,
and, of course, a way to know when it's done zipping :t
i was having a hard time figuring out how to call the NameSpace and CopyHere methods
now that i have those figured out, things should get a little easier :biggrin:
EDIT: updated IShellDispatch and Folder vTable structures to allow VARIANT's as DWORD's
EDIT: modified "Title" to "getTitle" in Folder vTable to avoid use of reserved keyword
modified "Title" to "lpTitle" in IShellDispatch, BrowseForFolder arguments - same reason
reports all over the web say that "CopyHere options don't work"
probably because they are passing the flags directly as an integer
it's a pointer to a VT_I4 VARIANT structure :biggrin:
Quote from: dedndave on June 03, 2014, 10:57:11 AM
reports all over the web say that "CopyHere options don't work"
...
http://msdn.microsoft.com/en-us/library/windows/desktop/bb787866(v=vs.85).aspx:
QuoteNote In some cases, such as compressed (.zip) files, some option flags may be ignored by design.
these seem to be pertinent for zipping files/folders
there may be more
(content removed, see next post)
added a couple more
IFNDEF FOF_SILENT
FOF_SILENT EQU 4 ;Do not display a progress dialog box.
ENDIF
IFNDEF FOF_RENAMEONCOLLISION
FOF_RENAMEONCOLLISION EQU 8 ;Give the file being operated on a new name if a file with the target name already exists.
ENDIF
IFNDEF FOF_NOCONFIRMATION
FOF_NOCONFIRMATION EQU 10h ;Respond with "Yes to All" for any dialog box that is displayed.
ENDIF
IFNDEF FOF_SIMPLEPROGRESS
FOF_SIMPLEPROGRESS EQU 100h ;Display a progress dialog box but do not show the file names.
ENDIF
IFNDEF FOF_NOCONFIRMMKDIR
FOF_NOCONFIRMMKDIR EQU 200h ;Do not confirm the creation of a new directory if the operation requires one to be created.
ENDIF
IFNDEF FOF_NOERRORUI
FOF_NOERRORUI EQU 400h ;Do not display a user interface if an error occurs.
ENDIF
ok - big mistake on my part
a pointer to the VARIANT structure is not passed
the entire VARIANT structure is passed
no wonder it looked so confusing :redface:
i will have to update my interface vTable structures
for asm code, it's best to define it as dwords, so it will take a little work
updated the IShellDispatch and Folder vTable structures in Reply #103
now, we are ready to rock and roll :biggrin:
no wonder everything kept crashing
the stack was probably out of balance
we PUSH'ed too few parms (the worst kind of imbalance)
it works - and it zips the files
i just have to figure out why my UNICODE aware hex routine is adding an extra space :lol:
then, i'll post an EXE with source
Stack Pointer: 0012FFC4
CoCreateInstance: Success
IShellDispatch::NameSpace(Target): Success
Folder::CopyHere(Source): (no return value)
Folder::Release: Success
IShellDispatch::Release: Success
Stack Pointer: 0012FFC4
Press any key to continue ...
doh !
i forgot the -1 :biggrin:
INVOKE awConOut,lengthof sz0000-1,offset sz0000
here it is.....
you will have to create a folder structure
C:\ZipTest
\New
\ZipIt
Test1.txt
\Test
Test2.txt
Test1.txt and Test2.txt are small text files
you can put whatever you like into the ZipIt folder
but, i am only using a 5 second delay to let it finish
the resulting ZIP file will be C:\ZipTest\New\New.zip
now, i can work on detecting when it's done
by the way - it builds and runs as UNICODE or ANSI
EDIT: changed some UNICODE Aware stuff - no functional changes
EDIT: more uneventful UNICODE Aware updates
Since peter_asm posted an example of finding the thread and waiting for it, here's a version that uses the shell notifications:
#define WIN32_LEAN_AND_MEAN
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <shldisp.h>
#include <shlobj.h>
#include <ole2.h>
#include <cstdio>
#define CHECK_FAIL(x) \
{ \
HRESULT hr = (x); \
if(FAILED(hr)) \
{ \
printf(#x " failed with error %#x\n", hr); \
goto exit; \
} \
}
const UINT g_zipFinMsg = WM_APP + 0x50;
LRESULT __stdcall ZipWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HANDLE hExitHandle = NULL;
switch(msg)
{
case WM_CREATE:
{
LPCREATESTRUCT pCS = reinterpret_cast<LPCREATESTRUCT>(lParam);
hExitHandle = static_cast<HANDLE>(pCS->lpCreateParams);
}
break;
case g_zipFinMsg:
{
SetEvent(hExitHandle);
DestroyWindow(hwnd);
}
break;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
void CreateZipFile(PCWSTR pZip)
{
CloseHandle(CreateFile(pZip, STANDARD_RIGHTS_WRITE, 0, NULL, OPEN_ALWAYS, 0x80, NULL));
}
HWND CreateZipWaitWnd(HANDLE hEvent)
{
WNDCLASSEX wndClass = {sizeof(wndClass), 0};
wndClass.lpfnWndProc = &ZipWndProc;
wndClass.hInstance = GetModuleHandle(NULL);
wndClass.lpszClassName = L"ZipWnd";
RegisterClassEx(&wndClass);
return CreateWindowEx(
0,
wndClass.lpszClassName,
L"",
WS_OVERLAPPEDWINDOW,
1,
1,
1,
1,
HWND_MESSAGE,
NULL,
wndClass.hInstance,
hEvent
);
}
BSTR GetFullZipPath(PCWSTR pZip)
{
DWORD charsReq = GetFullPathName(pZip, 0, NULL, 0);
BSTR zipPath = SysAllocStringLen(NULL, charsReq);
GetFullPathName(pZip, charsReq, zipPath, NULL);
return zipPath;
}
void ScriptyZip(PCWSTR pZip, PCWSTR pItemsToZip)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
IShellDispatch* pShellDisp = NULL;
Folder* pZipFolder = NULL, *pOutFolder = NULL;
FolderItems* pThingsToZip = NULL;
BSTR zipFile = GetFullZipPath(pZip);
CreateZipFile(zipFile);
BSTR outputDir = SysAllocString(pItemsToZip);
VARIANT varZip = {VT_BSTR}; varZip.bstrVal = zipFile;
VARIANT varOutDir = {VT_BSTR}; varOutDir.bstrVal = outputDir;
VARIANT varDispItems = {VT_DISPATCH};
VARIANT varCopyOpts = {VT_UI4};
HRESULT hRes = SHCoCreateInstance(NULL, &CLSID_Shell, NULL, IID_IShellDispatch, (void**)&pShellDisp);
// Folder object for the zip file
CHECK_FAIL(pShellDisp->NameSpace(varZip, &pZipFolder));
// Folder object for the output folder
CHECK_FAIL(pShellDisp->NameSpace(varOutDir, &pOutFolder));
// Get the items in the folder
CHECK_FAIL(pOutFolder->Items(&pThingsToZip));
varDispItems.pdispVal = pThingsToZip;
// create the window and setup the shell notification before
// CopyHere so we can catch even the quickest of operations
HANDLE hExitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
HWND hwndWaitWindow = CreateZipWaitWnd(hExitEvent);
if(!(hExitEvent && hwndWaitWindow))
{
goto exit;
}
SHChangeNotifyEntry entry = {ILCreateFromPath(pZip), FALSE};
ULONG regId = SHChangeNotifyRegister(
hwndWaitWindow,
SHCNRF_InterruptLevel | SHCNRF_ShellLevel,
SHCNE_CREATE | SHCNE_UPDATEITEM,
g_zipFinMsg,
1,
&entry
);
pZipFolder->CopyHere(varDispItems, varCopyOpts);
DWORD ret;
while((ret = MsgWaitForMultipleObjects(1, &hExitEvent, FALSE, INFINITE, QS_ALLINPUT)) <= 1)
{
// exit event set
if(ret == 0)
{
break;
}
// message received
else
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg); // don't need TranslateMessage
}
}
}
SHChangeNotifyDeregister(regId);
ILFree((LPITEMIDLIST)entry.pidl);
exit:
if(hExitEvent)
{
CloseHandle(hExitEvent);
}
if(pThingsToZip)
{
pThingsToZip->Release();
}
if(pZipFolder)
{
pZipFolder->Release();
}
if(pOutFolder)
{
pOutFolder->Release();
}
if(pShellDisp)
{
pShellDisp->Release();
}
SysFreeString(zipFile);
SysFreeString(outputDir);
CoUninitialize();
}
int wmain(int argc, wchar_t** argv)
{
if(argc != 3)
{
return puts("Usage: ScriptyZip file.zip c:\\dir\\or\\file\\to.add");
}
else
{
ScriptyZip(argv[1], argv[2]);
}
return 0;
}
updated the attachment in Reply #111
Quote from: dedndave on June 04, 2014, 12:30:28 PM
updated the attachment in Reply #111
Win7-32: Creates New.zip at 22 bytes (after pointing bstrPathTarget and szZipFile to existing folders) but fails for IShellDispatch.NameSpace :(
So it works for you, Gunther?
@adeyblue: pShellDisp->NameSpace(varOutDir, &pOutFolder) failed with error 0x80004005 :(
Jochen,
Quote from: jj2007 on June 04, 2014, 04:36:39 PM
So it works for you, Gunther?
at least with my old XP box.
Stack Pointer: 0013FFC4
CoCreateInstance: Success
IShellDispatch::NameSpace(Target): 00000001
IShellDispatch::Release: Success
Stack Pointer: 0013FFC4
Press any key to continue...
I can test it with Win7-64 not before Friday, because I'm currently on the island of Rügen (with bad internet access :().
Gunther
Working here (after a change - hard coded paths dave?), creates a 791KB new.zip
Windows 8.1 Pro x64
Stack Pointer: 0018FF90
CoCreateInstance: Success
IShellDispatch::NameSpace(Target): Success
Folder::CopyHere(Source): (no return value)
Folder::Release: Success
IShellDispatch::Release: Success
Stack Pointer: 0018FF90
Dave,
Did you test CopyHere's option flags ?
I'm curious which of them work for zip folders.
no - i didn't test them yet
but - i zipped the contents of masm32\include (longer time delay), and with the current flagset - no progress dialog, as desired
as for the hard-coded paths, yes
i wanted to spend time figuring out how the COM stuff worked
not spend time writing a fancy program - lol
the way it's written, you create the following folders
C:\ZipTest
C:\ZipTest\New
C:\ZipTest\ZipIt
the stuff you put into the ZipIt folder will be zipped
so, for test purposes, i put a text file, and a folder containing a text file into the ZipIt folder
Quote from: dedndave on June 04, 2014, 10:53:49 PM
the stuff you put into the ZipIt folder will be zipped
so, for test purposes, i put a text file, and a folder containing a text file into the ZipIt folder
That makes things a bit clearer.
Gunther
sorry - i made a mistake on that last attachment
UCSTR bstrPathSource, "C:\masm32\include",0 ;"C:\ZipTest\ZipIt",0
for testing, i zipped the contents of the include folder
but, i forgot to remove the test string :(
UCSTR bstrPathSource, "C:\ZipTest\ZipIt",0
so, the ISDZip4.zip update fixes that issue (Reply #111)
Reply #111 still doesn't work for me, the NameSpace thingy fails :(
I also don't see the compressed folder option when I right-click on a file; regsvr32 zipfldr.dll didn't help (yes, I rebooted).
I'd love to have a 3rd party independent zipper. Unzipping works like a charm now with a wrapper for Jørgen Ibsen's tiny inflate (tinf) library (http://www.ibsensoftware.com/download.html):
include \masm32\MasmBasic\MasmBasic.inc ; download (http://masm32.com/board/index.php?topic=94.0)
Init
SetCpUtf8 ; filenames in zipfiles are UTF-8 encoded, so we force codepage UTF-8
Print "*** Attention: Your console may not be able to display all Unicode file names correctly ***", CrLf$, CrLf$
Print cStyle$("#\tSize\tDate\t\tTime\tFilename\n")
UnzipInit "\Masm32\MasmBasic\Res\UnicodeTest.zip"
; UnzipInit returns a comment or an empty string in eax, the #files in edx, and the Sign? flag to signal an error
.if Sign?
Print eax ; if the sign flag is set, eax points to an error message
.else
push eax ; save the comment returned by UnzipInit
For_ ecx=0 To edx-1
PrintLine Str$(ecx+1), Tb$, Str$(GfSize(ecx)), Tb$, GfDate$(ecx), Spc2$, GfTime$(ecx), Tb$, Files$(ecx)
Next
pop eax
PrintLine "Zipfile comment:", CrLf$, eax
.While 1
Inkey At(0, 15) "Enter the number of the file you want to see, or Escape to exit", CrLf$, CrLf$, "File #: ", Space$(40)
.Break .if eax==VK_ESCAPE
lea ecx, [eax-"1"]
.if ecx<=Files$(?)
Locate(10, 17)
Print " ", Files$(ecx) ; erase rest of previous filename
FileWrite Files$(ecx), UnzipFile(ecx), GfLastWrite(ecx) ; restore the file, and don't forget the timestamp
Launch Cat$("..\RichMasm.exe "+Files$(ecx)+"#UTF")
.endif
.Endw
UnzipExit ; cleanup
.endif
Exit
end start
One hilarious thing is that you need at least two archivers to do simple things like adding files or comments:
- 7-Zip to add and delete files
- WinZip to add comments
it seems like unzipping is by far the more difficult task
there are so many different flavours of zip files :redface:
Quote from: dedndave on June 05, 2014, 12:26:39 PM
it seems like unzipping is by far the more difficult task
there are so many different flavours of zip files :redface:
yes, indeed. There are historical reasons for that. One reason is the Welch patent.
Gunther
yah - and, it has evolved over about 30 years - lol
the unzip algorithm is probably very simple, compared to the zip algo
Quote from: Gunther on June 05, 2014, 04:23:27 PM
One reason is the Welch patent.
Shouldn't that be "was the Welch patent"?
Michael,
Quote from: MichaelW on June 06, 2014, 05:06:26 AM
Shouldn't that be "was the Welch patent"?
yes, of course. It's history now, but it wasn't during the 90s.
Gunther
we have been trying to mimic the drag-n-drop method (i guess it's a drop target)
maybe we want to mimic the Send To shell extension, instead
although - that won't give us any control over the dialogs that may appear
it may give us insight as to the "right" way to know when the copy is done
the file lock method that i mentioned earlier may not work very well
at this point, the thread method seems best - but doesn't seem ideal, either
Quote from: jj2007 on June 04, 2014, 04:36:39 PM
@adeyblue: pShellDisp->NameSpace(varOutDir, &pOutFolder) failed with error 0x80004005 :(
Strange. I'm sure I tried it and it worked before I posted it. But trying it just now and it doesn't just not work, none of the functions fail, and it just straight up crashes. Anyway, I've updated it and tried it and fingers crossed it's good to go now (on Win7 x64).
EDIT: Or not. Argh! Seems MS don't want you using pidls in VARIANT's. In XP the type Namespace() wants is VT_ARRAY | VT_UI1, in 7 that's changed to VT_VECTOR | VT_UI1. Just to make matters worse, 7 has reused VT_ARRAY | VT_UI1 to mean a completely different structure. So I'll go revert the code back to using names.
Quote from: dedndave on June 06, 2014, 05:55:42 AM
we have been trying to mimic the drag-n-drop method (i guess it's a drop target)
maybe we want to mimic the Send To shell extension, instead
Do you shoot messengers around here? Send To is yet
another wrapper around drag & drop (http://i.imgur.com/Q5PelXT.png), at least in the case of zipfldr.
Quote from: dedndave on June 06, 2014, 05:55:42 AM
we have been trying to mimic the drag-n-drop method (i guess it's a drop target)
...
I think that it was adeyblue who has been trying to mimic the drag-n-drop method in his article.
We were using top level scriptable shell interfaces and two their methods: NameSpace and CopyHere.
The main problem was to wait for shell's threads termination . I've never debugged multythreaded applications before . It was an exciting quest .
Quote from: dedndave on June 06, 2014, 05:55:42 AM
...
maybe we want to mimic the Send To shell extension, instead
...
Sure ! A day or two ago I was playing with powershell thinking about this method .
So here's the sequence of actions in short:
creating Shell object -> NameSpace("C:\ZIPIT") ->ParseName("test.asm") -> now that we have IFolderItem object for out test file we want to see its Verbs -> IFolderItem ::Verbs -> now I dumped the result (count of verbs =15) ,there were all those items that you see when cklicking RMB ... EXCEPT Send To ... (I remember it was #8 - an empty line ) .
Problem #1: how to find Send To submenu of the context menu and parse its options
Problem #2: what if there's no "Zip folder" option in the "Send to" submenu ? Should we register it ? How?
Also I was thinking about creating custom drop target like that empty file on the desktop with .zfSendToTraget extension which zips everything that you drag'n'drop on it .
i don't think our "final" solution will be to use the Send To mechanism
however, we might be able to gain some insight from watching it :P
that takes care of problem #2
problem #1 is a little tricky
but, we should be able to follow the chain through the registry
a couple ways come to mind:
1) dredge through the registry chain
2) pretend we want to write a Send To shell extension and see how it works - lol
one of the zipfldr.dll resources is an INF file that registers it
[Version]
Signature="$CHICAGO$"
[RegAlways]
AddReg=ZipFldr.RegAlways
DelReg=Delete.RegAlways
RequiredEngine=SETUPAPI,%ERR_NOSETUPAPI%
[RegTakeDefault]
AddReg=ZipFldr.RegTakeDefault
DelReg=Delete.RegTakeDefault
RequiredEngine=SETUPAPI,%ERR_NOSETUPAPI%
[Unreg]
DelReg=ZipFldr.RegAlways,ZipFldr.RegTakeDefault
[ZipFldr.RegTakeDefault]
HKCR,.zip,,,CompressedFolder
HKCR,.zip,"Content Type",,"application/x-zip-compressed"
[Delete.RegTakeDefault]
[ZipFldr.RegAlways]
HKCR,CLSID\%CLSID_ZIPFLD%,,,CompressedFolder
HKCR,CLSID\%CLSID_ZIPFLD%\DefaultIcon,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPFLD%\InProcServer32,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPFLD%\InProcServer32,ThreadingModel,,Apartment
HKCR,CLSID\%CLSID_ZIPFLD%\ProgID,,,CompressedFolder
HKCR,CLSID\%CLSID_ZIPFLD%\ShellFolder,Attributes,%REGDW%,0x200001a0
HKCR,CLSID\%CLSID_ZIPFLD%\ShellFolder,UseDropHandler,,
HKCR,.zip\OpenWithProgids,CompressedFolder
HKCR,.zip\CompressedFolder\ShellNew,Data,%REGBINARY%,50,4B,05,06,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
HKCR,"Applications\zipfldr.dll",NoOpenWith,,
HKCR,CompressedFolder,,,"%COMPRESSED_FOLDER%"
HKCR,CompressedFolder,"FriendlyTypeName",%REGEXSZ%,"@%_SYS_MOD_PATH%,-10195"
HKCR,CompressedFolder\CLSID,,,%CLSID_ZIPFLD%
HKCR,"SystemFileAssociations\.zip\CLSID",,,%CLSID_ZIPFLD%
HKCR,CompressedFolder\DefaultIcon,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CompressedFolder\Shell\Open\Command,,,"rundll32.exe zipfldr.dll,RouteTheCall %%L"
HKCR,"CompressedFolder\shell\open",BrowserFlags,%REGDW%,0x0010
HKCR,"CompressedFolder\shell\open",ExplorerFlags,%REGDW%,0x0012
HKCR,CompressedFolder\Shell\Open\ddeexec,,,"[ViewFolder(""%%l"", %%I, %%S)]"
HKCR,CompressedFolder\Shell\Open\ddeexec,NoActivateHandler
HKCR,CompressedFolder\Shell\Open\ddeexec\application,,,"Folders"
HKCR,CompressedFolder\Shell\Open\ddeexec\ifexec,,,"[]"
HKCR,CompressedFolder\Shell\Open\ddeexec\topic,,,"AppProperties"
HKCR,CompressedFolder\shell\find,SuppressionPolicy,%REGDW%,0x00000080
HKCR,CompressedFolder\shell\find\command,,%REGEXSZ%,"%25%\Explorer.exe"
HKCR,CompressedFolder\shell\find\ddeexec,,,"[FindFolder(""%l"", %I)]"
HKCR,CompressedFolder\shell\find\ddeexec\application,,,"Folders"
HKCR,CompressedFolder\shell\find\ddeexec\topic,,,"AppProperties"
HKCR,CompressedFolder\ShellEx\ContextMenuHandlers\%CLSID_ZIPCONTEXTMENU%,,,"Compressed (zipped) Folder Menu"
HKCR,CompressedFolder\ShellEx\DropHandler,,,"%CLSID_ZipFolderDropHandler%"
HKCR,CompressedFolder\ShellEx\StorageHandler,,,"%CLSID_ZIPFLD%"
HKCR,MIME\Database\Content Type\application/x-zip-compressed,"Extension",,".zip"
HKCR,CLSID\%CLSID_ZIPDRAG%,,,"Compressed (zipped) Folder Right Drag Handler"
HKCR,CLSID\%CLSID_ZIPDRAG%\InProcServer32,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPDRAG%\InProcServer32,"ThreadingModel",,"Apartment"
HKCR,Folder\shellex\DragDropHandlers\%CLSID_ZIPDRAG%
HKCR,CLSID\%CLSID_ZIPSENDTO%,,,"Compressed (zipped) Folder SendTo Target"
HKCR,CLSID\%CLSID_ZIPSENDTO%,"FriendlyTypeName",%REGEXSZ%,"@%_SYS_MOD_PATH%,-10226"
HKCR,CLSID\%CLSID_ZIPSENDTO%\InProcServer32,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPSENDTO%\InProcServer32,"ThreadingModel",,"Apartment"
HKCR,CLSID\%CLSID_ZIPSENDTO%\DefaultIcon,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPSENDTO%\ShellEx\DropHandler,,,"%CLSID_ZIPSENDTO%"
HKCR,CLSID\%CLSID_ZIPSENDTO%,NeverShowExt
HKCR,CLSID\%CLSID_ZIPSENDTO%,NoOpen,,"%NOOPEN%"
HKCR,CLSID\%CLSID_ZIPSENDTO%,EditFlags,%REGDW%,0x00000001
HKCR,CLSID\%CLSID_ZIPCONTEXTMENU%,,,"Compressed (zipped) Folder Context Menu"
HKCR,CLSID\%CLSID_ZIPCONTEXTMENU%\InProcServer32,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZIPCONTEXTMENU%\InProcServer32,"ThreadingModel",,"Apartment"
HKCR,CLSID\%CLSID_ZipFolderDropHandler%,,,"Compressed (zipped) Folder DropHandler"
HKCR,CLSID\%CLSID_ZipFolderDropHandler%\InProcServer32,,%REGEXSZ%,%_SYS_MOD_PATH%
HKCR,CLSID\%CLSID_ZipFolderDropHandler%\InProcServer32,"ThreadingModel",,"Apartment"
HKCR,.ZFSendToTarget,,,CLSID\%CLSID_ZIPSENDTO%
HKLM,"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",%CLSID_ZIPFLD%,,"%COMPRESSED_FOLDER%"
HKLM,"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",%CLSID_ZIPDRAG%,,"Compressed (zipped) Folder Right Drag Handler"
HKLM,"Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved",%CLSID_ZIPSENDTO%,,"Compressed (zipped) Folder SendTo Target"
[Delete.RegAlways]
HKCR,CLSID\%CLSID_ZIPFLD%\shellex\ExtShellFolderViews
HKCR,CompressedFolder\ShellEx\ContextMenuHandlers\%CLSID_ZIPFLD%
HKCR,CompressedFolder\Shell\Explore
[Strings]
REGDW=0x00010001
REGEXSZ=0x00020000
REGBINARY=0x00000001
CLSID_ZIPFLD={E88DCCE0-B7B3-11d1-A9F0-00AA0060FA31}
CLSID_ZIPDRAG={BD472F60-27FA-11cf-B8B4-444553540000}
CLSID_ZIPSENDTO={888DCA60-FC0A-11CF-8F0F-00C04FD7D062}
CLSID_ZIPCONTEXTMENU={b8cdcb65-b1bf-4b42-9428-1dfdb7ee92af}
CLSID_ZipFolderDropHandler={ed9d80b9-d157-457b-9192-0e7280313bf0}
COMPRESSED_FOLDER = "Compressed (zipped) Folder"
NOOPEN="Drag Files onto this icon to compress them."
ERR_NOSETUPAPI="Can not register ZIPFLDR.DLL because SETUPAPI.DLL is missing."
[End]
looking at the code, it seems that it creates a COM interface
it would take some time to wade through it
I'm totally agree
Below I bookmarked several msdn Shell references for further reading
Shell Developer's Guide (//http://)
Working with Shell Extensions (http://msdn.microsoft.com/en-us/library/windows/desktop/ff468984(v=vs.85).aspx)
Creating Shell Extension Handlers (http://msdn.microsoft.com/en-us/library/windows/desktop/cc144067(v=vs.85).aspx)
Registering Shell Extension Handlers (http://msdn.microsoft.com/en-us/library/windows/desktop/cc144110(v=vs.85).aspx)
Well , adding "Compressed (zipped) folder" option to "Send to" submenu is not very complicated task:
How to Restore Missing "Compressed (zipped) Folder" Item in Send To Menu in Windows XP, ... (http://www.askvg.com/how-to-restore-missing-compressed-zipped-folder-item-in-send-to-menu-in-windows-xp-and-vista/)
Edit the Windows 7 "Send To" Menu Without Third-Party Tools (http://lifehacker.com/5640841/edit-the-windows-7-send-to-menu-without-third-party-tools)
KB310270 (http://support.microsoft.com/kb/310270)
Next,
all we need to check if "Compressed (zipped) folder" exists in "Send to" submenu is the list of Send to folder's items . We don't even have to know the full path to it .
Shell special folder ID for "Send to " = 9 (see here (http://masm32.com/board/index.php?topic=2845.0) ) ... so we just call
QuoteIDispatch::NameSpace(9)
and get required folder object .
QuoteIDispatch::NameSpace(9)
that just opens the SendTo target folder :P
a few interesting ordinals...
OLEAUT32.2 SysAllocString
OLEAUT32.8 VariantInit
OLEAUT32.9 VariantClear
OLEAUT32.17 SafeArrayGetDim
OLEAUT32.19 SafeArrayGetUBound
OLEAUT32.20 SafeArrayGetLBound
OLEAUT32.23 SafeArrayAccessData
OLEAUT32.24 SafeArrayUnaccessData
OLEAUT32.184 SystemTimeToVariantTime
OLEAUT32.411 SafeArrayCreateVector
SHELL32.16 ILFindLastID
SHELL32.17 ILRemoveLastID
SHELL32.18 ILClone
SHELL32.25 ILCombine
SHELL32.28 SHILCreateFromPath
SHELL32.67 Shell_MergeMenus
SHELL32.73 SHShellFolderView_Message
SHELL32.155 ILFree
SHELL32.165 SHCreateDirectory
SHELL32.182 ShellMessageBoxW
SHELL32.190 ILCreateFromPathW
SHELL32.256 SHCreateShellFolderView
SHELL32.743 SHCreateFileExtractIconW
SHELL32.744 Create_IEnumUICommand
SHELL32.745 Create_IUIElement
SHLWAPI.16 SHCreateThread
SHLWAPI.107 LoadStringWrapW, wrapper for LoadString
SHLWAPI.199 IUnknown_Set
SHLWAPI.215 SHAnsiToUnicode
SHLWAPI.217 SHUnicodeToAnsi
SHLWAPI.219 QISearch
SHLWAPI.346 SHUnicodeToUnicode
SHLWAPI.354 SHFormatDateTimeW
SHLWAPI.456 PathIsValidCharW
SHLWAPI.485 MapWin32ErrorToSTG
hmmmm....
try creating a file with the extension ".ZFSendToTarget" and writing to it
Next item to my TODO list:
I'll try the code from this brilliant article (http://blogs.msdn.com/b/oldnewthing/archive/2008/07/24/8768095.aspx) with zip folders as a target .
yes, Raymond Chen is always a good read
part two...
http://blogs.msdn.com/b/oldnewthing/archive/2008/07/25/8770548.aspx (http://blogs.msdn.com/b/oldnewthing/archive/2008/07/25/8770548.aspx)
QuoteSHSetInstanceExplorer
Well , as it always happens - in order to find right solution we must take a pause till the spontaneous insight :t
Part two contains reference to
Reading a contract from the other side: SHSetInstanceExplorer and SHGetInstanceExplorer (http://blogs.msdn.com/b/oldnewthing/archive/2008/05/28/8555658.aspx) article .
yes - i was just reading that one :biggrin:
Sad to say but I cannot try it immediately in MS VS and under the debugger
you're not missing any fun
QuoteIt seems that zipfldr.dll does not call SHGetInstanceExplorer despite creating worker threads.
This may be a bug in zipfldr.dll.
my guess is that it's not a bug
rather, a more involved method is used
but - this may give me a clue where to look
i can look at the thread code in zipfldr.dll
I found the origin of the quote :manipulating zipfldr.dll to create a Compressed folder (http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/d3e347cc-f4dc-44a6-8f84-977f958d89c6/manipulating-zipfldrdll-to-create-a-compressed-folder?forum=windowsgeneraldevelopmentissues)
no doubt that it's not a bug
I'll look into the zipfldr's thread too
i tried watching the reference count 3 ways:
IID_IShellDispatch
IID_Folder
IID_FolderItems
no luck on any of that
i have seen several examples where they look at Items.Count
that's probably a good method about 99.9% of the time - lol
just because the count is correct does not mean the last item was completely zipped and added
the Items.Count method would be reliable if we added a "bogey" item to the list
if we could be sure it was the last item to be added, we would know all the other items were complete
seems like a hack, but a good one :biggrin:
Went back and looked at the 'doesn't use a separate thread' IStorage method, swapped one hack for another and success! Single thread zipping - with subfolders.
Except, it doesn't work on XP. And you can't have two files anywhere in the zip with the same filename (zipfldr helpfully decides that temporary files it's completely done with should be read-only), and I'm sure there's something else I've forgotten.
So yeah, quite a lot of restrictions unless another mass of zipfldr owned hoops are jumped through. If nothing else it's an example of how to loop through directories and copy them somewhere else with the shell interfaces.
:t
still, nice to have an IStream example - useful for other things, too
Dave,
Quote from: dedndave on June 08, 2014, 09:27:40 PM
yes, Raymond Chen is always a good read
who is Raymond Chen?
Gunther
http://blogs.msdn.com/b/oldnewthing/
Raymond Chen is a MS programmer, that often blogs about code (see Sinsi's link)
he usually has a pretty good grasp on the code
and - i always learn by reading from the comments section, too
the same is true on CodeProject - the comments are a must-read
Dave, Sinsi,
thank you for that information. :t
Gunther