Ruby, MS Windows, extensions and entry points

Hi all,

I’m trying to write a Ruby extension that would make
the Etc module functions, such as getpwnam, work on
Win32 (in roughly the same manner).

The equivalent to the getpwnam call on Win32 is
NetUserGetInfo(). The only problem is that when I
wrap this in an extension, the call always fails. I
think I know why.

Take a look at the C program here (scroll down to the
example code):

When I build this program as-is, it works fine. One
thing to note, however, is that this program uses
“wmain” as an entry point instead of “main”,
presumably as a way of handling wide-strings. Any
attempt to change the entry point back to main causes
NetUserGetInfo() to return an unsuccessful result
(error code 2221).

Consequently, trying to wrap this code in an extension
causes a similar error, I’m guessing because of the
main vs wmain issue.

Is there a way around this? Below is a sample
extension.

Regards,

Dan

/* win32etc.c */
#include “ruby.h”
#include <windows.h>
#include <stdio.h>
#include <lm.h>

#ifndef UNICODE
#define UNICODE
#endif

static VALUE win32etc_getpwnam(VALUE mod, VALUE name)
{
DWORD dwLevel = 10;
LPUSER_INFO_10 pBuf = NULL;
NET_API_STATUS nStatus;
char* cname = STR2CSTR(name);

VALUE Passwd =
rb_struct_define(“Passwd”,“name”,“comment”,0);
VALUE pstruct = rb_struct_new(Passwd);

// Call the NetUserGetInfo function; specify level
10.
nStatus = NetUserGetInfo(NULL,(wchar_t
*)cname,dwLevel,(LPBYTE *)&pBuf);

// If the call succeeds, print the user
information.
if (nStatus == NERR_Success)
{
if (pBuf != NULL)
{

rb_struct_aset(pstruct,INT2NUM(0),rb_str_new2((char
*)pBuf->usri10_name));

rb_struct_aset(pstruct,INT2NUM(1),rb_str_new2((char
*)pBuf->usri10_comment));
}
}
else
{
rb_warn(“NetUserGetInfo() call failed”);
}

/* Free the allocated memory. */
if (pBuf != NULL)
{
NetApiBufferFree(pBuf);
}

return pstruct;
}

void Init_win32etc()
{
VALUE mEtc = rb_define_module(“Win32Etc”);

rb_define_module_function(mEtc, “getpwnam”,
win32etc_getpwnam, 1);

}

/*

extconf.rb

require “mkmf”
have_library(“netapi32”)
create_makefile(“win32etc”)
*/

/*

test.rb

require “win32etc”
Win32Etc.getpwnam(“your userid”)
*/

···

Do you Yahoo!?
Yahoo! SiteBuilder - Free, easy-to-use web site design software
http://sitebuilder.yahoo.com

Hi,

···

----- Original Message -----
From: “Daniel Berger” djberg96@yahoo.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Friday, August 08, 2003 12:29 PM
Subject: Ruby, MS Windows, extensions and entry points

Hi all,

I’m trying to write a Ruby extension that would make
the Etc module functions, such as getpwnam, work on
Win32 (in roughly the same manner).

The equivalent to the getpwnam call on Win32 is
NetUserGetInfo(). The only problem is that when I
wrap this in an extension, the call always fails. I
think I know why.

Take a look at the C program here (scroll down to the
example code):

When I build this program as-is, it works fine. One
thing to note, however, is that this program uses
“wmain” as an entry point instead of “main”,
presumably as a way of handling wide-strings. Any
attempt to change the entry point back to main causes
NetUserGetInfo() to return an unsuccessful result
(error code 2221).

Consequently, trying to wrap this code in an extension
causes a similar error, I’m guessing because of the
main vs wmain issue.

Is there a way around this? Below is a sample
extension.

It’s due to the Unicode Conversion problem.

Try with following code.

Regards,

Park Heesob

/* win32etc.c */
#include “ruby.h”
#include <windows.h>
#include <stdio.h>
#include <lm.h>

#ifndef UNICODE
#define UNICODE
#endif

