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

| Posted by APOO
Jan 23 2012

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(12 8) 

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

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

首先要说明的是,既然事关控制台,要做 8-bit 流的输入输出,就没有完美的解决方案。我个人的建议是,在 Windows 下,一切字符串操作,都应该尽可能使用 WCHAR 及相关函数。遇到需要跨平台和网络传输的情况,再使用 UTF-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 <http://bugs.python.org/issue6058>. 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 <http://bugs.python.org/issue1602>. # Credit to Michael Kaplan <http://blogs.msdn.com/b/michkap/archive/2010/04/07/9989346.aspx> # and TZOmegaTZIOY # <http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash/1432462#1432462>. try: # <http://msdn.microsoft.com/en-us/library/ms683231(VS.85).aspx> # HANDLE WINAPI GetStdHandle(DWORD nStdHandle); # returns INVALID_HANDLE_VALUE, NULL, or a valid handle # # <http://msdn.microsoft.com/en-us/library/aa364960(VS.85).aspx> # DWORD WINAPI GetFileType(DWORD hFile); # # <http://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx> # 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 # <http://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232>. 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 <http://bugs.python.org/issue2128>. 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 这么淫霸的函数,所以洗洗睡吧。

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

经验和资料 | Posted by APOO
Jan 16 2012

原文: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 :P rogram 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。

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

经验和资料 | Posted by APOO
Dec 03 2011

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

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

小悦悦事件之我见

感想和评论 | Posted by APOO
Oct 26 2011

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

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

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

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

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

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

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

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

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

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

但不管怎么说,这样的手段都太罪恶了。我觉得中的本意是好的,只是良,造了这么限的事儿出来。最后,永生。

Windows XP 原版(SP0)和SP1无法安装nVidia新驱动的解决方案

经验和资料 | Posted by APOO
Oct 21 2011

先装DX9就可以了。因为原版系统和SP1没有DX9,驱动找不到d3d9.dll,就会安装失败。

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

经验和资料 | Posted by APOO
Aug 01 2011

来自: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号。

[转载]一个冷笑话

感想和评论 | Posted by APOO
Apr 06 2011

转载自:http://blog.renren.com/blog/5588/719802216


话说,从前有一个特别、特别大的花园,花园里种着特别多的花花草草,还有些飞鸟鱼虫。人家都说,这所花园是王国里最漂亮的花园,够资格进入这个花园里的,都是最漂亮的花花草草和最轻捷的飞鸟鱼虫。

有一只小甲虫,它本来没资格进入这个花园。要进这个花园,一小时内得飞600多圈,它比规定的圈数少飞了60圈。但是花园的管理员瞧了瞧,觉得它飞的姿势挺特别,就把它招了进来。

大家听说小甲虫会用特别的姿势飞行,还飞到了王国最漂亮的花园里,都非常关注小甲虫。小甲虫进了花园以后,就在后头插了一根针,冒充蜜蜂。

这个假蜜蜂左边飞飞,右边飞飞,觉得这个花园实在是名不副实,就对着里头的花草鱼虫冷嘲热讽。

有几只金龟子想来看看它,它撅起说:

——哎呀,人家都说这花园里的金龟子颜色特别漂亮,是整个王国里最聪明最有魅力的金龟婿,可瞧瞧你们,都一脸呆样啊!

有一片向日葵一直跟着太阳转,它鄙视的飞过去说:

——哟,谁付给你们钱啦,你们就天天对着太阳点头哈腰?有人给你们天天上五帘卷西风毛钱的化肥吧?

几条鱼称赞着这花园里的清澈池水,它不屑地从鼻孔里哼出一声,说:

——最早最早刚有这花园的时候,这里的环境可比现在好多啦!当时的四大园丁更是顶呱呱!现在都是些什么玩意儿!

这只甲虫扮的假蜜蜂把自己的刺朝这边竖竖,朝那边撅撅。有的花草鱼虫觉得它说的挺有道理,也有的花草鱼虫对它的话嗤之以鼻。还有很多花园外头的人,经常翘着脚尖朝墙里看这只假蜜蜂,觉得它是勇敢的批判英雄。

