Using pipes on windows fails on 64-bit SketchUp windows.
This example works perfectly on SketchUp 32-bit windows, but it fails in createPipe.Call on SketchUp 64-bit windows.
I suspect maybe wrong pointer bitsize/type. I get some sort of wrong memory entry point error (in dutch
also see: CreatePipe function (namedpipeapi.h) - Win32 apps | Microsoft Learn
Any ideas what is going on and/or suggestion for a fix / workaround on 64-bit SketchUp?
Thanks in advance.
To reproduce run the following on 64-bit and 32-bit SketchUp (I tested on windows 7 and windows 10)
require 'Win32API'
NORMAL_PRIORITY_CLASS = 0x00000020
STARTUP_INFO_SIZE = 68
PROCESS_INFO_SIZE = 16
SECURITY_ATTRIBUTES_SIZE = 12
ERROR_SUCCESS = 0x00
FORMAT_MESSAGE_FROM_SYSTEM = 0x1000
FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000
HANDLE_FLAG_INHERIT = 1
HANDLE_FLAG_PROTECT_FROM_CLOSE =2
STARTF_USESHOWWINDOW = 0x00000001
STARTF_USESTDHANDLES = 0x00000100
def raise_last_win_32_error
errorCode = Win32API.new("kernel32", "GetLastError", [], 'L').call
if errorCode != ERROR_SUCCESS
params = [
'L', # IN DWORD dwFlags,
'P', # IN LPCVOID lpSource,
'L', # IN DWORD dwMessageId,
'L', # IN DWORD dwLanguageId,
'P', # OUT LPSTR lpBuffer,
'L', # IN DWORD nSize,
'P', # IN va_list *Arguments
]
formatMessage = Win32API.new("kernel32", "FormatMessage", params, 'L')
msg = ' ' * 4000
msgLength = formatMessage.call(FORMAT_MESSAGE_FROM_SYSTEM + FORMAT_MESSAGE_ARGUMENT_ARRAY, '', errorCode, 0, msg, 4000, '')
puts msg
msg.gsub!(/\000/, '')
msg.strip!
raise msg
else
raise 'GetLastError returned ERROR_SUCCESS'
end
end
def create_pipe # returns read and write handle
params = [
'P', # pointer to read handle
'P', # pointer to write handle
'P', # pointer to security attributes
'L'] # pipe size
createPipe = Win32API.new("kernel32", "CreatePipe", params, 'I')
read_handle, write_handle = [0].pack('I'), [0].pack('I')
sec_attrs = [SECURITY_ATTRIBUTES_SIZE, 0, 1].pack('III')
raise_last_win_32_error if createPipe.Call(read_handle, write_handle, sec_attrs, 0).zero?
[read_handle.unpack('I')[0], write_handle.unpack('I')[0]]
end
create_pipe
To be complete: it uses this part from the ruby standard library:
# -*- ruby -*-
# for backward compatibility
warn "Warning:#{caller[0].sub(/:in `.*'\z/, '')}: Win32API is deprecated after Ruby 1.9.1; use dl directly instead" if $VERBOSE
require 'dl'
class Win32API
DLL = {}
TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG}
POINTER_TYPE = DL::SIZEOF_VOIDP == DL::SIZEOF_LONG_LONG ? 'q*' : 'l!*'
def initialize(dllname, func, import, export = "0", calltype = :stdcall)
@proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1')
handle = DLL[dllname] ||= DL.dlopen(dllname)
@func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func, calltype)
rescue DL::DLError => e
raise LoadError, e.message, e.backtrace
end
def call(*args)
import = @proto.split("")
args.each_with_index do |x, i|
args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
end
ret, = @func.call(args)
return ret || 0
end
alias Call call
end