关于作者

用户名:APOO
笔名:APOO
地区: 北京-北京
行业:本科

日历  

快速登录

+ 用户名:
+ 密 码:

在线留言


链接分类α

链接分类β

链接分类γ

链接分类δ

链接分类ω

访问统计:
文章个数:507
评论个数:371
留言条数:4




Powered by BlogDriver 2.1

我思故我在

 

文章

Python的控制台输出语句print的一个bug

Python 的 print 语句有一个很奇怪的 bug。它的功能是向控制台输出字符,这本身不是问题。但是 Python 内部是支持 Unicode 字符串的,而 Unicode 字符串在用 print 输出时 print 要进行一次从 Unicode 到 ANSI/MBCS 编码的编码,编码后才会以 8-bit 流输出结果。

编码就编码吧,这也是很正常的。对于控制台程序来说,输出可能被重定向到文本文件。如 果不指定编码,重定向时就不知道以何种 8-bit 字节流写入文本文件,所以,输出到控制台的东西理论上也应该是经过编码的 8-bit 流。综上所述,确实有必要进行一次 WCHAR 到 char 的转码。

但是问题在于,Python 的 print 语句在转码时,居然用的是 strict 规则。即,待输出字符串若含有当前代码页之外的字符,就会在转码过程中出现不可转码的文字,从而抛出 exception。print 语句又不处理这个 exception,导致一个平平常常 print 语句竟然会引起 Python 程序的异常!这简直是不可思议。


比如说你写了这么一段代码:

a = u'测试啊'
print a

然后把控制台切到某个不包含这些汉字的编码页例如 437,输入chcp 437。然后再运行这段程序,就会看到异常。实际上直接输出到控制台的是另外一种 UnicodeEncodeError 异常,因为控制台设置了代码 页,Python 会试图转码到那个代码页。而更典型的(使开发者发现问题的)异常通常是把输出重定向到文件时,看到的下面这个更典型的异常:

UnicodeEncodeError: 'ascii' codec can't encode character u'\xa1' in position 0-2: ordinal not in range(128)

注意,控制台直接输出有异常,重定向输出也会有异常。这两种异常在系统内部具体过程不同,但原理都是一样的。就是 python 遇到了它认为不能把 Unicode 字符编码成 8-bit 流的情况。区别在于,输出到控制台时,python 会试图按照控制台设置的代码页去编码,而重定向时干脆就按 ASCII 编码,那自然是只有128以内的字符才能显示出来。由此可以看出,输出到控制台时产生的异常更隐蔽,因为绝大部分程序员都是在一种编码下编码+ 开发的,很少有考虑到这方面的情况。在一种编码下开发,写进代码的字符串,以及从文本读出来的字符串,通常也能在这个编码下在控制台输出,从而把问题的发 现推迟到了用户(使用了不同代码页)阶段,或是推迟到了重定向输出的时候(因为重定向默认用 ASCII 编码,字符集最小)。知道了原因,会觉得错误可以理解。

说句题外话,令我最不能理解的是,一个好好的 print 语句,输出字符串也不是 zero-terminated,不存在烫烫烫烫过了越到不可访问内存崩溃了的结果,竟然会导致程序异常!首先别跟我说让程序员去控制print 里字符串的内容,这有的时候程序根本控制不了。比如,读出一个文件并显示内容的时候。也别跟我说去 try-except,连 print 都失败了你叫程序员情何以堪啊?看来只能想想办法自己解决这个问题了。

首先要说明的是,既然事关控制台,要做 8-bit 流的输入输出,就没有完美的解决方案。我个人的建议是,在Windows下,一切字符串操作,都应该尽可能使用 WCHAR 及相关函数。遇到需要 跨平台和网络传输的情况,再使用UTC-8编码的char字符串。在与古老的 ANSI/MBCS 程序交互时,在严格限制的情况下使用该种编码的 char 字符串。尽管并没有完美的解决方案,在实际情况中,Windows 下 Python 程序也许应该可以有更好的表现。


解决方案一、最简单解决重定向异常的方法是:

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

然后再输出就可以了。直接调sys.setdefaultencoding()这个函数是不行的,必须要reload一次。具体原因可以参见http://docs.python.org/library/sys.html,我就没有深入研究了。

