A little help with Win32API

Started by azdesign, August 25, 2012, 04:31:40 am

Previous topic - Next topic

azdesign

I'm a newbie scripter and still have no clue at all about Win32API.  :^_^': How do I achieve the same result of below code in Win32API ?

text = 'message1:=:Hello'
part1 = line.split(':=:')[0]
part2 = line.split(':=:')[1]
p part1, ' - ', part2


The code I meant was the split method from String. How to do it in Win32API and achieve the same result ?
This is related with unicode support, any non-standard character got messed up when being processed by String methods. So I thought, what if the string operation done by kernel, and ruby store the result. I tried fiddling with japanese text before; extracting it from a file, store it in a variable, display it, no problem. This time, that text I got from a text file MUST be splitted, in order to be stored in a hash. But the splitting results in 'invalid byte sequence in Windows-31J' aka messep up, returns nil.  :facepalm:

Thanks in advance :D

By the way,
Is Win32API is actually C codes ? I'm familiar with C, but if it IS C, then all I have to learn is to use those C codes in ruby
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

G_G

In order to use Win32 calls in your script, the desired library must be coded in C with the methods exported. What you'll most likely have to do is create your own library with a method you can pass a string to. Then take the string and alter it C side, then return it.

azdesign

Still getting hold of learning from here
http://ruby-doc.org/docs/ProgrammingRuby/html/ext_ruby.html

I get the idea of using C as extension library in ruby, now learning how to use it.
Why would I put so much effort in this ? I know many other flexible, modern game engine, but rpg maker just that, give me all the resources I need on developing a game, I don't have to make sprite, I dont have to create my own map editor, etc. And why XP ? because I hate vx/ace dwarf characters  >:(. And XP-ization of VX/Ace is more pain in the ass of this one. Convert tileset, charset, tweak map editor behavior, edit this edit that. There is a some kind of market out there you know for indie RPG games using RPG Maker, else than Amaranth Games, which I think more profit. I want to try its worth.  :)
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

ForeverZer0

August 25, 2012, 07:17:45 pm #3 Last Edit: August 25, 2012, 07:23:16 pm by ForeverZer0
Its impossible to help you with Win32API when you aren't even saying what exactly you are trying to do, or dll/library you are trying to do it with. The Ruby side of Win32API isn't really that difficult, but you do need to to know the library you are using in order to use it.

Here's an example from the MCI Audio Player script I wrote.

MCISendString = Win32API.new('winmm', 'mciSendString', 'PPLL', 'L')


First, we create an object to hold the Win32API instance. Convention is to use the same of the function you are creating a wrapper for, in this case it is MCISendString.

Next, we create the Win32API object, which takes 4 arguments.

  • 1. The name of dll. This example uses a library found in System32 on all Windows systems, so I don't need a path or anything. If it was your own custom library, you need to include a path, or simply place the dll in the game's main directory. The dll I am getting a function from is "winmm"

  • 2. This the name of the function you are getting. You have to know what functions are in the library for this. If its your own custom-made dll, then you would know this. I know it because you can find the documentation for it on MSDN. This site is invaluable when using Windows API functions, and you will need it for the following 2 arguments as well.

  • 3. This argument defines the argument types that will be passed to the function you are wrapping. I am sure if you looked at the Win32API documentation, then you saw the different "key" letters to use. For the most part, "p" is for pointer, which is most commonly used for objects and strings, "I" is for integer (a 4-byte number), "L" is for long (an 8-byte number). There are others, but those are the most common. If you look at the mciSendString documentation, we see that it takes 4 arguments.
MCIERROR mciSendString(
 LPCTSTR lpszCommand,
 LPTSTR lpszReturnString,
 UINT cchReturn,
 HANDLE hwndCallback
);

The first is the command to send to MCI, which is a string: "P"
The second is the string, or buffer that the function will write the the result of the function to: "P"
The third is the size of the buffer we are pointing to, in bytes: "I"
The fourth is the handle used for the callback. In this particular situation, we don't really need this. A handle is simply a number used to identify a window in Windows. Its simply an integer: "I"

  • 4. this last argument for a Win32API object is similar to the last argument, but instead we are defining the type used for the return value. The documentation for mciSendString says this:

