* ext/win32ole/win32ole.c (ole_event_free): invoke IConnectionPoint::Unadvise

before invoking IConnectionPoint::Release.

* test/win32ole/test_win32ole_event.rb, test/win32ole/err_in_callback.rb,
  test/win32ole/test_err_in_callback.rb: Use ActiveX Data Object
  Library instead of InternetExplorer.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@20089 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
suke 2008-11-02 13:22:42 +00:00
parent 510d2a7d2e
commit 2d45869c76
5 changed files with 330 additions and 366 deletions

View File

@ -1,3 +1,12 @@
Sun Nov 2 22:12:41 2008 Masaki Suketa <masaki.suketa@nifty.ne.jp>
* ext/win32ole/win32ole.c (ole_event_free): invoke IConnectionPoint::Unadvise
before invoking IConnectionPoint::Release.
* test/win32ole/test_win32ole_event.rb, test/win32ole/err_in_callback.rb,
test/win32ole/test_err_in_callback.rb: Use ActiveX Data Object
Library instead of InternetExplorer.
Sun Nov 2 22:06:55 2008 Masaki Suketa <masaki.suketa@nifty.ne.jp> Sun Nov 2 22:06:55 2008 Masaki Suketa <masaki.suketa@nifty.ne.jp>
* win32/Makefile.sub: add RUNRUBYOPT. [ruby-dev:37009] * win32/Makefile.sub: add RUNRUBYOPT. [ruby-dev:37009]

View File