这个不会影响控制台直接输出,只会影响重定向,所以最好是写 utf-8 反正连 Windows 的记事本都可以打开 UTF-8 的文本。当然这么做也有不足,就是如果某一个程序,调用了你写的 Python 程序,把输出重定向到它的窗口里,这时这个程序很可能是按系统默认编码去解码的,用户就看到一片乱码了。这个没什么好办法,要么外围程序做好点可以设置控 制台解码,要么你就只能获取一下当前控制台编码设置(不知道 Python 里有没有好方法,我可以用 Windows API 做到),当然这样的话就无法防止异常了……


解决方案二、用 print a.encode("gbk", "replace") 取代 print a:

对控制台来说,由于输出的是字节流,所以具体显示成什么字符,取决于控制台的代码页设置。输出重定向也是一样,取决于你打开文件的方式。如果打开文件发现乱码了,那你要说:一定是我打开的方式不对!

这个方案好处在于可以让程序完全像使用了 Windows ANSI 函数的程序那样工作。输入、输出全都是按某个特定编码来做的,仿佛程序内部固化的字符串就是按某个特定编码写的。不过,程序里有几千个 print 就得换几千次就不说了,万一你换漏了,又要出悲剧。

当 然,既然完全像一个 Windows ANSI 程序的行为,那么不可避免的问题就是乱码。假设你所有字符串都按 GBK 在输入输出时编码了,那如果用户设置的控制台代码页根本就不是 GBK 呢?又乱码了不是……而且既然我输入输出都是 GBK,干嘛程序内部还要用 Unicode 呢?大概就只是为了防止内部处理时即出现异常吧。

最关键的是这实在不是一个程序员的作风。就没有自动化一点的方案吗?


解决方案三、更改 sys.stdout 的编码:

既然问题出在 sys.stdout 的编码往往不能满足字符集需求上,为什么不直接更改它的编码呢?http://www.doughellmann.com/PyMOTW/codecs/ 提供了一种方案:

import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

这 个方案的好处就是它同时影响控制台直接输出和重定向输出,比方案一强,已经达到了方案二的水平。不过它面临一个方案二没有而方案一还有的问题,就是如果设 置的不是 "utf-8",那么就有可能出 UnicodeEncodeError。如果设置的是 "utf-8",那就要面临配套设施不完善而看到的乱码问题。

最要命的是,其实你是根本无法在控制台设置成 cp65001 的情况下让程序正常运行的!这是方案二也会同样遇到的问题。假设我们设置了 utf-8,要想在控制台正常阅读输出结果,那也就要把控制台用 chcp 65001 设置成 UTF-8。但是,设置之后,python 会以为当前代码页叫 "cp65001",不认,会出这个错误:

LookupError: unknown encoding: cp65001

呃,好吧,这也是有办法可以解决的,出自 http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash

import codecs
codecs.register(lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None)

这样 Python 就认 "cp65001" 这个东西就是 "utf-8" 的别名了。这样,你就可以在控制台 chcp 65001 然后看到输出字符了。不过遗憾的是,这只是理论上的。实际上如果你 print a 的时候第一个字符不是纯 ASCII 的,即 Unicode 码在 128 以上,根本无法正常显示。我们不妨把前面学到的知识都拼起来,写一段代码,期望它能正常工作吧:

#coding=utf-8
a = u'测试啊'

import sys
reload(sys)
sys.setdefaultencoding("utf-8")

import codecs
codecs.register(lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None)

print a.encode("utf-8", "replace")

实际上运行结果是:

???试啊Traceback (most recent call last):
  File "C:\Python25\Test1.py", line 11, in
    print a.encode("utf-8", "replace")
IOError: [Errno 2] No such file or directory

这 莫名其妙的 IOError 是怎么回事?而且字符串第一个字符也无法正常显示,会变成若干个“?”。该字符在 UTF-8 中是几个字节,就有几个“?”字符。我™想破了脑袋也想不出 Python 是怎么写出这样的 bug 来的!注意,不是说第一个字符是纯 ASCII 就可以了,只是那样做的话输出来的异常信息是可以看,但是异常还是有的。如果是用 sys.stdout = codecs.getwriter() 法直接 print a 的话,出现的错误是:

???试啊Traceback (most recent call last):
  File "C:\Python25\Test1.py", line 13, in
    print a
  File "C:\Python25\lib\codecs.py", line 304, in write
    self.stream.write(data)
