1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <time.h> #include <errno.h> #include <fcntl.h> #include <string> using namespace std; void ivrLog(const char *format, ...) { va_list argptr; char buf[1024 * 2]; va_start(argptr, format); vsprintf(buf, format, argptr); va_end(argptr); struct timeval tv; int iRet = gettimeofday(&tv, NULL); time_t t = tv.tv_sec; tm* local = localtime(&t); char timeBuf[256]; strftime(timeBuf, 254, "[%Y-%m-%d %H:%M:%S", local); sprintf(timeBuf + strlen(timeBuf), ":%d] ", (int)(tv.tv_usec / 1000)); string dispStr = timeBuf; dispStr += buf; printf("%s", dispStr.c_str()); // please ensure /sdcard/Test dir existed first char *logFileName = (char *)"/sdcard/Test/log.txt"; int fd = open(logFileName, O_CREAT|O_WRONLY|O_APPEND, 0666); if (fd != -1) { write(fd, dispStr.c_str(), dispStr.length()); close(fd); } } |
Android jni fopen返回NULL而errno为13
除了要在AndroidManifest.xml加
1 2 3 | <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
关键是看传给fopen或open的文件路径是否“正确”,比如传入“/storage/6236-6439/down/log.txt”之类的路径会返回NULL,errno为EACCES。
比如传入“/sdcard/Test/log.txt”之类的路径则一切正常没任何问题。
如果还是没看到文件被创建,那就该好好看看该jni库是否被正确加载了。
recv 10060
请检查socket连接时有没有设置超时。
Linux可复入的互斥量pthread_mutex_t
互斥量可重入(可复入)的概念是:同一个线程,进入一互斥量后,不出此互斥量而再次进入此互斥量,线程不会死等。
pthread_mutex_init调用时第二个参数传入NULL,即为默认的mutex初始化方式。请记住,这种默认的初始化的方式,生成的是不可重入的互斥量,即同线程重复进入会死等(或叫死锁)。这与Windows上InitializeCriticalSection初始化恰相反。
要pthread_mutex_t可重入,需要使用PTHREAD_MUTEX_RECURSIVE_NP参数,具体见本文后所附源码。
本人已抽象出跨平台的互斥量源码库,其中的互斥量都是可重入的,在Linux和Windows上都可以统一调用,先说使用举例。
定义:
1 | WhMutex mutex_; |
初始化:
1 | whMutexInit(&mutex_); |
反初始化:
1 | whMutexFini(&mutex_); |
当要把一段代码设为临界段时:
1 2 3 4 5 | { WhMutexGuard guard(&mutex_); // your critical section code here. int i; } |
以下是抽象互斥量库源码(直接复制过去就可编译):
Mame自定义按钮和组合键即FC游戏里的单发连发及侍魂里的大刀
先明确一下概念,一个是物理键,比如键盘上的键,一个是功能键,比如子弹键。
1,有时候我们想一个物理键对应多个功能键,比如在《侍魂2》里,我们想很容易地砍出大刀,就是这种需求。
在mame64.exe里,我们可以按TAB键,在“Input (general)”或“Input (this Machine)”子菜单中,给一个功能键指定多个物理键,那么就能做出一个物理键触发多个功能键,满足了这种需求。
在mamep.exe里,我们同样按TAB键,顶层菜单中会出现“自定义按钮”的选项,点进去,在某个自定义按钮条目上,同时按数字键1、2、3、4中的若干个,会提示此自定义按钮对应了哪几个功能键。然后,在顶层菜单的输入设置里,会找到4个自定义按钮的物理键指定条目,这样设置下来,同样可以砍出大刀。
2,有时候我们想多个物理键对应一个功能键,比如一个物理键对应FC游戏里的单发A键,另一个物理键对应FC游戏里的连发A键。
对于这个需求,mame64.exe是做不到的,因为mame64.exe的连射功能(Autofire Settings)只指定给功能键,这样无论指定多少个物理键给这个指定了连射的功能键,都不能在键盘上,一键单发子弹,另一键连发子弹。
但mamep.exe可行,因为mamep.exe的“连射设置”是指定给自定义按钮的,自定义按钮是介乎物理键与功能键中间的一层。方法如下:
我们以“P1 按钮 1”为子弹键为例来说明。按TAB键,在顶层菜单中,进入mamep.exe的“连射设置”,将“P1 按钮 1”条目设为关闭,将“P1 自定义按钮 1”条目设为打开。
回到顶层菜单,进入“输入设置(一般)”,再进入“玩家 1 控制”,将“P1 按钮 1”设为比如键盘上的U键,将“P1 自定义按钮 1”设为比如键盘上的N键。
再次回到顶层菜单,进入“自定义按钮”,在“P1 自定义按钮 1”条目上,点击一下键盘上的数字键1,使此条目右边只有一个圈起来的1字。
好,设置完成,这时按TAB键关闭菜单,开始游戏,就会发现,键盘上U键是单发子弹,键盘上的N键是连发子弹。完美!
Mame游戏里的作弊
将下载的包cheat.7z放在mame64.exe的同层目录中,再运行mamepgui.exe(M+GUI)启动游戏,加载的若是mame64.exe,按一下TAB键,顶层菜单中会看到“Cheat”的设置,点进去,同时可看到在“Cheat”菜单里还有“连发设置”也叫“连射设置”的功能。
注意这里是mame64.exe(mame32.exe/mame.exe)而非mamep.exe,关于两者的区别见:
Mame里的按键连射设置
对于玩《雷电1》《雷电2》之类街机游戏苦于手指按不过来,子弹连射是多么渴望的功能。
而按键的“连射设置”选项的位置,在mamep.exe里和mame64.exe里有所不同。
当mamepgui.exe(M+GUI)启动游戏,加载的是mamep.exe后,按一下TAB键,顶层菜单中会看到“连射设置”。
当mamepgui.exe(M+GUI)启动游戏,加载的是mame64.exe后,按一下TAB键,顶层菜单中会看到“Cheat”选项,点进去会看到“Autofire Settings”选项,点进去就可设置连发了。
Mame画面增强HQ2X
mamep.exe即Multiple Arcade Machine Emulator Plus!,它和mame64.exe是两码事,是两种不同的分枝。
“图像增强”这个选项只在mamep.exe里有,它包括:Scale3x、Super Eagle、HQ3X、3xBRZ之类的选择内容。
“图像增强”在mame64.exe里是没有的,也即运行mamepgui.exe(M+GUI),将“MAME可执行文件”选项设置为mame64.exe,导出完xml list后,在选项里的全局->核心视频->核心屏幕下,是看不到“图像增强”的条目的,只是“视觉效果”条目内容比将“MAME可执行文件”选项设置为mamep.exe时增多了。
好像mamep.exe不再更新了,我这下载的最后版本是0.168。
而我写本文时下载的mame64.exe最新版本为0.200。
mamepgui.exe最新版本为1.8.2。
Gdiplus::Image打开gif文件返回为空
原因是Gdiplus需要先初始化:
1 2 3 4 | ULONG_PTR gdiPlusToken_; GdiplusStartupInput gdiplusstartupinput; GdiplusStartup(&gdiPlusToken_, &gdiplusstartupinput, NULL); |
退出时,反初始化:
1 | GdiplusShutdown(gdiPlusToken_); |
gSoap内存泄露OpenSSL memory leak
怎么完美调用gSoap库呢?
首先与用的是stdsoap2.c还是stdsoap2.cpp有关。
我这里举例内编的是stdsoap2.cpp,那么一个WebService调用过程如下:
1 2 3 4 5 6 7 8 9 10 11 12 | void aWebServiceCall() { struct soap soapCtx; soap_call___ns1__XXX(&soapCtx, url.c_str(), ...); if (soapCtx.error) { printf("soap error: %d, %s, %s\n", soapCtx.error, *soap_faultcode(&soapCtx), *soap_faultstring(&soapCtx)); } soap_end(&soapCtx); } |
看到没,C++版的soap只需要在每次调完soap_call___ns1__XXX之类的接口后,调用一次soap_end就完事了,这是完美的写代码流程。但实际运行后还是有内存泄露,研究发现,这个内存泄露是由于OpenSSL的屁股没揩干净造成的。最终无leak的结尾函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | void doSSLLeftCleanup() { // thread-local cleanup ERR_remove_state(0); // thread-safe cleanup CONF_modules_unload(1); // global application exit cleanup (after all SSL activity is shutdown) ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); int i, n; STACK_OF(SSL_COMP) *ssl_comp_methods; ssl_comp_methods = SSL_COMP_get_compression_methods(); n = sk_SSL_COMP_num(ssl_comp_methods); for (i = 0; i < n; i++) { (void)sk_SSL_COMP_delete(ssl_comp_methods, i); } sk_SSL_COMP_free(ssl_comp_methods); } |
但遗憾的是,对于openssl-1.0.1p,SSL_COMP_get_compression_methods返回的是一个内部静态变量,没有导出函数可以将其置为NULL,也即doSSLLeftCleanup不能调第二次,所以,请在所有的最后的最后调一次。但这也从另一方面说明,这种内存泄露是不会累加的,不管gSoap接口函数调用多少次。
注:这里的gSoap版本为:2.8.37