{"id":3503,"date":"2013-08-14T07:00:00","date_gmt":"2013-08-14T07:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2013\/08\/14\/whats-the-point-of-letting-you-change-the-gcl_cbclsextra-if-it-has-no-effect\/"},"modified":"2013-08-14T07:00:00","modified_gmt":"2013-08-14T07:00:00","slug":"whats-the-point-of-letting-you-change-the-gcl_cbclsextra-if-it-has-no-effect","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20130814-00\/?p=3503","title":{"rendered":"What&#039;s the point of letting you change the GCL_CBCLSEXTRA if it has no effect?"},"content":{"rendered":"<p>\nThe documentation for\n<a HREF=\"http:\/\/msdn.microsoft.com\/library\/ms633589\">\nthe <code>Set&shy;Class&shy;Long&shy;Ptr<\/code> function<\/a>\nmentions\n<\/p>\n<blockquote CLASS=\"q\"><p>\n<b>GCL_CB&shy;CLS&shy;EXTRA<\/b>:\nSets the size, in bytes, of the extra memory associated with the class.\nSetting this value does not change the number of extra bytes already allocated.\n<\/p><\/blockquote>\n<p>\nWhat&#8217;s the point of letting the application change a value if it\nhas no effect?\n<\/p>\n<p>\nThe <code>GCL_CB&shy;CLS&shy;EXTRA<\/code>\nclass long grants access to the\n<code>cbCls&shy;Extra<\/code> value that was originally\npassed in the\n<code>WND&shy;CLASS<\/code>\nstructure when you called\n<code>Register&shy;Class<\/code>,\nor the Ex-versions <i>mutatus mutandis<\/i>.\nThe intent is for it to be used with\n<code>Get&shy;Class&shy;Long<\/code> so you can read\nthe value back,\nin case you forgot,\nor if you are inspecting somebody else&#8217;s class\n(for example, because you want to superclass it,\nalthough <code>Get&shy;Class&shy;Info<\/code> is probably\na better choice).\nBut since\n<code>Get&shy;Class&shy;Long<\/code> and\n<code>Set&shy;Class&shy;Long<\/code> take the\nsame class index parameter,\nonce it&#8217;s defined for one, it&#8217;s defined for the other.\n<\/p>\n<p>\nOkay, well, first, let&#8217;s explain why it has no effect:\nThe class has already been created.\nThe <code>cbCls&shy;Extra<\/code> tells the window manager\nhow much extra memory to allocate in the class when it is created.\nAfter the class is created, the value isn&#8217;t really used any more,\nbut Windows hangs on to the value since it needs to report the\nvalue when you call\n<code>Get&shy;Class&shy;Info<\/code>.\nTrying to change the value is like\nmaking changes to a blueprint after the building has\nfinished construction.\nThe blueprints are still on file at the planning office,\nbut changing them has no effect on the building.\n(Though it will\nmislead the fire chief who is studying the blueprints\nin order to\n<a HREF=\"http:\/\/www.codinghorror.com\/blog\/2009\/02\/are-you-an-expert.html\">\ndecide how to put out the fire that is raging on one\nof your upper floors<\/a>.)\n<\/p>\n<p>\nOkay, so why does Windows let you change the values if they\nhave no effect?\n<\/p>\n<p>\nLet&#8217;s look at the values of those class longs:\n<\/p>\n<pre>\n#define GCL_MENUNAME        (-8)\n#define GCL_HBRBACKGROUND   (-10)\n#define GCL_HCURSOR         (-12)\n#define GCL_HICON           (-14)\n#define GCL_HMODULE         (-16)\n#define GCL_CBWNDEXTRA      (-18)\n#define GCL_CBCLSEXTRA      (-20)\n#define GCL_WNDPROC         (-24)\n<\/pre>\n<p>\nHow very strange.\nThey&#8217;re all even numbers,\nand negative, too.\nAnd the value <code>-22<\/code> is skipped,\nwhich lies between\n<code>GCL_CB&shy;CLS&shy;EXTRA<\/code>\nand\n<code>GCL_WND&shy;PROC<\/code>.\n<\/p>\n<p>\nLet&#8217;s look at what the values were in 16-bit Windows:\n<\/p>\n<pre>\n#define GCL_MENUNAME        (-8)\n#define GCW_HBRBACKGROUND   (-10)\n#define GCW_HCURSOR         (-12)\n#define GCW_HICON           (-14)\n#define GCW_HMODULE         (-16)\n#define GCW_CBWNDEXTRA      (-18)\n#define GCW_CBCLSEXTRA      (-20)\n#define GCL_WNDPROC         (-24)\n#define GCW_STYLE           (-26)\n<\/pre>\n<p>\nOkay, now it looks even more suspicious.\nAll of the special class values were words\n(as indicated by the <code>W<\/code> in <code>GCW<\/code>),\nexcept for two longs (<code>GCL<\/code>),\nand the gap exactly falls right where a long would go.\n<\/p>\n<p>\nYou&#8217;ve probably figured it out by now.\nIn 16-bit Windows, the internal <code>CLASS<\/code>\nstructure looked like this:\n<\/p>\n<pre>\ntypedef struct <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2008\/03\/27\/8338530.aspx\">tag<\/a>CLASS\n{\n    ... blah blah blah ...\n    UINT      style;            \/\/ offset -26 from extraBytes\n    WNDPROC   lpfnWndProc;      \/\/ offset -24 from extraBytes\n    int       cbClsExtra;       \/\/ offset -20 from extraBytes\n    int       cbWndExtra;       \/\/ offset -18 from extraBytes\n    HMODULE   hModule;          \/\/ offset -16 from extraBytes\n    HICON     hIcon;            \/\/ offset -14 from extraBytes\n    HCURSOR   hCursor;          \/\/ offset -12 from extraBytes\n    HBRUSH    hbrBackground;    \/\/ offset -10 from extraBytes\n    LPSTR     lpszMenuName;     \/\/ offset -8 from extraBytes\n    LPSTR     lpszClassName;    \/\/ offset -4 from extraBytes\n    BYTE      <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2004\/08\/26\/220873.aspx\">extraBytes[1]<\/a>;    \/\/ offset 0 (extra bytes start here)\n}\nCLASS;\n<\/pre>\n<p>\nWhen a class was created, the class extra bytes were appended\ndirectly to the <code>CLASS<\/code> structure, which meant that\nyou could use negative offsets to access the internal class\nstructures.\n<\/p>\n<pre>\nWORD GetClassWord(HWND hwnd, int index)\n{\n    CLASS *pcls = GetWindowClassPointer(hwnd);\n    WORD *pw = (WORD*)&amp;pcls-&gt;cls_extraBytes[index];\n    return *pw;\n}\nLONG GetClassLong(HWND hwnd, int index)\n{\n    CLASS *pcls = GetWindowClassPointer(hwnd);\n    LONG *pl = (LONG*)&amp;pcls-&gt;cls_extraBytes[index];\n    return *pl;\n}\nWORD SetClassWord(HWND hwnd, int index, WORD wNewValue)\n{\n    CLASS *pcls = GetWindowClassPointer(hwnd);\n    WORD *pw = (WORD*)&amp;pcls-&gt;cls_extraBytes[index];\n    WORD wPrevValue = *pw;\n    *pw = wNewValue;\n    return wPrevValue;\n}\nLONG SetClassLong(HWND hwnd, int index, LONG lNewValue)\n{\n    CLASS *pcls = GetWindowClassPointer(hwnd);\n    LONG *pl = (LONG*)&amp;pcls-&gt;cls_extraBytes[index];\n    LONG lPrevValue = *pl;\n    *pl = lNewValue;\n    return lPrevValue;\n}\n<\/pre>\n<p>\nExcept of course that the original code was written in\nassembly language, so it was more like\n<\/p>\n<pre>\nFindClassExtraBytes proc\n      mov  bx, [bp][2][4] ;; caller's hwnd\n      mov  bx, [bx].wnd_pcls ;; get the class for the window\n      add  bx, cls_extraBytes ;; move to extra bytes\n      add  bx, [bp][2][4][2] ;; pointer to the requested bytes\n      ret\n;; use helper macros from cmacros.inc\ncProc GetClassWord, &lt;FAR, PUBLIC&gt;\nParmW hwnd\nParmW index\ncBegin\n      call FindClassExtraBytes\n      mov  ax, [bx]      ;; get the word\ncEnd\ncProc GetClassLong, &lt;FAR, PUBLIC&gt;\nParmW hwnd\nParmW index\ncBegin\n      call FindClassExtraBytes\n      mov  ax, [bx]      ;; get the low word\n      mov  dx, [bx][2]   ;; get the high word\ncEnd\ncProc SetClassWord, &lt;FAR, PUBLIC&gt;\nParmW hwnd\nParmW index\nParmW newValue\ncBegin\n      call FindClassExtraBytes\n      mov  ax, newValue\n      xchg ax, [bx]      ;; exchange value\ncEnd\ncProc SetClassLong, &lt;FAR, PUBLIC&gt;\nParmW hwnd\nParmW index\nParDL newValue\ncBegin\n      call FindClassExtraBytes\n      mov  ax, newValue[0] ;; low word\n      mov  dx, newValue[2] ;; high word\n      xchg ax, [bx][0]     ;; exchange low word\n      xchg dx, [bx][2]     ;; exchange high word\ncEnd\n<\/pre>\n<p>\nIn other words, the negative offsets were exactly the values\nneeded to access the corresponding fixed fields in the\n<code>CLASS<\/code> structure as if they were extra bytes.\n(Again, I marvel at how 16-bit Windows\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2006\/08\/09\/693280.aspx\">\nmanaged to accomplish\nwhat it did in so little code<\/a>.\nThe actual code was even tighter than this.)\n<\/p>\n<p>\nThere were programs that said,\n&#8220;Hey, since I know I can change this value all I want,\nand it won&#8217;t have any effect,\nI can use it as a <i>secret hiding place<\/i>,&#8221;\nand instead of storing data in a more sane location,\nthey just squirreled it away in the\n<code>GCL_CB&shy;CLS&shy;EXTRA<\/code>.\n<\/p>\n<p>\nWindows blocked changes to\n<code>GCL_CB&shy;CLS&shy;EXTRA<\/code> starting in\nWindows&nbsp;95,\nbut a compatibility loophole was created\nso that 16-bit programs written for older versions of Windows\ncould still get the old behavior where they could modify\na value that had no effect,\njust so that they could use it as a secret hiding place.\n<\/p>\n<p>\nBut for all 32-bit programs and newer 16-bit programs,\nattempting to modify the\n<code>cbCls&shy;Extra<\/code> value will fail with\n<code>ERROR_INVALID_PARAMETER<\/code>.\n<\/p>\n<p>\n<b>Bonus chatter<\/b>:\nAnother secret hiding place that applications discovered\nwas storing data in the\nwindow extended style bits,\n<code>dwEx&shy;Style<\/code>.\n&#8220;Thanks, Windows, for adding four more bytes of data to\neach window.\nI&#8217;ll use it to store a pointer!\n(I&#8217;m sure Windows won&#8217;t mind.)&#8221;\nThere is code in the window manager to enforce the rule\nthat you must use <code>Set&shy;Window&shy;Pos<\/code> to\nchange the\n<code>WS_EX_TOP&shy;MOST<\/code> style\nrather than calling\n<code>Set&shy;Window&shy;Long<\/code>,\nbut there is a compatibility loophole:\nIf your application was written for Windows&nbsp;3.1\nand you are setting extended styles that didn&#8217;t exist\nin Windows&nbsp;3.1,\nthen the window manager says,\n&#8220;I think I know what you&#8217;re up to&#8221;\nand suspends the rules so that the application can go ahead\nand use the extended window style as a secret hiding place.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The documentation for the Set&shy;Class&shy;Long&shy;Ptr function mentions GCL_CB&shy;CLS&shy;EXTRA: Sets the size, in bytes, of the extra memory associated with the class. Setting this value does not change the number of extra bytes already allocated. What&#8217;s the point of letting the application change a value if it has no effect? The GCL_CB&shy;CLS&shy;EXTRA class long grants access [&hellip;]<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-3503","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The documentation for the Set&shy;Class&shy;Long&shy;Ptr function mentions GCL_CB&shy;CLS&shy;EXTRA: Sets the size, in bytes, of the extra memory associated with the class. Setting this value does not change the number of extra bytes already allocated. What&#8217;s the point of letting the application change a value if it has no effect? The GCL_CB&shy;CLS&shy;EXTRA class long grants access [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/3503","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=3503"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/3503\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=3503"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=3503"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=3503"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}