{"id":283,"date":"2010-01-10T16:38:06","date_gmt":"2010-01-10T14:38:06","guid":{"rendered":"http:\/\/www.ticklishtechs.net\/2010\/01\/10\/shell-lib-reloaded-another-os-another-bug\/"},"modified":"2020-08-13T20:45:26","modified_gmt":"2020-08-13T18:45:26","slug":"shell-lib-reloaded-another-os-another-bug","status":"publish","type":"post","link":"https:\/\/www.ticklishtechs.net\/2010\/01\/10\/shell-lib-reloaded-another-os-another-bug\/","title":{"rendered":"Shell-Lib reloaded: Another OS, another bug."},"content":{"rendered":"
Some time ago we blogged about a nifty little .net assembly that enables you to access the basic Windows shell operations<\/a> in shell32.dll . You may want to do so especially in Win7 because your file operations integrate with the fancy Win7 dialogs, the flashing progress bar etc. <\/p>\n The problem we blogged about in the former post<\/a> was a missing "Pack"-instruction in the marshalling-description of a structure. I guess we didn’t test it with WinXP64… but some days ago we tests it with Win7\/64 and it failed. We figured out that we introduced a new bug that occurred on 64 bit machines only. After some more testing we found out that: <\/p>\n It seems that the "SHFILEOPSTRUCT" structure in shell32.dll has different layouts in Win7\/64 and Win7\/32. I guess the guys at MS aim for performance and tell the compiler to optimize. The compiler optimizes by assuming the optimal Pack-value, which usually is the byte-width of the processor; 4 for 32bit, 8 for 64bit. <\/p>\n The "Pack"-value controls the alignment of the starting addresses of each single member of a structure (often called "offset"). <\/p>\n Let’s say you have this struct: <\/p>\n struct MammaMia <\/code> <\/p>\n Usually you don’t care for the exact way your data is stored in memory, but when you pass it from .NET to the Win-API you have to make sure Win-API receives and returns exactly the right format. This process is called "Marshalling". <\/p>\n One crucial instruction for doing so is the StructLayout attribute. <\/p>\n [StructLayout(LayoutKind.Sequential, Pack = 1)] <\/code> <\/p>\n (Note: There are a lot more options for the StructLayout attribute and a lot more attributes that help you at marshalling.) <\/p>\n LayoutKind.Sequential<\/strong> means that your data is represented in memory in the same order as you declared it: First "sweet16", than "bite" and then "tanga". <\/p>\n Pack=1<\/strong> tells .NET that every byte can be the starting point of the next member. So .NET produces this structure in memory:<\/p>\n <\/strong><\/p>\n <\/p>\n <\/p>\n Now Pack=2<\/strong> makes sure that only every 2nd byte can be the starting point of the next member. That leads – of course – to unused bytes in memory: <\/p>\n <\/p>\n <\/p>\n The higher the Pack-value, the more unused bytes you get:<\/p>\n <\/p>\n Is this a waste of memory? Sure. But processors can access those addresses a bit faster than other addresses. So it’s an performance\/memory tradeoff. <\/p>\n After some more testing, I figured out that the correct Pack-value for Win7\/64 is 8, while the correct Pack-value for Win7\/32 is 2 (see former post). Why 2? I’d expect 4 here. I don’t know and Microsoft wouldn’t provide the source code I guess. Now it was obvious how to make die ShellLib-assembly work with 32 and 64bit: Check what machine we run on and use a different marshalling. Since I had to declare different structs here, the major code is copying data back and forth, not very interesting.<\/p>\n The only interesting part here is: How do you check on what kind of machine you run? I was looking for some property at System.Environment, but didn\u2019t find anything. Then Andreas<\/a> pointed me to this<\/a> post and the solution is too simple to cross one\u2019s mind:<\/p>\n \u201cJust do a IntPtr.Size<\/strong> (static method) and since IntPtr is designed to be an integer whose size is platform specific, it will be 8 if you are on a 64-bit machine and 4 if you are on a 32-bit machine<\/strong>.\u201c<\/p>\n\n
What effect does the Pack-value have at all?<\/h3>\n
<\/p>\n
{
public int16 sweet16;
public byte bite;
public string tanga;
} <\/p>\n <\/p>\n
struct MammaMia
{
public int16 sweet16;
public byte bite;
public string tanga;
} <\/p>\n\n\n
\n Pack=1<\/strong><\/td>\n <\/td>\n<\/tr>\n \n byte<\/td>\n data<\/td>\n<\/tr>\n \n 0<\/td>\n sweet16<\/td>\n<\/tr>\n \n 1<\/td>\n sweet16<\/td>\n<\/tr>\n \n 2<\/td>\n bite<\/td>\n<\/tr>\n \n 3<\/td>\n tanga<\/td>\n<\/tr>\n \n 4<\/td>\n tanga<\/td>\n<\/tr>\n \n \u2026<\/td>\n \u2026<\/td>\n<\/tr>\n \n 20<\/td>\n last character of tanga<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n \n\n
\n Pack=2<\/strong><\/td>\n <\/td>\n<\/tr>\n \n byte<\/td>\n data<\/td>\n<\/tr>\n \n 0<\/td>\n sweet16<\/td>\n<\/tr>\n \n 1<\/td>\n sweet16<\/td>\n<\/tr>\n \n 2<\/td>\n bite<\/td>\n<\/tr>\n \n 3<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 4<\/td>\n tanga<\/td>\n<\/tr>\n \n 5<\/td>\n tanga<\/td>\n<\/tr>\n \n \u2026<\/td>\n \u2026<\/td>\n<\/tr>\n \n 21<\/td>\n last character of tanga<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n \n\n
\n Pack=4<\/strong><\/td>\n <\/td>\n<\/tr>\n \n byte<\/td>\n data<\/td>\n<\/tr>\n \n 0<\/td>\n sweet16<\/td>\n<\/tr>\n \n 1<\/td>\n sweet16<\/td>\n<\/tr>\n \n 2<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 3<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 4<\/td>\n bite<\/td>\n<\/tr>\n \n 5<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 6<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 7<\/td>\n ??? (unused)<\/td>\n<\/tr>\n \n 8<\/td>\n tanga<\/td>\n<\/tr>\n \n 9<\/td>\n tanga<\/td>\n<\/tr>\n \n \u2026<\/td>\n \u2026<\/td>\n<\/tr>\n \n 25<\/td>\n last character of tanga<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n
Maybe in former Windows version they preferred a middle-course between performance and saving memory. <\/p>\nThe simple solution<\/h3>\n