<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>空雪小屋 &#187; VC</title>
	<atom:link href="http://blog.sorayuki.net/?cat=21&#038;feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://blog.sorayuki.net</link>
	<description>现在域名是 blog.sorayuki.net ~</description>
	<lastBuildDate>Tue, 29 Nov 2022 08:27:41 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.0</generator>
	<item>
		<title>使用VS2022编译7zip的控制台程序</title>
		<link>http://blog.sorayuki.net/?p=651</link>
		<comments>http://blog.sorayuki.net/?p=651#comments</comments>
		<pubDate>Tue, 29 Nov 2022 07:58:52 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>
		<category><![CDATA[问题解决]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=651</guid>
		<description><![CDATA[7zip除了官方下载安装的图形界面工具之外，是有独立控制台程序的，在CPP\7zip\Bundles里面。这个 &#8230; <a href="http://blog.sorayuki.net/?p=651" class="more-link">继续阅读<span class="screen-reader-text">使用VS2022编译7zip的控制台程序</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>7zip除了官方下载安装的图形界面工具之外，是有独立控制台程序的，在CPP\7zip\Bundles里面。这个控制台程序还分为全功能版（7zz）的和精简的（7zr），但官方那边能下载到的只有精简的，全功能的那个要自己编译。</p>
<p>在用VS2022编译全功能版的时候我遇到了问题。虽然官方提供了nmake用的编译脚本，但是它跑起来总是报错，而且报错的地方在Windows SDK里面、而不是7z自己的代码。报的错误是C2279，出现在mapi.h头文件里、诸如“这个typedef地方不能写throw标识符”这样的东西。</p>
<p>网上搜索之后能找到的相关的东西不太多，有一个说法是C++标准的问题，现在的Windows SDK需要配合新版本的C++标准（C++17之类）才能正常使用。看了一下7zip源码里nmake脚本加的参数，也确实没有指定标准。</p>
<p>本来想试试换个旧版的Windows SDK行不行，一看要下载那么多东西……烦了，就没试。最后我的做法是，<span style="color: #ff0000;">打开CPP下的Build.mk，找到定义 CFLAGS 的那一行，加个 /std:c++17</span>。文件是只读的，要去掉只读属性才能保存。再编译会变成另外的错误，报异常规范不匹配。最简单的<span style="color: #ff0000;">去掉警告视为错误（搜索，去掉-WX）</span>可以编译通过。</p>
<p>另外我发现它没加链接时代码生成这样的优化，暂时不知道为什么。试着开了一下，用7zz b看跑分，和不开看不出区别。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=651</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC++里4、8、12和16字节的成员指针</title>
		<link>http://blog.sorayuki.net/?p=580</link>
		<comments>http://blog.sorayuki.net/?p=580#comments</comments>
		<pubDate>Tue, 26 Jul 2016 18:41:56 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=580</guid>
		<description><![CDATA[背景信息简单提一下……大概就是有两次被这事情坑了，我一直以为它只是一个普通指针的大小，32位下是4字节。 第一 &#8230; <a href="http://blog.sorayuki.net/?p=580" class="more-link">继续阅读<span class="screen-reader-text">VC++里4、8、12和16字节的成员指针</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>背景信息简单提一下……大概就是有两次被这事情坑了，我一直以为它只是一个普通指针的大小，32位下是4字节。</p>
<p>第一次，是在前一家公司的时候，遇到一个应用场景，一个头文件需要同时在C和C++里用，里面存有一个成员函数的指针，和void*一起放在union里，是专门给C++用的；C的话这个地方就用条件编译语句换成单个void*。结果是我离开项目组之后，后面用到这段代码的人发现这个成员指针有的时候比void*大。但是那个时候已经不在组里了，也不知道具体那边应用场景是啥样，最后简单把void*替换成char[32]了事，更多的也没在意。</p>
<p>第二次，是在现在这家公司的时候，有个同事在某个头文件里定义的类里面包含了另一个类的成员函数指针，结果这个头文件在两个不同的cpp文件里包含，这个类的大小是不一样的……访问成员变量的时候偏移也不同，数据就坏了。但是cpp里头文件包含的顺序调换一下就能好……同事说是那个成员函数指针的大小会不同，不过那会儿工作有点忙，结果是没放在心上，后来就忘了这事儿了。</p>
<p>最后一次是网友Advance在代码开了最高警告级别的时候，有一个成员指针被报没有对齐到16字节边界，然后就怀疑这个指针的大小。我突然想起之前同事的代码在不同文件访问变量所用的偏移不同的事情，就打算简单调查一下。</p>
<p><span id="more-580"></span></p>
<p>最开始是想起了同事说的成员函数指针大小会比普通指针大的问题，到搜索引擎上搜了个微软msdn社区的帖子，有人提到<strong>什么样的情况成员函数指针是什么样的大小，帖子链接是：<span style="text-decoration: underline;"><a href="https://social.msdn.microsoft.com/Forums/vstudio/en-US/2793a64f-ec09-495c-b995-4f5b98a26321/vc-wrong-pointer-size-?forum=vcgeneral" target="_blank">social.msdn.microsoft.com</a></span></strong>，里面大概是说，一个最基本的类，它的成员函数指针是4字节；如果这个类有多个基类，那么它的成员函数指针会变成8个字节，如果这个类有虚基类，那么它的成员函数指针是12字节。这个类如果都不知道是啥样的，只有声明没有定义，那么就是16字节。</p>
<p>看完了帖子，就说到这里。至于这8、12、16字节里面是啥样的，没有说。那就自己试一下吧……</p>
<h2>4字节的情况</h2>
<p>4字节的情况我觉得没啥好说的，和普通的函数指针一样。一定要说有区别的话，就是多送了一个参数：this指针。</p>
<h2>8字节的情况</h2>
<p>说是有多个基类的情况会变成8个字节，于是用下面这样的代码来试验，看看编译以后会成什么样</p>
<pre>#include &lt;stdio.h&gt;

struct A { int a; void af() { printf("a=%d\n", a); } };
struct B { int b; void bf() { printf("b=%d\n", b); } };

struct C : public A, public B
{
    int c;
    void cf() { printf("c=%d\n", c); }
};

int main()
{
    C x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    void (C::*p1)() = &amp;C::af;
    void (C::*p2)() = &amp;C::bf;
    void (C::*p3)() = &amp;C::cf;
    printf("%d\n", sizeof(p1));
    (x.*p1)();
    (x.*p2)();
    (x.*p3)();
}
</pre>
<p>看了一下编译后生成的代码，这8个字节里面，前4个字节是函数的地址，就和前一种情况——没有多个基类——的时候是一样的。至于后4个字节，是调用函数的时候送入的this指针的偏移。至于为什么要偏移，可以参考<span style="text-decoration: underline;"><strong><a href="?p=577" target="_blank">这篇博客</a></strong></span>。</p>
<p>上面的p1、p2、p3变量里，偏移分别是0、4、0。</p>
<p>大概就是这种感觉：</p>
<pre>int main()
{
    C x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    union MemPtr {
        struct {
            void(*funcPtr)();
            int thisOffset;
        };
        void (C::*val)();
    } p;
    p.val = &amp;C::bf;
    __asm {
        lea eax, p
        mov edx, MemPtr::thisOffset
        mov ecx, dword ptr [eax+edx]
        lea eax, x
        add ecx, eax
        lea eax, p
        mov edx, MemPtr::funcPtr
        mov eax, dword ptr [eax+edx]
        call eax
    }
}</pre>
<p>简单解释一下，就是调用的时候，this=&amp;x + p.thisOffset。</p>
<p>上面程序运行结果是b=2。</p>
<h2>12字节的情况</h2>
<p>多的不说，直接上测试代码</p>
<pre>#include &lt;stdio.h&gt;

struct D {
    virtual ~D(){}
    int d;
    void df() { printf("d=%d\n", d); }
};

struct A : virtual public D { int a; void af() { printf("a=%d\n", a); } };
struct B : virtual public D { int b; void bf() { printf("b=%d\n", b); } };

struct C : public A, public B
{
    int c;
    void cf() { printf("c=%d\n", c); }
};

int main()
{
    C x;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    x.d = 4;

    void (C::*p)() = &amp;C::df;
    printf("%d\n", sizeof(p));
    (x.*p)();
}</pre>
<p>因为对虚基类情况下对象在内存中的表示不熟，但是现在又太迟了，这个就以后再研究……先看它这个指针在干嘛。</p>
<p>这个12字节的指针，①最前面4字节放的是函数的地址，和4字节、8字节的情况一样；②中间4字节塞的东西是“虚表中的偏移”：有虚基类的情况，编译器会在虚表里面塞上这个虚基类的对象在当前对象中的偏移。因为可能会有多个这种虚基类，在调用成员函数指针的时候，this指针应该指向谁（换句话说也就是this指针要如何调整），是靠中间这4字节决定的。在上面例子中，就是从C*转换到D*的过程；③最后4字节存放的是this指针在这样调整之后，还要如何调整：在上面例子中因为D已经是最基本的类了，所以这里是0；但如果指向的是D的多个基类中其中一个的成员函数的指针，那么这里就不是0。</p>
<p>简单说就是这12字节，有8字节是说明this指针要怎么转换。</p>
<p>把上面的代码再弄复杂一点，比如</p>
<pre>#include &lt;stdio.h&gt;
struct X { int x; void xf() { printf("x=%d\n", x); } };
struct Y { int y; void yf() { printf("y=%d\n", y); } };

struct D : public X, public Y { virtual ~D(){} int d; void df() { printf("d=%d\n", d); } };

struct A : virtual public D { int a; void af() { printf("a=%d\n", a); } };
struct B : virtual public D { int b; void bf() { printf("b=%d\n", b); } };

struct C : public A, public B
{
    int c;
    void cf() { printf("c=%d\n", c); }
};

int main()
{
    C x;
    x.x = -1;
    x.y = -2;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    x.d = 4;

    void (C::*p)() = &amp;C::xf;
    (x.*p)();
}
</pre>
<p>观察生成的代码就可以发现，这个p的后8个字节的数据是4和4。前一个数据代表取虚表中第4字节的偏移对this进行第一次调整，后一个数据代表在这之后继续像8个字节那种情况对this进行第二次调整。</p>
<h2>16字节的情况</h2>
<p>说实话，16字节的情况我自己已经看不出名堂来了。代码最终修改到</p>
<pre>#include &lt;stdio.h&gt;

struct C;
void (C::*p)();

struct X { int x; void xf() { printf("x=%d\n", x); } };
struct Y { int y; void yf() { printf("y=%d\n", y); } };

struct D : public X, public Y { virtual ~D(){} int d; void df() { printf("d=%d\n", d); } };

struct A : virtual public D { int a; void af() { printf("a=%d\n", a); } };
struct B : virtual public D { int b; void bf() { printf("b=%d\n", b); } };

struct C : public A, public B
{
    int c;
    void cf() { printf("c=%d\n", c); }
};

int main()
{
    printf("%d\n", sizeof(p));
    C x;
    x.x = -1;
    x.y = -2;
    x.a = 1;
    x.b = 2;
    x.c = 3;
    x.d = 4;

    p = &amp;C::xf;
    (x.*p)();
}
</pre>
<p>这种程度，比起前面可以看出就是多了个前置声明而已。和前面12字节的时候相比，生成的代码在前面12字节的基础上，往中间4字节和最后4字节的数据之间插入了一个4字节的整数0。但是在下面的代码里却没有用到这个0。</p>
<p>调用的地方代码变得复杂了，因为前置声明的缘故，这个指针不知道最后会被用到什么样的地方上面。于是调用这个指针的时候，代码会先判断这个指针的最后4个字节是不是0，如果是，就直接拿第二个4字节整数来对this做偏移然后调用函数；如果不是，就取虚表中这个位置上的数据去对this做偏移，然后再取成员函数指针里第二个4字节数据直接加到this上做第二次偏移，再然后调用函数。</p>
<p>至于指针中第三个4字节整数是干嘛用的，确实还没调查出来。凌晨2:40时间太晚了，明天还要上班，就不作大死了，下次有时间有精力再调查。</p>
<p><strong>补充：</strong>生成的代码里没有用到这个0是因为在调用的地方，已经知道类的结构是什么样的、不需要这个0了。不然它4个4字节数据分别是函数地址、第一次this指针偏移调整、虚基类、虚基类之后的再一次this指针偏移调整。</p>
<h2>同一个头文件里的类在不同CPP包含大小会不同的原因</h2>
<p>说穿了其实很简单，一个类只有前置声明的时候，它的成员函数指针是16字节；但是如果某个CPP已经包含过这个“只有前置声明的类”的完整定义的时候，它的成员函数指针就是4或者8或者12字节。有的CPP包含了这个有类的完整定义的头文件，有的CPP却没有、只能看到前置声明。况且包含了完整定义头文件的CPP，可能是在声明这样的指针之前包含，也可能是在声明这样的指针之后包含，所以调换一下头文件顺序结果也会不一样。</p>
<p>那么这样的问题如何解决？网友featherwit给出了一个靠谱的方案：<span style="text-decoration: underline;"><strong><a href="https://msdn.microsoft.com/en-us/library/yad46a6z.aspx" target="_blank">使用编译参数/vmg</a></strong></span>（其实之前那个MSDN社区的帖子里，那人似乎也提到过了，说加了这个参数就一定是12字节）。不过不知道为什么我试出来是加了这个参数以后就都是16字节，总之是统一大小了。</p>
<p>&nbsp;</p>
<p><del>果然还是在意16字节的情况下第3个4字节整数存的到底是什么。<br />
</del>←已经补充了</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=580</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC++中多重继承的指针转换</title>
		<link>http://blog.sorayuki.net/?p=577</link>
		<comments>http://blog.sorayuki.net/?p=577#comments</comments>
		<pubDate>Mon, 11 Jul 2016 14:20:58 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=577</guid>
		<description><![CDATA[C++是支持多重继承的。不是像其他一些面向对象语言那样是“一个类实现多个接口”，而是真的多重继承。C++又是能 &#8230; <a href="http://blog.sorayuki.net/?p=577" class="more-link">继续阅读<span class="screen-reader-text">VC++中多重继承的指针转换</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>C++是支持多重继承的。不是像其他一些面向对象语言那样是“一个类实现多个接口”，而是真的多重继承。C++又是能从源代码生成机器码的语言，这种情况下，就要使得 在某个类的指针上调用对象的方法或访问成员变量（不管这个指针指向的就是这个类或者是这个类的子类）都能用同样的机器码实现，就会出现同一个对象，在用不同类型的指针表示它的时候，指针变量保存的地址会不同。它是把一个这样的对象分成几个部分，每个部分都是一个完整的父类型代表的对象，这样当父类型指向对应部分的时候，就能够完全像对待父类型的对象那样对待它。比如有类A和B，然后C同时继承了A和B，那么在C对象的内存空间里，就会有一块可以用A*指，还有另一块可以用B*指。</p>
<p><span id="more-577"></span></p>
<p>因为在调用delete的时候，它会先调用对象的析构函数，然后释放对象所占的内存。在这种情况下我想到，按照堆的特性，从堆申请的一块空间在释放的时候，要把这个空间的首地址送进去，而不是送入这块空间中的其他的什么地址。但是如果一个类X继承了B1和B2（B1在前B2在后），那么用X*指向这个对象和用B2*指向这个对象，指针保存的地址是不同的。此时如果对着B2*来delete，那么会发生什么样的事情……于是有了下面这个简单的代码进行试验。</p>
<pre>struct B1 { int a; };
struct B2 { int b; };
struct X : public B1, public B2 { int c; };

int main()
{
    X* x = new X();
    B2* b2 = x;
    delete b2;
}</pre>
<p>转到反汇编之后，可以看到它就是把b2的地址送到delete里面去调用，果然崩掉了。</p>
<p>这段代码从字面上来一眼似乎看不出什么问题，仔细考虑以后想到，以前学C++的时候，有这么个说法，一个类如果要用来做基类，那么应该要有虚拟析构函数。于是就又有了下面这样的试验代码。</p>
<pre>struct B1 { virtual ~B1(){} int a; };
struct B2 { virtual ~B2(){} int b; };
struct X : public B1, public B2 { int c; };

int main()
{
    X* x = new X();
    B2* b2 = x;
    delete b2;
}</pre>
<p>加了虚拟析构函数之后，程序果然运行起来就不崩了。查看反汇编，会发现在delete b2的时候，它直接调用虚函数表里的B2析构函数的指针（在X对象里，B2虚表里的析构函数是X对象的），而这个函数里面的内容是把指针从B2偏移回来，变成X*类型，然后执行X的析构函数，析构函数的最后自带delete的调用，于是这样释放起来就没有问题了。</p>
<p>虚表里的析构函数不太一样，虚表里保存的析构函数指针的是执行了原来的析构函数之后还delete的。如果对象是栈上创建的，那么就是只调用原来的析构函数）。</p>
<p>其实这都很早以前做的实验了。然而为什么会突然想到这个问题呢？今天上班的时候，同事在试验这个东西，就是在对象里面把this指针打印出来，看看它到底是什么样的。比如下面这样的代码</p>
<pre>#include &lt;stdio.h&gt;

struct B1 {
    virtual ~B1(){}
    int a;
};

struct B2 {
    virtual ~B2(){} 
    virtual void F() { printf("%p\n", this); }
    int b; 
};

struct X : public B1, public B2 {
    void F() override { printf("%p\n", this); }
    int c; 
};

int main()
{
    X* x = new X();
    B2* b2 = x;
    x-&gt;F();
    b2-&gt;F();
    delete b2;
}</pre>
<p>只是在刚才实验的代码上做了少量修改。根据前文的结论，x和b2虽然指向的是同一个对象，但是里面存的地址是不同的。也就是说，在下面调用F的时候，送入的this指针参数是不同的（后面证明这个想法是错的）。但是运行的时候，这两条输出确实输出了同样的地址。这是怎么做到的呢？</p>
<p>这种调整不能是在b2-&gt;F()的时候做的。因为在编译期的时候，不知道这个b2指向的是什么对象，可能是X可能是B2，不能随便给指针做偏移、转类型。这样可以得出，那么也就是这个F调用的时候必须要能接受一个类型为B2的指针；但是前面x-&gt;F()调用的时候，送进去的是类型为X的指针（看了反汇编，这个说法是错的），但不管是B2*上调用还是X*上调用，F是同一个函数。这里面的矛盾，最直白的方法还是看反汇编了。</p>
<p>结果看了反汇编，才注意到刚才思考的时候漏掉的东西：虽说在执行b2-&gt;F()的时候不能给this指针做类型转换，因为不知道它真实的类型；但是对于X类型来说，它却可以不管三七二十一就让F执行的时候接受的this参数是B2类型的：因为能看到X类型的定义的地方，都知道这个F是从B2来的，又因为能调用F的地方，都是能看到X定义的地方，所以在调用F的地方是一定可以把X*转成B2*的。然后真正的B2对象的虚表里是不可能有X::F函数的，所以在这个函数里，就可以把接收到的this参数，从B2*转回X*。</p>
<p>这种实现方式，外面用X类型来调用F的时候，因为知道F是从B2来的，可以把this指针转成B2*再送入；外面用B2类型调用F的时候，因为F就是B2里声明的，那么直接让this类型为B*送入就成。因此，就可以不管调用者是通过X类型调用的F还是通过B2类型调用的F，执行出一样的结果。</p>
<p>所以前面想当然认为“送入的this指针是不同的”是不正确的。送进去的this指针，还真就是相同的，类型都是B2*。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=577</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WDK编译boost(1.34.1)::regex</title>
		<link>http://blog.sorayuki.net/?p=517</link>
		<comments>http://blog.sorayuki.net/?p=517#comments</comments>
		<pubDate>Sun, 31 Aug 2014 16:38:02 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[函数库]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=517</guid>
		<description><![CDATA[Windows Driver Kit里面的STL库啊啥的，基本是停留在VC6时代。不过也有一个好处，可以链接到 &#8230; <a href="http://blog.sorayuki.net/?p=517" class="more-link">继续阅读<span class="screen-reader-text">WDK编译boost(1.34.1)::regex</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Windows Driver Kit里面的STL库啊啥的，基本是停留在VC6时代。不过也有一个好处，可以链接到msvcrt.dll、msvcp60.dll，因为这两个文件大家都有，所以可以减小最后生成的可执行文件的体积。然后比起VC6.0，WDK可以直接从微软那边下载来，而VC6直到最后也没像2005、2008、2010、2012、2013那样有个Express的免费版啥的。于是如果想自己弄了简单的小东西如果想要体积小、要链接到msvcrt.dll，感觉WDK是一个挺好的选择……</p>
<p><span id="more-517"></span></p>
<p>不过WDK里面的东西并不完善，所以Windows SDK可能还是要准备一个。考虑到要把它当作VC6来处理，所以最新能用的也就是Platform SDK 2003 February。其实本来没打算装这个，因为WDK里面已经带了一套Windows的头文件和库，但是在编译boost的那个bjam的时候，发现WDK里面没有TlHlp32.h文件（当然前提是你已经把api、crt那些的文件夹都加到Include环境变量里了），于是知道它带的东西还是有少的，就装个SDK来补充一下。</p>
<p>安装SDK的时候也还遇到了问题。因为这个SDK版本比较老，它有x86和ia64两个setup.exe、setup64.exe但是却没有给amd64（x86-64）用的setup，于是无法启动安装程序。上网搜索解决方案是直接到文件夹里面去运行它的那些msi安装包来安装组件。那些msi在安装的时候会提示选择要安装的组件、要安装到什么文件夹的。</p>
<p>现在的Boost已经抛弃对VC6.0的支持了，网上找了一圈相关信息，能支持VC6的最新版本是1.34.1（也有说1.34.1的Regex编译起来有问题，1.33.1更容易搞定，不过我没试验过），那就找到这个指定版本下载来，解压。</p>
<p>然后boost解压之后需要一个bjam控制之后的编译过程，在boost\tools\jam\src里可以找到一个build.bat，用它来构建bjam.exe。如果只有WDK、并且在bat的参数指定了MSVC、环境变量指定了MSVCDir为WDK的文件夹（具体开build.bat进去看，很容易找到），就会出现前面说的找不到TlHlp32.h的事情。</p>
<p>接下来，<a href="./?p=515">前一篇博客</a> 有提到WDK的编译器和msvcp60.dll配合有问题，编译器默认wchar_t就是wchar_t但是msvcp60认为它是unsigned short，于是在链接的时候出现符号链不上的问题。自己编译的时候还好办，知道/Zc:wchar_t，但是由别人编译的时候问题就有点麻烦，因为不知道怎么加。我研究了很久总之也没找到方法……不管怎么样，就算开-n的时候也能看到/Zc:wchar_t不过看不到/Zc:wchar_t。这样的命令行：<br />
bjam stage release toolset=msvc-6.0 --with-regex runtime-link=shared link=static<br />
最终开到boost\tools\build\v2\tools\msvc.jam文件里面，找到了/Zc:wchar_t，改成/Zc:wchar_t-，再编译，换了个错误，说swprintf函数什么什么不匹配。网上一搜，原来它有个BOOST_NO_SWPRINTF宏，索性就用上了。就是说把<br />
/Zc:wchar_t<br />
改成了<br />
/Zc:wchar_t- /DBOOST_NO_SWPRINTF</p>
<p>再编译，终于能出lib文件了。</p>
<p>仔细查看这一行的上下文，可以发现它是在你编译器指定是VC6.0的时候加入的特定处理。但是，明明有一个&lt;cxxflags&gt;在上面，试了好几种命令行写法，最终也没能成功把参数通过命令行弄进去。</p>
<p>总之经过这样一搞，最终成功在WDK里用起Boost.regex了。顺便一说，测试的结果，因为regex引擎加入进去的关系，最终生成的可执行文件大小只比VC2010用自带regex、静态链接生成的可执行文件的大小小了60KB左右（原来180KB现在120KB）</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=517</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WindowsDDK里用std::wstring的问题</title>
		<link>http://blog.sorayuki.net/?p=515</link>
		<comments>http://blog.sorayuki.net/?p=515#comments</comments>
		<pubDate>Sat, 30 Aug 2014 15:30:37 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>
		<category><![CDATA[问题解决]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=515</guid>
		<description><![CDATA[今天晚上尝试帮某网友制作一个伪春菜的插件，能够检查两次调用过程中剪贴板里面的东西有没有变化（用GetClipb &#8230; <a href="http://blog.sorayuki.net/?p=515" class="more-link">继续阅读<span class="screen-reader-text">WindowsDDK里用std::wstring的问题</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>今天晚上尝试帮某网友制作一个伪春菜的插件，能够检查两次调用过程中剪贴板里面的东西有没有变化（用GetClipboardSequenceNumber函数）和获取剪贴板里的内容（EnumClipboardFormats和GetClipboardData）。这里都不是重点，重点是我用了 <a href="./?p=9">这一篇博客</a> 里面提到的CSaori库来编写。这个库里面的字符是wchar_t类型，这很好，也用了std::basic_string&lt;wchar_t&gt;（其实就是std::wstring），这也很好。不好的地方在于，我为了减小最终DLL的体积、用Windows Driver Kit里面的编译器来编译程序使得动态链接到运行库的时候，报错了。</p>
<p><span id="more-515"></span>
<p>程序本身是没有问题，因为我用VS2010编译器编译它就正常，而且还能运行，运行起来也没问题。仔细查看DDK编译器的报错信息，大概是</p>
<pre style="height: 221px; width: 696px">error LNK2019: unresolved external symbol "__declspec(dllimport) p
ublic: wchar_t&amp; __thiscall std::basic_string&lt;wchar_t,struct std::char_traits&lt;wch
ar_t&gt;,class std::allocator&lt;wchar_t&gt; &gt;::operator[](unsigned int)" (__imp_??A?$bas
ic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@QAEAA_WI@Z) reference
d in function "public: bool __thiscall CSAORIInput::parseString(class std::basic
_string&lt;wchar_t,struct std::char_traits&lt;wchar_t&gt;,class std::allocator&lt;wchar_t&gt; &gt;
 const &amp;)" (?parseString@CSAORIInput@@QAE_NABV?$basic_string@_WU?$char_traits@_W
@std@@V?$allocator@_W@2@@std@@@Z)</pre>
<p>这个样子的，目测意思是链接不上std::wstring。</p>
<p>感觉不应该啊，它不是支持宽字符字符串对象的吗。然后上网搜搜出了这么一个东西：</p>
<p><a title="http://stackoverflow.com/questions/5988457/is-wchar-t-supported-in-c-wdk-stl-i-get-unresolved-external-symbols" href="http://stackoverflow.com/questions/5988457/is-wchar-t-supported-in-c-wdk-stl-i-get-unresolved-external-symbols">http://stackoverflow.com/questions/5988457/is-wchar-t-supported-in-c-wdk-stl-i-get-unresolved-external-symbols</a></p>
<p>感觉好坑……编译器和库不够匹配的结果吗。库用的是VC6.0里面的吧，但是因为编译器是新的，默认wchar_t是内置类型，而库并不是这样的，最终链不上。</p>
<p>最终加上了 /Zc:wchar_t- 参数，它就好了。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=515</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>编辑VC生成的导入库文件</title>
		<link>http://blog.sorayuki.net/?p=383</link>
		<comments>http://blog.sorayuki.net/?p=383#comments</comments>
		<pubDate>Thu, 13 Feb 2014 08:21:33 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=383</guid>
		<description><![CDATA[还是接着之前那篇关于制作导入库的工具的后续。另一种方法，虽然局限性更大，但是可以满足大多数情况了。 灵感很简单 &#8230; <a href="http://blog.sorayuki.net/?p=383" class="more-link">继续阅读<span class="screen-reader-text">编辑VC生成的导入库文件</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>还是接着之前那篇关于制作导入库的工具的后续。另一种方法，虽然局限性更大，但是可以满足大多数情况了。</p>
<p><span id="more-383"></span>
<p>灵感很简单，在查看关于DLL调用方面的文章的时候，突然看到了一篇关于MinGW下制作lib文件之类内容的。因为我以前用过MinGW编译器，同样也搞过这个DLL导入的方面。于是就想起来当时用dlltool工具的时候，确实有看到它能够支持对导入名进行调整的参数。但是那个时候并没有对这方面有多少深入的研究，其实说白了就是没有理解那些参数实际想要表达的到底是什么意思。这下有了前面对VC下隐式调用DLL里函数的经验，一下子知道了那些到底是想表达什么，比如导入名中去掉prefix，或者kill掉后面的@nn，之类之类。</p>
<p>然后呢，前面在研究导入库格式的时候，看微软提供的那个规格说明文档，里面也有提到这方面的东西，比如去掉prefix或者kill掉后面的@nn之类。</p>
<p>说简单点，就是正常情况下比如写了一个这样的def文件</p>
<pre><p>LIBRARY MyDLL
EXPORTS
  function
  dosomething</p></pre>
<p>此时如果用lib.exe工具（其实就是link.exe /lib，因为WDK中没有lib.exe所以提一下）从这个def文件生成lib文件，那么这个lib文件将从DLL导入function和dosomething，同时向链接器提供_function和_dosomething。这样的话对于__cdecl方式导出的函数是没有问题的。但是如果DLL的导出符号仍然是function和dosomething，但是调用方式却是__stdcall，就会遇上了问题：如果function有一个int型参数，那么链接器将去寻找_function@4而不是_function，因为找不到符号，所以链接失败了。</p>
<p>如果DLL中导出的符号名是function@4，此时生成的lib就会从DLL导入function@4并向链接器提供_function@4，但是实际上以__stdcall方式调用，但是最后导出的时候不带@nn的情况是非常多的。最典型的情况就是Windows API。而且实话说吧因为@nn这样的实在不怎么好看，所以我自己写DLL的时候也会通过使用DEF文件的方式或者#pragma comment(linker, "/EXPORT:xxxxxx")这样的预编译语句让导出的函数不带如此的后缀。</p>
<p>于是这样需求就变得简单：让生成的导入库从DLL引入的时候是不带后缀的，但是向链接器提供的符号是带后缀的。</p>
<p>这并非不可能，调用Windows API的时候就是这样：Windows SDK里面那些LIB文件，就是实现这样的功能。对其格式进行研究，根本就是和普通的导入库一样。除了……呃，除了某些细节上的参数。</p>
<p>于是我的目的就是修改这些细节上的参数。<a href="http://msdn.microsoft.com/library/windows/hardware/gg463119.aspx">Microsoft PE and COFF Specification</a>里也提供了这方面的详细资料。</p>
<p>方法的话，遍历LIB文件中的每个member，LIB文件的格式可以从上面提到的资料中找到，不会太复杂。<strong>注意每个member的第一个字节似乎是对其到偶数边界的，没有对齐的用\n来补。</strong>在遍历的过程中分析前四个字节就知道这个member是不是声明了一个导入函数，如果是，根据导入的格式分析其头部信息，然后获得DLL名、函数名等。</p>
<p>在这个导入member的“头部信息”中，有关于导入名应该如何计算的属性。但是LIB.exe工具却没有这方面的参数可以调整，默认就是先前面给你加一个_下划线，然后方式是NoPrefix就是再把这个下划线去掉。也就是说，def文件中如果写的是function，那么它就先加一个下划线，变成_function，作为向链接器提供的符号。然后导入名生成方式是NoPrefix，也就是链接器在链接的时候会再从_function去掉前缀的下划线变回function作为从DLL导入的名。</p>
<p>这个导入名的计算方式有</p>
<p>IMPORT_ORDINAL：通过标号导入，此时不会计算导入名<br />IMPORT_NAME：原原本本的，和向链接器提供的符号保持一致<br />IMPORT_NAME_NOPREFIX：不带前缀的，LIB.EXE生成的就是这种。前缀是指?、@或者可选的下划线_<br />IMPORT_NAME_UNDECORATE：不带前缀并且只取@之前的部分，此时如果向链接器提供的符号名是_function@4，那么导入名就是function，也就是我们需要的。</p>
<p>有了这些资料，思路就非常清楚，比如有以下两个函数</p>
<p>
<pre>__declspec(dllimport) int __stdcall add2(int, int);
__declspec(dllimport) int __stdcall add3(int, int, int);</pre>
</p>
<p>然后DLL里的导出名分别是add2和add3而不是add2@8和add3@12，此时可以写这样一个DEF文件</p>
<pre>LIBRARY doadd
EXPORTS
  add2@8
  add3@12</pre>
<p>此时生成的lib文件中，向链接器提供的符号这一边已经是所需要的_add2@8和_add3@12了。然后这个时候要修正的是导入名计算方式，因为现在是IMPORT_NAME_NOPREFIX，要改成IMPORT_NAME_UNDECORATE才行。这个时候就要用十六进制编辑器来修改，但是手工做这种事情实在太麻烦，这种事情应该给程序去做。所以就写了个工具，来解决这种问题。</p>
<p><a href="http://blog.sorayuki.net/wp-content/uploads/2014/02/image.png"><img title="image" style="display: inline" alt="image" src="http://blog.sorayuki.net/wp-content/uploads/2014/02/image_thumb.png" width="970" height="254"></a></p>
<p>图片为修改一个函数的导入名计算方式后的截图。改完以后保存，就可以实现目的了。</p>
<p>这里给出这个自制工具的下载：（上传时间：2014年2月13日。工具有修改的话，这里可能不会同步更新。）</p>
<div id="scid:fb3a1972-4489-4e52-abe7-25a00bb07fdf:9ded0bf0-87e0-48c7-8574-1d5c6554cdbf" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">
<p>点击下载 <a href="http://blog.sorayuki.net/wp-content/uploads/2014/02/ImpLibEditor.7z" target="_blank">工具 </a></p>
</div>
<p>运行需要.net framework 2.0环境。修改完成后记得保存。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=383</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从DLL导入时符号的问题</title>
		<link>http://blog.sorayuki.net/?p=352</link>
		<comments>http://blog.sorayuki.net/?p=352#comments</comments>
		<pubDate>Fri, 27 Sep 2013 10:10:59 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=352</guid>
		<description><![CDATA[虽然DLL互相调用啊啥的玩得不少了，但是其实没有认真研究过符号和__declspec(import)这类东西之 &#8230; <a href="http://blog.sorayuki.net/?p=352" class="more-link">继续阅读<span class="screen-reader-text">从DLL导入时符号的问题</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>虽然DLL互相调用啊啥的玩得不少了，但是其实没有认真研究过符号和__declspec(import)这类东西之间的关系。今天碰巧遇到了，就稍微看一下。</p>
<p><span id="more-352"></span>
<p>&nbsp;</p>
<p>编译DLL的时候加了__declspec(dllexport)就能把符号导出，这是没问题的，也能生成一个导入库LIB，这个LIB用来以导入表的方式链接到DLL里的符号用。</p>
<p>但是在调用DLL里这些函数啊什么的时候，又有一个问题，就是写函数声明的时候，加__declspec(dllimport)也可以，不加也可以。经过对比研究，汇编级跟踪，大致弄明白是怎么一回事了。</p>
<p>使用__declspec(dllimport)声明的时候，生成的代码是 CALL [xxxxxxxx] 这样，其中的xxxxxxxx就是导入的符号在导入表中的地址，xxxxxxxx对应的符号是__imp__打头的那些。</p>
<p>如果不使用__declspec(dllimport)声明，那么生成的代码是 CALL xxxxxxxx这样的，其中xxxxxxxx指向的是一句 jmp [yyyyyyyy] 这样的代码，其中xxxxxxxx对应的符号是不带__imp__前缀的那些，yyyyyyyy是导入的符号在导入表中的位置。我推测这一句jmp（似乎俗称stub）是LIB文件中提供的代码。</p>
<p>也就是说在使用nasm编译一个obj来塞入LIB用来干点什么事的时候，对于__imp__系的符号，要用dd，放在.data节；对于不带__imp__前缀的符号，要用 jmp xxxxxxxx，放在.code节。</p>
<p>&nbsp;</p>
<p>对了顺便说一下，如果是使用equ让它指向另外一个地址的话，我尝试的时候，让它指向extern的某个符号是失败的，最终会导致这个equ到另一个常量的符号没有导出，具体可见<a href="?p=223" target="_blank">这篇博客</a>。但是可以给它一个常量。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=352</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC自制COM组件</title>
		<link>http://blog.sorayuki.net/?p=289</link>
		<comments>http://blog.sorayuki.net/?p=289#comments</comments>
		<pubDate>Sun, 19 May 2013 07:37:39 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=289</guid>
		<description><![CDATA[Com组件这东西实在是麻烦，以前也一直没有碰过。 过程是这样子的，一天在百度知道上看到有人求助一段C++代码转 &#8230; <a href="http://blog.sorayuki.net/?p=289" class="more-link">继续阅读<span class="screen-reader-text">VC自制COM组件</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Com组件这东西实在是麻烦，以前也一直没有碰过。</p>
<p>过程是这样子的，一天在百度知道上看到有人求助一段C++代码转成VB。我又不熟悉VB，于是就顺手给封装成DLL给了他。</p>
<p>然后提问者回复说，能不能给搞成能用CreateObject方式创建对象的那种方式来调用。这一看，应该要com吧？但是我不会写com。想着稍微掌握一点以后可能也能用到，就去找教学啥的稍微尝试一下。</p>
<p>主要参考的文章是 <a title="http://icodeguru.com/VC%26MFC/InsideAtl/ch02c.htm" href="http://icodeguru.com/VC%26MFC/InsideAtl/ch02c.htm">http://icodeguru.com/VC%26MFC/InsideAtl/ch02c.htm</a> 和 <a title="http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567/Step-by-Step-COM-Tutorial.htm" href="http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567/Step-by-Step-COM-Tutorial.htm">http://www.codeguru.com/cpp/com-tech/activex/tutorials/article.php/c5567/Step-by-Step-COM-Tutorial.htm</a> 这两篇。一开始是看后者，后面也看了前者，结合起来，大概理清一点思路了。</p>
<p><span id="more-289"></span></p>
<p>因为COM组件是面向对象的，所以要制作COM组件前，先确定要有什么类。具体百度知道上那个问题在这里 <a title="http://zhidao.baidu.com/question/551068312.html?sort=6&amp;old=1#answer-1388747223" href="http://zhidao.baidu.com/question/551068312.html">http://zhidao.baidu.com/question/551068312.html</a> 。根据教学，写了一个idl文件，定义如下内容</p>
<blockquote><p><em><span style="font-family: Courier New; font-size: small;">import "unknwn.idl";</span></em></p>
<p><em><span style="font-family: Courier New; font-size: small;">[<br />
uuid(EB35191C-DCC7-49DA-BC10-A1E0E9CEECA8),<br />
object,<br />
helpstring("Get Hash code for 156542994")<br />
]<br />
interface IGetHash : IUnknown<br />
{<br />
HRESULT GetHash([in]BSTR szStr, [out,retval]LONG* pOut);<br />
}</span></em></p>
<p><em><span style="font-family: Courier New; font-size: small;">[<br />
uuid(AB18183F-A79B-49E7-92E8-514A056B2FCB),<br />
helpstring("Library for GetHash, by 156542994")<br />
]<br />
library MyGetHashLib<br />
{<br />
importlib("stdole32.tlb");<br />
interface IGetHash;<br />
}</span></em></p></blockquote>
<p><span style="font-family: Georgia;">里面的uuid是用软件生成的。写了这么一个gethash.idl以后，用midl工具处理一下，生成gethash_i.c, gethash.h, gethash.tlb等文件。</span></p>
<p><span style="font-family: Georgia;">然后是类的编写，从IGetHash派生出CGetHash类，写上GetHash的实现，以及从IUnknown里面来的AddRef的实现、Release的实现、QueryInterface的实现。这些函数都是stdcall方式调用的。AddRef和Release的实现没什么好说，就是智能指针而已，在Release到0的时候要释放，delete this ←这样。</span></p>
<p><span style="font-family: Georgia;">至于QueryInterface，它的意思是当前对象能不能转换成另一个接口的指针。因为我这个CGetHash的父类有IUnknown和IGetHash，所以当参数中指定的那个iid为IID_IUnknown和IID_IGetHash的时候，调用AddRef并给输出参数赋值，同时返回S_OK；否则就输出参数置0。</span></p>
<p><span style="font-family: Georgia;">这样这个类就写出来了，不过还没办法用。COM组件用了工厂模式，要为这个类写一个工厂才行。于是从IClassFactory派生出一个CClassFactory，实现IUnknown的AddRef、Release、QueryInterface和实现IClassFactory的CreateInstance即可。其中CreateInstance的第一个参数pOuter，这个到底是COM的什么特性我还不知道，也没有用到，总之按照教学说的如果这个不是0就返回CLASS_E_NOAGGREGATION好了。然后CreateInstance里面的实现要偷懒一点，就直接创建出CGetHash对象，然后用它的QueryInterface把iid和pOut送进去，之后Release。因为如果参数符合，那么在QueryInterface里面会增加引用计数，Release的时候也就不会被删掉。这样CreateInstance也搞定了。LockServer是啥我不知道，看msdn上介绍似乎是什么lock了以后能更快还是啥的……总之直接返回S_OK。有说法是Lock的时候调用AddRef然后Unlock的时候调用Release就行。不过不太清楚……刚学</span></p>
<p><span style="font-family: Georgia;">dll形式的COM的最重要接口DllGetClassObject，这个接口是用来获取工厂的。三个参数：clsid、iid和pOut。其中clsid代表类的id，就是刚刚那个CGetHash。也就是说不光是IGetHash要有id，这个CGetHash尽管不对外暴露但是也还是要有id。于是用id生成器给它生成一个id。到时候这个函数被回调的时候，比如VB里面用了CreateObject来创建它，clsid会送进来CLSID_CGetHash，iid会送进来IID_IClassFactory，判断好这个情况，其他时候丢个CLASS_E_CLASSNOTAVAILABLE就行。</span></p>
<p><span style="font-family: Georgia;">至于那些Register和Unregister要写不写都行，写的话就你自己维护，不写的话就用户手工去改注册表或者什么的都可以，总之在HKEY_CLASS_ROOT里面添加一个代表你类的名字的键值比如MyGetHashLib.GetHash，在下面建立一个CLSID把你类（CGetHash）的id写进去，然后在HKEY_CLASS_ROOT的CLSID下面建一个你类的id为名的子键，下面创建一个ProgID一个InprocServer32，前者一样填MyGetHashLib.GetHash，后者填你dll的完整路径，大功告成</span></p>
<p><span style="font-family: Georgia;">在VB里面添加引用之后就可以这么调用：</span></p>
<p>Dim a As IGetHash<br />
Set a = CreateObject("MyGetHashLib.GetHash")<br />
MsgBox Hex(a.GetHash("cc"))</p>
<p>比用Declare那个看起来是不是高端洋气了？w</p>
<p>把tlb文件内嵌进去的话，根据 <a href="http://support.microsoft.com/kb/122285/en-us">http://support.microsoft.com/kb/122285/en-us</a> 这篇文章的说法，资源id设1，类型typelib即可。不过这样就有一个疑问，因为xp style的时候也要求资源id设1……如果想同时都要的话不知道应该如何处理呢。</p>
<p>下载代码： <a href="http://blog.sorayuki.net/wp-content/uploads/2013/05/gethashcom.7z">gethashcom</a></p>
<p>之后我想了想，既然它都是通过QueryInterface来的，那么我只要在它Query的时候返回正确的接口就成了，现在既然这个类这么简单，那我把工厂和产品合起来也未尝不可。</p>
<p>于是出来了下面这个把IClassFactory和IGetHash合起来的版本</p>
<p>下载代码： <a href="http://blog.sorayuki.net/wp-content/uploads/2013/05/gethashcom2.7z">gethashcom2</a></p>
<p>画了个示意图，希望能好懂一些。以后自己要是忘了也可以参考</p>
<p><a href="http://blog.sorayuki.net/wp-content/uploads/2013/05/com_sample.jpg"><img class="alignnone size-full wp-image-294" title="com_sample" src="http://blog.sorayuki.net/wp-content/uploads/2013/05/com_sample.jpg" alt="" width="534" height="346" /></a></p>
<p>DllCanUnloadNow最好也实现一下（本例中没有写这个……），目测判断这个dll创建出来的对象是不是全部已经全部没有引用了，如果是，那么就返回S_OK；不是，就返回S_FALSE</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=289</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC里Tab控件页面的子窗体确定尺寸</title>
		<link>http://blog.sorayuki.net/?p=284</link>
		<comments>http://blog.sorayuki.net/?p=284#comments</comments>
		<pubDate>Tue, 14 May 2013 13:12:48 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=284</guid>
		<description><![CDATA[果然问题就是在英语不好。msdn虽然有说但是看得实在是…… .net倒是很方便，直接控件拖拖拖，这个都给你实现 &#8230; <a href="http://blog.sorayuki.net/?p=284" class="more-link">继续阅读<span class="screen-reader-text">VC里Tab控件页面的子窗体确定尺寸</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>果然问题就是在英语不好。msdn虽然有说但是看得实在是……</p>
<p>.net倒是很方便，直接控件拖拖拖，这个都给你实现了的。</p>
<p>但是sdk或者mfc里就比较麻烦。Tab控件上面的页面如果切换了，不是里面的内容要跟着变吗？但是tab控件不管里面的内容变，它只管发一个通知给你。怎么变你自己实现。于是最方便的方法就是弄很多子窗口，在切换的时候更换子窗口。</p>
<p>其实就是你先GetClientRect一下拿到它整个的矩形区域，然后TabCtrl_AdjustRect把这“整个”转换成“里面”。之后你去创建子窗口就可以了。</p>
<p>那个TabCtrl_AdjustRect说真的确实是早就看到了，但是它的说明看得半懂不懂。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=284</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>VC编写的本地代码程序使用XP Style</title>
		<link>http://blog.sorayuki.net/?p=283</link>
		<comments>http://blog.sorayuki.net/?p=283#comments</comments>
		<pubDate>Tue, 14 May 2013 13:00:24 +0000</pubDate>
		<dc:creator><![CDATA[空雪梦见]]></dc:creator>
				<category><![CDATA[VC]]></category>
		<category><![CDATA[Windows编程]]></category>

		<guid isPermaLink="false">http://blog.sorayuki.net/?p=283</guid>
		<description><![CDATA[只是避免在鱼龙混杂的网络上找到莫名其妙不可靠资料的担忧 msdn上有，不过临时去翻msdn挺麻烦的，因为没记住 &#8230; <a href="http://blog.sorayuki.net/?p=283" class="more-link">继续阅读<span class="screen-reader-text">VC编写的本地代码程序使用XP Style</span> <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>只是避免在鱼龙混杂的网络上找到莫名其妙不可靠资料的担忧</p>
<p>msdn上有，不过临时去翻msdn挺麻烦的，因为没记住在哪个分类里面的话（汗</p>
<p><strong>分类在Windows Desktop App Development——Windows Application UI Development——Windows Controls——Visual Styles——Enabling Visual Styles</strong></p>
<p>然后若遇上网络很慢，要人命</p>
<p><span id="more-283"></span>首先是VC2005或以上的解决方法：
<p>直接链接器参数搞起</p>
<div id="scid:887EC618-8FBE-49a5-A908-2339AF2EC720:1f0431f6-e792-4945-b377-2f1d62f93f1a" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">         <code>
<pre>"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"

 </pre>
<p></code>
      </div>
<p>或者用#pragma加进去</p>
<div id="scid:887EC618-8FBE-49a5-A908-2339AF2EC720:d5f7c626-c22d-49e6-a843-80f4bb983bb9" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">
        <code>
<pre>#pragma comment(linker,""/manifestdependency:type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"")

 </pre>
<p></code>
      </div>
<p><strong>然后调用InitCommonControls();</strong>，链接到 ComCtl32.lib即可</p>
<p>但是msdn里面专门注明了要vs2005以上。大概以前版本不行吧。</p>
<p>然后就是用manifest清单文件的办法，是一个xml，以资源方式加入：</p>
<div id="scid:887EC618-8FBE-49a5-A908-2339AF2EC720:edb4728e-07fb-43a7-802e-4b259c76d72d" class="wlWriterEditableSmartContent" style="float: none; padding-bottom: 0px; padding-top: 0px; padding-left: 0px; margin: 0px; display: inline; padding-right: 0px">
        <code>
<pre>&lt;?xml version='1.0' encoding='UTF-8' standalone='yes'?&gt;
&lt;assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'&gt;
  &lt;trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"&gt;
    &lt;security&gt;
      &lt;requestedPrivileges&gt;
        &lt;requestedExecutionLevel level='asInvoker' uiAccess='false' /&gt;
      &lt;/requestedPrivileges&gt;
    &lt;/security&gt;
  &lt;/trustInfo&gt;
  &lt;dependency&gt;
    &lt;dependentAssembly&gt;
      &lt;assemblyIdentity type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*' /&gt;
    &lt;/dependentAssembly&gt;
  &lt;/dependency&gt;
&lt;/assembly&gt;


 </pre>
<p></code>
      </div>
<p>资源类型是24（RT_MANIFEST），<br />资源id是1（CREATEPROCESS_MANIFEST_RESOURCE_ID）。不是1不知道行不行，也许可以。</p>
<p>然后调用InitCommonControls();，链接到 ComCtl32.lib即可</p>
<p>里面这些YourApplication啥的省略掉它就赫然不能启动了。略烦……不想写的时候就这么留着吗。</p>
<p><strong>2014-1-2添加</strong>：链接器如果没有/MANIFEST:NO或者用了/MANIFEST的话，会生成一个manifest文件。如果前面用的是pragma方法，此时可以得到一个小得多的manifest文件。基于此，我把上面那个xml的内容简化成我得到的manifest文件的内容了，本来是例子里的，长不少。</p>
<p>vc6里加入这样的资源，按钮之类的也能出xp style了。目测MinGW GCC下应该也行。</p>
<p>&nbsp;</p>
<p><strong>2013-6-5补充：注意啊！不InitCommonControls它可能连对话框都不出来了！所以一定要这个啊！我今天解决的一个问题就是因为没有这个……</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.sorayuki.net/?feed=rss2&#038;p=283</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