QuoteReturn value

Returns zero if successful or an error otherwise. The low-order word of the returned DWORD value contains the error return value. If the error is device-specific, the high-order word of the return value is the driver identifier; otherwise, the high-order word is zero. For a list of possible error values, see MCIERR Return Values.


The long and the short, it returns a number. If no error occurs, its 0, otherwise its the error code, which we would need to use "mciGetErrorString" to get the message for. The argument return type therefore is an integer: "I"

Okay, now we have constructed the wrapper for the function, which is stored in the Ruby constant "MCISendString". In order to use it, you have to use the "call" method, and pass it arguments that match the type you defined in the constructor.

From here, you would need to know the proper commands to send to the MCI, but that's not part of what you are trying to learn, so don't get hung up on that. I'll give two examples, the first is a simple call without needing to "get a value", and the second with one.

errorCode = MCISendString.call('close all', nil, 0, 0)

The first argument is a string which is the command.
The second argument is are buffer for the return value. Since we don't care about a return value, we can simply pass nil, and nothing will be written to.
The third argument is the size of the buffer, in our case 0, since we are not passing one.
The fourth is the handle. We don't care about that either in this example since it has no bearing. Other times you may need to pass this, which you can make a Win32API function and "GetWindowHandle" to get this value. We aren't worrying about it, so just say 0.

If everything went okay, errorCode should be equal to 0. If there was an error, it would now contain the MCI error code. Not all functions even return a value, which you would have defined the return type as "V" for void if that was the case.

Alright, now for another example that gets a string value from the method. Win32API cannot just simply return a string, just primitive types, and a Ruby string is an entirely different animal from a Windows string. What we do is create a place in memory for it to write data to, like this.

buffer = "\0" * 128

This creates an "empty" string that is 128 bytes.
To use it to get a value, you would do something like this:
MCISendString.call("status ALIASNAME volume", data, 128, 0)

The first argument is our command.
the second is the buffer we created. It will contain the value after the call. We are actually only sending a pointer to it, not the actual object, but you don't really need to worry about that.
The third argument is the size of the buffer. We made ours 128 bytes, so we pass that.
The fourth is the handle argument again, which we are disregarding and simply passing 0.

Now, after the call is made, "buffer" now contains the value of the volume, but it is also stilled padded with all the extra bytes we used. It is still 128 bytes, so we need to remove the extra empty bytes.

buffer.delete!("\0")


Now, if you read the value of "buffer", it will contain the value of the volume as a string. If you wanted to convert it to an integer for further processing, you could simply use "to_i" on it to convert.

This is a basic example using an existing and well-documented function, so as you can see, you need to be more specific if you wanted detailed help on a particular library. Feel free to ask any questions. I'm sick of typing, so I am gonna end it here.






I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

azdesign

