Quick Sign In:  

Forum: VirtualDJ Plugins

Topic: Want to use .Net in your plug-ins? Read this!

This topic is old and might contain outdated or incorrect information.

I’ve noticed that there is an interest in building plug-ins with .Net, and I myself am a big .Net fan. I’ve developed a method for using .Net code in Virtual DJ plug-ins. Whether it’s worth the trouble or not is up to you, and it’s probably not suitable for most plug-in projects. I’m afraid that those of you wishing to build effects plug-ins in .Net might still be out of luck due to the overhead introduced with the method I’m about to show. BUT I haven’t tried it since my focus with the VirtualDJ “SDK” is not audio processing, but interapplication communication. If you try it to perform some audio processing and it is successful, let me know.
Basically what my code does is call methods in a .Net assembly through a COM-callable wrapper. It allows you to code a DLL in C#, VB.Net, Managed C++, etc. and treat it as a COM object in a plug-in. There’s probably a way to translate the whole damned VirtualDJ plug-in framework into an assembly with a CCW, but this would probably take a hell of a lot of manual translation of data types on the interface to trick VisualJockey into seeing the assembly as a COM object. Not something I care to touch with a ten foot pole.
So here we go. Start a VirtualDJ C++ plug-in project, save it, and just set it aside or close it. You won’t need it for a while. Now start a new DLL (Class Library) project in VS.Net in C# or whatever. Define an interface detailing what methods you want exposed to the outside world in your class library. Design an interface that will do the processing and remember to only use primitive data types unless you want a whole world of hell to play with. That means ints, doubles, chars, strings, etc. having to deal with fu**ing _bstr_ts for strings in your C++ code is bad enough. You’ll see what I mean later.
Here’s my interface:
using System;
using System.Runtime.InteropServices;

namespace plugExtension
{
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface _Messenger
{
void showMessage(string msg);
void updateValue(int val);
void showWindow(bool show);

}
}

Make sure you add the [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] line, or this will not be a COM interface.

Then go ahead and write a class that implements the interfaces. All methods that you want exposed through COM must be public, but you can add any other private methods you please for internal procedures. I’ve coded a class that has a simple window with a progress bar that reflects the value of a slider in VirtualDJ:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace plugExtension
{
[ClassInterface(ClassInterfaceType.None)]
public class Messenger:_Messenger
{
private Form1 window;
public Messenger()
{
window = new Form1();
}

public void showMessage(string msg)
{
MessageBox.Show(msg);
}

public void updateValue(int val)
{
window.updateProgress(val);
}

public void showWindow( bool yes)
{
if(yes)
{
window.Show();
}
else
{
window.Hide();
}
}

}
}