static VALUE win32etc_getpwnam(VALUE mod, VALUE name)
{
DWORD dwLevel = 10;
LPUSER_INFO_10 pBuf = NULL;
NET_API_STATUS nStatus;
char *cname = STR2CSTR(name);
char dest[256];
wchar_t wszUserName[UNLEN+1];

VALUE Passwd = rb_struct_define(“Passwd”,“name”,“comment”,0);
VALUE pstruct = rb_struct_new(Passwd);

MultiByteToWideChar( CP_ACP, 0, cname,
strlen(cname)+1, wszUserName,
sizeof(wszUserName)/sizeof(wszUserName[0]) );

// Call the NetUserGetInfo function; specify level 10.
nStatus = NetUserGetInfo(NULL,wszUserName,dwLevel,(LPBYTE *)&pBuf);

// If the call succeeds, print the user information.
if (nStatus == NERR_Success)
{
if (pBuf != NULL)
{
WideCharToMultiByte( CP_ACP, 0, pBuf->usri10_name, -1,
dest, 256, NULL, NULL );
rb_struct_aset(pstruct,INT2NUM(0),rb_str_new2(dest));

WideCharToMultiByte( CP_ACP, 0, pBuf->usri10_comment, -1,
dest, 256, NULL, NULL );
rb_struct_aset(pstruct,INT2NUM(1),rb_str_new2(dest));
}
}
else
{
rb_warn(“NetUserGetInfo() call failed”);
}

/* Free the allocated memory. */
if (pBuf != NULL)
{
NetApiBufferFree(pBuf);
}

return pstruct;
}

void Init_win32etc()
{
VALUE mEtc = rb_define_module(“Win32Etc”);

rb_define_module_function(mEtc, “getpwnam”,win32etc_getpwnam, 1);

}

Hi,

···

At Fri, 8 Aug 2003 13:23:23 +0900, Park Heesob wrote:

It’s due to the Unicode Conversion problem.

Try with following code.

But it would spoil portability. Etc module should support it.

I’m not sure if this works or even compiles.

Index: ext/etc/etc.c

RCS file: /cvs/ruby/src/ruby/ext/etc/etc.c,v
retrieving revision 1.11
diff -u -2 -p -r1.11 etc.c
— ext/etc/etc.c 31 Jul 2003 14:03:34 -0000 1.11
+++ ext/etc/etc.c 8 Aug 2003 04:49:59 -0000
@@ -24,4 +24,9 @@
#endif

+#if defined(_WIN32) && !defined(CYGWIN)
+#include <lm.h>
+#define HAVE_ST_PW_COMMENT 1
+#endif
+
static VALUE sPasswd, sGroup;

@@ -126,5 +131,30 @@ etc_getpwnam(obj, nam)
if (pwd == 0) rb_raise(rb_eArgError, “can’t find user for %s”, RSTRING(nam)->ptr);
return setup_passwd(pwd);
-#else
+#elif defined(_WIN32)

  • DWORD level = 10;
  • DWORD w2mflag = WC_DISCARDNS|WC_SEPCHARS|WC_DEFAULTCHAR|WC_COMPOSITECHECK;
  • LPUSER_INFO_10 buf = NULL;
  • char* cname = StringValueCPtr(name);
  • int len = strlen(cname) + 1;
  • wchar_t *wname = ALLOCA_N(wchar_t, len);
  • struct passwd pwd;
  • if (!MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED, cname, len, wname, len) ||
  • NetUserGetInfo(NULL, wname, level, (LPBYTE *)&buf) != NERR_Success) {
  • rb_raise(rb_eArgError, “can’t find user for %s”, cname);
  • }
  • MEMZERO(&pwd, struct passwd, 1);
    +#define COPY2PWD(w, s) do { \
  • len = WideCharToMultiByte(CP_OEMCP, w2mflag, buf->w, NULL, 0, NULL, NULL); \
  • pwd.s = ALLOCA_N(char, len + 1); \
  • WideCharToMultiByte(CP_OEMCP, w2mflag, buf->w, pwd.s, len, NULL, NULL);
    +} while (0)
  • COPY2PWD(usri10_name, pw_name);
  • COPY2PWD(usri10_comment, pw_comment);
  • if (buf) {
  • NetApiBufferFree(buf);
  • }
  • return setup_passwd(&pwd);
    +#else
    return Qnil;
    #endif


Nobu Nakada