Hi,

I would like to have a Unicode window in my application. Thus, I use to create my window RegisterWindowClassW, CreateWindowExW and so on. So far, this is working.

But I'd like my application to work on Windows 9x too. Therefore, I want to use the RegisterWindowClassA, CreateWindowExA and so on APIs if the system is not NT.

Of course, I could write all code involving these APIs twice, like this:


.if UnicodeSupported == TRUE
;Do Unicode-APIs
.else
;Do ANSI-APIs
.endif


But this is neither elegant nor good for the size of my executable. Therefore, I was wondering whether there is an opportunity to make something like this:


.if UnicodeSupported == TRUE
mov CreateWindowEx, CreateWindowExA
.else
mov CreateWindowEx, CreateWindowExW
.endif

invoke CreateWindowEx, ...


I hope somebody understands my problem and can help me.

Thanks,
Claus
Posted on 2004-10-23 15:39:58 by Claus von der Burchard
Perhaps this is what you want? http://www.microsoft.com/globaldev/handson/dev/mslu_announce.mspx
Posted on 2004-10-24 07:08:02 by f0dder
Hi,

I am sorry, I think my description was a little bit confusing. I want to create Aliases which I can use in my whole program so that I don't have always to check which API I need when I want to create a window.

I want to realize that when I say:
invoke CreateWindow, ...

CreateWindowA is invoked in Win9x, CreateWindowW in WinNT.

I know how to check the OS, I just don't know how to set these aliases.
I hope somebody can help me.

Claus
Posted on 2004-10-24 09:59:30 by Claus von der Burchard
Hi,

does nobody understand me or does nobobody know the answer?
Posted on 2004-10-25 12:10:27 by Claus von der Burchard
Claus von der Burchard,

Quite a while ago, I remember a thread which discussed something very much like what you have described. The thread discussed modifying the jump table that MASM creates to handle calling procedures.

When the source code is assembled, you would have a default CreateWindow procedure which could be either the ANSI 'A', version or the Unicode 'W' version, this would be decided by you, the programmer. When the source is assembled, MASM creates a jump table with the CreateWindow procedure--as well as all the others you use/define--with the address of the address of the actual function. In the process of running your code, when you get to a procedure call, control is passed to an address in the jump table, which is a jump to another address. It is this address which can be changed at run time to cause your program to execute one routine when in an ANSI environment, and another routine when in a Unicode environment.

With this process, you would determine--at runtime initialization--which environment you are in, and modify the jump table once. Then each time the procedure is called, the appropriate routine will be called without any additional checking.

I've described this as if I really know what I'm talking about, but I don't! So, hopefully some one with more knowledge will be able to comment on and/or verify what I've just said.

This could also be used to run one routine for an MMX machine, and another on an SSE2 machine.? All with the same source and executable.

hth

farrier
Posted on 2004-10-25 13:29:14 by farrier
Hi,

thank you for your description.
I just don't know how to do it, that is my problem.

Claus
Posted on 2004-10-25 13:54:07 by Claus von der Burchard
EliCZ has some includes for UNICODE support. Oddly, I can't find it on his web site. Maybe he includes it with other sources, or email him - he had the same problem you are experiencing.

Unicode is better imho because the Windows API uses it natively - ASCII API's are converted to Unicode. Unfortunately, it is not supported by MASM directly. GoASM does support Unicode to a greater degree.
Posted on 2004-10-25 16:12:56 by bitRAKE
Hi,

hm, I did not find it either ... nor do I find an email :cry: .
Funny, I thought this problem would be really easy.

Thanks for your help anyway,
Claus
Posted on 2004-10-25 16:51:26 by Claus von der Burchard
In my opinion, your best bet is to follow f0dder's (implicit) advice - use the Unicode API, and make sure MSLU is installed on older systems.

A run-time system as described by farrier won't work. The reason is that the arguments passed to the A/W functions are different.
Posted on 2004-10-25 18:56:10 by death
The important bits are located in:
http://www.anticracking.sk/EliCZ/export/DrvSkel.zip

.\Common\APImacro.mac

You'll also have to create new includes for the API, and use a different Windows.inc, iirc. I have the full package on another drive - might be the one setting on my desk or the one that crashed...I also have it on CD.

I will find it because it sounds like exactly what you want. Many x86'ers decided Unicode was a waste of time and few have moved forward with it. There are some obsticals, but having a larger native audience is very important.

Looks like EliCZ is keeping busy.
Posted on 2004-10-25 20:38:18 by bitRAKE
Claus,

You are likely to find that about 1% of win9x boxes have the unicode layer added to them so you are basically stuck with handling both ANSI and unicode string data. The approach to take is OS version detection and switch to the code that suits either ANSI or UNICODE.

The includes in MASM32 are either ANSI with an equate after them or UNICODE which still retains the trailing "W" API name. You can store UNICODE string in resource files and obtain tem for use with an API call, with a bit of trickery you can use a macro to most literal strings in the data section and if you need to you can convert ANSI to UNICODE with an API call so there is probably a way to avoid complete string duplication in your code.
Posted on 2004-10-25 21:04:33 by hutch--
Hi,

You are likely to find that about 1% of win9x boxes have the unicode layer added to them so you are basically stuck with handling both ANSI and unicode string data. The approach to take is OS version detection and switch to the code that suits either ANSI or UNICODE.


Isn't that exactly what I said I wanted to do?
My only problem is that I don't know how to switch between the APIs cleverly. What I want is that I check the OS at the start (I know how to do this part), then to set an Alias called "CreateWindow" referring either to "CreateWindowW" or to "CreateWindowA", so that I can say in the whole rest of the program simply "CreateWindow" and the right API is taken. I could create stuff like a single PROC for each API switching it, I just hope there is a more performant solution, just exchanging the pointers.

