Link C Obj Files into VB6

Author: Dave
Date: 04.03.14 - 12:54pm

In the last post I covered some tools which allow you to create standard dlls in VB6. Once I got playing with the tool to modify the link process, I thought I would keep going a bit and see what else I could do.

I really wanted a way to link in compiled code written in C, directly into my VB6 projects. This need doesnt come up that often, but it does come up. Maybe you have a decoder written in C, that you dont want to have to port. Or rather do some small operations that C makes easy, but are a mess in VB6. There could also be performance reasons with say a compression routine or similar.

Anyway, the example I went with, was creating 64 bit numbers and showing them as a string. VB6 has the native 8 byte currency type which can house a 64bit number, but it cant manipulate them.

So first up, how can we include code compiled in C into our VB6 binary. This turns out to actually be quite easy, all you have to do is add the compiled .obj file to the linker command line. It also turns out that if your C code defined the functions as exports, the linker will automatically place them in the export table which works for exe or dll the same.

Now to use the functions from VB6, you just access them normally as if it was a dll using the Windows API Declare syntax. So how do you debug it while running code in the IDE you ask? There isnt much point in doing this trick, if you cant debug the other parts of your code in the IDE. Thats easy too. When you write your C code, you compile it as a standard dll and use the Declares to access the library. This lets you debug everything traditionally.

Then when your ready to compile, you use the exact same C object file that made the dll, and just link it into the exe build process.

The trick is, that you wrap your raw API access routines, in another function which tests to see if the code is running in the IDE or not. If it is, then it uses the external API declares. If its running as a compiled code, then it uses a second set of API declares that reference the compiled executable itself.

Since the functions are exported, it all functions as if it was using an external dll, just that no separate file is required for distribution. A sample is below:

unsigned __int64 __stdcall to64(unsigned int hi, unsigned int lo){
#pragma EXPORT
	unsigned __int64 ret=0;
	ret = hi;
	ret = ret << 32;
	ret += lo;  
	return ret;

'for accessing linked in C code
Private Declare Function to64 Lib "project1.exe" _
		(ByVal hi As Long, ByVal lo As Long) As Currency
'for debugging in the IDE 
Private Declare Function dll_to64 Lib "c_obj.dll" Alias "to64" _
		(ByVal hi As Long, ByVal lo As Long) As Currency

Function doto64(hi As Long, lo As Long) As Currency
    If isIde() Then
        doto64 = dll_to64(hi, lo)
        doto64 = to64(hi, lo)
    End If
End Function

Dim a As Currency
a = doto64(&H11223344, &H55667788)
While this trick is most often needed for complex math routines you dont want to port to VB, there are times where you will also need to use some Windows API from your C obj file routines.

Math and stuff runs fine, sub function calls should also run fine. But using the Windows API cause some problems. This is because the obj file will contain details on the import table, which will override the settings the VB6 executable requires to run, leading to a corrupted executable.

Dont worry though, there is a way around it. The way I chose to go, was to access any WinApi i needed through dynamically looked up function pointers. To load the pointers, I included an Init function which took the addresses of LoadLibrary and GetProcAddress as arguments to help me boot strap it.

typedef FARPROC  (__stdcall *GetProc)(HMODULE a0,LPCSTR a1);
typedef HMODULE  (__stdcall *LoadLib)(LPCSTR a0);
typedef int (__stdcall *SysAllocSBL)(void* str, int sz);
typedef int (__cdecl *Sprnf)(char *, const char *, ...);
typedef int (__cdecl *Strlen)(char *);

GetProc getproc;
LoadLib loadlib;
Sprnf sprnf;
Strlen strln;
SysAllocSBL sysAlloc;

int __stdcall init(int lpfnGetProc, int lpfnLoadLib){
#pragma EXPORT 	 
	 int h = 0;
	 int failed = 0;

	 //_asm int 3
	 getproc = (GetProc)lpfnGetProc;
         loadlib = (LoadLib)lpfnLoadLib;

	 h = loadlib("msvcrt.dll");
	 sprnf = (Sprnf)getproc(h,"sprintf");
	 strln = (Strlen)getproc(h,"strlen");
	 h = loadlib("oleaut32");
	 sysAlloc = (SysAllocSBL)getproc(h,"SysAllocStringByteLen");
	 if( (int)sprnf == 0) failed++;
	 if( (int)strln == 0) failed++;
	 if( (int)sysAlloc == 0) failed++;

	 return failed;

Linking in the resulting obj file lead to no further problems. Given that you wont be using this technique for general programming tasks, Limiting the API you use in your C Stubs is a very realistic design constraint.

Bottom line, if you are doing something complex, it would be worthy of its own DLL anyway.

There are also other ways to use C Object files in your VB6 code. With a little bit more experimentation, and some web searching, I was able to find examples of how to replace entire Module files with Cpp counterparts as well as only replacing individual functions found in VB6 modules.

Replacing the entire module is a little bit slicker than the export technique, because you dont have to write all the wrappers and manage two sets of declare statements. When in the IDE, it operates normally through the declare. Once compiled, and the module replaced, all the functions are literally running the same code but compiled in. Note however that the VB6 declare syntax, the run time does some extra processing for you marshaling data from C strings to the COM BSTR type. I do include a string example, but you might have to play around with it some more to get it the way you want it.

As far as replacing individual functions from a module, I took a cheap shot approach, where I opened up the .obj file in binary mode and overwrote the function name declaration in the one you want to override. The linker then uses the implementation you provide in the extra C Obj file automatically.

For entire module replacement, you just replace the vb6 .obj file with the C one.

To help me with these experiments I have extended Jim Whites vb LinkTool. Examples of all of these techniques are available in the source archive I have put on github.

One other nice addition I made while I was in there was to add a POSTBUILD command so that you could automatically run a command line every time your executable was built. This is handy for copying a dll to other project directories, running upx etc.

Comments: (0)

Leave Comment:
Email: (not shown)
Message: (Required)
Math Question: 77 + 79 = ? followed by the letter: A 

About Me
More Blogs
Main Site
C libs for VB6
Console tricks
FireFox temp dir
OCX License
Extract substring
VB6 Console Apps
VB6 Debugger View As Hex tooltips
VB6 - C Share registry data
VB6 Addin Missing Menus
VB6 Class Init Params
VB6 isIn function
Python and VB6
Python pros and cons
download web Dir
vc rand in python
VB6 Language Enhancement
Register .NET as COM
UDT Tricks pt2
Remote Data Extraction
Collection Extender
VB6 FindResource
DirList Single Click
Reset CheckPoint VPN Policy
VB6 BSTR Oddities Explained
SafeArrays in C
BSTR and Variant in C++
Property let optional args
Misc Libs
Enum Named Pipes
Vb6 Collection in C++
VB6 Overloaded Methods
VB6 Syncronous Socket
Simple IPC
VB6 Auto Resize Form Elements
Mach3 Automation
Exit For in While
C# self register ocx
VB6 Class Method Pointers
JS Debugger
Duktape Debug Protocol
QtScript 4 VB
Vb6 Named Args
vb6 Addin Part 2
VB6 Addin vrs Toolbars
OpenFile Dialog MultiSelect
Duktape Example
DukTape JS
VB6 Unsigned
.Net version
TitleBar Height
.NET again
VB6 Self Register OCXs
Query Last 12 Mos
Progid from Interface ID
VB6 to C Array Examples
Human Readable Variant Type
ScriptBasic COM Integration
CodeView Addin
ScriptBasic - Part 2
Script Env
printf override
History Combo
Disable IE
API Hooking in VB6
Addin Hook Events
FastBuild Addin
VB6 MemoryWindow
Link C Obj Files into VB6
Vb6 Standard Dlls
CStr for Pascal
Lazarus Review
asprintf for VS
VB6 GlobalMultiUse
Scintilla in VB6
Dynamic Highlight
WinVerifyTrust, CryptMsgGetParam VB6
MS GLEE Graphing
printf for VB6
C# App Config
Tero DES C# Test
VC 2008 Bit Fields
Speed trap
C# Db Class Generator
VB6 vrs .NET (again)
FireFox Whois Extension
git and vb6
Code Additions
Compiled date to string
C# ListView Sorter
VB6 Wish List
C# Process Injection
C# PE Offset Calculator
VB6 Async Download
Show Desktop
coding philosophy
Code release
Dll Not Found in IDE
Advanced MSScript Control
random tip
Clipart / Vector Art
VB6 Callback from C#
Binary data from VB6 to C#
CSharp and MsScriptControl
HexDumper functions
Js Beautify From VB6 or C#
vb6 FormPos
Inline Asm w VB6
The .NET Fiasco
One rub on computers
Universal extractor