IOError: [Errno 0] Error

所以实际上是根本没法用的。我测试的版本是 Python 2.5.2,不知道后续版本是否有改进。

而且还有一个问题是如果你 chcp 65001 之后,打过一些汉字或者用 type 显示过文件,就会发现怎么光标的位置都不对啊!换行也不对啊喂后面怎么好多东西超出去了看不到啊!

没 错恭喜你遇到了最头疼的问题!在 cp65001 下,并不像那些中国、日本、韩国的代码页下面那样区分全角和半角,所有的字符在计算光标的时候都占同样的宽度,但是字体渲染仍然正常。也就是说,如果(假 设一行设置的是 80 个字符)你在一行里写了 80 个汉字,那么前 40 个渲染的时候就已经把整行占满了,可是没有自动换行,自动换行要到 80 列才有,所以后 40 个汉字就看不见了。

坑爹呀。

遗憾的是这还根本没有解决办法。要想让全角字符正确地占两个半角字符的宽度,就只能用一些支持这个特性的代码页,比如 cp936,就是 GBK。当然,这样就不能显示全部 Unicode 字符了,万一有用户输入了这个,就只能被替换成 ? 或者其它什么东西了。

所以说,只要还跟该死的 char 字节流打交道,跟 stdout 打交道,就没法有一个完美方案。


解决方案四、彻底不使用stdout:

这 堆乱七八糟的事情从根本上来说是因为控制台的 stdout 只能接受 8-bit 字节流,也就是 char,所以才有了这么多有的没的编码问题。如果能够让 python 在用 print 的时候底层使用一个接受 WCHAR 的函数来做事,也许事情就有很大转机。

事实上,还是在 http://stackoverflow.com/questions/5109970/linux-python-encoding-a-unicode-string-for-print 就有一篇终极解决方案。它用接受 WCHAR 的 Windows API 做控制台输出,而同时把重定向交由原有方式处理,在兼顾重定向的情况下,实现了控制台下最完美的输出方案。

首先请看代码:

