Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET bindings #73

Closed
wants to merge 21 commits into from
Closed

.NET bindings #73

wants to merge 21 commits into from

Conversation

Herve-M
Copy link

@Herve-M Herve-M commented Sep 21, 2015

This PR take part of solving issue #20.

It keep the way project where organized.

  • New C# assembly project into WinSparkle-2013 and WinSparkle-2015 (WinSparkleDotNet)
  • New WinForm example project into examples-2013 and examples-2015 (example_dotnet.csproj)

And try to correct most of the problems from pull #55.

This don't add the feature as unique DLL for C#. This will come after, if this is validated.
Target WinSparkle API 0.5.X (from master)

@xan2622
Copy link

xan2622 commented Oct 3, 2015

I hope this PR will soon be reviewed.
It is a pre-requisite for any development on the updates system in OpenRA (and winsparkle integration in the openra project)...

@vslavik
Copy link
Owner

vslavik commented Oct 3, 2015

I hope this PR will soon be reviewed.

Please don't add just-bugging comments like that. I just returned from a long trip and am overwhelmed a bit. And this is a big patch to review and has implications I have to deal with (such as manually updating a generated makefile, which is bad but OTOH you had to do it because the tool used doesn't support C#...).

Just two nitpicky notes a glance, before digging into it further (which I'll try to do a.s.a.p.):

  • It would be nice to have logical commits, one-thing-per-commit, with good commit messages (see https://github.com/agis-/git-style-guide)
  • Some bit says it "targets 0.4" — please make sure it matches the current master branch (see what changed).
  • There's something wrong with the projects/solution file — they have AnyCPU arch included, but WinSparkle binding must be x86 and x64 and an assembly using it (the example?) can't by AnyCPU then, can it? The Mixes Platforms bit seems to be a result of a similar project editing accident too.

@Herve-M
Copy link
Author

Herve-M commented Oct 3, 2015

  • Some bit says it "targets 0.4", a part of "Language settings" from winsparkle.h that I can't map easily because C# don't use old Win32 LANGID and also because I don't know which revision of ISO 639 [1-6] code is used in WinSparkle.

The rest is doable :)

@vslavik
Copy link
Owner

vslavik commented Oct 3, 2015

that I can't map easily because C# don't use old Win32 LANGID

Neither does the WinSparkle API. It's immediately obvious to anyone reading winsparkle.h that you have both language code and LANGID options; it's of course OK to only use one in .NET if it maps better to native stuff.

I don't know which revision of ISO 639 [1-6] code is used in WinSparkle.

WinSparkle does the normal and sensible thing that everybody does: using the shortest code that does the job, i.e. -2 or -3. Not sure how that's related to bindings, though, as far as I can tell it should be pretty irrelevant...

@vslavik vslavik mentioned this pull request Oct 8, 2015
@@ -1,9 +1,44 @@
# User-specific files
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems overreaching, I think you must have pulled it from some presets repo. Let’s keep the ignores to what the projects actually produce?

@vslavik
Copy link
Owner

vslavik commented Oct 15, 2015

No more comments from me, sorry for the delay — and thanks!

The API is a bit low-level, basically 1-to-1 mapping of the C one. That’s certainly good as the first step, but a proper native C# API would be better (e.g. things could be done with properties, there’s some inconsistency here between what is and what isn’t “propertized”).

Some way to get metadata from the main assembly would be nice too, because the native-C version can do that (with native metadata). Honestly, I’m not sure how .NET works in this regard, maybe the compiled assemblies have VERSIONINFO metadata that already work with WinSparkle and I’m just babbling...

@vslavik
Copy link
Owner

vslavik commented Dec 4, 2015

Any change of having at least some of the issues fixed? Would be a shame if nothing came out of it after you spent time implementing this PR and I spent time taking it seriously and reviewing it...

@Herve-M
Copy link
Author

Herve-M commented Dec 4, 2015

I'm working on it! Just that University takes a lot of time at the end of the year ..
I'll try to do it before the Christmas holidays.

@vslavik
Copy link
Owner

vslavik commented Dec 6, 2015

Oops, sorry — no problem at all! I was just curious about the status, thanks for letting me know. Good luck with your studies!

@Herve-M
Copy link
Author

Herve-M commented Dec 25, 2015

Big update:

WinSparkleDotNet

WinSparkle.cs : is a 1-to-1 API mapping of the C API, updated to latest version
WinSparkleAttribute.cs : implement a custom assembly attribute for storing App Cast URL
WinSparkleNet.cs : a C# class helper / API, making easier to integrate WinSparkle into .Net app.

example_app has been updated to use as many functionalities as possible.

For the moment, metadata are extracted from the (managed structures of) main assembly.
The "AnyCPU" come from dotnet_example and don't (normally) affect other project.

PS: thanks :)

