Ruby, MS Windows, extensions and entry points

D

Daniel Berger

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):

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netusergetinfo.asp

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
 
P

Park Heesob

Hi,
----- Original Message -----
From: "Daniel Berger" <[email protected]>
To: "ruby-talk ML" <[email protected]>
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):

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netmgmt/netmgmt/netusergetinfo.asp

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);

}
 
N

nobu.nokada

Hi,

At Fri, 8 Aug 2003 13:23:23 +0900,
Park said:
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
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top