2017年5月25日 星期四

2017年2月16日 星期四

[Android] UnsatisfiedLinkError

症狀


E/AndroidRuntime: FATAL EXCEPTION: Thread-3056
Process: com.perfectcorp.beautycircle, PID: 14917
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"
at java.lang.Runtime.loadLibrary(Runtime.java:367)
at java.lang.System.loadLibrary(System.java:1076)
at com.sina.weibo.sdk.net.HttpManager.(SourceFile:83)
at com.sina.weibo.sdk.net.NetUtils.internalHttpRequest(SourceFile:46)
at com.sina.weibo.sdk.utils.AidTask.loadAidFromNet(SourceFile:344)
at com.sina.weibo.sdk.utils.AidTask.access$3(SourceFile:331)
at com.sina.weibo.sdk.utils.AidTask$2.run(SourceFile:203)
at java.lang.Thread.run(Thread.java:818)

線索

  1. 這問題發生在 Nexus 9 的機器上,HTC Nexus 9 是一台 ARM64 的機器
  2. java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.perfectcorp.beautycircle-1/base.apk"],nativeLibraryDirectories=[/data/app/com.perfectcorp.beautycircle-1/lib/arm64, /data/app/com.perfectcorp.beautycircle-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]] couldn't find "libweibosdkcore.so"

解法

  • Root cause 在於 Nexus 9 是 ARM64 的機器,但 libweibosdkcore.so 卻只有 x86。
  • 為了解決這問題,解法會是指定 .so 的目錄
  1. 在 app gradle 的 defaultConfig 加上 ndk { abiFilters "armeabi-v7a" }
  2. 在 project的根目錄下,找到 gradle.properties (不存在則新建)"android.useDeprecatedNdk=true 指定目錄",不讓系统自動找.so檔

Reference

  1. http://www.jianshu.com/p/cb05698a1968
  2. https://corbt.com/posts/2015/09/18/mixing-32-and-64bit-dependencies-in-android.html

2012年1月9日 星期一

[C++] Mutex


1. 同一個時間內只能夠有一個執行緒擁有mutex。
2. 同一個時間內只能夠有一個執行緒進入critical section。
3. Mutex速度較慢。因為Critical Section不需要進入OS核心,直接在User Mode 就可以進行動作。
4. Mutex可以跨Process使用。Critical Section則只能夠在同一個Process使用。
5. 等待一個mutex時,你可以指定『結束等待』時間長度,但對於critical section則不行。

用CreateMutex來產生Mutex
HANDLE WINAPI CreateMutex(
  __in_opt  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  __in      BOOL bInitialOwner,
  __in_opt  LPCTSTR lpName
);
// The name can have a "Global\" or "Local\" prefix to explicitly create the object in the global or session namespace. The remainder of the name can contain any character except the backslash character (\).
用ReleaseMutex來釋放Mutex
BOOL WINAPI ReleaseMutex(
  __in  HANDLE hMutex
);
簡單的範例
CMutex::CMutex(std::wstring wstrMutexName)
{
 std::wstring wstrMutex = L"Global\\";
 wstrMutex += wstrMutexName;
 m_mutex = ::CreateMutex(NULL, FALSE, wstrMutex.c_str());
}

CMutex::~CMutex()
{
 ::CloseHandle(m_mutex);
}

HRESULT CMutex::Lock()
{
  HRESULT hr = E_FAIL;
 DWORD dRet = WaitForSingleObject(m_mutex, INFINITE);
 switch (dRet)
 {
  case WAIT_TIMEOUT:  // Time out
   break;
  case WAIT_OBJECT_0:  // Process over
    hr = S_OK;
   break;
  case WAIT_OBJECT_0 + 1: // Don't Know
   break;
 }
 return hr;
}

void CMutex::Unlock()
{
 ::ReleaseMutex(m_mutex);
}
如果想要利用Mutex讓一個Process在同一時間只被執行一次的話,可以用下列的方法檢查

1.Mutex的Create是否有Error
CMutex g_vthumbMutex(strModuleName);
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
 return;
}
2. 用OpenMutex
HANDLE WINAPI OpenMutex(
  __in  DWORD dwDesiredAccess,
  __in  BOOL bInheritHandle,
  __in  LPCTSTR lpName
);
Reference:

[C++] Enumerate file in directory.

bool findFileInDir(wchar_t* rootDir)
{
 wchar_t fname[BUFFSIZE];
 ZeroMemory(fname, BUFFSIZE);

 WIN32_FIND_DATA fd;
 ZeroMemory(&fd, sizeof(WIN32_FIND_DATA));

 HANDLE hSearch;
 wchar_t filePathName[BUFFSIZE];
 wchar_t tmpPath[BUFFSIZE];
 ZeroMemory(filePathName, BUFFSIZE);
 ZeroMemory(tmpPath, BUFFSIZE);

 wcscpy(filePathName, rootDir);
 BOOL bSearchFinished = FALSE;

 if( filePathName[wcslen(filePathName) -1] != L'\\' )
 {
  wcscat(filePathName, L"\\");
 }

 wcscat(filePathName, L"*");

 hSearch = FindFirstFile(filePathName, &fd);

 //Is directory
 if( (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  && wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
 {
  wcscpy(tmpPath, rootDir);
  wcscat(tmpPath, fd.cFileName);
  wcscat(tmpPath, L"\\");
  findFileInDir(tmpPath);
 }
 else if( wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
 {
  wsprintf(fname, L"%ls%ls", rootDir, fd.cFileName);
  DEBUG_MSG((L"[findFileInDir] filename: %ls", fname));
 }

 while( !bSearchFinished )
 {
  if( FindNextFile(hSearch, &fd) )
  {
   if( (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    && wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
   {
    wcscpy(tmpPath, rootDir);
    wcscat(tmpPath, fd.cFileName);
    wcscat(tmpPath, L"\\");
    findFileInDir(tmpPath);
   }
   else if( wcscmp(fd.cFileName, L".") && wcscmp(fd.cFileName, L"..") )
   {
    wsprintf(fname, L"%ls%ls", rootDir, fd.cFileName);
    DEBUG_MSG((L"[findFileInDir] filename: %ls", fname));
   }
  }
  else
  {
   if( GetLastError() == ERROR_NO_MORE_FILES )          //Normal Finished
   {
    bSearchFinished = TRUE;
   }
   else
    bSearchFinished = TRUE;     //Terminate Search
  }
 }

 FindClose(hSearch);
 return true;
}

[C++] How to get free disk space

int getFreeSpace(wstring wstrWorkingFolder)
{
 int nFreeSpace = 0;

 HINSTANCE hModule = ::LoadLibrary(L"KERNEL32.DLL");
 if(hModule)
 {
  PFNGETDISKFREESPACEEX pDiskFreeSpaceEx = NULL;

  // Determine function to use
  pDiskFreeSpaceEx = reinterpret_cast(::GetProcAddress(hModule, "GetDiskFreeSpaceExA"));
  if(!pDiskFreeSpaceEx)
  {
   DWORD dwSectorsPerCluster = 0;
   DWORD dwBytesPerSector    = 0; 
   DWORD dwFreeClusters      = 0; 
   DWORD dwClusters          = 0; 

   if(::GetDiskFreeSpace(wstrWorkingFolder.c_str(), &dwSectorsPerCluster, &dwBytesPerSector, &dwFreeClusters, &dwClusters) == TRUE)
   {
    nFreeSpace = dwFreeClusters * dwBytesPerSector * dwSectorsPerCluster;
   }
  }
  else
  {
   ULARGE_INTEGER uliFreeBytesAvailableToCaller;
   ULARGE_INTEGER uliTotalNumberOfBytes;
   ULARGE_INTEGER uliTotalNumberOfFreeBytes;

   if(::GetDiskFreeSpaceEx(wstrWorkingFolder.c_str(), &uliFreeBytesAvailableToCaller, &uliTotalNumberOfBytes, &uliTotalNumberOfFreeBytes) == TRUE) 
   {
    nFreeSpace = static_cast<__int64>(uliFreeBytesAvailableToCaller.QuadPart / (1024 * 1024));
   }
  }

  // Release library
  ::FreeLibrary(hModule);
 }
 return nFreeSpace;
}

[C++] Check Process

bool CheckProcess(wstring wstrExeName)
{
 bool bHasProcess = false;
 unsigned long aProcesses[1024], cbNeeded, cProcesses;
 if(EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
 {
  cProcesses = cbNeeded / sizeof(unsigned long);
  for(unsigned int i = 0; i < cProcesses; i++)
  {
   if(aProcesses[i] == 0)
    continue;

   HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, aProcesses[i]);
   wchar_t buffer[50];
   GetModuleBaseNameW(hProcess, 0, buffer, 50);
   CloseHandle(hProcess);
   if(wstrExeName == wstring(buffer))
   {
    bHasProcess = true;
    break;
   }
  }
 }
 return bHasProcess;
}

[C++] Kill Process

bool KillProcess(wstring wstrExename)
{
 bool bOK = false;
 WCHAR wszCmd[2048];
 int nSize = 160;
 PROCESS_INFORMATION pi;
 STARTUPINFOW si;
 ZeroMemory(&si, sizeof(STARTUPINFOW));
 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
 si.cb = sizeof(STARTUPINFOW);
 si.dwFlags = STARTF_USESHOWWINDOW;
 si.wShowWindow = SW_HIDE;

 swprintf(wszCmd, L"taskkill /F /IM %s", wstrExename.c_str());
 if (CreateProcessW(NULL, wszCmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE|IDLE_PRIORITY_CLASS, NULL, NULL, &si, &pi))
 {
  int nKillWaitTime = 30000;
  int nRet = WaitForSingleObject(pi.hProcess, nKillWaitTime);
  switch (nRet)
  {
  case WAIT_TIMEOUT:  // Time out
   break;
  case WAIT_OBJECT_0:  // Process over
   break;
  case WAIT_OBJECT_0 + 1: // Don't Know
   break;
  }
  CloseHandle(pi.hProcess);
  pi.hProcess = NULL;
  CloseHandle(pi.hThread);
  pi.hThread = NULL;
  bOK = true;
 }
 return bOK;
}