??xml version="1.0" encoding="utf-8" standalone="yes"?> 事情的v因是Q想找个?Windows ?Mac 的构建方案。第一考虑自然?CMakeQ毕竟基本上是事实标准了?/p>
但是研究了一?Modern CMakeQ也是?target 为核心的理念。但发现看了好几天文档,也折腑և了可用的东西Q但仍然是没梳理清楚什么理c(din)原理。然?CMake 本n语法很复杂Q再加上?target 一套概念,要给 target 讄各种属性之cȝQ有点强?OOP 的感觉……但其实我们只是需要一?include_dir ?lib_dir 而已Q其他都是Q云~ 但如果退回到传统模式Q不?Modern 概念呢,好像可以就Q但W一不去用一个工L(fng)最新模式,好像有点不上q的感觉Qpython 2 除外Q;W二QCMake 的两大痛点——语法特立独行、文档晦涩难懂——还是让人有点不爽?/p>
那蟩出来看别的选择呢?目前相对成熟的也只有 Google ?gn+ninja Ҏ(gu)了。gn q套东西?Chromium 里是完全配置好的Q用hq算手Q但要是独立拿出来呢Q就没那么便宜了。关键是它的 toolchain 是要自己定义的?/p>
之前q在公司搞客L(fng)的时候,大家׃ Chromium 里面?build、build_overrides {等东西全部拷出来,好家伙,几百 MB 甚至?G。但是公叔R嘛,没h干不干净Q怎么快怎么来。后来又看到 Google 自家?Crashpad 里面也用了这套构建,但工具链被简化了一下,?mini_chromium。这个比 Chromium 里的多了,是可以拿q来直接用的Q缺一些配|可以自己加。但是呢Q像我们q种z癖Q仍然是受不鸟的。所以呢Q我们要q干净净的徏立一套工具链?/p>
首先Q我们明定位。gn ?ninja 都是开发机上需要预装的Q不是Y件提供的。Chromium 的搞法是自己提供Qgn 的文档也说让开发者提供工兗但q套思\跟传l的理念是冲H的。同Ӟ自己安装工具成本是比较低的:(x) 自己下蝲的设|到 PATHQ测? 希望做到提供一?git repoQ用?clone 到自己项目的 build 目录Q然后用者只要在 .gn 文g里配|?/p>
可以用我们提供的工具链,?PC 三端q行构徏?/p>
使用者的唯一负担是~写自己?BUILD.gn 首先我们?gn 的文档,以及(qing)它的例程 simple_build 里的工具N|:(x) https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn q个是可以直接用的,只不q只?linux 端(当然 mac 也能用)。我们再l合 chrome 里的工具N|,q行一些完善?/p>
首先我们了解 gn 体系需要的最配|是什么?/p>
W一Q它需要在根目录写一?.gn 文gQ在里面定义 buildconfigQ指到另一个文Ӟ一般是 W二、BUILDCONFIG.gn 里面需要设|默认工具链Q也是写一?/p>
W三、定义工具链Q如上例?//build/toolchain:gcc?/p>
需要在 build/toolchain 下徏?BUILD.gn 文gQ内Ҏ(gu) 最后在 toolchain 里定义各U?tool?/p>
q部分文档在q里Qhttps://gn.googlesource.com/gn/+/main/docs/reference.md#func_tool 单复qC下,可被定义的工hQ?/p>
Q其他的先不看了) 我们来看一?https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn 的一些关键配|:(x) 可以看到Qcc ?cxx 执行 command 后,生成 .o 文gQ然后这?.o 文g可以作ؓ(f) alink、solink、link ?inputsQ被它们 command l箋使用Q最后输出静态库、动态库以及(qing)可执行文件?/p>
其余属性可以查文档了解含义?/p>
主要配置在这里:(x)https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/gcc_toolchain.gni 也是 gcc 的,?simple_build 里的大同异Q没有特别的?/p>
主要配置在这里:(x)https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/apple/toolchain.gni 区别有:(x) 基本上是Ҏ(gu)上面分析的要炚w|,最l结果在此:(x)https://github.com/Streamlet/gn_toolchain 新增的一些差异有Q?/p>
增加全局参数 is_debugQ可以在 mac 下生?dSYM 不?python 脚本Q直接是 mac 下加了一?templateQapp_bundleQ用来生?xxx.appQ主要配|来自于 create_bundle 文档里的例子 win 下增加了一些配|集 动?静态链?CRTQ?/build/config/win:console_subsystem?/build/config/win:static_runtime 控制台程序、Win32 E序Q?/build/config/win:console_subsystem?/build/config/win:windows_subsystem q个其实一般情况下用不着Q只要入口函数是 main/WinMainQlink 默认是控制台程?Win32 E序 XP 支持Q?/build/config/win:console_subsystem_xp?/build/config/win:windows_subsystem_xp 具体实现是链接参数加 /SUBSYSTEM:CONSOLE,5.01 ?/SUBSYSTEM:WINDOWS,5.01。关键是后面的版本号 5.01Qؓ(f)了加版本号则必须指定子系l名Uͼ所以分?console_subsystem_xp ?windows_subsystem_xp。又Qxp q里提供了两?subsystem 的配|集Q非 xp 也提供两个?/p>
但是我们没有?_WIN32_WINNT=0x0501、WINVER=0x0501、_USING_V110_SDK71_Q也没有指定必须使用 7.0 版本?SDKQ这些都是非必须的,只要不用?XP 以后d?API 卛_。用者可以在自己?target 里面定义q些宏?/p>
提供一个用案例:(x)https://github.com/Streamlet/gn_toolchain_sample 因ؓ(f)它以 git submodule 形式引用?https://github.com/Streamlet/gn_toolchainQ所?git clone 以后Q需? 然后在根目录执行Q(保 gn ?ninja 已经?PATH 中) 卛_?/p>
Mac 下会(x)额外生成一?objc 目 objc_console_application 以及(qing)一?app_bundleQns_application.app?/p>
Win ?x)额外生成一?Win32 目 win32_application?/p>
Win 下需要先执行一?Visual Studio 带的命o(h)行环境,?VS 2022 Community ?“x64 Native Tools Command Prompt for VS 2022”,cl {工h?x)可用?/p>
如果要测?XPQ?2位)Q用“x86 Native Tools Command Prompt for VS 2022”进入,CD 到项目目录,执行Q?/p>
我们只用几十 KB 的大完成了跨端支持Q是很轻量的一个配|。如果?zhn)觉得实用q认可这U方式,Ƣ迎一hl护、完善?/p>
q次是因为傻逼大微Y改了 System32\IME\shared 里的东西Q导致旧pȝ拯来的文g?System32\IME\shared 的东西不兼容了。解x式很暴力Q从以前的版本复?System32\IME\shared q来?/p> 下蝲Q?a >https://www.streamlet.org/software/mspyforever/ Q原发于 GitHub PagesQ?018-10-13 13:36:04Q?/p> ?A、B 两个H口QA ?B ?OwnerQB 不激zM抢焦炏V在 B ?WM_LBUTTONDOWN 的时候,讄 A H口?CaptureQ在 A ?WM_LBUTTONUP 的时?ReleaseCapture?/p> 操作是,?B 上按下鼠标,然后一直按住鼠标飘啊飘。在某一时机?B q掉Ql动鼠标。此?A q在Qƈ?A 仍然?Capture 状态,但是 A 收不?A 可视范围外的 WM_MOUSEMOVE 了!在全q程?A 也没有收?WM_CAPTURECHANGED?/p> 下面是栗子:(x) http://pan.baidu.com/s/1mgurJIS 求解释。求解决。谢谢~Q?/p> [2]发布个工P一键恢复Win8/8.1中的微Y拼音长句模式Q新体验模式Q?/a> ?[1] 中,我们扑֛?Win8 里的微Y拼音C验模式;?[2] 中,依照 ePig 提供的方?/a>Q我们在 Win8.1 中也搬回了微软拼x体验模式Q但留下了一炚w憾,不支?Metro 应用?/p> Q其实还有一点,没有昄图标。) 原本我觉得无所谓,因ؓ(f)我反正不太用 Metro 应用QMetro 下就用自带的微Y拼音h式好了。可是,可是Q输入法理器中Q比如保留一个兼?Metro 的输入法Q导致桌面下也比如保留简h式了Q这样切换输入法l常切错Q离所谓“一个输入法”的伟大目标相差甚远。于是乎Q不能忍。然后昨天v来看了下q个问题?/p> 原先微Y拼音输入法的一个不知什么的GUID?{81d4e9c9-1d3b-41bc-9e6c-4b40bf79e35e}Q该 GUID 代表的输入法下有两个输入模式Q新体验和简捗(q个构架和相兛_Ҏ(gu)不是很懂Q具体请L?TSF (Text Service Framework) 框架相关文档。) Win 8 下只是删除了C验模式的那个注册表项q对注册表项加权限保护达到禁用目的。Win8.1 下的变化是,老的文g被删除了Q且 {81d4e9c9-1d3b-41bc-9e6c-4b40bf79e35e} 被用作新的微软拼音的 GUID 了,里面也不再分两个模式了(其实只有原先的简h式)。按?ePig 提供的方?/a>Q从 Win8 中复制相xӞq把文g中的 GUID 改了Q重新注册进厅R这Ҏ(gu)在一键恢复工h带的文g中是?GUID 末尾 e 改ؓ(f)?fQ完整的是 {81D4E9C9-1D3B-41BC-9E6C-4B40BF79E35F}。按道理应该完全兼容才是Qv?Win8 下是支持 Metro 的。微软拼韛_早就?TSF 框架了(而不?ImmQ,据我之前的了解,要支?MetroQ输入法使用 TSF 框架好了?/p> 想不通的情况下,来比较?Win8.1 下自带的微Y拼音Q简h式)和我们新加的微Y拼音C验模式的注册表项差异Q?/p> 发现自带的多了几(U框部分Q。于是仿照着把这些补上去Q结果确实不昄“仅适用于桌面版”了Q可是好像也没法输入了……于是每ơ只加一,依次试过去(ID法,鄙视Q。最后发现前两个 Q也是 {13A016DF-560B-46CD-947A-4C3AF1E0E35D}、{25504FB4-7BAB-4BC1-9C69-CF81890F0EF5} 可以要,最?{74769ee9-4a66-4f9d-90d6-bf8b7c3eb461} 不能要?/p> 再看?Win8 的,果然也是有这两个 Category 的:(x) 然后q事情就成了:(x) 一键工具已更新Q下载页面:(x)http://www.streamlet.org/Software/MSPYForever/ 那么q两?GUID 到底是什么呢QGoogle 一下…?/p> 关于前者,http://msdn.microsoft.com/zh-cn/library/windows/apps/hh967425.aspx#set_compatibility_flag 中有说明如下Q?/p> 声明兼容?/em> IME 通过使用 ITfCategoryMgr::RegisterCategory 为其 IME 注册cd GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT 来声明其兼容 Windows 应用商店应用?/em> 其中 GUID_TFCAT_TIPCAP_IMMERSIVESUPPORT 是 {13A016DF-560B-46CD-947A-4C3AF1E0E35D}?/p> {25504FB4-7BAB-4BC1-9C69-CF81890F0EF5} 也是个预定义的|叫做 GUID_TFCAT_TIPCAP_SYSTRAYSUPPORTQ按字面理解Q系l托盘支持,可是查不到官方说明啊Q?a title="http://msdn.microsoft.com/en-us/library/ms629012.aspx" target="_blank">http://msdn.microsoft.com/en-us/library/ms629012.aspx 不管怎么_(d)问题是解决了。大快h心?/p> 再顺侉K下大微Y?/p> 看这张图Q我在写代码或写Word或者写别的文字Q开着中文语言下的中文输入法(写代码怎么?x)开中文Q不要在意这些细节……)Q然后来了条 QQ 消息。我MQ务栏点企鹅图标。结果刚点下Q因ZQ务栏、桌面是英文语言下的式键盘Q输入法指示器变?ENGQ羃短了Q企鹅图标跑双MQ点IZQ!Q?/p> 正因为如此,我以前L把英文语a删掉Q只有中文语aQ下面一个输入法Q靠 Shift 在输入法内切换中英文。可?Shift 很容易误操作有木有。最佳的方式q是中文语言下的式键盘……这个问题很早之前研I过几个时Q但没啥l果Q后来想Q如果真的没办法了,q脆写一个空的输入法原样输入输出Q叫做“美式键盘”得了。ؓ(f)啥中文下׃l键盘布局呢?Z么呢Z么呢Z么呢Q?/p> 昨天又去|上搜了下,l果高h已经l出了方法了Q不知道哪篇是原创,׃引用了,有兴的自己搜。这里还有个半官方的出处Q?a title="http://answers.microsoft.com/zh-hans/windows/forum/windows_8-ime/windows-8%E7%9A%84%E8%BE%93%E5%85%A5%E6%B3%95/91917117-c5fd-4c61-ac2e-da0dd29d12fc" target="_blank">http://answers.microsoft.com/zh-hans/windows/forum/windows_8-ime/windows-8%E7%9A%84%E8%BE%93%E5%85%A5%E6%B3%95/91917117-c5fd-4c61-ac2e-da0dd29d12fc q下同在中文语言下了QCtrl+Shift 切输入法Q怎一个舒服了得~Q至此,W(xu)in8、Win8.1 的输入法问题Ҏ(gu)个h而言已经全部解决了,可以大规模重装系l了?/p> 忍不住再黑下大微软:(x)http://answers.microsoft.com/zh-hans/windows/forum/windows_8-ime/%E5%85%B3%E4%BA%8E%E4%B8%AD%E6%96%87%E7%89%88windo/b5c3190f-bb82-4855-97f0-00d6011e3e33Q这个答非所问,是不是在搞笑Q?/p> 首先贴个图,大家来一起念台词~ 念完了木有?很激情澎湃义愤填膺有木有Q?/p> q事情最早追溯到前年 8 月的一文章?a href="http://www.shnenglu.com/Streamlet/archive/2014/02/17/188249.html" target="_blank">十个步骤扑֛ Win8 中的微Y拼音C验模?/a>》,其实是手工注册一个COM完事Q只是傻逼大微YL弄了注册表权限来屏蔽Q操作v来略微繁琐。到目前为止QW(xu)in8重装pȝ已经不下十次了,每次都是q样手工操作Q我已经厌倦了?/p> 另外q有Win8.1上的问题Q由于傻逼大微Y已经完全删除了文Ӟ没法这么搞了,q从Win8拯文g来,也无法简单注册用。加上我?.1非常非常不感冒,一直没ȝI。前些天看到之前的那文章里 Charles Leigh 回复了两文章(ePig 那篇是原创吧貌似Q感谢)Q提供了解决Ҏ(gu)。于是上个周末到现在捣鼓个一键恢复工P方便自己以后重装用,也方便广大微拼党?/p> 微拼党(包括我)孜孜不倦的q求微拼长句模式的情怀Q让我非常感动。希望傻逼大微Y看到我们的心声。别搞什么破词组输入法了Q你搞不q本土这么多厂家的,你的下限也没有本土厂家低Q唯一的优势可能就是没q告没弹H了吧。至于输入算法什么的Q在词组模式里面Ҏ(gu)不以体现得太多Q长句模式才是考验啊。回头吧Q?/p> 下蝲面Q?a title="http://www.streamlet.org/Software/MSPYForever/" target="_blank">http://www.streamlet.org/Software/MSPYForever/ CodePlex 目面Q?a title="https://mspyforever.codeplex.com/" target="_blank">https://mspyforever.codeplex.com/ 请微拼党们多传播。有 Bug ?qing)时反馈?/p>构徏pȝ安装
gn --version
以及(qing) ninja --version
Q能q行卛_目标
buildconfig = "//build/BUILDCONFIG.gn"
工具链搭?/h2>
基础概念
buildconfig = "//build/BUILDCONFIG.gn"
set_default_toolchain("//build/toolchain:gcc")
toolchain("gcc") {
# ...
}
工具链中的工?/h3>
"cc": C ~译?br />
"cxx": C++ ~译?br />
"cxx_module": 支持 module ?C++ ~译?br />
"objc": Objective C ~译?br />
"objcxx": Objective C++ ~译?br />
"rc": Windows 资源脚本~译?br />
"asm": 汇编?br />
"swift": Swift ~译?/li>
"alink": 静态库链接?br />
"solink": 动态库链接?br />
"link": 可执行文仉接器toolchain("gcc") {
tool("cc") {
command = "gcc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
# ...
}
tool("cxx") {
command = "g++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
# ...
}
tool("alink") {
command = "rm -f {{output}} && ar rcs {{output}} {{inputs}}"
outputs = [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
# ...
}
tool("solink") {
command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
outputs = [ sofile ]
# ...
}
tool("link") {
command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
outputs = [ outfile ]
# ...
}
tool("stamp") {
command = "touch {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
}
}
Ҏ(gu) Chromium 中的配置
Linux
Mac
Win
建立我们的工具链
gn gen out --args="is_debug=true"
传入。默?is_debug ?falseQ开启所有优化?/p>
$ld ... && dsymutil ... && strip
使用案例
git submodule update --init
一下?/p>
gn gen out
ninja -C out
gn gen out --args="target_cpu=\"x86\""
ninja -C out
ȝ
]]>
]]>
]]>
]]>
]]>
]]>
2*2的会(x)被弄成:(x)
更大的图片,表现为右边缘和下边缘渐变Q?/p>
GDI+自作聪明了…?/p>
解决Ҏ(gu)Q?/p>
Graphics g;
g.SetInterpolationMode(InterpolationModeNearestNeighbor);
g.SetPixelOffsetMode(PixelOffsetModeHalf);
效果Q?/p>
Ҏ(gu)记录?/p>
参考资料:(x)http://bbs.csdn.net/topics/310212346
微Y拼音输入法的C验模式,其实是微软拼音一直以来的主要模式。笔者从1.5版开始接触微软拼音以来就一直是q种模式Q主要特点就是长句输入、二ơ确认,在第一ơ确认的时候,文字p入仿?jng)实际输入区域,但是文字下面有虚U,此时我们可以用光标键左右UdQ就像下图显C的一P(x)
而在输入拼音的时候,我们其实无需开启候选词H口Q也不用看屏q:(x)
输入完毕之后按空|观察首选是否正,如果正确的话再一ơ空格确认,否则按一下右光标键回到句首开始选词。非常方便,也显得很专业?
?fn)惯于词l输入的同学可能喜欢时刻盯着屏幕出现的候选词Q一旦有错立ȝ正选择。而长句输入则不必q样Q输入整个句子,让输入法在整句的语境中替你选词Q命中率?x)高很多Q特别是在打长篇文章的时候,非常有用?
很多时候别人在看我输入的时候,发现屏幕上有错别字,?x)忍不住提醒我错了,但是当我最后确认的时候,往往又对了,?x)显得有一点点疑惑Q但是通常不说。有的是,我在打别人名字的时候,他们发现一开始的错别字,也会(x)忍不住提醒我错了Qƈ且对输入法把某h的名字弄成另外一个字非常感兴,有时?x)以?嘲笑"那个人。然后当我最后回来选词的时候,他们?指责"q嘛一开始不选对的字……我已经?fn)惯了。但是整句输入带来的便捷之处Q大部分人是没法认识到的。所以我今天在这里花很大的篇q介l一下?
遗憾的是Q微软拼?.5?.0?.0都没人叫好。到Office2003的时候,因ؓ(f)"C?模式的出玎ͼ使得一部分人叫好,但那部分人其实还是把它当作词l输入法来用的。到Office2010出来的时候,又有一部分人叫好,实际上很大一部分人是??模式叫好。到现在Win8成了体中文语a下默认输入法Q即便有强推的味道,但好多hq是叫好Q而新体验模式的消失,却很有人问z?
下面正式来看标题中提到的问题。Win8安装完毕后简体中文下只有一个微软拼音简PC验模式不见了Q!Q?
有需要的同学Q请跟随W者,一步一步找回新体验模式?
1、打开开始屏q,在英文状态下输入regeditQ然后回车,打开注册表编辑器。(遇到UAC提醒Q请选择"?Q?
2、在左侧展开目录?wi),一直到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CTF\TIP\{81d4e9c9-1d3b-41bc-9e6c-4b40bf79e35e}Q?
微Y对这一做了特D的权限保护Q它的权限和盔R的那几个不一P我们要取得写权限?
3、右键单d、选择权限Q打开如下囄H口Q?
4、单?高"Q再打开一个窗口:(x)
5、在所有者的双点击"更改"Q输?everyone"或者当前登录用户名Q按"定"Qƈ选中"替换子容器和对象的所有?Q然后按应用。(不要关闭q个H口Q等下我们还要改回来。)
6、然后点d下方的启用承(按了后该按钮变ؓ(f)用l承Q,q中"使用可从此对象承的权限目替换所有子对象的权限项?。最后按"应用"Q遇到提C选择"??
现在Q这个窗口变成这个样子了Q?
保留q个H口Q我们进行下一步?
7、打开开始屏q,在英文状态下输入"cmd"Q左侧出?命o(h)提示W?Q右键点击选中它,再到屏幕下方点击"以管理员w䆾q行"Q遇到UAC提示选择"?。这h们打开h理员权限的命o(h)提示W窗口,工作路径位于System32目录?
8、输?regsvr32 ime\imesc\imsctip.dll"Qƈ按回车?
直到看到下图的提C,点击定Q关闭命令提C符H口?
9、回到刚才的高安全讄H口Q点d下方?用l承"按钮Q这时出C图提C:(x)
选择W二,"从此对象中删除所有以l承的权?Q?
q中"使用可从此对象承的权限目替换所有子对象的权限项?Q按"应用"Q遇到提C选择"??
10、点MҎ(gu)有者右边的"更改"Q输?nt service\trustedinstaller"Q按"定"Qƈ选中"替换子容器和对象的所有?Q然后按"应用"?
xQ我们将注册表权限恢复如初了?
打开输入法管理界面:(x)
我们可以看到多出来的"Microsoft Pinyin NewExperience"了,它就是我们要扄C验模式!
(zhn)剧的是Q貌似新体验模式在Metro界面下有问题Q每ơ确认输入,光标都会(x)回到最前面Q注Q确认这是当时新微博Win8客户端的BugQ不是输入法的问题,新浪微博貌似已经修正此问题)。也许是因ؓ(f)有这些BugQ微软才隐藏它的吧。不q,桌面模式下用v来未发现M问题哦?
Q所有内定w?VC ~译q_Q?/p>
一、x86
名称 | 传参方式 | 栈清?/td> | C 语言函数重命名(例:(x)int func(int, double)Q?/td> |
__cdecl | 从右臛_压栈 | 主调函数 | 前面加“_”(_funcQ?/td> |
__stdcall | 从右臛_压栈 | 被调函数 | 前面加“_”,后面加“@”再加参数十q制字节敎ͼ_func@12Q?/td> |
__fastcall | 前两个不大于DWORD长度的参C左至叛_别存?ECX、EDXQ其余从双左压?/td> | 被调函数 | 前面加“@”,后面加“@”再加参数十q制字节敎ͼ@func@12Q?/td> |
__thiscall | ECX ?thisQ其余从双左压?/td> | 被调函数 | 仅用?C++ |
二、x64
名称 | 传参方式 | 栈清?/td> | |
__fastcall | 前四个整?点数放?RCX/XMM0、RDX/XMM1、R8/XMM2、R9/XMM3Q其余压栈?br>如果?4 个参数分别ؓ(f) int、float、long、doubleQ它们将分别被存?RCX、XMM1、R8、XMM3 | 被调函数 |
64位编译环境下Q可以指?__cdecl、__stdcall、__fastcallQ但是编译器?x)忽略它们。两个显C指定了不同调用U定的函C构成重蝲Q而构成重定义错误?/p>
E微解释下,因ؓ(f)有可能有Z(x)误会(x)Q放新线E里面去不就可以了?q没有解决问题。如此的话,你那个线E函数怎么写?或者线E函数里调用的某个Q务函数怎么写?MQ多U程虽然L出现在这些问题的解决Ҏ(gu)中,但不是多U程解决了这个问题。嗯……不知道说清楚了没?
目前我心里的{案只有q一U模式:(x)
bool DoTask(HANDLE hQuitSignal)
{
while (!QuitCondition)
{
if (WaitForSingleObject(hQuitSignal, 0) == WAIT_OBJECT_0)
{
return false;
}
// Do something
}
return true;
}
其中Q?/ Do something”部分要l化到瞬间执行完成的l度?/p>
但是我很困惑的是Q如果这些Q务很J重Q难道我必须每进行一些操作就 if (WaitForSingleObject(hQuitSignal, 0) == WAIT_OBJECT_0) (g)查下吗?q样岂不是这U检代码充斥在d中了Q?/p>
不知各位有何l验和体?x),求教~
GUI框架Q要做的事情我想大概是这么几步:(x)
前三步要走的比较?yu)心Q第四步是体力劳动?/p>
W一步,W(xu)indows下可参考的是MFC方式、WTL方式Q以?qing)利用Window相关属性中的某些空位。前不久刚初步看qWTL的机Ӟ虽然当时没写GUI框架的打,不过也有Ҏ(gu)术准备的意思了。现学现用吧。这里一个可以预见的问题?4位兼容,现在没有试环境Q先不管?/p>
接下来看W二步了Q所要做的事情就是把 WndProc 下的 一?case 有效地组lv来,或者换个写法。之前还真不知道 MFC/WTL ?BEGIN_MSG_MAP。以为很高深的,想不到就是拼装成一个大?WndProc。先抄了Q做成一个可q行的版本。但是,q方面会(x)直接军_以后的大部分使用方式Q单单抄一下意义不大。后来去 @OwnWaterloo 曾推荐过?@cexer 的博客上逛了几圈Q第一圈看了一些描q性文字,W二圈大概看了下技术,W三圈是挖坟Q那个传说中?cppblog W一高楼啊。。其中有一个用方式很新颖Q嗯……是那个不需要手动写映射代码Q直接实现消息处理函数的方式。不q我后来觉得q是不要q种样子了,凭我个h的直觉,如果我写下这L(fng)处理函数Q我大概?x)因Z知道何时注册了这个函数而找不到调用来源而感到郁闗在Windows回调机制的媄响下Q我可能?x)很抱有偏见地认为,只有直接来自WndProc的调用,才算是来源明的Q不需要l追t的——当Ӟq是建立在我不熟(zhn)这个框架的基础上的。框架必焉要隐藏调用来源,以及(qing)其他一些细节,但是在这一步,我觉得稍微有Ҏ(gu)?/p>
刚才说到的都是静态绑定。现在我有点們于动态绑定。从使用方便E度上来看,动态绑定更L(fng)zL。从性能上,动态绑定下Q消息到处理函数的查找过E可以更快,静态绑定只能遍历。当Ӟ未必“添加处理函数”这L(fng)接口提供l最l用P但是q个操作对于整个控g体系的Ş成应该蛮有帮助的吧。比如MFC下一个控件类使用Message Map做了一些事情,l承cd无法直接l承q个动作Q于是可能需要做两套处理函数调用机制Q一套是l内部承用的,一套是l用L(fng)。如果在最开始的基类保存一个消息映,每个消息对应一族处理函敎ͼ每个l承c都可以d处理函数Q但不删除父cdd的函敎ͼq样可以在一套Message Map机制下获得父cȝ行ؓ(f)。以上,不知道考虑得对不对Q欢q讨论?/p>
其中Q父cM存子cȝ出的可调用体q正执行是个问题。折腾了一些时_(d)都没有成功。我比较U结Q想知道除了用function之类的玩意儿外还有没有其他简单可行的办法。后来去@zblc的群上问Q?a href="mailto:zblc@vczh" target="_blank">@vczh也说需要一套function机制。看来是逃不开q个问题了。嗯……想h大约两个月前一个同事从codeproject找来了一个GUI框架看,看到几行整整齐齐?AddMsgHandler(WM_CREATE, XXX(this, &MyWindow::OnCreate));Q叹不已。我当时打趣_(d)q很单的Q无非是搞了?function 而已Q哥哥两天就能搞定。于是他们叫我两天搞定。我鼓捣?0分钟Q搞不定Q只好丢一句,真的很简单的Q类似boost::functionQ你ȝ一下就知道了,哥哥要干zM?/p>
既然现在q是l不开q个问题Q那q是搞一下了Q搞好以后就权且当做l他们交作业吧。我?x)另写一文章说说function的事情,q里先略q。现在开始假设这个设施已l造好了。那么,H口cM大概可以q么定义相关cdQ?/p>
typedef Function<bool (WPARAM, LPARAM)> MsgHandler;
typedef List<MsgHandler> MsgHandlerList;
typedef Map<UINT, MsgHandlerList> MsgMap;
然后再定义一个变量:(x)
MsgMap m_MsgMap;
它用于保存消息映。最l的回调函数可以写成Q?/p>
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
bool bHandled = false;
MsgMap::Iterator itMsgMap = m_MsgMap.Find(uMsg);
if (itMsgMap != m_MsgMap.End())
{
for (MsgHandlerList::Iterator it = itMsgMap->Value.Begin();
!bHandled && it != itMsgMap->Value.End(); ++it)
{
bHandled = (*it)(wParam, lParam);
}
}
return bHandled ? TRUE : DefWindowProc(m_hWnd, uMsg, wParam, lParam);
}
最后给个添加消息映的接口Q?/p>
void AppendMsgHandler(UINT uMsg, MsgHandler pMsgHandler)
{
m_MsgMap[uMsg].PushBack(pMsgHandler);
}
到目前ؓ(f)止,我们的窗口类大致上可以写成这P(x)
#include <Windows.h>
#include <tchar.h>
#include "../GUIFramework/xlWindowBase.h"
class Window : public xl::WindowBase
{
public:
Window()
{
AppendMsgHandler(WM_ERASEBKGND, MsgHandler(this, &Window::OnEraseBackground));
AppendMsgHandler(WM_PAINT, MsgHandler(this, &Window::OnPaint));
AppendMsgHandler(WM_LBUTTONUP, MsgHandler(this, &Window::OnLButtonUp));
AppendMsgHandler(WM_RBUTTONUP, MsgHandler(this, &Window::OnRButtonUp));
AppendMsgHandler(WM_DESTROY, MsgHandler(this, &Window::OnDestroy));
}
protected:
bool OnEraseBackground(WPARAM wParam, LPARAM lParam)
{
return false;
}
bool OnPaint(WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps = {};
BeginPaint(m_hWnd, &ps);
RECT rect = { 200, 200, 400, 400 };
DrawText(ps.hdc, _T("Hello, world!"), -1, &rect, DT_CENTER | DT_VCENTER);
EndPaint(m_hWnd, &ps);
return false;
}
bool OnLButtonUp(WPARAM wParam, LPARAM lParam)
{
MessageBox(m_hWnd, _T("LButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
return false;
}
bool OnRButtonUp(WPARAM wParam, LPARAM lParam)
{
MessageBox(m_hWnd, _T("RButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
return false;
}
bool OnDestroy(WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return false;
}
};
在最基础?WindowBase 里,搞成q样大概差不是很多了。暂时先看第三步。到目前为止Q我所听说q的 GUI 框架都是真正的框Ӟg没有“GUI 库”。ؓ(f)什么一定要以承某个基cȝ方式来用呢Q如果像下面q样使用呢?
class Window
{
private:
xl::WindowBase m_WindowBase;
public:
Window()
{
m_WindowBase.AppendMsgHandler(WM_ERASEBKGND, MsgHandler(this, &Window::OnEraseBackground));
m_WindowBase.AppendMsgHandler(WM_PAINT, MsgHandler(this, &Window::OnPaint));
m_WindowBase.AppendMsgHandler(WM_LBUTTONUP, MsgHandler(this, &Window::OnLButtonUp));
m_WindowBase.AppendMsgHandler(WM_RBUTTONUP, MsgHandler(this, &Window::OnRButtonUp));
m_WindowBase.AppendMsgHandler(WM_DESTROY, MsgHandler(this, &Window::OnDestroy));
}
};
q个问题Q不知道各位有没有什么思考?
q有一个问题是Q接下去要不要将 WPARAM ?LPARAM 的含义彻底解析掉Q搞成一pd PaintParam、EraseBackgroundParam、LButtonUpParam、RButtonUpParamQDestroyParamQ让使用的时候与原始消息参数d隔离呢?
最后一步,虽说是体力活Q但q跟最l的应用场合密切相关Q需要提供怎么L(fng)功能是一仉要考量的事?/p>
目前走在W二步,所以下面的两个问题思考得不多。求l验Q求意见?/p>
问题Q?br>有没有存在一U好的方式,让DLL能够被动态加载,q且能够方便地得到里面的 C++ class 信息Q?br>备选:(x)
1、别想了Q老老实实地用吧Q还是导出纯C函数= =
2、大胆的导出 class 吧,如果动态加载,自己L那些~译后名字吧。?br>3、COM 形式Q可是,要注册到pȝ中去Q凭I多了系l注册表依赖
4、还有吗Q?br>5、甚臛_以抛开DLLQ有没有cM的一U方式,可用于二q制代码的模块划分以?qing)闭源的代码重用Q?/p>
Q至于跨q_啥的先不考虑吧,暂定Windowsq_下吧Q?/p>
请不吝指教~
typedef DWORD (WINAPI *PTHREAD_START_ROUTINE)(
LPVOID lpThreadParameter
);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;
q样Q当我们实现U程cȝ时候,可以:(x)
class Thread
{
private:
HANDLE m_hThread;
public:
BOOL Create()
{
m_hThread = CreateThread(NULL, 0, StaticThreadProc, (LPVOID)this, 0, NULL);
return m_hThread != NULL;
}
private:
DWORD WINAPI ThreadProc()
{
// TODO
return 0;
}
private:
static DWORD WINAPI StaticThreadProc(LPVOID lpThreadParameter)
{
((Thread *)lpThreadParameter)->ThreadProc();
}
};
不过Q这P成员函数 ThreadProc() 便׃一个参敎ͼq通常无伤大雅QQ何原本需要从参数传入的信息都可以作ؓ(f)成员变量?ThreadProc 来读写。如果一定有些什么是非从参数传入不可的,那也可以Q一U做法,创徏U程的时候传入一个包?this 指针信息的结构。第二种做法Q对?class 作单例限制——如果现实情况允许的话?/p>
所以,有额外参数的回调函数都好处理。不q的是,W(xu)indows 的窗口回调函数没有这样一个额外参敎ͼ(x)
typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
q得对H口?C++ 装变得困难。ؓ(f)了解册个问题,一个很自然的想法是Q维护一份全局的窗口句柄到H口cȝ对应关系Q如Q?/p>
#include <map>
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
protected:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static std::map<HWND, Window *> m_sWindows;
};
?Create 的时候,指定 StaticWndProc 为窗口回调函敎ͼq将 hWnd ?this 存入 m_sWindowsQ?/p>
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = StaticWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
RegisterClassEx(&wcex);
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
m_sWindows.insert(std::make_pair(m_hWnd, this));
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
?StaticWindowProc 中,?hWnd 扑ֈ thisQ然后{发给成员函数Q?/p>
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<HWND, Window *>::iterator it = m_sWindows.find(hWnd);
assert(it != m_sWindows.end() && it->second != NULL);
return it->second->WndProc(message, wParam, lParam);
}
Qm_sWindows 的多U程保护略过Q下同)
据说 MFC 采用的就是类似的做法。缺Ҏ(gu)Q每?StaticWndProc 都要?m_sWindows 中去?this。由于窗口类一般会(x)保存H口句柄Q回调函数里?hWnd 没多大作用了,如果q个 hWnd 能够被用来存 this 指针好了,那么p写成q样Q?/p>
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
q样看上d爽多了。传说中 WTL 所采取?thunk 技术就是这么干的。之前,只是听过q遥q的传说Q今天,l于有机?x)走q这个传说去看一看。参考资料是一不知原始出处的文章《深入剖析WTL—WTL框架H口分析》,以及(qing)部分 WTL 8.0 代码Q还有其他ؕ七八p的文章?/p>
WTL 的思\是,每次在系l调?WndProc 的时候,让它gɼ差地先走到我们的另一处代码,让我们有Z(x)修改堆栈中的 hWnd。这处代码可能是cMq样的:(x)
__asm
{
mov dword ptr [esp+4], pThis ;调用 WndProc Ӟ堆栈l构为:(x)RetAddr, hWnd, message, wParam, lParam, ... ?[esp+4]
jmp WndProc
}
׃ pThis ?WndProc 需要被事先修改Q但又无法在~译前定好)Q所以我们需要运行的时候去修改q部分代码。先弄一个小E序探测下这两行语句的机器码Q?/p>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return 0;
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, NULL, NULL, MB_OK);
__asm
{
mov dword ptr [esp+4], 1
jmp WndProc
}
return 0;
}
最前面?MessageBox 是ؓ(f)了等下调试的时候容易找到进入点?/p>
然后使用 OllyDbgQ在 MessageBoxW 上设|断点,执行到该函数q回Q?/p>
q里我们看到Qmov dword ptr [esp+4] 的机器码?C7 44 24 04Q后面紧接着的一?DWORD ?mov 的第二个操作数。jmp 的机器码?e9Q后面紧接着的一?DWORD 是蟩转的相对地址。其?00061000h - 0006102Bh = FFFFFFD5h?/p>
于是定义q样一个结构:(x)
#pragma pack(push,1)
typedef struct _StdCallThunk
{
DWORD m_mov; // = 0x042444C7
DWORD m_this; // = this
BYTE m_jmp; // = 0xe9
DWORD m_relproc; // = relative distance
} StdCallThunk;
#pragma pack(pop)
q个l构可以作ؓ(f)H口cȝ成员变量存在。我们的H口cȝ在变成了q样子:(x)
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk m_thunk;
protected:
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};
g了点什么……创建窗口的时候,我们是不能直接把回调函数讑ֈ StaticWndPorc 中去的,因ؓ(f)q个函数是希望被写成q样子的Q?/p>
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
那么臛_需要一个(f)时的回调函数Q在q个函数里去讄新的回调函数Q设?m_thunk 上)Q再?m_thunk 来调?StaticWndProcQStaticWndProc 再去调用 WndProcQ这h个过E就通了?/p>
但是Q(f)时回调函数还是需要知道从 hWnd ?this 的对应关pR可是现在我们不能照搬用刚才?m_sWindows 了。因为窗口在创徏q程中就?x)调用到回调函数Q需要用到 m_sWindows 里的 thisQ而窗口被成功创徏之前Q我们没法提前拿?HWND 存入 m_sWindows。现在,换个Ҏ(gu)Q存当前U程 ID ?this 的对应关pR这Pq个cd成了Q?/p>
#include <map>
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk m_thunk;
protected:
static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
然后实现 Create ?TempWndProcQ?/p>
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = TempWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
RegisterClassEx(&wcex);
DWORD dwThreadId = GetCurrentThreadId();
m_sWindows.insert(std::make_pair(dwThreadId, this));
m_thunk.m_mov = 0x042444c7;
m_thunk.m_jmp = 0xe9;
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
assert(it != m_sWindows.end() && it->second != NULL);
Window *pThis = it->second;
m_sWindows.erase(it);
WNDPROC pWndProc = (WNDPROC)&pThis->m_thunk;
pThis->m_thunk.m_this = (DWORD)pThis;
pThis->m_thunk.m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)&pThis->m_thunk + sizeof(StdCallThunk));
m_hWnd = hWnd;
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);
return pWndProc(hWnd, message, wParam, lParam);
}
差不多可以了Q调试一下。结果,?thunk 的第一行出错了。我原以为地址错了神马的Q尝试把 thunk.m_mov 改ؓ(f) 0x90909090Q再q行Q还是出错。于是傻掉了……过了好一?x)儿才意识到Q可能是因ؓ(f) thunk 在数据段Q无法被执行。可是,很久很久以前偶滴一个敬q老师?TC 中鼓捣程序运行时改变自n代码Ӟ貌似无此问题啊。。。然后查呀查,原来?Windows 在的数据执行保护搞的鬹{于是,需要用 VirtualAlloc 来申请一D|执行权限的内存。WTL 里面也是q么做的Q不q它gl护了一块较大的可执行内存区作ؓ(f) thunk 内存池,我们q里从简。最后,整个程l于跑通了。最l代码清单如下:(x)
#include <Windows.h>#pragma pack(push,1)
typedef struct _StdCallThunk
{
DWORD m_mov;
DWORD m_this;
BYTE m_jmp;
DWORD m_relproc;
} StdCallThunk;
#pragma pack(pop)
class Window
{
public:
Window();
~Window();
public:
BOOL Create();
protected:
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam);
protected:
HWND m_hWnd;
StdCallThunk *m_pThunk;
protected:
static LRESULT CALLBACK TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static std::map<DWORD, Window *> m_sWindows;
};
std::map<DWORD, Window *> Window::m_sWindows;
Window::Window()
{
}
Window::~Window()
{
VirtualFree(m_pThunk, sizeof(StdCallThunk), MEM_RELEASE);
}
BOOL Window::Create()
{
LPCTSTR lpszClassName = _T("ClassName");
HINSTANCE hInstance = GetModuleHandle(NULL);
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.lpfnWndProc = TempWndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = lpszClassName;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
RegisterClassEx(&wcex);
DWORD dwThreadId = GetCurrentThreadId();
m_sWindows.insert(std::make_pair(dwThreadId, this));
m_pThunk = (StdCallThunk *)VirtualAlloc(NULL, sizeof(StdCallThunk), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
m_pThunk->m_mov = 0x042444c7;
m_pThunk->m_jmp = 0xe9;
m_hWnd = CreateWindow(lpszClassName, NULL, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (m_hWnd == NULL)
{
return FALSE;
}
ShowWindow(m_hWnd, SW_SHOW);
UpdateWindow(m_hWnd);
return TRUE;
}
LRESULT Window::WndProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONUP:
MessageBox(m_hWnd, _T("LButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
break;
case WM_RBUTTONUP:
MessageBox(m_hWnd, _T("RButtonUp"), _T("Message"), MB_OK | MB_ICONINFORMATION);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(m_hWnd, message, wParam, lParam);
}
LRESULT CALLBACK Window::TempWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<DWORD, Window *>::iterator it = m_sWindows.find(GetCurrentThreadId());
assert(it != m_sWindows.end() && it->second != NULL);
Window *pThis = it->second;
m_sWindows.erase(it);
WNDPROC pWndProc = (WNDPROC)pThis->m_pThunk;
pThis->m_pThunk->m_this = (DWORD)pThis;
pThis->m_pThunk->m_relproc = (DWORD)&Window::StaticWndProc - ((DWORD)pThis->m_pThunk + sizeof(StdCallThunk));
pThis->m_hWnd = hWnd;
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pWndProc);
return pWndProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK Window::StaticWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return ((Window *)hWnd)->WndProc(message, wParam, lParam);
}
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Window wnd;
wnd.Create();
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
刚才有一处,?this 指针的时候,我很武断地把它与当前U程 ID 兌h了,其实q正?WTL 本n的做法。它?CAtlWinModule::AddCreateWndData 存的 thisQ最l会(x)把当前线E?ID ?this 作关联。我是这么理解的吧,同一U程不可能同时有两处在调?CreateWindowQ所以这样取回来?this 是可靠的?/p>
好了Q到此ؓ(f)止,边试验边记录的,不知道理解是否正。欢q指Z当之处,也欢q提出相关的问题来考我Q欢q介l有x问题的新Ҏ(gu)、新思\Q等{,MQ请各位看官多指教哈?/p>
前两天终于有可以随时操作的且重现几率非常高的机器了,试了一下,发现一个规律:(x)只要在调?InternetOpenURL 之前调用q?SHGetFolderPathQ此问题的重现几率就非常高;如果没有调用q?SHGetFolderPathQ则基本不出现?/p>
目前|上扑ֈ的一个几乎唯一的帖子是 http://social.msdn.microsoft.com/forums/en-US/vcgeneral/thread/2982efc6-8403-4577-9dba-ad5cfdf01753Q现象几乎一模一栗只可惜没有有h(hun)值的回复。该文章的作者指出的 VPN {网l原因好像不是关键,在我q里是很普通的局域网Q一栯出现?/p>
试代码如下Q?/p>
#include <Windows.h>
#include <tchar.h>
#include <ShlObj.h>
#include <WinInet.h>
#pragma comment(lib, "wininet.lib")
#define URL _T("http://www.baidu.com/")
int main()
{
TCHAR szCommonAppData[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, szCommonAppData);
HINTERNET hInternet = InternetOpen(_T("WCU"), INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY, NULL, NULL, 0);
if (hInternet == NULL)
{
return 0;
}
HINTERNET hInternetFile = InternetOpenUrl(hInternet, URL, NULL, 0, INTERNET_FLAG_NO_UI | INTERNET_FLAG_RELOAD, 0);
if (hInternetFile == NULL)
{
InternetCloseHandle(hInternet);
return 0;
}
InternetCloseHandle(hInternetFile);
InternetCloseHandle(hInternet);
return 0;
}
在能够出现此问题的机器上QCtrl + F5 直接q行Q几乎每ơ必玎ͼ如果 F5 调试q行Q则几率一点,但是跑个七八ơ左叛_本上能出现。目?XP 32/64 上都有发现这个问题,Vista/Win7 上暂时没有发生此现象。(如果 InternetOpenURL 换成 InternetConnect、HttpOpenRequest、HttpSendrequestQ则?crash ?HttpSendRequest 内。)
附g是一个测试工E,附带上了 Debug、Release 版本?EXE、PDB 文g以及(qing) Crash 时的 Dump 文g。请有心人帮忙看看。^_^
点击下蝲
可是Q如果这个问题确实存在,Z么网上查到的相关内容q么呢Q奇怪~
好讨厌,ri啊ri。。?/p>
各位有没有突破方法:(x)扑ֈ一个Q何用户都可以可靠d的位|?
记一W。明天l看 64 位的?/p>
==================================================
WinXP x64: 0x158
Vista x64: 0x160
Win7 x64: 0x200
但如果从控制面板=>d删除E序Q程序和功能Q里执行Q相当于执行 msiexec /…)Q在q一?x)儿弹出?UAC 对话框中选管理员用户Q此?HKEY_CURRENT_USER 为管理员用户的数据,但是 SH?pd API 的执行环境却是标准用戗查看两?MsiExec.exe q程Q其中一个是 System 用户的,也就?Windows Installer 服务对应的进E;另一个是理员用L(fng)Q但是用 Process Explorer 查看Q它的所有环境\径全是标准用L(fng)?/p>
觉得有点奇怪,是以C。^_^