import sys
if sys.platform == "win32":
    import codecs
    from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int
    from ctypes.wintypes import BOOL, HANDLE, DWORD, LPWSTR, LPCWSTR, LPVOID

    original_stderr = sys.stderr

    # If any exception occurs in this code, we'll probably try to print it on stderr,
    # which makes for frustrating debugging if stderr is directed to our wrapper.
    # So be paranoid about catching errors and reporting them to original_stderr,
    # so that we can at least see them.
    def _complain(message):
        print >>original_stderr, isinstance(message, str) and message or repr(message)

    # Work around .
    codecs.register(lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None)

    # Make Unicode console output work independently of the current code page.
    # This also fixes .
    # Credit to Michael Kaplan
    # and TZΩTZIOY
    # .
    try:
        #
        # HANDLE WINAPI GetStdHandle(DWORD nStdHandle);
        # returns INVALID_HANDLE_VALUE, NULL, or a valid handle
        #
        #
        # DWORD WINAPI GetFileType(DWORD hFile);
        #
        #
        # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode);

        GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32))
        STD_OUTPUT_HANDLE = DWORD(-11)
        STD_ERROR_HANDLE  = DWORD(-12)
        GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32))
        FILE_TYPE_CHAR   = 0x0002
        FILE_TYPE_REMOTE = 0x8000
        GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD)) \
                             (("GetConsoleMode", windll.kernel32))
        INVALID_HANDLE_VALUE = DWORD(-1).value

        def not_a_console(handle):
            if handle == INVALID_HANDLE_VALUE or handle is None:
                return True
            return ((GetFileType(handle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR
                    or GetConsoleMode(handle, byref(DWORD())) == 0)

        old_stdout_fileno = None
        old_stderr_fileno = None
        if hasattr(sys.stdout, 'fileno'):
            old_stdout_fileno = sys.stdout.fileno()
        if hasattr(sys.stderr, 'fileno'):
            old_stderr_fileno = sys.stderr.fileno()

        STDOUT_FILENO = 1
        STDERR_FILENO = 2
        real_stdout = (old_stdout_fileno == STDOUT_FILENO)
        real_stderr = (old_stderr_fileno == STDERR_FILENO)

        if real_stdout:
            hStdout = GetStdHandle(STD_OUTPUT_HANDLE)
            if not_a_console(hStdout):
                real_stdout = False

        if real_stderr:
            hStderr = GetStdHandle(STD_ERROR_HANDLE)
            if not_a_console(hStderr):
                real_stderr = False

        if real_stdout or real_stderr:
            # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars,
            #                           LPDWORD lpCharsWritten, LPVOID lpReserved);

            WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), \
                                        LPVOID)(("WriteConsoleW", windll.kernel32))

            class UnicodeOutput:
                def __init__(self, hConsole, stream, fileno, name):
                    self._hConsole = hConsole
                    self._stream = stream
                    self._fileno = fileno
                    self.closed = False
                    self.softspace = False
                    self.mode = 'w'
                    self.encoding = 'utf-8'
                    self.name = name
                    self.flush()

                def isatty(self):
                    return False
                def close(self):
                    # don't really close the handle, that would only cause problems
                    self.closed = True
                def fileno(self):
                    return self._fileno
                def flush(self):
                    if self._hConsole is None:
                        try:
                            self._stream.flush()
                        except Exception, e:
                            _complain("%s.flush: %r from %r"
                                      % (self.name, e, self._stream))
                            raise

                def write(self, text):
                    try:
                        if self._hConsole is None:
                            if isinstance(text, unicode):
                                text = text.encode('utf-8')
                            self._stream.write(text)
                        else:
                            if not isinstance(text, unicode):
                                text = str(text).decode('utf-8')
                            remaining = len(text)
                            while remaining > 0:
                                n = DWORD(0)
                                # There is a shorter-than-documented limitation on the
                                # length of the string passed to WriteConsoleW (see
                                # .
                                retval = WriteConsoleW(self._hConsole, text,
                                                       min(remaining, 10000),
                                                       byref(n), None)
                                if retval == 0 or n.value == 0:
                                    raise IOError("WriteConsoleW returned %r, n.value = %r"
                                                  % (retval, n.value))
                                remaining -= n.value
                                if remaining == 0: break
                                text = text[n.value:]
                    except Exception, e:
                        _complain("%s.write: %r" % (self.name, e))
                        raise

                def writelines(self, lines):
                    try:
                        for line in lines:
                            self.write(line)
                    except Exception, e:
                        _complain("%s.writelines: %r" % (self.name, e))
                        raise

            if real_stdout:
                sys.stdout = UnicodeOutput(hStdout, None, STDOUT_FILENO,
                                           '<Unicode console stdout>')
            else:
                sys.stdout = UnicodeOutput(None, sys.stdout, old_stdout_fileno,
                                           '<Unicode redirected stdout>')

            if real_stderr:
                sys.stderr = UnicodeOutput(hStderr, None, STDERR_FILENO,
                                           '<Unicode console stderr>')
            else:
                sys.stderr = UnicodeOutput(None, sys.stderr, old_stderr_fileno,
                                           '<Unicode redirected stderr>')
    except Exception, e:
        _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,))


    # While we're at it, let's unmangle the command-line arguments:

    # This works around .
    GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32))
    CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int)) \
                            (("CommandLineToArgvW", windll.shell32))

    argc = c_int(0)
    argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))

    argv = [argv_unicode[i].encode('utf-8') for i in xrange(0, argc.value)]

    if not hasattr(sys, 'frozen'):
        # If this is an executable produced by py2exe or bbfreeze, then it will
        # have been invoked directly. Otherwise, unicode_argv[0] is the Python
        # interpreter, so skip that.
        argv = argv[1:]

        # Also skip option arguments to the Python interpreter.
        while len(argv) > 0:
            arg = argv[0]
            if not arg.startswith(u"-") or arg == u"-":
                break
            argv = argv[1:]
            if arg == u'-m':
                # sys.argv[0] should really be the absolute path of the module source,
                # but never mind
                break
            if arg == u'-c':
                argv[0] = u'-c'
                break

    # if you like:
    sys.argv = argv

简单来说这段代码做了这么几个事:

1、如果输出到控制台,改用 WriteConsoleW()。
2、如果输出被重定向,用 utf-8 编码输出。
3、用 GetCommandLineW() 和 CommandLineToArgvW() 获取命令行参数,在最后一行取代 sys.argv 传入的参数。