get { return _version; }
set
{
Debug.Assert(!_initialized, "Can't set Version after initialization !");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialization ! -> initialization!.
Including a space before punctuation is not proper.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My French touch who go out !

@GitCop
Copy link

GitCop commented Jan 28, 2016

Thanks for contributing! Unfortunately there were the following style issues with your Pull Request:

  • Commit: c27d034
    • Your subject line is longer than 50 characters

Please see https://github.com/agis-/git-style-guide (you can use git push -f to update this PR)


This message was auto-generated by https://gitcop.com

);

[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate bool CanShutdownCallback();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that correct? The corresponding C declaration is as follows:

typedef int (__cdecl *win_sparkle_can_shutdown_callback_t)();

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It certainly isn’t, thanks for catching that! WinSparkle C API uses cdecl, wrapping it as stdcall will crash.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same applies to the other delegate declarations.

How about that bool vs. int return type. Will that be marshalled correctly?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will correct that asap and will verify the other declaration.

@FrenchBen
Copy link

While trying to get this to compile I'm getting a "Missing WinSparkle.dll" in the example app.
I tried to add the released "Winsparkle.dll" as a reference to the WinSparkleDotNet solution, but it keeps complaining about it not being a valid assembly or COM component

Any idea how I can get this to run?

@ThomasBarnekow
Copy link
Contributor

You can only add references to .NET assemblies or COM components. Native DLLs like WinSparkle.dll must be added to your project like you would add a content file (e.g., .xml, .html). The File Properties should be configured as follows:

  • Build Action = Content (None also works) and
  • Copy to Output Directory = Copy if newer (Copy always also works).

@Herve-M
Copy link
Author

Herve-M commented May 12, 2016

You must manually copy the native DLL by hand to do the work, for the moment. I will add a post-build-script or make that native DLL is packed into the managed one in a near future.
The solution provided by @ThomasBarnekow is good but it don't take into account the type of build (Release or Debug).

@vslavik
Copy link
Owner

vslavik commented May 12, 2016

I will add a post-build-script or make that native DLL is packed into the managed one.

I was going to say that was IMHO the best approach, System.Data.SQLite used to do it, but apparently they don’t anymore?

@ThomasBarnekow
Copy link
Contributor

@Herve-M My solution does take the build type into account. I'm using that for many different content files, including DLLs. If the output path is bin\Release for your Release configuration and bin\Debug for Debug, for example, then you will find your content files in either of those directories depending on which configuration was active when building. In other words, Visual Studio will put your content files wherever it puts the assembly produced by your project.

This also works in multi-project solutions where project A references project B, for example, and project B defines a DLL as Content / Copy if newer. After building, you will find the DLL in project B's and A's output directories. And if you switch between the Debug and Release configurations, you will find it in both the bin\Debug and bin\Release folders of projects A and B.

@ThomasBarnekow
Copy link
Contributor

@vslavik I'm using the Costura.Fody nuget package (also hosted as a project on GitHub) to embed WinSparkle.dll in a managed assembly.

For example, in my solution I have a C# project into which that nuget package (and a prerequisite package called Fody) is installed. That project contains a folder called costura32 which is supposed to contain native 32-bit DLLs to be embedded into the assembly produced by the project. WinSparkle.dll is contained in that folder and, thus, gets embedded when building the project (or solution).

@vslavik
Copy link
Owner

vslavik commented May 12, 2016

@vslavik I'm using the Costura.Fody nuget package (also hosted as a project on GitHub) to embed WinSparkle.dll in a managed assembly.

To clarify, this (and what I now realize probably is indeed what @Herve-M meant) is not the right approach. I meant using a mixed-mode assembly.

@ThomasBarnekow
Copy link
Contributor

To clarify, this (and what I now realize probably is indded what @Herve-M meant) is not the right approach. I meant using a mixed-mode assembly.

That sounds like an interesting approach as well. After having looked at the page you linked, I found an example showing how you would even mix unmanaged C++, C++/CLI, and C# code in one assembly.

If you think this through, however, what you are effectively saying is that we don't need a C# wrapper for WinSparkle.dll. Using a mixed-mode assembly would mean we should produce a C++/CLI (i.e., managed) wrapper by essentially replicating the C API defined in dll_api.cpp in the method of a C++/CLI class.

@FrenchBen
Copy link

FrenchBen commented May 12, 2016

Thanks for everyone's input, as this is all rather new to me, I may be making mistakes.
Steps I took to build this:

  1. Open WinSparkleDotNet.csproj
  2. Download the release 0.5 of WinSparkle and copied the dll to the above solution src/WinSparkleDotNet/WinSparkle.dll
  3. Build Solution
  4. Close and open examples/example_dotnet.csproj
  5. Remove reference to missing WinSparkleDotNet assembly
  6. Add reference via browsing to the newly built WinSparkleDotNet.dll
  7. Change CPU to match that of WinSparkleDotNet (x64) and hit Start

Pretty much a noob to .NET but trying to understand what I'm doing wrong with the above steps?
(after the build, I see the proper WinSparkle.dll and WinSparkleDotNet.dll in the bin/x64/Release/ dir)

@ThomasBarnekow
Copy link
Contributor

ThomasBarnekow commented May 12, 2016

Could you provide some background on what you are trying to achieve? And what is WinSparkleDotNet.sln (I can't see that solution in this repository)?

Using WinSparkle in your .NET solution can be really easy. For example, if you wanted to use the WinSparkle.dll in your C# project, you would just have to declare the WinSparkle API functions in a static wrapper class or in the class that is using those API functions. For example:

public static class WinSparkle
{
    [DllImport("WinSparkle.dll", EntryPoint = "win_sparkle_init", CallingConvention = CallingConvention.Cdecl)]
    public static extern void Init();

    ...

    [DllImport("WinSparkle.dll", EntryPoint = "win_sparkle_set_appcast_url", CallingConvention = CallingConvention.Cdecl)]
    public static extern void SetAppcastUrl([MarshalAs(UnmanagedType.LPStr)] string url);

    ...
}

Assuming you added WinSparkle.dll as I described before, you could then use those static methods in your code like so:

    WinSparkle.SetAppcastUrl(yourAppcastUrl);
    ...
    WinSparkle.Init();

Just putting WinSparkle.dll somewhere doesn't do anything, unless you put it into the correct output folder. If you manually put it into the output folder, though, you'd have to do that again once you clean the solution, for example.

Instead of writing your own wrapper, you can certainly use an existing wrapper like the one proposed in this PR. I've not worked with it and don't know whether it includes a project that you can just launch to play with WinSparkle.

@FrenchBen
Copy link

FrenchBen commented May 12, 2016

@ThomasBarnekow I'm using this PR as the basis. I edited my question to reflect the proper naming
It includes the WinSparkleDotNet solution (`.csproj) which defines the DLL Import that you mention.

My above attempt is to get this PR up and running using

  • WinSparkle.dll
  • WinSparkleDotNet solution
  • Example_dotNet sample WPF app

-- edit --
I think I see my mistake - I need to simply use the WinSparkle-2015.sln and build that entirely. Not use the separate components

@ThomasBarnekow
Copy link
Contributor

@FrenchBen OK, here is what you should ensure (going bottom-up):

  1. The wrapper project (WinSparkleDotNet.csproj) must contain WinSparkle.dll as a Content file that is copied to the output directory (if newer or always).
  2. The example project (example_dotnet.csproj) must reference the wrapper project, not the assembly.
  3. If (1) and (2) are set up like I said, you will find the WinSparkle.dll in the wrapper AND example projects' output directories. If you did not set up (1) as I said, you will have to add the WinSparkle.dll to the example project as in (1). But regardless of how you do it, at the end of the day you must have WinSparkle.dll, WinSparkleDotNet.dll, and whatever the example project produces in one output folder.
  4. The bitness of the managed assemblies should not be in conflict with WinSparkle.dll. Did you use x64\Release\WinSparkle.dll (64-bit) or Release\WinSparkle.dll (32-bit)?

Having said that, I have not looked at this PR in detail and don't know whether there are any issues that might contribute to your problem.

@vslavik
Copy link
Owner

vslavik commented May 13, 2016

I have not looked at this PR

Moderation note: Please let’s limit the discussion under this PR to the subject of review of this PR only.

@Herve-M: Let me know when you think it’s merge-ready (since you’re still making changes, I assume it’s not) or when there’s something you want me to look at — GitHub doesn’t send email notifications for pushed changes, only for added comments, so I didn’t even realize there were new commits until the recent commentstorm. (Organizing (and describing) the commits as @GitCop says in the linked guide would be nice w.r.t. ease of reviewing, but not necessary — this is a very important PR, I’m OK with squashing the branch myself.)

[DllImport("WinSparkle.dll", EntryPoint = "win_sparkle_set_appcast_url", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetAppcastUrl(
[param: MarshalAs(UnmanagedType.AnsiBStr)] string url
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you marshal as UnmanagedType.AnsiBStr if the WinSparkle API is declared as follows?

WIN_SPARKLE_API void __cdecl win_sparkle_set_appcast_url(const char *url)

Using UnmanagedType.LPStr would be a better fit. An UnmanagedType.AnsiBStr is a COM-style BSTR with a prefixed length and ANSI characters.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add that Thomas pointed this already previously in another instance — when such an issue is found, you need to go through all such bugs, not just the one commented on.

@vslavik vslavik changed the title Net Wrapper + Example .NET bindings Jul 30, 2016
@ThomasBarnekow
Copy link
Contributor

@vslavik, @Herve-M, has this PR been abandoned?

I am asking because I could contribute a solution or project that provides a managed wrapper for the native WinSparkle.dll that:

  • can be built for "Any CPU";
  • determines at runtime whether it is running within a 32 or 64-bit process; and
  • dynamically references the corresponding 32 or 64-bit WinSparkle.dll.

I needed that for Microsoft Office add-ins that would normally be used by 32-bit Office but sometimes also with 64-bit Office. And I wanted to avoid building platform-specific assemblies.

The solution uses two classes, one called WinSparkle and another one called WinSparkleWrapper. The first one is meant to be used by managed callers. The second one is meant to describe the native DLL.

Pointing to the 32-bit native DLL merely as an example, WinSparkleWrapper looks like this:

    internal static class WinSparkleWrapper
    {
        [...]

        [DllImport("libs\\x86\\WinSparkle.dll",
            EntryPoint = "win_sparkle_set_appcast_url",
            CallingConvention = CallingConvention.Cdecl,
            CharSet = CharSet.Ansi)]
        public static extern void SetAppcastUrl([MarshalAs(UnmanagedType.LPStr)] string url);

        [DllImport("libs\\x86\\WinSparkle.dll",
            EntryPoint = "win_sparkle_set_app_details",
            CallingConvention = CallingConvention.Cdecl,
            CharSet = CharSet.Unicode)]
        public static extern void SetAppDetails(
            [MarshalAs(UnmanagedType.LPWStr)] string companyName,
            [MarshalAs(UnmanagedType.LPWStr)] string appName,
            [MarshalAs(UnmanagedType.LPWStr)] string appVersion);

        [...]
    }

The WinSparkle class uses delegates that are dynamically built based on the above description of the native methods. For example:

    public static class WinSparkle
    {
        public delegate void SetAppcastUrlDelegate(string url);

        public static readonly SetAppcastUrlDelegate SetAppcastUrl;

        static WinSparkle()
        {
            [...]

            SetAppcastUrl = CreateDelegate(typeof(SetAppcastUrlDelegate),
                nameof(WinSparkleWrapper.SetAppcastUrl)) as SetAppcastUrlDelegate;

            [...]
        }
    }

Managed code would simply use the WinSparkle class (not the WinSparkleWrapper) as follows:

    public static class Program
    {
        public static void Main(string[] args)
        {
            [...]

            WinSparkle.SetAppcastUrl("https://your.domain.com/path/to/appcast.xml");

            [...]
        }
    }

The question is whether you would be interested in this contribution and, if so, how it should be integrated. The issue starts with my using of Visual Studio 2017, with which I cannot even build WinSparkle right now (tried the solution for VS2015), and my C/C++ skills are a bit rusty (last significant development in C/C++ in 1994).

@vslavik
Copy link
Owner

vslavik commented Jun 30, 2017

@ThomasBarnekow Please make a separate issue or PR if you wish to discuss your code unrelated to this PR (except for the general subject) — this is not the place.

has this PR been abandoned?

It seems pretty clear that @Herve-M has no interest in it anymore, yes.

@Herve-M
Copy link
Author

Herve-M commented Jun 30, 2017

@ThomasBarnekow This PR can be considered as abandoned, I will close it.

@Herve-M Herve-M closed this Jun 30, 2017
@ThomasBarnekow
Copy link
Contributor

@vslavik, I have seen that there is at least one NuGet package (see WinSparkle.Net) and corresponding GitHub project (see Sparkle.Net) that already provides a solution like mine (different implementation but same outcome). Thus, I think it doesn't make sense to spend time on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants