Repository: Bitmessage/PyBitmessage

Issue:
I can not run Bitmessage when I double click it.
I run v0.6,  I can not run Bitmessage when I double click it.

debug.log :
2016-08-29 19:01:30,918 - ERROR - C PoW test fail.
Traceback (most recent call last):
  File "F:\Coin\FTC\pack\PyInstaller-2.1-0.6\bitmessagemain\build\bitmessagemain\out00-PYZ.pyz\proofofwork", line 194, in <module>
  File "F:\Coin\FTC\pack\PyInstaller-2.1-0.6\bitmessagemain\build\bitmessagemain\out00-PYZ.pyz\ctypes", line 365, in **init**
WindowsError: [Error 126] 
2016-08-29 19:01:30,918 - ERROR - C PoW test fail.
Traceback (most recent call last):
  File "F:\Coin\FTC\pack\PyInstaller-2.1-0.6\bitmessagemain\build\bitmessagemain\out00-PYZ.pyz\proofofwork", line 204, in <module>
  File "F:\Coin\FTC\pack\PyInstaller-2.1-0.6\bitmessagemain\build\bitmessagemain\out00-PYZ.pyz\ctypes", line 365, in **init**
WindowsError: [Error 126] 



Discussion:
As the logs show PyInstaller 2.1, this looks like the 0.6.0 binary from the download directory on bitmessage.org (I use PyInstaller 3.2 in my binary releases). This binary is known to be broken. Download the 0.6.1 from github, the links on the bitmessage.org website also point to github.

Download the 0.6.1,I still get a PoW error.  But I run 0.4.5 is OK

2016-08-30 00:12:37,875 - ERROR - OpenCL fail: 
Traceback (most recent call last):
  File "openclpow.py", line 31, in initCL
LogicError: Context failed: invalid device
2016-08-30 00:12:37,881 - ERROR - C PoW test fail.
Traceback (most recent call last):
  File "proofofwork.py", line 198, in <module>
  File "proofofwork.py", line 98, in _doCPoW
ValueError: Procedure probably called with too many arguments (12 bytes in excess)

One CPoW error is ok because two different DLL loading methods are tried. Only when you get two, like in the first post, is that a reason to worry about. OpenCL error is also ok, it means you don't have OpenCL -compatible GPU and/or drivers. 0.4.5 doesn't have either (also, there is no official 0.4.5).

I presume other than the PoW error messages it runs?

Regarding the C PoW errors, this is ultimately simply determining the calling convention of the DLL. Is it really necessary to support multiple calling conventions? Compilers usually have an option to specify the calling convention to use. Maybe in the future there could be a configuration option to specify the library to use and its calling convention but for now it should suffice to simply support a single convention. I don't imagine many users will be impacted by reducing support to just cdecl libraries.

Well, one of them is generated by MSVS, and the other by MinGW. Or I could just rework the log message so that it is more clear what's happening.

I've successfully built and tested both 32-bit and 64-bit versions of the DLL on AppVeyor using your MSVS [build instructions](https://docs.google.com/document/d/1XcPtVLBis7t_dyZzPh0h__Ve2iR8XAOsMaO7eKeAu5Q) and they only load using CDLL.

I've now also managed to build both the 32-bit and 64-bit DLL using MinGW on AppVeyor and they also load into Python via CDLL. So both MSVS and MinGW use cdecl by default and the generated libraries can be accessed from Python via CDLL. At the very least CDLL should be tried first and then WinDLL (if at all).

@bmng-dev ~~If you try to load a CDLL as WinDLL, it will succeed, and only later when you call the C function, it will thrown an error.~~ Other way around. That's the reason why I put the order the way it is now.

Yes I understand that. I'm questioning why WinDLL is even being attempted as it always seems to be producing that error. Where is this DLL that allegedly has BitmessagePOW defined as using stdcall and why is it being given preferential treatment over the "official" DLL?

@bmng-dev I think that when I tried to compile it with MSVS, it created a WinDLL, see the binaries here: https://github.com/mailchuck/PyBitmessage/releases/tag/v0.5.2.

Now I can't get a suitable version of MSVS for free (the ~~trial~~ express versions of old MSVS don't provide a 64bit compiler, and I want to use old libraries to support the most Windows installations possible) and I don't want to pay ridiculous license just to build a dll, so I use MinGW.

I think I got it wrong in the previous post. When you try to init a cdll with the windll call, it will fail right away, but when you init a windll with a cdll call, it will succeed, and only fail later, when you actually call the function. This is why the init order is the way it is.

The issue with 0.5.2 and 0.5.3 release was the DLLs were linked against the new Windows 10 Universal C Runtime which weren't bundled with the release. When those dependencies are available the DLLs work as expected with CDLL and fail with WinDLL. Unfortunately bundling the Universal C Runtime is not feasible because it was not designed for "app-local deployment" (there are about 41 DLLs required to support Windows XP through to Windows 10)

The behavior where WinDLL will fail when calling the function while CDLL will fail immediately upon loading the DLL is the result of trying to load the same DLL twice. However it is possible to access both cdecl and stdcall functions from the same library by using the existing underlying OS handle when trying to load it again:

``` python
import ctypes
lib = WinDLL('bitmsghash32.dll')
lib.stdcall_function()
lib2 = CDLL('bitmsghash32.dll', handle=lib._handle)
lib2.cdecl_function()
```

So ultimately there is no need for attempting WinDLL at all. CDLL works perfectly fine as long as the library's dependencies can be found and isn't loaded multiple times.

Do I have to compile bitmsghash independently on  Windows 10 ? 
We haven't  "official" DLL?

No, you don't have to do anything. The official DLL's are bundled with the appropriate Windows executable. There was a build problem in the 0.6.0 release resulting in the two errors you initially reported. The one error you reported when trying 0.6.1 can be safely ignored, the absense of the second error means the DLL is being used. PyBitmessage works fine without the DLL - the DLL just reduces the time it takes to send a message.

I'll have to bump this one for later too. I build the 64bit binary now with mingw wherever I can instead of MSVC, so at least it runs correctly everywhere I tested it. There's still the extra error. I didn't have time to investigate the WinDLL/CDLL differences.

Resolving patch:
diff --git a/src/proofofwork.py b/src/proofofwork.py
--- a/src/proofofwork.py
+++ b/src/proofofwork.py
@@ -291,7 +291,6 @@ def init():
     global bitmsglib, bmpow
 
     openclpow.initCL()
-
     if sys.platform == "win32":
         if ctypes.sizeof(ctypes.c_voidp) == 4:
             bitmsglib = 'bitmsghash32.dll'
@@ -305,8 +304,7 @@ def init():
             bmpow.restype = ctypes.c_ulonglong
             _doCPoW(2**63, "")
             logger.info("Successfully tested C PoW DLL (stdcall) %s", bitmsglib)
-        except:
-            logger.error("C PoW test fail.", exc_info=True)
+        except ValueError:
             try:
                 # MinGW
                 bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib))
@@ -315,9 +313,12 @@ def init():
                 bmpow.restype = ctypes.c_ulonglong
                 _doCPoW(2**63, "")
                 logger.info("Successfully tested C PoW DLL (cdecl) %s", bitmsglib)
-            except:
-                logger.error("C PoW test fail.", exc_info=True)
+            except Exception as e:
+                logger.error("Error: %s", e, exc_info=True)
                 bso = None
+        except Exception as e:
+            logger.error("Error: %s", e, exc_info=True)
+            bso = None
     else:
         try:
             bso = ctypes.CDLL(os.path.join(paths.codePath(), "bitmsghash", bitmsglib))


Test patch: