Windows: How to link dll with target=x86_64-windows-msvc?

If I set the target to x86_64-windows-gnu it works.

However, if I set it to x86_64-windows-msvc it fails to link.
error: lld-link: .zig-cache\o\320d857c0552b55ee2db1d4ddda36f58\hello.dll: bad file type. Did you specify a DLL instead of an import library?

I think this is because zig is not passing the /lldmingw flag to the lld linker.

b.addLibrary with .linkage = .dynamic produces dll with import library .lib file so I tried using exe.linkLibrary instead of exe.linkSystemLibrary, but it still didn’t work.

error: warning(link): unexpected LLD stderr:
lld-link: warning: undefined symbol: hello

My project setup:

// src/hello.c
#include <stdio.h>
void hello() {
  puts("hello");
}
// src/main.zig
const hello = struct {
    extern "hello" fn hello() void;
};
pub fn main() void {
  hello.hello();
}
// build.zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe_mod = b.createModule(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    const exe = b.addExecutable(.{
        .name = "learn_zig",
        .root_module = exe_mod,
    });
    b.installArtifact(exe);

    const hello_mod = b.createModule(.{
        .target = target,
        .optimize = optimize,
    });
    const hello = b.addLibrary(.{
        .name = "hello",
        .root_module = hello_mod,
        .linkage = .dynamic,
    });
    hello.addCSourceFiles(.{
        .files = &.{"src/hello.c"},
        .flags = &.{},
    });
    hello.linkLibC();
    b.installArtifact(hello);

    exe.step.dependOn(&hello.step);
    exe.addLibraryPath(hello.getEmittedBinDirectory());
    exe.linkSystemLibrary("hello"); // exe.linkLibrary(hello);

    // ...
}

Between hello.linkLibC(); and b.installArtifact(hello); try either of these lines

exe.root_module.addImport("hello", hello_mod);
exe_mod.addImport("hello", hello_mod);

the 3 lines after are not required

exe.root_module.addImport("hello", hello_mod);
exe_mod.addImport("hello", hello_mod);

That works, but it statically links hello :thinking:. I want it to be dynamically linked instead.

exe.linkLibrary(hello) is what you want.

remove the system link thing you did, also the importing of hello_mod is useless as it has no zig source and (at least on 0.14.1) breaks compilation because of that.

1 Like

I believe the problem is that you are missing dllexport attribute in hello.c.

1 Like

Thanks @vulpesx and @yataro! Using exe.linkLibrary(hello) and dllexport did the trick! I forgot about that since the GNU version worked without it, lol.

// build.zig
const exe_mod = b.createModule(.{
    .root_source_file = b.path("src/main.zig"),
    .target = target,
    .optimize = optimize,
});
const exe = b.addExecutable(.{
    .name = "learn_zig",
    .root_module = exe_mod,
});
b.installArtifact(exe);

const hello_mod = b.createModule(.{
    .target = target,
    .optimize = optimize,
});
const hello = b.addLibrary(.{
    .name = "hello",
    .root_module = hello_mod,
    .linkage = .dynamic,
});
hello.addCSourceFiles(.{
    .files = &.{"src/hello.c"},
    .flags = &.{},
});
hello.linkLibC();
b.installArtifact(hello);

exe.linkLibrary(hello);