这个是我目前能找到的最完美的解决方案了。在控制台下也能不出错,在重定向的时候也可以按 UTF-8 去编码成 char 字节流。唯一的问题是 Python 2.5.2 里似乎没有 LPVOID。我用 c_void_p 取代 LPVOID,似乎是可行的。

当然,它仍然有前述不可避免的问题。例如在非原生支持汉字的代码页(简936繁950日932韩949)下,光标和换行的位置会出问题。如 果对汉字显示有很高的要求,不妨调用 Windows API 设置一下控制台的代码页。此外,输出重定向到外围程序时,如果外围程序不能设置按 UTF-8 解码,就会看到乱码的问题也依然存在。这些问题,就留待读者自行解决吧。


最后,特别说明一下以上问题都是 Windows 平台限定的。Linux 下问题没有这么显著(现在的Linux发行版本多数都设置了默认代码页为 UTF-8),而且就算用户代码页不是 UTF-8,也没有 Windows 下 WriteConsoleW 这么淫霸的函数,所以洗洗睡吧。

- 作者: APOO 2012年01月19日, 星期四 11:52  回复(0) |  引用(0) 加入博采

[转载+评论]NSIS插件编写入门

原文:http://clseto.mysinablog.com/index.php?op=ViewArticle&articleId=1910084



Many schools taught programming but very few of them taught deploying software. You may be so concentrated while programming your great software and haven't felt the difficulties of deploying it to others' computer. If you really want to deploy your software to general public, I would like to tell you: try to spend several days (or even weeks) to write and test the installer of your great software.

Very often, your software requires some prerequisites to run and your installer will try to check it, download some required components and do some post configurations. There are quite a bunch of logics behind and the installer is a completely distinct project from your original program !

I had spent several days to study NSIS. Why choose NSIS ? It is popular and free of charge. However, it only targets Microsoft Windows platform. You may feel annoying at first about NSIS: "Oh ... my God ! I just want to deploy a few files. I don't want to learn a new language to do such stupid stuff." Yes, that's right. If your program has only one exe, that's a waste of time to do so. But if your installer really need some checking like those previously stated, you will find that NSIS is worth to learn.

Here to mind you one important thing: The NSIS you can download from http://nsis.sourceforge.net/Main_Page is an ANSI version. There's another Unicode version at http://www.scratchpaper.com/ (but the installer made by this Unicode version can't be run on Windows 9X).

There are already many tutorials on how to write an NSIS installer so I don't want to repeat. Here, I would like to talk about writing a plugin to extend the functionalities of an installer ....

Actually, there's already an example plugin at "C:Program Files\NSIS\Unicode\Examples\Plugin" (depends on where you install NSIS), you can take a look at it.

Background knowledge 

NSIS plugin is nothing but a DLL exporting one or several C functions (one function one functionality) and which has this kind of prototype:

__declspec(dllexport) void mypluginfunc(HWND hWndParent, int string_size, TCHAR *variables, stack_t **stacktop, extra_parameters *extra);

It uses normal C function calling convention and has no return value. No return value ! So, how to communicate with the script ? Use the stack (not the OS process level stack but a stack maintained internally by the NSIS virtual machine) or those predefined global variables ($0, $1, ..., $9, $R0, $R1, ..., $R9. NSIS calls them registers). Also, all parameters on the stack are in string format. To pass an integer, a conversion is needed. Inefficient ? Yes, it is but we are not writing a real-time 3D shooting game.

What's that pointer pointed to by "extra" above ? I don't know either. This testing plugin won't make use of it. Try to find it out yourself.

Plugin step by step

(1) 

I usually use Visual studio to write C programs. So launch a new dll project which exports some symbols. Before writing any code, first change the Runtime library: Project -> Properties -> C/C++ -> Code Generation -> Runtime library. For debug mode, choose "Multi-threaded Debug (/MTd)" whereas release mode, choose "Multi-threaded (/MT)". If not doing so, you will need to deploy the C runtime together with your installer.

(2)

Copy the following files from "C:\Program Files\NSIS\Unicode\Examples\Plugin" and include them to your working project:

api.h, nsis_tchar.h, pluginapi.h, pluginapi.lib

They are the necessary header and object files. Set your project to link with "pluginapi.lib". The functions inside pluginapi.lib are declared in pluginapi.h.

