Solved

Porting UNIX C code to windows DLL (16/32)

Posted on 1997-06-30
8
319 Views
Last Modified: 2013-11-15
OK, I developed UNIX utility in C. This is fairly complicated Parser. It accepts sql string (usualy created on the fly dynamic SQL, so it is normally one huge string) and then formats it nicely with all the intends, multiple levels of nested subselect, etc. so it is very easy to debug its syntax. Works great on UNIX, recognises all sql key words, smart enough to detect sql parameters, etc..

Now I want to port it to Windows DLL. I have 1.52 VC++ and 4.0 VC++, both installed on NT 3.51. I tried to create a DLL in both versions and failed. I have no problem creating primitive DLL on both 16/32 platforms that accepts the string and changes it and returns back (e.g. to VB application), but my utility does not want to work. I am able to build it, then I can call it from the main program (VB 3.0 for 16 bit DLL, VB 4.0 for 32) and my VB crashes.

There is no MFC functionality required, no C++ functionality, just plain UNIX C, that I want to make a DLL. This is really helpful application for those who creates dynamic SQL, I want to distribute it as a shareware. I will be very gratefull if you will be able to guide me through my porting process step by step. I know C pretty good, but not VC++. As additional bonus I will send you the registered version of this DLL when it will be finished, if you can help me. Thank you.
0
Comment
Question by:lapides
8 Comments
 
LVL 15

Expert Comment

by:Tommy Hui
Comment Utility
Make sure you have the .c extension on your files and not .cpp. Then for each function you want to make available from your DLL add the _export keyword to it.

void _export foo(void)
{
}

That's all that is needed.
0
 
LVL 3

Expert Comment

by:vinniew
Comment Utility
we use __dllexport in front of all the functions in a dll.

BTW, the parsing code for SQL is included in the MFC souce with VC++.




0
 

Author Comment

by:lapides
Comment Utility
Your 15 words answer did not make my life any easier.

Not a clue! Given as is it provides zero help and desrves zero points.

Your answer may be very useful for someone who already knows the answer, but for person like me the value of your answer is nill.

1. I already wrote that I build my DLL succesfully, I also was able to call it from outside, but then my VB calling program crashed.

This would imply that I shoulda declare it as an _export otherwise how coulda my calling program call it?

Nevertheless I renamed my cpp source to c and rebuild it and run it with debugger (using my calling VB program as a driver). It crashed inside my DLL on the first line. (I just run it under VC++ 1.52)

2. Now if you know how to do this port and WILLING to spend a bit more time and be more descriptive and show me what I am doing wrong please do.

Here what I did in detail:

3. Once again, I do not know what is going wrong. Should it be OK to build this DLL with VC++ 1.52? or with VC++4.0? Would I be able to call my 16 bit DLL when running 16 bit apps under NT? Would I be able to call this 16 bit DLL from 32 bit apps (e.g. VB 5.0 or PB 5.0)?

4. Then I have added to my program the code in the very beginning:

#define WINNDOWSVERSION 1

#ifdef WINNDOWSVERSION
   #include <windows.h>
   #include <ole2.h>

   #ifdef _WIN32
     #define CCONV __stdcall
     #define NOMANGLE
   #else
     #define CCONV FAR PASCAL _export
     #define NOMANGLE EXTERN_C
     #include <stdlib.h>
     #include <compobj.h>    
     #include <dispatch.h>
     #include <variant.h>
     #include <olenls.h>  
   #endif
#endif

When I compile under UNIX I comment the first line.

5. Then I changed the declaration of the function to be like this:
/***************************************************************
    external DLL functions declarations  
****************************************************************/
#ifdef WINNDOWSVERSION
   #ifdef _WIN32
      NOMANGLE short CCONV dsqld(BSTR lsIn,short liSQLLen,
                              BSTR lsEr,short liErrorLen);
   #else
      NOMANGLE short CCONV dsqld(LPSTR lsIn,short liSQLLen,
                              LPSTR lsEr,short liErrorLen);
   #endif
#else
   int dsqld(char *lsSQL,char **lsOut,char *lsError, int liErrorLen);
#endif

BTW you see that "_export" is there in 16 bit version and it is abcent in 32 bit. But in 32 bit version (VC++ 4.0) I use def file where I declare this function as an export one, I also use this
def file with explicit external declaration.

Now as you see the interface is slightly different under UNIX and WINDOWS. Under UNIX I pass the pointer *lsSQL, dsqld() allocates memory for the output, parses the input string, inserts into it
intends, newline where they belong and then populates this allocated memory and passes back the pointer to this buffer. The calling program under UNIX can see this pointer.

Because this is impossible on WINDOWS I have changed the interface. My programm will accept lsIn, internally put all the additional characters (via allocating temporary memory in its own address space), and then just before exit it will copy the string from the internal buffer to lsIn overwriting the input. It is the responsibility of the calling programm under WINDOWS to allocate
the memory for input parameter, populate it and pass to my function along with the allocated length. My program will then make sure that if it writes (copies) the string in lsIn the length of this string is less then liSQLLen - the second parameter. The same story with the lsError.

6. Those were the only changes that I made.

7. I then started VC. It asked me what kind of new project I would like to build. I choose DLL with
no MFC. Then I added to this project two files - my source code that I renamed to dsqld.cpp
originally and then tried today with dsqld.c and the dsqld.def.

8. Then I created the simpliest executable (VB program) that calls my function.