等到这只假蜜蜂进入花园的第三年,这个花园正要开始自己一百年的庆典。假蜜蜂嗡嗡嗡嗡,唱了一首新歌,讲这个花园的坏话,说这里到处都是缺点:金龟子呆滞,向日葵腹黑,池鱼目光短浅。自己在这里住了三年,还是觉得这里是个烂到不行的花园。

有一个人实在忍不住了,就对它说:

“既然你这么讨厌这里,为什么还不离开呢?”

这只甲虫大叫道:

“怎么可能离开!我离开这个园子,还能装Bee吗?!”

通过Mail for Exchange在S60v3上使用Google Sync简直就是垃圾

感想和评论 | Posted by APOO
Mar 14 2011

只支持日历和联系人就不说了,支持程度也是糟糕得一塌糊涂。日历里所有的“纪念日”全都变成“会议”了,联系人的项目保存到Google里的也缺了很多,
如果再同步回手机,哼哼,等着丢数据吧!而且第一次运行还会提示“您的帐号没有与当前设置同步的权限。请与您的管理员联系。”这样的错误信息。在Google日历里随便建个项目就可以修正此bug。当然这个新建的项目会被带到手机里……

试了一下诺基亚官方的Ovi Contacts同步。虽然网页版界面只有英文,不过还是好用很多的。数据都是完整地保存到服务器上的不会丢东西。但是如果你设置了多个同名字段,顺序可能会变化,而诺基亚现在又不能同步自定义字段名称,所以这点比较头疼。

ATI显卡在执行glCreateProgramObjectARB()时出现segmentation fault

经验和资料 | Posted by APOO
Mar 11 2011

Segmentation Fault occurs with glCreateProgramObjectARB() under Linux, if some ATI Mobility Radeon cards are used.

Recently,
when I was doing a project under Linux, I found my code runs well on
department computers but not on my own laptop. When the program calls
glCreateProgramObjectARB(), it fails with Segmentation Fault and program
is terminated.

以上供搜索引擎索引使用。下面开始正文。看不懂的都学中文去!恕在下没有精力翻译成又臭又长的英语。

最近写作业的时候发现同样的程序,在Windows下好好的,在Linux下不行。同样是Linux下呢,在学校机房就能用,在我的笔记本电脑上就是不行。具体症状是只要一调用OpenGL的glCreateProgramObjectARB(),
程序立刻报一个segmentation
fault(就是你不小心用了野指针的时候会发生的那种错误),然后就崩溃退出了。跟同学讨论发现有这个问题的都是ATI Mobility
Radeon!我的笔记本电脑是HD 3650。某同学是HD 5470。所以这么看来大概是驱动有问题吧。

然后有同学说不能用开源驱动,要用ATI最新的闭源驱动11.2就好了。可是我用的就是这个11.2,上周刚装的,还是一样不行。刚才Ubuntu 10.04提示有新内核,问我要不要升级。我想那就升级试试看吧,于是就升级。从2.6.32-28升级到了2.6.32-29,然后按惯例重装显卡驱动。这么一搞居然就好了。反馈给那位同学,他说他的也是2.6.32-29。不过他还听说2.6.33系列是不行的是会崩溃的。也不知道是不是真的。

总之问题解决了就好。

在网上闲逛遇到一个无良淘宝卖家

消息 | Posted by APOO
Feb 23 2011

店名:国际手机
网址:http://shop10268120.taobao.com/
评价:http://rate.taobao.com/user-rate-32f3de7e5bc140c1431fe68db44f3a74.htm
论坛:http://www.wto520.com/

事情经过在这儿:http://www.hiapk.com/thread-820036-1-1.html

【1楼】楼主

12月30日在国际手机拍下NS,国际手机说12月31日晚间将发货。12月31日晚间索要发货单号被拒。

1月1号早上再次索要发货单号,客服把我踢来踢去,问了好几个人,最后说准备给我发出的手机经他们检测有问题,不发货了。

然后要我们一批拍下的人都退款。

我表示可以等到货,客服告诉我,他们市场部的同事说了,半个月之内都不会来货。

好吧,无奈退款

结果呢,我们都退款了之后,立刻加价上架。

和国际手机交涉之后,国际手机的NS再度下架。

