Skip to content

Commit

Permalink
Merge pull request #55 from ashie/last-error
Browse files Browse the repository at this point in the history
Add API.last_error
  • Loading branch information
cosmo0920 authored Apr 17, 2021
2 parents 9e30bb6 + 693fbd3 commit 3604d7c
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
12 changes: 6 additions & 6 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
version: '{build}'

image: Visual Studio 2019

init:
# use a minimal path
- set PATH=C:\ruby%ruby_version%\bin;C:\Program Files\7-Zip;C:\Program Files\AppVeyor\BuildAgent;C:\Program Files\Git\cmd;C:\Windows\system32
Expand All @@ -26,6 +28,10 @@ test_script:
environment:
matrix:
- ruby_version: _trunk
- ruby_version: 30-x64
- ruby_version: 30
- ruby_version: 27-x64
- ruby_version: 27
- ruby_version: 26-x64
- ruby_version: 26
- ruby_version: 25-x64
Expand All @@ -34,12 +40,6 @@ environment:
- ruby_version: 24
- ruby_version: 23-x64
- ruby_version: 23
- ruby_version: 22-x64
- ruby_version: 22
- ruby_version: 21-x64
- ruby_version: 21
- ruby_version: 200-x64
- ruby_version: 200

matrix:
allow_failures:
Expand Down
50 changes: 50 additions & 0 deletions ext/win32/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,35 @@ typedef struct {
int prototype[20];
} Win32API;

typedef struct ThreadData {
DWORD win32api_error;
} ThreadData;

static inline ThreadData* thread_data_get(void);
static ID id_thread_data;

static ThreadData* thread_data_init(void)
{
ThreadData* td;
VALUE obj;

obj = Data_Make_Struct(rb_cObject, ThreadData, NULL, -1, td);
rb_thread_local_aset(rb_thread_current(), id_thread_data, obj);

return td;
}

static inline ThreadData* thread_data_get()
{
VALUE obj = rb_thread_local_aref(rb_thread_current(), id_thread_data);

if(obj != Qnil && TYPE(obj) == T_DATA){
return (ThreadData*) DATA_PTR(obj);
}

return thread_data_init();
}

static void api_free(Win32API* ptr){
if(ptr->library)
FreeLibrary(ptr->library);
Expand Down Expand Up @@ -781,6 +810,7 @@ static VALUE api_call(int argc, VALUE* argv, VALUE self){
uintptr_t return_value;
int i = 0;
int len;
ThreadData* thread_data = thread_data_get();

struct{
uintptr_t params[20];
Expand Down Expand Up @@ -975,6 +1005,8 @@ static VALUE api_call(int argc, VALUE* argv, VALUE self){
}
}

thread_data->win32api_error = GetLastError();

/* Return the appropriate type based on the return type specified
* in the constructor.
*/
Expand Down Expand Up @@ -1029,6 +1061,21 @@ static VALUE api_call(int argc, VALUE* argv, VALUE self){
return v_return;
}

/*
* call-seq:
* Win32::API.last_error
*
* Return the last Win32 error code of the current executing thread.
*
* The error code shouldn't be retrieved by calling GetLastError() manually
* because Ruby's internal code may call other Win32 API and reset the error
* code before it.
*/
static VALUE get_last_error(VALUE self)
{
return INT2NUM(thread_data_get()->win32api_error);
}

/*
* Wraps the Windows API functions in a Ruby interface.
*/
Expand Down Expand Up @@ -1061,6 +1108,9 @@ void Init_api(){
/* Miscellaneous */
rb_define_alloc_func(cAPI, api_allocate);

/* GetLastError alternative */
rb_define_singleton_method(cAPI, "last_error", get_last_error, 0);

/* Win32::API Instance Methods */
rb_define_method(cAPI, "initialize", api_init, -1);
rb_define_method(cAPI, "call", api_call, -1);
Expand Down
6 changes: 6 additions & 0 deletions test/test_win32_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ def test_call_return_value_on_failure
assert_equal(0xFFFFFFFF, @gfa.call('C:/foobarbazblah'))
end

def test_last_error
@gfa.call('C:/foobarbazblah')
error_file_not_found = 2
assert_equal(error_file_not_found, API.last_error)
end

def test_dll_name
assert_respond_to(@gcd, :dll_name)
assert_equal('kernel32', @gcd.dll_name)
Expand Down

0 comments on commit 3604d7c

Please sign in to comment.