9. Then in VC I specified the debug option, executable program, set breakpoint in the first line and run it. It stopped on the first line, which was:

   if(NULL==(lsSQL=(char *)malloc(liSQLLen))){

and crashed.

10. What I am doing wrong?

11. In my C code I use (char *), may be under WINDOWS I have to use LPSTR everywhere instead?

12. why calling my file cpp instead of c is a crime?

I will appreciate your REAL advice very much, but it sure will take some time on your side. Keep in
mind please that I have about 10 years C experience on UNIX and zero on Windows. And please start
with the basics (1.52 vs 4.0, 16 vs 32, what project to start, etc.)




0
 
LVL 4

Expert Comment

by:jos010697
Comment Utility
Are you sure you want to support that 16 bit stuff? I ported
an application to WNT lately, from a Un*x environment.
I know next to nothing about WNT (and I'd like to keep
it that way, thank you ;-) but the porting process was a
breeze ... most of the time was spent setting the right
compiler and linker flags in those dialog window thingies ...

FYI, the application used the dlopen() stuff on the Unix
boxes which translated into dll stuff easily. Sockets
stuff were easy to port too (to my surprise ;-)

Have you exported/imported all the object names from
the 'base' executable and the dlls properly?

kind regards,

Jos aka jos@and.nl
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 3

Expert Comment

by:vinniew
Comment Utility
OK, now we're getting somewhere.  

Here are the facts, I think:

1. you compiled the DLL with no errors/warnings
2. you are using BSTR for VB-compatibility purposes.
3. your dll is C
4. your testing app is VB


since BSTR more or less is a pain in the butt to work with, I would recommend that you use LPSTR everywhere instead.  I think that BSTR requires memory allocation and some kind of setup call first, too.  CString::sysAlloc or something like that.  Anyway, if you make the stuff LPSTR (compiles down to char *), your life will be easier.

BTW, do a TRACE (VC 4.0) or fprintf (debug file in VC 1.52) to tell you what lsSQLLen is.  I'd need that to help you.

So, really, if you get rid of that BSTR stuff (you don't need it.  VB can send char* if they are referenced/dereferenced correctly), you should be rid of your problems.


 
0
 

Author Comment

by:lapides
Comment Utility
FYI

Hi experts...

So I solved my problems. The facts are:

1. I have created both DLLs, on the same machine (NT 3.51) under VC++1.52 and VC++4.0.

The first one works with VB3.0 (16 bit) the second one works with VB 4.0 (32 bit).

2. The source code for both DLLs identical (and different from UNIX code!!!!).

3. BTW, both DLLs are declared as *.cpp not *.c. Everything works. Beats me can't understand why your suggestion about *.c extension is so important...

4. Also, there is no need to use tracing in the ourtput file under 1.52 (another your suggestion). Both versions (1.52 and 4.0) allow debugging DLL by specifiing in debug settings the external exe (which was my VB driver).

5. The ONLY reason for my grief, that caused DLL to behaiv crazy (non-detectable with simple debugging) was as follows.

Inside my original code there was a fragment where I wanted to save the string in the temporary buffer. So under UNIX I did:

char *lsSave;

void SaveTmp(char *lsIn,char **lsSave);

and then I call this SaveTmp, like this

SaveTmp(lsIn,&lsSave)

Pointer to the pointer is absolutely natural and essential under UNIX. This is because when I call SaveTmp I do not know where to save, it is done within SaveTmp: first memory is allocated and then it is populated, then inside the calling program I can readily access this lsSave, it points to the right place.

By some unknown reason VC++ does not like this (thanx Billy) and goes crazy.

As soon as I allocated the memory lsSave just before calling SaveTmp (and changed it's interface to:
void SaveTmp(char *lsIn,char *lsSave);

it was immediately tamed. (lsSave does only populating of lsSave not allocating)

Thanx for trying to help. I understand that my question was not the easiest one.

Regards,
Andrew.

0
 
LVL 2

Expert Comment

by:sprinkmeier
Comment Utility
re: .C nv .CPP
c++ has function overloading (ie. foo(int) and foo(float) can happily coexist) so it uses "mangling" to distinguish the two for the linker (calling them somehting like foo_int and foo_float).
This is because the compiler decides which version of foo to use, and has to have some way of telling the linker (in effect, the linker thinks the two foo's are completley different functions)

re: weird problem with char **:
somehting do do with thunking, ie. seg:offs vs. flat addressing perhaps?

0
 

Accepted Solution

by:
davidoz earned 200 total points
Comment Utility
I have written DLLs and called them from VB. If you don't get the calling conventions right, it can easily crash the system!

you can have your DLL in a .cpp file, just make sure you use extern "C" if you do. Declare the function like this:

extern "C" DWORD _declspec(dllexport) __stdcall theFunction(char *buf)

This will generate an external entry point called _theFunction@4
where the 4 is the total number of bytes needed for args

In VB, declare the call like this:
Declare Function foo Lib "SOMETHING.DLL" Alias "_theFunction@4" (ByVal s as String) As Long

This will pass the string as you want. Even though you are using ByVal, you can still modify the string. You need to make sure you are passing a big enough buffer, so you may want to preallocate the destination string with Spaces().

Hope this helps,

DW
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

This article covers general Notes 8.5 troubleshooting information including recreating the Notes\Data folder.
If your app took Google’s lash recently, here are the 5 most likely reasons.
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now