这种做法强悍吧,已经淘宝投诉了。

【16楼】国际手机老李

这家伙 批评的目的我懂...

那天价格上错了 3700多 还是3800多啊!  傻×都懂得  是错了 那几天行情是 4600 多!

所以 就装空子 恶意拍下  懂的..

肯定不能发的!  

改回价格  那价格才是纠正错误再次发生改做的事情!

【21楼】楼主

desperado115

(13:28:04):

你们就说吧,NS要加价多少,别跟我说什么检查有问题。原装全新的一批货全部有问题,怎么可能呢?实在点,要加多少。就喜欢这一款。别加太多就好了

国际手机:晶晶

(13:29:01):

您好,不是价钱的问题了,这个现在拿不到货,我们也没办法哦

desperado115

(13:29:4 8) :

什么时候能拿到呢?我等你们拿货吧

国际手机:晶晶

(13:31:34):

昨天问过了,这个机子现在比较紧张,市场部那边同事说可能半个月到不了货,到货时间现在不能确定的哦


老李,要上图片吗?

谁恶意拍下,你说话要负责任。


我拒绝接受国际手机的任何道歉,并将投诉到底

【37楼】楼主

那天我问了三个商家,其中一个是国际。

一家比国际贵50,一家比国际贵100。

我选择最便宜的一家,如果这算恶意,那么你选最贵的,您就是那个傻~X,不是吗?

【38楼】楼主

可以截图上来证明,也可以把国际客服跟我沟通的记录发上来。

国际老李,你需要对你所说的话负责。

【39楼】heartready

过了愤青的年龄了。看见这个帖子,有感而发,唠叨两句。看完目前的帖子,我总结一下,看看对不对。

事情的经过:

1.国际手机的nexus s标价3700 - 3800

2.lz和其他几个网友拍了并付款

3.国际不发货

4.lz到这里发发牢骚

5.老李回帖声明,通过老李买的找老李负责,不通过他买的没办法

不知道以上的总结对不对。下面说说我的看法

1.商家货物标价低,别管什么原因:促销也好,标错了也好,在下架之前被人拍了并付款,别管顾客什么原因,就算恶意,一个负责任的人,一个诚信的商家应该按照该价格履行交易。我要是做店家,没货高价从别人那里收购再发给客户。

2.16楼老李的回复

这家伙 批评的目的我懂...

那天价格上错了 3700多 还是3800多啊!  傻×都懂得  是错了 那几天行情是 4600 多!

所以 就装空子 恶意拍下  懂的..

肯定不能发的!  

改回价格  那价格才是 ...
港厦国际 发表于 2011-1-5 16:26

先不说意思,就凭里面的一个“傻X”,我以后就不会跟你做生意了。

再来说说意思,老李说了行情4600多,所以店家的价格是很有诱惑力的,顾客图便宜拍了,这难道是顾客的问题?

【39楼】ivanvera

楼主是不是3800元拍的NEXUS S啊???

然后商家推说质量问题让你退货?

之前在机 锋 也碰到一个买家反映了类似的问题

也是先拍了再付款下单,然后再推说手机检测质量有问题,让你退货,其实是商家的一种策略,提高交易量的一种手段而已

【42楼】迷路的机器人

还是你看明白了,其他人要不是拖就是马甲,就是这么一回事,估计刚开始卖便宜了,再看人家的卖价,觉得自己亏大了,所以之前低价拍的就找理由不发货,卖给后来高价,但也不能做得太明显,要不淘宝会封店的。所以他必须想办法让你们全部先退款了,他才能重新高价上架。理由么,一大堆了,反正现在买的人都是急着
要的,肯定不会等,店家找理由拖你一两个星期,你自己都会要求退款了,而且淘宝的规则,卖家不发货,只要愿意退款就行了,不按照聊天记录时间发货,对卖家
是没有影响的。这也是淘宝的漏洞,应该按照国际贸易的做法,卖家也要交一笔钱到淘宝,作为履约保证,设立一个日期,没有发货迟一天就扣多少钱到买家账户上。

老李的话,我没看出他能有什么作用了,他一直强调售后这一块,说白了,就算当时找他下单了,呵呵,我觉得结果一样,还是不能成交的(不成交就还不是售后),还是想着办法让你退款的,因为少赚七八百,而且还是五个。呵呵,希望到这,各位能看明白了,利益作怪而已。

我在淘宝被逼退款了两次,就是因为卖家不按当时聊的时候发货,改口说要等喔,现在不能给你一个准确的时间,我问过年前会到不,答有可能到不了。后来退款后
都成了“宝贝快照”了,不约而同的提价,可笑的是,那人在我最后一个退款不到半天,在机
锋高价叫卖现货,被我拆穿了,还一个劲的叫嚣,说,钱退你了,你还想怎样?但是,那都是信用不高的小卖家,我能怎样?投诉,就算成功了明天换个马甲还是在
卖。国际手机怎么说呢,你也算一个不小的店了,还是这样作为,这样的口碑,投诉你应该。

dell,hp,lenovo,那个没有标错价过?但是每家的处事方式影响有多大??dell得罪了很多消费者,很多人以后再也不会去买dell了,因为觉得你自己的错误造成的,可以不承认,那对消费者有何保证???就是你说的话都可以随时变废话,不认账的了??

【45楼】cardmaster

支持LZ。不管商家是弄错价格还是看别人的价格觉得自己低了后悔了,既然开出了价格就该有所担当。不能找这样的借口逼人退款的,更不能说别人恶意拍下。另外老李那句“傻X”让人心寒。这样的商家,希望坛子里别再推荐了。

【46楼】wildlee

又一无良商家浮出水面。建议国际老李速度换个马甲吧。

【48楼】pigxiang

支持lz。

觉得商家标价错了,不肯损失而发货,

也得诚恳一点向买家道歉,而不是找各种各样的理由。

那个商家的一句“X”也真够.......难道是是小学辍学就出来卖手机的?这素质....

【64楼】楼主

您老不看清楚帖子,就别多说话。

前面写清楚了,5个人拍的,我联系了其他人,都没有发货。

事先有问,才拍的。

我一开始直说国际手机有问题,老李就开始神马“恶意拍下”,神马“傻x”了

注意,我首先是顾客,你说卖手机就可以说顾客是“傻X”了吗?

如果你说可以,我无语,您也就别再回帖了。

【67楼】楼主

客服的价格给错了,就直说。

谁是JS谁清楚。

价格确实是便宜,我作为买家,不买便宜的,要去买贵的。

至于我买去是卖还是自用,我就想不通了,便宜的东西就不能自用了?

就算我是拿去卖,那又关你老李神马事?

至于说5个都是我拍下的,自己去查地址和记录,我还不至于有那么多名字和地址。天南海北的,再收集起来一起卖,不划算。

你想尽办法向我身上泼污水,到时候都是要还的。

【71楼】迷路的机器人

不过,我敢肯定地说一句,当初的那个3800绝对不是计算错误,529.9美金发至免税州,用最便宜的快递,甚至人肉,躲过海关,绝对能做到3800有
赚,如果国际有渠道搞到低于529.9进货价,更不用说了。如果你这也叫算错价钱,那为什么是这个价?纯粹巧合?相信大家心里有数,国际有专人老手计价,
有老板看住,不是新手售后定价的,出错的几率很大么?就算出错为什么就是这个数?

刚出来的时候印城小胖4150包关税直邮,他买的手机都要税的,现在小胖的价钱当然不是这个价了,但人家小胖厚道,说了多少就多少,就算买的便宜,也要给先拍的人发货,而不是先给后面拍的价高的发货,拖住前面的不发耗死你。

所以我还是相信,国际手机觉得赚的太少了,要违约,就这么简单,而且是恶意违约,而不是诚恳的寻求解决之道。而且我猜测国际的做法是先低价卖出口碑,然后借口市场价格调整,提价去赚钱,毕竟新品出来没人买过价又高的话不好卖的。但估计觉得实在利润太少而且同行都不是这个价,就出现这样的事情了。


然后我特意去看了一眼这个手机店,网页上是这么写的:

“亲爱的买家朋友们请注意:中差评不能解决问题……直接中差评等于默认放弃店铺提供的售后服务及保修。”

我看这家店也是极品到一个境界了……