- In the world of Windows development, Win32 is the name of the entire programming interface (the API) used to interact with the operating system. When they later moved to 64-bit, instead of renaming it to "Win64," they kept the name Win32 for the API itself to maintain developer familiarity. Technically, 64-bit Windows programs run on the Win32 API for 64-bit systems. So, when the OS says "not a valid Win32 application," it really means "not a valid Windows DLL/exe"
- msvcr120.dll is a Dynamic Link Library (DLL) file that is a core component of the Microsoft Visual C++ Redistributable (Runtime) for Visual Studio 2013. Since the problematic PC never had any Visual Studio installed on it, it was missing the runtime dependency of my DLL.
- Shared runtimes are used to reduce the size of the compiled binary, but they introduce a dependency on the target operating system to provide the runtime.
- You can check the dependencies of your DLL or EXE by using Visual Studio's dumpbin.exe. On cmd, dumpbin /dependents filename.dll shows you the DLLs filename.dll depends on.
- If you see MSVCR....dll, you need the C Runtime.
- If you see MSVCP....dll, you also need the C++ Standard Library.
- If you see KERNEL32.dll or USER32.dll, don't worry, those are part of Windows itself and are always present.
- Previously, I discussed how to embed the C runtime in Linux. You can also embed/bake the C/C++ Runtime into your binary with Visual Studio via Project Properties > C/C++ > Code Generation > Runtime Library
- The default of /MD (Multi-threaded DLL) or /MDd (Multi-threaded Debug DLL) uses shared runtime
- Changing it to /MT (Multi-threaded), embeds the code into the DLL, leaving it with zero external dependencies. You can verify that your DLL has no dependencies (besides KERNEL32.dll) with dumpbin.exe.
- The disadvanages of /MT
- Larger file size
- If you have five different DLLs all compiled with /MT, each one has its own copy of the runtime in RAM. If they were compiled with /MD, they would all share a single instance of the shared DLL in memory.
- If a security flaw is found in the Microsoft C++ Runtime, Windows Update cannot fix your app. You would have to recompile your project with the latest patches and send the new DLL to your users.
- If you use /MT, make sure that any object created inside your DLL is also destroyed inside your DLL (e.g., using a DestroyObject() function you provide).
- For Debug, use /MDd because it is optimized for finding bugs, filling uninitialized memory with specific patterns (like 0xCCCCCCCC) etc.
- Windows folder names can be confusing:
- C:\Windows\System32: Contrary to the name, this folder is for 64-bit DLLs on a 64-bit version of Windows.
- C:\Windows\SysWOW64: This folder is for 32-bit DLLs. WOW64 stands for "Windows on Windows 64-bit".
Hardware in the Loop Aerospace Simulation Development
C++ and MATLAB Simulink tips for HWIL simulation software engineers
Wednesday, February 4, 2026
Embedding Visual C++ Runtime into DLL
Saturday, December 27, 2025
Reading unsigned data in Java
In binary files, a single byte is often used to represent numbers from 0 to 255 (unsigned). However, in Java, a byte is signed, ranging from -128 to 127, because Java doesn't have a unsigned types (with the exception of the 2 byte char type). A raw byte with value 0xF0 in a file meant to represent 240 will become -16 when using Javas's ByteBuffer.get(). To fix this:
int unsignedByte = buffer.get() & 0xFF;
Explanation: When performing bitwise operations, Java automatically "promotes" the 8-bit byte to a 32-bit signed integer. If the byte is 0xF0 = 240 (11110000), Java sees the leading 1 and assumes it is a negative number. Through Sign Extension, it fills the new 24 bits with 1s to preserve that negative value (-16) in the larger container.
Original Byte: 11110000 (-16, see two's complement) Promoted Int : 11111111 11111111 11111111 11110000 (Still -16)Now, you apply the mask 0xFF (255). In binary, 0xFF as a 32-bit integer is 00000000 00000000 00000000 11111111:
11111111 11111111 11111111 11110000 (The promoted -16) & 00000000 00000000 00000000 11111111 (The 0xFF mask) ------------------------------------- 00000000 00000000 00000000 11110000 (The result: 240)
By "ANDing" the promoted integer with 0xFF, you effectively clear out all the 1s created by sign extension, leaving only the original 8 bits and giving you the correct unsigned value of 240.
Similary, reading an unsigned short (16-bit) from file:
int unsignedShort = buffer.getShort() & 0xFFFF;
Reading an unsigned int (32-bit):
long unsignedInt = buffer.getInt() & 0xFFFFFFFFL;
Note that an unsigned 32-bit integer can exceed the capacity of a Java int. You must jump up to a long and use a long literal mask (noted by the L at the end of the mask value).
When writing a value that is first multplied by a scale factor of 2^31 (1 << 31), Java assumes the 1 << 31 is an int and shifting 1 by 31 places puts it into the sign bit position of a 32bit int, which results in the negative value of -2147483648. Correct usage:
double val = 123;
long scaleFactor = (1L << 31); // Will be positive 2147483648 because long is 64bit
// and shifting by 31 won't put the 1 into sign bit
// position
long val_scaled = (long) (val * scaleFactor);
Wednesday, October 1, 2025
Why C/C++ circular dependency causes "syntax error"
// headerB.h
#ifndef HEADER_B_H
#define HEADER_B_H
struct structB { int x; };
#endif
// headerA.h
#ifndef HEADER_A_H
#define HEADER_A_H
#include "headerB.h"
void methodA(structB param);
#endif
// main.cpp
#include "headerB.h" // headerB processed first
#include "headerA.h" // headerB already included, so HEADER_B_H is defined
// #include "headerB.h" does NOTHING
// structB is unknown! → SYNTAX ERROR
The "syntax error" occurs because the compiler doesn't know what structB is when it tries to compile methodA. If headerB includes headerA (directly or indirectly), you have a circular dependency. The C++ preprocessor just does text substitution - it doesn't understand C++ syntax. It can't detect circular dependencies because include guards prevent infinite loops, so the circular dependency becomes an incomplete type error instead.
Friday, July 11, 2025
Double-to-Int Conversion with Bit Shifting
We often need to pack a large numeric range into 32 bits. For instance, timestamps in microseconds over a 36 minute period exceed Integer.MAX_VALUE. By discarding the least significant bits (via right shift), we can fit the value and later we can recover it by the same amount of left shift. However, increasing the number of shifts decreases accuracy. What we need to optimize is to find the right amount of bit shift that covers the range and minimizes error. The following Java code investigates this:
Monday, June 16, 2025
C/C++ header mismatch bug
I encountered a problem where a field in a global struct (myStruct) held a valid value before entering a function foo, but turned into garbage after entering it. When I consulted AI tools, they suggested that foo might be allocating very large local arrays, causing a stack overflow that could corrupt the global structure. Another possibility was an out-of-bounds write elsewhere in the code.
After a week of debugging and trying various solutions—such as increasing the thread's stack size—I discovered the root cause: The function foo was defined in a C library with multiple versions. Each version resided in a different folder but had the same file names. Which folder was used depended on a #define. I was including the header from one version of the library, but linking against the implementation from another. If the struct definitions had matched, this wouldn’t have caused an issue, but they differed—evident from the differing sizeof(myStruct). As a result, myStruct was interpreted using the wrong layout, leading to corrupted values from an incorrect memory region.
Sunday, June 15, 2025
C++ pointer bug
#include <iostream>
class A {
public:
int val;
};
void reset(A *p_a) {
if (p_a != NULL) {
delete p_a;
}
p_a = new A();
}
int main() {
A *p_a = new A();
p_a->val = 5;
std::cout << "Before reset, p_a->val:" << p_a->val << "\n";
reset(p_a);
std::cout << "After reset, p_a->val:" << p_a->val << "\n";
return 0;
}
The reset function receives a copy of the pointer p_a, not a reference to it. When you modify p_a inside the function (with p_a = new A()), you're only changing the local copy - the original pointer in main() remains unchanged. What actually happens:
- p_a in main() points to an A object with val = 5
- reset() receives a copy of this pointer
- reset() deletes the original object (memory is freed)
- reset() creates a new object, but assigns it only to the local copy
- The original p_a in main() still points to the deleted memory
- Accessing p_a->val after reset() is undefined behavior (accessing freed memory)
//Reference to pointer
void reset(A *&p_a) {
if (p_a != nullptr) {
delete p_a;
}
p_a = new A();
// Call with: reset(p_a);
An even better fix is to use smart pointers, which removes the necessity for the reset function:
auto p_a = std::make_unique<A>();
You can detect such problems by enabling AddressSanitizer (ASAN) in Visual Studio:
- Right-click your project → Properties
- Go to Configuration Properties → C/C++ → General
- Set Enable Address Sanitizer to Yes (/fsanitize=address)
- Go to Configuration Properties → C/C++ → Optimization
- Set Optimization to Disabled (/Od) for better debugging
- Set Whole Program Optimization to No
- Go to Configuration Properties → C/C++ → Debug Information Format
- Set to Program Database (/Zi) or Program Database for Edit & Continue (/ZI)
- Open your C/C++ project in Eclipse CDT
- Right-click project → Properties
- Navigate to C/C++ Build → Settings
- Under Tool Settings:
- GCC C++ Compiler → Miscellaneous
- GCC C Compiler → Miscellaneous
- Add to "Other flags": -fsanitize=address -g -O1
- Project Properties → C/C++ Build → Settings
- GCC C++ Linker → Miscellaneous
- Add to "Other objects": -fsanitize=address
Tuesday, May 27, 2025
Fuzzy Logic and Quake III Bots
Fuzzy logic is often used in decision-making systems where a detailed mathematical model of the system is unavailable or impractical. Instead of relying on equations, fuzzy logic encodes expert intuition into human-readable rules. These rules allow systems to make decisions based on approximate or linguistic input values, such as “low health” or “enemy nearby.”
For simple systems — say, with just one input and one output — fuzzy logic may be overkill. In those cases, a 1D interpolation (similar to proportional navigation) is often enough to generate smooth behavior transitions. But as systems grow more complex, fuzzy logic scales better than maintaining large interpolation grids or rigid condition trees.
While neural networks have become dominant in many domains, fuzzy logic still offers distinct advantages, especially in embedded or control-focused systems. Fuzzy logic requires structured human insight, while neural networks thrive on raw data and pattern discovery. For complex or poorly understood systems, writing fuzzy rules is impractical. Advantages of fuzzy logic over neural networks:
- Interpretability: Fuzzy rules are readable and understandable by developers and domain experts.
- Minimal training: Rules encode prior knowledge, reducing or eliminating the need for extensive data-driven training.
- Lightweight tuning: At most, fuzzy systems may require optimizing rule weights — a much simpler process than full network training.
One of the most interesting uses of fuzzy logic in gaming came from Quake III Arena. The bots in the game used fuzzy logic to evaluate possible behaviors — such as attack, search for health, search for a better weapon, retreat. Each action was assigned a desirability score based on fuzzy evaluations of current game state (e.g., health, distance to enemy, ammo). At each tick, the bot would choose the highest-scoring action.