@ -128,7 +128,7 @@ const IID IID_IMultiLanguage2 = {0xDCCFC164, 0x2B38, 0x11d2, {0xB7, 0xEC, 0x00,
#define WC2VSTR(x) ole_wc2vstr((x), TRUE) #define WC2VSTR(x) ole_wc2vstr((x), TRUE)
#define WIN32OLE_VERSION "1.3.7" #define WIN32OLE_VERSION "1.3.8"
typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX) typedef HRESULT (STDAPICALLTYPE FNCOCREATEINSTANCEEX)
(REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*); (REFCLSID, IUnknown*, DWORD, COSERVERINFO*, DWORD, MULTI_QI*);
@ -8124,7 +8124,11 @@ find_default_source(VALUE ole, IID *piid, ITypeInfo **ppTypeInfo)
static void static void
ole_event_free(struct oleeventdata *poleev) ole_event_free(struct oleeventdata *poleev)
{ {
OLE_FREE(poleev->pConnectionPoint); if (poleev->pConnectionPoint) {
poleev->pConnectionPoint->lpVtbl->Unadvise(poleev->pConnectionPoint, poleev->dwCookie);
OLE_RELEASE(poleev->pConnectionPoint);
poleev->pConnectionPoint = NULL;
}
free(poleev); free(poleev);
} }

View File

@ -1,15 +1,9 @@
require 'win32ole' require 'win32ole'
ie = WIN32OLE.new('InternetExplorer.Application') db = WIN32OLE.new('ADODB.Connection')
ie.visible = true db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
WIN32OLE_EVENT.message_loop ev = WIN32OLE_EVENT.new(db)
sleep 0.2 ev.on_event('WillConnect') {|*args|
ev = WIN32OLE_EVENT.new(ie)
ev.on_event('NavigateComplete2') {|*args|
foo foo
} }
ie.navigate(ARGV.shift) db.open
while ie.readystate != 4 WIN32OLE_EVENT.message_loop
WIN32OLE_EVENT.message_loop
sleep 0.2
end

View File

@ -21,27 +21,12 @@ if defined?(WIN32OLE)
" -I " + e " -I " + e
}.join("") }.join("")
@script = File.dirname(__FILE__) + "/err_in_callback.rb" @script = File.dirname(__FILE__) + "/err_in_callback.rb"
@param = create_temp_html
@param = "file:///" + @param.gsub(/\\/, '/')
end end
end end
def create_temp_html
fso = WIN32OLE.new('Scripting.FileSystemObject')
@dummy_file = fso.GetTempName + ".html"
cfolder = fso.getFolder(".")
@str = "This is test HTML file for Win32OLE (#{Time.now})"
f = cfolder.CreateTextFile(@dummy_file)
f.writeLine("<html><body><div id='str'>#{@str}</div></body></html>")
f.close
@f = @dummy_file
dummy_path = cfolder.path + "\\" + @dummy_file
dummy_path
end
def test_err_in_callback def test_err_in_callback
if @ruby if @ruby
cmd = "#{@ruby} -v #{@iopt} #{@script} #{@param} > test_err_in_callback.log 2>&1" cmd = "#{@ruby} -v #{@iopt} #{@script} > test_err_in_callback.log 2>&1"
system(cmd) system(cmd)
str = "" str = ""
open("test_err_in_callback.log") {|ifs| open("test_err_in_callback.log") {|ifs|
@ -51,34 +36,5 @@ if defined?(WIN32OLE)
end end
end end
def ie_quit
WIN32OLE_EVENT.message_loop
sh = WIN32OLE.new('Shell.Application')
sh.windows.each do |w|
i = 0
begin
i = i + 1
next if i > 100
WIN32OLE_EVENT.message_loop
sleep 0.1
next if /#{@dummy_file}/ !~ w.locationURL
e = w.document.all.item("str")
if e && e.innerHTML == @str
w.quit
WIN32OLE_EVENT.message_loop
sleep 0.2
break
end
rescue
retry
end
end
end
def teardown
ie_quit
File.unlink(@f)
File.unlink("test_err_in_callback.log")
end
end end
end end

View File

@ -4,50 +4,52 @@ rescue LoadError
end end
require 'test/unit' require 'test/unit'
if defined?(WIN32OLE_EVENT) def ado_installed?
class TestWIN32OLE_EVENT < Test::Unit::TestCase installed = false
module IE if defined?(WIN32OLE)
db = nil
begin
db = WIN32OLE.new('ADODB.Connection')
db.connectionString = "Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
db.open
db.close
db = nil
installed = true
rescue
end end
def create_temp_html
fso = WIN32OLE.new('Scripting.FileSystemObject')
dummy_file = fso.GetTempName + ".html"
cfolder = fso.getFolder(".")
f = cfolder.CreateTextFile(dummy_file)
f.writeLine("<html><body>This is test HTML file for Win32OLE.</body></html>")
f.close
dummy_path = cfolder.path + "\\" + dummy_file
dummy_path
end end
installed
end
if defined?(WIN32OLE_EVENT)
dotest = ado_installed?
if !dotest
STDERR.puts("\n#{__FILE__} skipped(ActiveX Data Object Library not found.)")
end
if dotest
class TestWIN32OLE_EVENT_ADO < Test::Unit::TestCase
CONNSTR="Driver={Microsoft Text Driver (*.txt; *.csv)};DefaultDir=.;"
module ADO
end
def message_loop def message_loop
WIN32OLE_EVENT.message_loop WIN32OLE_EVENT.message_loop
sleep 0.1
end
def wait_ie
while @ie.readyState != IE::READYSTATE_COMPLETE
message_loop
end
end
def setup
WIN32OLE_EVENT.message_loop
@ie = WIN32OLE.new("InternetExplorer.Application")
if !defined?(IE::READYSTATE_COMPLETE)
WIN32OLE.const_load(@ie, IE)
end
@ie.visible = true
message_loop
@event = ""
@event2 = ""
@event3 = ""
@f = create_temp_html
end end
def default_handler(event, *args) def default_handler(event, *args)
@event += event @event += event
end end
def setup
@db = WIN32OLE.new('ADODB.Connection')
if !defined?(ADO::AdStateOpen)
WIN32OLE.const_load(@db, ADO)
end
@db.connectionString = CONNSTR
@event = ""
@event2 = ""
@event3 = ""
end
def test_s_new def test_s_new
assert_raise(TypeError) { assert_raise(TypeError) {
ev = WIN32OLE_EVENT.new("A") ev = WIN32OLE_EVENT.new("A")
@ -55,87 +57,90 @@ if defined?(WIN32OLE_EVENT)
end end
def test_s_new_without_itf def test_s_new_without_itf
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event {|*args| default_handler(*args)} ev.on_event {|*args| default_handler(*args)}
@ie.navigate("file:///#{@f}") @db.open
while @ie.busy @db.close
WIN32OLE_EVENT.new(@ie) 10.times do |i|
WIN32OLE_EVENT.new(@db)
GC.start GC.start
message_loop message_loop
@db.open
message_loop
@db.close
end end
assert_match(/BeforeNavigate/, @event) assert_match(/WillConnect/, @event)
assert_match(/NavigateComplete/, @event)
end end
def test_on_event def test_on_event
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event {|*args| default_handler(*args)} ev.on_event {|*args| default_handler(*args)}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_match(/BeforeNavigate/, @event) assert_match(/WillConnect/, @event)
assert_match(/NavigateComplete/, @event)
end end
def test_on_event_symbol def test_on_event_symbol
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event(:BeforeNavigate2) {|*args| ev.on_event(:WillConnect) {|*args|
handler1 handler1
} }
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("handler1", @event2) assert_equal("handler1", @event2)
end end
def test_on_event2 def test_on_event2
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event('BeforeNavigate') {|*args| handler1} ev.on_event('WillConnect') {|*args| handler1}
ev.on_event('BeforeNavigate') {|*args| handler2} ev.on_event('WillConnect') {|*args| handler2}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("handler2", @event2) assert_equal("handler2", @event2)
end end
def test_on_event3 def test_on_event3
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event {|*args| handler1} ev.on_event('WillConnect') {|*args| handler1}
ev.on_event {|*args| handler2} ev.on_event('WillConnect') {|*args| handler2}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("handler2", @event2) assert_equal("handler2", @event2)
end end
def test_on_event4 def test_on_event4
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event{|*args| handler1} ev.on_event{|*args| handler1}
ev.on_event{|*args| handler2} ev.on_event{|*args| handler2}
ev.on_event('NavigateComplete'){|*args| handler3(*args)} ev.on_event('WillConnect'){|*args| handler3(*args)}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert(@event3!="") assert_equal(CONNSTR, @event3)
assert("handler2", @event2) assert("handler2", @event2)
end end
def test_on_event5 def test_on_event5
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event {|*args| default_handler(*args)} ev.on_event {|*args| default_handler(*args)}
ev.on_event('NavigateComplete'){|*args| handler3(*args)} ev.on_event('WillConnect'){|*args| handler3(*args)}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_match(/BeforeNavigate/, @event) assert_match(/ConnectComplete/, @event)
assert(/NavigateComplete/ !~ @event) assert(/WillConnect/ !~ @event)
assert(@event!="") assert_equal(CONNSTR, @event3)
end end
def test_unadvise def test_unadvise
ev = WIN32OLE_EVENT.new(@ie, 'DWebBrowserEvents') ev = WIN32OLE_EVENT.new(@db, 'ConnectionEvents')
ev.on_event {|*args| default_handler(*args)} ev.on_event {|*args| default_handler(*args)}
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_match(/BeforeNavigate/, @event) assert_match(/WillConnect/, @event)
ev.unadvise ev.unadvise
@event = "" @event = ""
@ie.navigate("file:///#{@f}") @db.close
wait_ie @db.open
message_loop
assert_equal("", @event); assert_equal("", @event);
assert_raise(WIN32OLERuntimeError) { assert_raise(WIN32OLERuntimeError) {
ev.on_event {|*args| default_handler(*args)} ev.on_event {|*args| default_handler(*args)}
@ -144,7 +149,7 @@ if defined?(WIN32OLE_EVENT)
def test_non_exist_event def test_non_exist_event
assert_raise(RuntimeError) { assert_raise(RuntimeError) {
ev = WIN32OLE_EVENT.new(@ie, 'XXXX') ev = WIN32OLE_EVENT.new(@db, 'XXXX')
} }
dict = WIN32OLE.new('Scripting.Dictionary') dict = WIN32OLE.new('Scripting.Dictionary')
assert_raise(RuntimeError) { assert_raise(RuntimeError) {
@ -153,98 +158,103 @@ if defined?(WIN32OLE_EVENT)
end end
def test_on_event_with_outargs def test_on_event_with_outargs
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
# ev.on_event_with_outargs('BeforeNavigate'){|*args| @db.connectionString = 'XXX' # set illegal connection string
# args.last[5] = true # Cancel = true assert_raise(WIN32OLERuntimeError) {
# } @db.open
ev.on_event_with_outargs('BeforeNavigate2'){|*args|
args.last[6] = true # Cancel = true
} }
bl = @ie.locationURL ev.on_event_with_outargs('WillConnect'){|*args|
@ie.navigate("file:///#{@f}") args.last[0] = CONNSTR # ConnectionString = CONNSTR
wait_ie }
assert_equal(bl, @ie.locationURL) @db.open
message_loop
assert(true)
end end
def test_on_event_hash_return def test_on_event_hash_return
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){|*args| ev.on_event('WillConnect'){|*args|
{:return => 1, :Cancel => true} {:return => 1, :ConnectionString => CONNSTR}
} }
bl = @ie.locationURL @db.connectionString = 'XXX'
@ie.navigate("file:///#{@f}") @db.open
wait_ie assert(true)
assert_equal(bl, @ie.locationURL)
end end
def test_on_event_hash_return2 def test_on_event_hash_return2
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){|*args| ev.on_event('WillConnect'){|*args|
{:Cancel => true} {:ConnectionString => CONNSTR}
} }
bl = @ie.locationURL @db.connectionString = 'XXX'
@ie.navigate("file:///#{@f}") @db.open
wait_ie assert(true)
assert_equal(bl, @ie.locationURL)
end end
def test_on_event_hash_return3 def test_on_event_hash_return3
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){|*args| ev.on_event('WillConnect'){|*args|
{'Cancel' => true} {'ConnectionString' => CONNSTR}
} }
bl = @ie.locationURL @db.connectionString = 'XXX'
@ie.navigate("file:///#{@f}") @db.open
wait_ie assert(true)
assert_equal(bl, @ie.locationURL)
end end
def test_on_event_hash_return4 def test_on_event_hash_return4
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){|*args| ev.on_event('WillConnect'){|*args|
{'return' => 2, 'Cancel' => true} {'return' => 1, 'ConnectionString' => CONNSTR}
} }
bl = @ie.locationURL @db.connectionString = 'XXX'
@ie.navigate("file:///#{@f}") @db.open
wait_ie assert(true)
assert_equal(bl, @ie.locationURL)
end end
def test_on_event_hash_return5 def test_on_event_hash_return5
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){|*args| ev.on_event('WillConnect'){|*args|
{6 => true} {0 => CONNSTR}
} }
bl = @ie.locationURL @db.connectionString = 'XXX'
@ie.navigate("file:///#{@f}") @db.open
wait_ie assert(true)
assert_equal(bl, @ie.locationURL)
end end
def test_off_event def test_off_event
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event{handler1} ev.on_event{handler1}
ev.off_event ev.off_event
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("", @event2) assert_equal("", @event2)
end end
def test_off_event_arg def test_off_event_arg
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){handler1} ev.on_event('WillConnect'){handler1}
ev.off_event('BeforeNavigate2') ev.off_event('WillConnect')
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("", @event2) assert_equal("", @event2)
end end
def test_off_event_arg2
ev = WIN32OLE_EVENT.new(@db)
ev.on_event('WillConnect'){handler1}
ev.on_event('ConnectComplete'){handler1}
ev.off_event('WillConnect')
@db.open
message_loop
assert_equal("handler1", @event2)
end
def test_off_event_sym_arg def test_off_event_sym_arg
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
ev.on_event('BeforeNavigate2'){handler1} ev.on_event('WillConnect'){handler1}
ev.off_event(:BeforeNavigate2) ev.off_event(:WillConnect)
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert_equal("", @event2) assert_equal("", @event2)
end end
@ -256,25 +266,16 @@ if defined?(WIN32OLE_EVENT)
@event2 = "handler2" @event2 = "handler2"
end end
def handler3(url) def handler3(*arg)
@event3 += url @event3 += arg[0]
end end
def teardown def teardown
@ie.quit if @db && @db.state == ADO::AdStateOpen
message_loop @db.close
@ie = nil
i = 0
begin
i += 1
File.unlink(@f) if i < 10
rescue Errno::EACCES
message_loop
retry
end end
message_loop message_loop
GC.start @db = nil
message_loop
end end
class Handler1 class Handler1
@ -285,15 +286,15 @@ if defined?(WIN32OLE_EVENT)
@val3 = nil @val3 = nil
@val4 = nil @val4 = nil
end end
def onStatusTextChange(t) def onWillConnect(conn, uid, pwd, opts, stat, pconn)
@val1 = t @val1 = conn
end end
def onProgressChange(p, pmax) def onConnectComplete(err, stat, pconn)
@val2 = p @val2 = err
@val3 = pmax @val3 = stat
end end
def onPropertyChange(p) def onInfoMessage(err, stat, pconn)
@val4 = p @val4 = stat
end end
end end
@ -308,26 +309,26 @@ if defined?(WIN32OLE_EVENT)
end end
def test_handler1 def test_handler1
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
h1 = Handler1.new h1 = Handler1.new
ev.handler = h1 ev.handler = h1
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert(h1.val1) assert_equal(CONNSTR, h1.val1)
assert_equal(h1.val1, ev.handler.val1) assert_equal(h1.val1, ev.handler.val1)
assert(h1.val2) assert_equal(nil, h1.val2)
assert(h1.val3) assert_equal(ADO::AdStateOpen, h1.val3)
assert(h1.val4) assert_equal(ADO::AdStateOpen, h1.val4)
end end
def test_handler2 def test_handler2
ev = WIN32OLE_EVENT.new(@ie) ev = WIN32OLE_EVENT.new(@db)
h2 = Handler2.new h2 = Handler2.new
ev.handler = h2 ev.handler = h2
@ie.navigate("file:///#{@f}") @db.open
wait_ie message_loop
assert(h2.ev != "") assert(h2.ev != "")
end end
end
end end
end end