(3)

Try to write our testing plugin C function like this:

extern "C" {

// This is an example of an exported function.
MYPLUGIN_API void mypluginfunc(HWND hWndParent, int string_size,
                               TCHAR *variables, stack_t **stacktop,
                               extra_parameters *extra)
{
    EXDLL_INIT();

    WCHAR szComponent[256];
    popstring(szComponent);

    int len = (int)wcslen(szComponent);

    // make a little change to input parameter
    for (int i = 0; i < len; ++i)
       szComponent[i] += 1;

    // push back on the stack
    pushstring(szComponent);
}

}

Remeber to wrap the function by 'extern "C"', otherwise, VC "thinks" it is a C++ function (since it is inside a cpp file) and hence function name decoration may result.

In the function, first insert the macro "EXDLL_INIT()", which sets up the stack and other parameters.

The rest of the function is just to pop the string passed in, change it a little bit and push back on the stack so that caller can get the modified string.

(4)

Compile the DLL (debug or release version). Then copy the DLL to NSIS plugin directory: "C:\Program Files\NSIS\Unicode\Plugins"

(5)

Lastly, write a testing NSIS script to test this plugin:

OutFile "helloworld.exe"

# default section
Section

   MessageBox MB_OK "Hello world ! Yes..."
   myplugin::mypluginfunc "FDFDqqq我們"
   Pop $0
   MessageBox MB_OK "ret = $0"

SectionEnd

Save the script as "hello.nsi" and compile it with "C:\Program Files\NSIS\Unicode\makensis" hello.nsi".

(6)

Try to run the resulting "helloworld.exe". Since it is a Unicode version, you can pass non-English string to the plugin and see the modified string (2 chinese characters as above). But to mind you that you have to save the script in Unicode format (i.e. from notepad -> save as -> Encoding -> Unicode).

A screenshot to display the modified string (the 2nd MessageBox above):

Picture

Source of the above example: 2197742-mynsisplugin.zip

Enjoy!


我的补充:

1、你从NSIS网站上也能下载到NSIS Plugin Example。但是那个for是NSIS ANSI的。如果要开发for NSIS Unicode的,得从NSIS Unicode的安装目录拿东西。

2、函数的传入参数string_size既不是你从NSIS传入参数的字符串的个数,也不是字符串的长度。而是NSIS编译时的一个常量的值。这个值在NSIS ANSI版里固定为1024,在NSIS Unicode版里上升到8196。参见http://www.scratchpaper.com/home/faq,“Where's the large string version of the Unicode NSIS?”。

3、传入参数是在程序里用popstring(LPTSTR)获取的。如果你是用popstring、pushstring等函数,且在函数参数处用TCHAR来修饰variables,则编译出来的dll文件只能用于ANSI或Unicode二者其一。也许通过强制使用PopStringA和PushStringA可以让一个dll适应ANSI和Unicode,但是我没试过。参见pluginapi.h。

4、传入参数在程序里popstring的时候是从左到右获得的。例如传入参数是pluginName::functionName "123" "456",第一次popstring会得到123,第二次456。

- 作者: APOO 2012年01月16日, 星期一 11:52  回复(0) |  引用(0) 加入博采

诺基亚702T + 北京神州行cmwap无限流量卡

把北神无限卡放进702T
开机
出现3.5G标志(当然是刷过open固件的)
上网
虽然还是只能上cmwap(当然也可以上MM-Nokia商店专用接入点cmcc cmmm)
不过速度已经轻松飙到了170KB/s
下个QQ几秒钟
太爽了!

不过不知道3G模式下上网会不会另外收钱
晚上看看话费变化
没问题的话北神流量卡就值大钱了
15元/月
全国漫游
3G无限流量上网
orz

- 作者: APOO 2011年12月3日, 星期六 18:45  回复(0) |  引用(0) 加入博采

小悦悦事件之我见
刚才看了一篇文章,题目《小悦悦事件的44个疑点, 某些无良媒体用鲜血制造的“新闻”》,觉得作者分析得挺在理的。有些感触,不吐不快。

我记得我刚到美国那会儿,也是哪个非洲国家,在万恶的美帝的煽动下,国内搞了革命。那时候有些人在转发一个视频,说是非洲的某独裁者多么多么可恨,竟然枪杀了这么可爱的一个女孩。我就点进视频进去看了一下。不看不知道,看了才发现美国民众的智商,确实没有下限。

视频明显是一个手机拍摄的,有摇晃,看来看去。画面的远处是示威的人群,而摄影者远离人群。突然,画面里走进一个年轻女生,手机就跟着她拍。拍摄者远离示威人群,显然女生也是远离示威人群的,而且看都没有看那边一眼。也许是怕惹麻烦,所以躲着走吧。可是,就在她走过镜头前的时候,突然仰面朝天倒在地上。一发从远处射来的子弹,准确地穿过了她的心脏。少女倒在地上,鲜血从口中不断涌出,身上的伤口也染红了胸前的衣服。她的眼睛还睁着,仿佛还在纳闷自己为什么会被击中。

我也很纳闷。明明示威者就在视频的远方,而女孩是从画面一侧走到另一侧的。不论是示威者打向远方的子弹,还是政府军还击的子弹,都应该是平行于摄像机视角方向的,不应该从侧面打穿这个女孩。我能得出的唯一结论,就是这是无良的西方媒体,为了给背后的西方势力造势,煽动非洲小国闹革命,而打的一出舆论战。你看到的一切,都是一向标榜公正无私的西方媒体,为了他们一向标榜民主自由的主子,而安排的一场好戏。除了那个女生。

也许她不相信西方人会带来美好的生活,这样起码她还用自己的声明验证了西方帝国主义列强的虚伪。如果她像我们国家的一些民逗、带路党一样相信西方势力的介入是会给自己带来美丽新生活的,那她就真的是一个悲剧了。可能她至死都没有搞明白,自己怎么就成了西方势力侵略祖国过程中的一枚炮灰。不过,这并不是她的错。这样的编排就像是一场无差别杀人游戏,导演这出戏的人,只需要一个年轻、漂亮,容易激发观众同情心的姑娘走过自己的镜头前,杀死毫无防备的她,一出好戏就拍完了。经过那里的任何人,都可能成为枪下的冤魂。

至于后来这个非洲小国的革命进行得怎么样了,我是没有关注。非洲国家名字都不好记,过了几天也就忘了。不过看看利比亚就知道。据说法国总统几年前还赞扬卡扎菲把利比亚带向了繁荣富强,让利比亚人民过上了好日子。结果没几年,法国倒戈一击,带头轰炸利比亚。每当看到国际社会上这些外国政客翻云覆雨的丑恶嘴脸,我都十分庆幸毛主席当年让全国人民勒紧裤腰带也要搞出核武器来。吃几年苦搞出来了,以后再怎么发展,都不怕外国势力打压,人民真正能有个盼头。要是一开始就让人民过上悠闲安逸的日子,那肯定是稍微发展一点,世界老大就要来阻止你了。这就像高考前拼命备考一样,苦一时,安逸一世。

别跟我说美国不打法国不打英国是因为英法联军是民主国家,根本原因是两点:一、英法不跟美国拧着干,美国说啥就是啥;二、欧洲的经济再怎么干也干不过美国了,不然就是不听话。现在有可能超越美国的国家就中国一个,为什么?硬实力上能抗衡,经济上才有可能发展。经济基础决定上层建筑,那又是什么决定经济基础呢?是军事实力。没有军事实力,守不住财,当然也就别想发财。发一点人家都会来抢的。

咱还是说回小悦悦这个事件。我觉得,这个事儿之所以蹊跷,还跟它发生的时机有关系。最近想必关心实事的人也听说了,一帮民知民逗正因为一个盲人的事儿而跟政府对着干呢。如果你说中央政府想在这个时候制造一点社会事件转移视线,确实是太可能了。就搞新闻的那帮唯恐天下不乱的人,什么时候这么好心出来表扬好人好事了?在我印象里弘扬正气那一贯是CCTV干的事儿,各种小报,尤其是南方XX报,最喜欢挑社会阴暗面报道。让人觉得过不下去了,思想阴暗了,这报社还要自命不凡,说我是大胆揭露歪门邪道。我说你揭露可以,但不光要惩恶,还要扬善。最近某些媒体老拿医患纠纷说事儿,这就是心里烂到根儿上了。这个是后话,暂且不表。

所以我就想,要是有什么组织,能让全国媒体这么一根筋地赞扬真善美,那可能还真是中央政府有这个本事了。所以很可能是中央想转移视线,找了广东的媒体,让他们搞一个大事件出来。不仅要轰动,还要能引发大讨论。最好是能让美分党和五毛党对掐的,最好是能让共产党和社会主义躺着也中枪的,这样最能把自己择出去。结果媒体一听,很高兴(或者“很无奈”,按某些媒体人一贯的说法),就出去找啊找,终于找到了原文中提到的这么一对夫妇。可能真的是生了个女儿不想要,想再生个儿子。三方面一拍即合,才有了今天你们看到的这出戏。

当然,还是得说在造假这种事儿上我们国人比老美聪明多了。起码车是从路上开过来的,没有从店铺里冲出来。这比射在非洲少女胸口的那一枪要真实得多。可是还是有很多很多的问题,能被原文作者这样细心的人挑出来。毕竟,老百姓没有影帝的本事。至于那位拾荒大妈,我觉得真不是演员。可能她只是被算准了,在那一天那一时刻会在这条路上经过。可能也没计算得这么精确,说不定不管是谁,从那里路过了,只要不是演员,就会去扶一把,救一把,让这出没有胜利者的戏走向封镜。

但不管怎么说,这样的手段都太罪恶了。我觉得中央的本意是好的,只是媒体无良,造了这么没下限的事儿出来。最后诚挚地祝愿天下无良的新闻人全家死光光,永世不得超生。

- 作者: APOO 2011年10月26日, 星期三 01:43  回复(0) |  引用(0) 加入博采

Windows XP 原版(SP0)和SP1无法安装nVidia新驱动的解决方案
先装DX9就可以了。因为原版系统和SP1没有DX9,驱动找不到d3d9.dll,就会安装失败。

- 作者: APOO 2011年10月21日, 星期五 17:06  回复(0) |  引用(0) 加入博采

[转载]Windows 7下ThinkPad T500 指纹识别软件故障的解决

来自:http://hi.baidu.com/zt37/blog/item/634e20a41d54b7fc9052eee3.html


年前换了t500从oem的vista折腾到win7 旗舰版颇费周章。结果。升级后蛮高科技的指纹识别功能over了。

每次开机注册都说“无法与传感器通讯”


虽然装驱动的时候有些古怪。但用thinkvantage toolbox检查设备显示正常

心生疑惑。看网上搜到的消息都说和驱动有关。还有说跟BIOS设置有关的。但是上面的检查结果说明bios设置并没有关掉指纹设备,驱动也应该是可用的。(ps:驱动是从联想官网下的win7指纹驱动,且用联想的驱动自动检查工具检查说明系统不需要更新驱动)

因此对网上下驱动调bios的说法不再信服。转而认为是软件问题。

在联想官方已经收录的问题说明文档中也看到在以前升级vista系统后也出现过类似无法识别现象。估计是老毛病又犯了。

按此思路在查询了一下反映无法连通指纹设备的软件编号如下

3.3058

从联想支持中心搜了一下。貌似最新版已经到了5.9版本。下了装上看看

版本升级了了,软件名也从“lenovo指纹”升级成了“thinkvantage指纹软件”

安装重启。指纹注册ok了。看来毛病是出在指纹软件上。不是bios问题。也不是驱动问题。

lenovo的中文支持网站上没有找到相关的软件更新,这个软件是从他英文网页上搜出来的

http://www-307.ibm.com/pc/support/site.wss/document.do?sitestyle=lenovo&lndocid=MIGR-73583
APOO评论:我也是升级到Win7之后发现指纹识别不能用了。唯一的区别在于,我装的是T500页面上推荐给Win7用的3.3.2.43版本,依然不行。曾经用过一个1.x的版本是能用的,但是后来用Lenovo Software Update升级之后找不到这个旧版本了,也只好作罢。看来也许是联想的网站更新不力,没有把最新的指纹识别软件放到T500的页面里;也许ThinkVantage是更老的软件,比Lenovo指纹软件1.x还老,所以能用。嗨,谁知道呢……

没下的抓紧去下吧,没准什么时候IBM的网站就要撤掉Think系列相关的页面了。据说是8月31号。

- 作者: APOO 2011年08月1日, 星期一 07:47  回复(0) |  引用(0) 加入博采