It is important that you include the using System.Runtime.InteropServices; line and [ClassInterface(ClassInterfaceType.None)]
MUST be on a line right before the class definition. Also notice that this class implements(“public class Messenger:_Messenger”) the interface definition, “_Messenger.” This is what makes the code callable.
So that’s pretty straight forward, but now is when we start getting dirty. You will need to add your assembly to the global assembly cache and register it for COM interop. It’s not as bad as it sounds, really. I’ve coded a little drag and drop app that takes some of the work out of it for you. The first step is to generate a strong name for your assembly. Start off by building your .Net project so you have a DLL file. To simplify things, you are going to need to add two entries to your PATH system variable. Get to your System Properties and on the Advanced tab (In XP) there’s an “Environment Variables” button. Click it and find the PATH variable under the “System variables” group. Edit it and add a path to your most recent framework directory and the Bin folder in the .Net SDK. Separate each entry with a semicolon. The first path will probably be something like C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\; and the second C:\Program files\Visual Studio.Net\SDK\Bin. You might have to do some hunting, so just be intelligent about it. The goal here is to have sn.exe, gacutil.exe, and regasm.exe in your path.
Now open a command prompt window and run “sn.exe –k keyfile.snk” to generate a strong name key file. It might take a few seconds. Move this key file to someplace safe within your project’s folders. Open your project’s AssemblyInfo.cs file and edit the AssemblyKeyFile line at the bottom to reflect the location of your key file. I put mine in the project’s root source folder, so it looks like this:
[assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
The path is relative to the output, so you might have to fiddle with the dotdots to get it to find the file. Build the DLL now and it will be equipped with the key. Don’t delete that key file, though. You might have to rebuild later.
Next, you’ll need to register the COM interface and add the assembly to the Global Assembly Cache. Use my AutoRegister tool to do this. Just drag and drop the DLL you just built onto the AutoRegister window. If you got no errors, everything’s kosher. You can go to C:\Windows\Assembly and look for the name of the assembly. It should be there. In addition, a .tlb file was created in the same folder that AutoRegister.exe is in. Take that .tlb file (It’s a type library) and move it to your VirtualDJ plug-in project’s root code folder.
The worst is over, now. Switch over to your VirtualDJ plug-in C++ project. At the top of the main source file, add the following:
#import "yourDLLname.tlb"
using namespace yourAssemblyName;
The #import value should be the name of the type library file you moved. The namespace will need to be the same thing that is in the Assembly Name value in the project properties dialog for the .Net DLL project. If you changed the assembly name and it has spaces in it, replace the spaces with underscores. For example, if the assembly name is “My Big DLL” then the namespace is “My_Big_DLL” in the COM.
In my code I have:
#import "plugExtension.tlb"
using namespace plugExtension;

In the class definition, define a pointer variable to the interface you defined in the COM, like so:
plugExtension::_Messenger* messenger;

Make sure you create a pointer to the INTERFACE and not the class you created. For example, the interface I created was called _Messenger while the class is just Messenger. Now in the OnProvidedInterfaceInit method, add code to instantiate the COM object similar to this:
try
{
plugExtension::_MessengerPtr pointer(__uuidof(plugExtension::Messenger));
messenger = pointer;

}
catch(_com_error e)
{
DisplayText((TCHAR*)e.ErrorMessage());
}

I did not define the plugExtension::_MessengerPtr. That was automatically generated by the CCW routine we did. Yours will look something like YourAssemblyName::YourInterfaceNamePtr. The second part must be YourAssemblyName::YourClassName. First one is the interfacePtr, the second one is the class. Be casreful with these things because it can be easy to switch them around and your code won’t work then. The try catch will help with debugging if something goes wrong. Now you are free to use the COM object’s methods however you want. Just call the methods you defined in the COM interface. Pay attention to the Intellisense cues, though. Some data types do not translate so cleanly. Remember the _bstr_t comment? Well, if any of your method parameters were strings, they have just been converted to _bstr_ts! Are we having fun yet?
So now when you build your plug-in and put it into the VirtualDJ effects folder, you should be able to load it and the methods from the COM object should be functional. If it crashes VirtualDJ, you probably switched an interface/class/interface pointer argument around somewhere. If you use my code (with the try-catch as above), the plug-in interface text below the sliders mich change to “Class not registered.” Try deleting any instances of your assembly in the GAC (C:\windows\assembly) and then re-register your .Net DLL and make sure you move the .tlb to the VirtualDJ and then do a Clean and complete rebuild. If you make changes to your .Net assembly, you will probably have to re-register and rebuild.
Check out my demo projects for source. It’s really not as bad as it sounds. Just register the .Net assembly using the AutoRegister util I provided, and then move the type library (.tlb) to the C++ project’s source folder, and do a build. Copy the .dll from “Release” into your Effects folder and load “testplug.” You can move the left slider up and down and click button 1 to pop a .Net MessageBox up with the slider’s value. Click button 2 to show or hide a .Net form with a progress bar. You can use the second slider and see the progress bar’s value be changed as the slider is moved. I hope this helps some of you that want to do your coding in .Net. Hopefully there will be a way to do it natively in a future version!
Good luck!
DJ Skwerl

***I will post a link to my AutoRegister utility and full source/project files when I find a place to put them this weekend.***
 

Posted Fri 24 Sep 04 @ 9:58 pm
Ooh. Shoot, that didn't retain the tabs! Sorry the source looks so nasty. I'll try to get those project files up ASAP.

DJ Skwerl
 

Posted Fri 24 Sep 04 @ 10:00 pm
 

Posted Sun 26 Sep 04 @ 9:04 pm
kaleoPRO InfinityMember since 2003
Thank you for this :)
 

Posted Mon 27 Sep 04 @ 7:32 am
This looks very interesting, but how is the performance compared to native C++?

macourteau
 

Posted Mon 27 Sep 04 @ 10:52 am
Actually, I have no idea. I'm relatively confident that there would be a major performance hit. On a 3.4 gHz computer, it might not be so bad if nothing else is being used but VirtualDJ. I haven't tried it, though. This method works great for my needs since I do not do any type of audio processing. The other downside is distribution of a plug-in constructed this way, since you can't just drag and drop the DLLs into the effects folder. The good news is that although registering the COM wrapper is a pain in the ass to do manually, but can be done relatively simply with a basic Visual Studio installer project. If anyone tries doing some processing with this, take a few minutes to experiment and please post the results. :)

DJ Skwerl
 

Posted Mon 27 Sep 04 @ 3:51 pm
On second thought, although I have no specific info on audio processing with this, I guess I can give some general tips on reducing the overhead for COM interop. The most important thing is to minimize function calls to the COM object. In general, it takes about 10 instructions to call a native platform (C++) function, while a COM interop method will take about 50. Try to do a much work as possible inside the object, and work with large chunks of data. The fewer .Net functions called directly from C++ code, the better. For a bit more performance, code top-down style and with less functional decomposition within the object instead of structured OO. Also, although it will be kind of shitty to maintain and debug, you can make your arguments of type IntPtr and/or do some of the marshalling manually using InAttribute and OutAttribute. MSDN has some articles on how to do this. Again, if you keep your function calls to a minimum, you won't have to worry about messing with this much. Good luck!

DJ Skwerl
 

Posted Mon 27 Sep 04 @ 4:20 pm
djcelPRO InfinityModeratorMember since 2004
Otherwise if you want to use managed C++ ( = .NET in C++) in your plug-ins with the current SDK, i have something easier :-)
Add: #using < mscorlib.dll> at the beginning of the .cpp file after #include "vdjdsp2.h"
 

Posted Wed 14 Sep 05 @ 12:56 am
crookoHome userMember since 2011
can u provide source code link plz?
 

Posted Tue 19 Jul 11 @ 1:13 am
This thread is from 2005.............

Huey
 

Posted Tue 19 Jul 11 @ 1:24 am


(Old topics and forums are automatically closed)