We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now


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

lapides asked
Medium Priority
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.
Watch Question

Tommy HuiEngineer

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.

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++.


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:


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

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

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 _WIN32
      NOMANGLE short CCONV dsqld(BSTR lsIn,short liSQLLen,
                              BSTR lsEr,short liErrorLen);
      NOMANGLE short CCONV dsqld(LPSTR lsIn,short liSQLLen,
                              LPSTR lsEr,short liErrorLen);
   int dsqld(char *lsSQL,char **lsOut,char *lsError, int liErrorLen);

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

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

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.




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


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.


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?

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,


Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.


Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.