Principally, I could try something like this:


invoke LoadLibrary, ADDR szUser32 ;String "user32.dll"

mov hLib, eax

invoke GetProcAddress, eax, ADDR szCreateWindow ;String "CreateWindowExA" or "CreateWindowExW"

mov hCreateWindow, eax

push ...
push ... ;push the parameters for CreateWindowEx
call hCreateWindow

invoke FreeLibrary, hLib


I did not test it, but it should work. Still, the code is horrible and I think this would be even worse in performance.

@bitrake: Thank you for efforts. I will have a look at the zip-File tomorrow.
Concerning the API-Declaration, my INCLUDE-Files at the MASM32 include also the Unicode APIs.

And, finally: The Unicode- and ANSI-APIs I want to use have the same syntax, so that there is no problem.
Posted on 2004-10-25 21:23:51 by Claus von der Burchard
Hi,

you could try to change the IAT entries directly. That would look like



.if (bUnicode)
mov eax, _imp_CreateWindowExW@48
mov _imp_CreateWindowExA@48, eax
...
.endif


But, as already mentioned, you will have to modify all string parameters as well.
Posted on 2004-10-25 23:09:49 by japheth
What I meant by 'different arguments' is that you need to pass Unicode strings to W functions and multi-byte strings to A functions. If you want to switch them at run-time, you could:
I could create stuff like a single PROC for each API switching it, I just hope there is a more performant solution, just exchanging the pointers.

You'd also need to convert the string arguments*. But this is a very bad idea, isn't it?
You are likely to find that about 1% of win9x boxes have the unicode layer added to them so you are basically stuck with handling both ANSI and unicode string data. The approach to take is OS version detection and switch to the code that suits either ANSI or UNICODE.

How about installing it yourself with your application or telling the user to install it.

The idea of having two versions of your software is appalling, IMHO.


* And returned strings, and another procedure for each callback procedure receiving/returning strings, etc.
Posted on 2004-10-26 09:28:30 by death
I wrote "Writing Unicode programs" which covers these questions.
See www.GoDevTool.com.
This is part of the GoAsm help file.
Posted on 2004-10-26 14:20:55 by jorgon
Hi,

you could try to change the IAT entries directly. That would look like



.if (bUnicode)
mov eax, _imp_CreateWindowExW@48
mov _imp_CreateWindowExA@48, eax
...
.endif


But, as already mentioned, you will have to modify all string parameters as well.


Hi, this does not work out, as _imp_CreateWindowExW@48 does not exist (I am using MASM32).

Therefore, the link to the tutorial postet by jorgon does not work out for me.

I think I found the best solution for me, as I can get the pointer to the API, but I can't change it.
Still, I can do the push/call-Job. Just an example how it works out with a MessageBox (not so many arguments):


.if UnicodeSupport == FALSE
mov eax, MessageBoxA
.else
mov eax, MessageBowW
.endif

push MB_ICONINFORMATION Or MB_YESNO
push OFFSET szMsgTitle
push OFFSET szAbortText
push 0
call eax


Thanks for everybody trying to help me :).

Claus
Posted on 2004-10-26 15:57:29 by Claus von der Burchard
As implied by others, the above code won't work on both systems, as is. You will need to initialize every szText "constant" with the appropriate character code version.

MessageBoxW requires Unicode strings containing 16-bit (double byte) characters. It will not take UTF-8 (which is compatible with 8-bit ANSI), it will only do Unicode-16L (16-bit little-endian). You will get garbage characters (or a lot of missing ones) when displaying with the W version. If you convert all your strings to Unicode, then the A version will display only the first character because the second byte will be the end-of-string byte, 0.
Posted on 2004-10-26 16:22:20 by tenkey
Hi tenkey,

thanks, I know that, I just wanted to show how I switched between the APIs.
In my real program I am going to write, of course this is solved in the proper way with a ANSI-String and a UNICODE-String, using some cool characters ;-D.

Claus
Posted on 2004-10-26 16:53:25 by Claus von der Burchard
Claus,

The only viable solution for the string data that saves massive duplication is to use resource strings that are ALWAYS stored as UNICODE. These can then be accessed by either ANSI or UNICODE version of the API LoadString().

The mess you are then left with is what is the most efficient way to write the dual code forms. Going through the entire code with .IF blocks for each API that is either ANSI or UNICODE would be very messy to code and very inefficient in size terms.

What may be worth a look is writing ANSI, UNICODE and common code as three blocks and switcing to the appropriate block at startup depending on the OS version.
Posted on 2004-10-26 19:32:05 by hutch--
hutch-- said what I wanted to say much better than I did (I didn't even know what to call it). The Unicode vs. ANSI is a good example of why we could use an example of modifying the IAT. What I envisioned when I originally read the previous thread, was a DLL which could be loaded at startup which could determine the environment and equpment, and modify the program to call the appropriate procedures, then unload the DLL. I think because of the problem "death" described, we might have to create procedures of our own, one each for each API or hardware reality we have to account for, and then use this as a wrapper to execute the appropriate code.

For example:

ANSI vs. Unicode
FPU vs. MMX
9x vs. NT
NT vs. XP
...

japheth tried to provide some code but I haven't seen a real program that does what we have discussed. I'm gonna play with it some, but if someone has a working example, please share :)

Thanks,

farrier
Posted on 2004-10-27 01:28:03 by farrier