August 25, 2012, 07:52:03 pm #4 Last Edit: August 25, 2012, 08:10:58 pm by azdesign
 :( Sorry if I did not describe it clearly what I'm looking for. Here is the flow of what scenario I wanted help with :

1. Ruby : Open a text file, get the text of a single line, call Win32API method using that text as argument
2. Win32API : the method is : Split that string by a delimiter, and return the result back to ruby
3. Ruby : Take returned string and store it in a variable

Simply, I wanted this text "example1:=:value of example 1" to become "example1" and "value of example 1", both stored in 2 ruby variables. I want the splitting done by Win32API for reason I have stated on my first post.

From your reply, I have learned that Win32API is using existing windows library to use with, and honestly that's news to me. So far I think that this Win32API class was defined in ruby along of all of its methods. If you're asking me which dll/library I'm trying to fiddle with, the answer is I don't know, I haven't browse the entire collection of dlls that process a string. I thought this is just ruby running any c code I wrote; I was wrong then, it using existing dlls. That answer why there is only few documentation in ruby docs regarding Win32API, the rest of the documentation are in MSDN  :facepalm:

----EDIT----

source : http://stackoverflow.com/questions/6542362/c-split-string-function
This splitting function seem not to be provided by windows API. So I'm going to change my question :
What is the step to create a custom dll to be used in ruby ? consider that I have already know the C code
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

ForeverZer0

You are not understanding what Win32API even is. There is no reason you need it for anything you listed. What you need is a one-liner.

v1, v2 = IO.readlines('text_doc.txt')[LINE_NUMBER].split(':=:')


Replace LINE_NUMBER with an actual value. The line numbers start at 0, not 1.
Creating a DLL to do that would be silly, and just be extra overhead. It would be smarter to simply store the lines of text to limit the amount if IO access, too.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

azdesign

August 25, 2012, 10:37:56 pm #6 Last Edit: August 25, 2012, 10:39:12 pm by azdesign
I'm sorry, this is seems to be a misunderstanding  :(
The solution you just posted was the same like my first post was. In the end, we're just using ruby String's split method. I did explain why I avoid this method, because it is process by 1.8.1 ruby, means any japanese text got messed up. Split, regex, any string related operation result in messing the non-standard characters.

That was my question in the beginning, how to achieve the same result of String's split method using non-ruby methods ? That's why I mentioned Win32API though I really have no idea of it. I thought if the splitting done by external parser, the japanese text will be fine, that's all.

By the way, thank you for keeping up with me, I really appreciate it. I'm sorry if my english seems off, I'm no native  :^_^':
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

ForeverZer0

Open the file with the proper encoding.
Win32API is even less compatible with string encoding than Ruby is.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

azdesign

August 25, 2012, 11:06:16 pm #8 Last Edit: August 25, 2012, 11:09:26 pm by azdesign
I did open and save the file in proper encoding, the non-standard text was fine. Extract from text and storing it in a variable, store it into yet another variables, calling its value, display and draw it in windows or console window, not a problem. Problem arise when I try to apply string related methods such as split. If what you said is true about encoding support of Win32API, then this seems to be a dead end. I thought Win32API can help the encoding issue.

I don't have the choice to migrate to newer RM or another engine, so maybe I should just discover another alternatives without the need of applying string methods.

Anyway, Thanks for the feedback, you helped me learn new things.
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

ForeverZer0

I still dunno exactly what your problem is.
I made a text file, in UTF-8 encoding, with the following text:
Chinese|你怎么样
Japanese|お元気ですか


I added this line to the editor:
File.open('text.txt', 'rb') {|f| f.each_line {|l| p l.split('|') } }


...and it worked just as expected.
I am done scripting for RMXP. I will likely not offer support for even my own scripts anymore, but feel free to ask on the forum, there are plenty of other talented scripters that can help you.

azdesign

Every time I extract the first line and split it, the first split always got this black diamond with "?" at the center symbol.   :uhm:
Without the split, text was fine, after split, that symbol appeared. I don't know. If you can do it on your code, then maybe its just me. I experiments for hours, and still have no clue at all and then I decided to ask here.

It's okay though, I already discover the alternate code which does not require any splitting. Thanks really, maybe my machine problem, or anything, I don't know. Next time, I'll test it also in my other pc.  :shy:
~ Check out my deviantart http://my-az-design.deviantart.com/ ~

G_G

What version of RMXP are you using? If you're using the PK Edition, you might want to look into getting an official version. If you are on an official version, maybe try getting the updated version from Enterbrain's site. These are the only reasons I can think of that the code works for Zer0 and not you.

azdesign

Yeah, thanks for pointing this up, I should buy it and get the latest update. I need to experiments further until I'm sure, I will be using XP for the rest of my game development. I mean, this is an outdated ruby game engine and maybe will never get its update will it worth the money, that was my thought. So, I'll just continue using it until I'm sure. When the time come, I'll buy it
~ Check out my deviantart http://my-az-design.deviantart.com/ ~