I just reported this bug on Connect :
.NET 4.0 supports covariance and contravariance in delegates and interfaces. Unfortunately, C++/CLI does not support this as of VC++ 2010 RTM. Considering that the most popular use for C++/CLI today is as an interop language, the compiler not supporting a feature that’s going to be heavily used in the BCL and in new C# libraries is a very serious problem. It will impose limits on how effective C++/CLI will be as an interop-language.
If you think this is a serious enough problem, please vote the bug up so it gets more attention. Thank you.
Tags: C++/CLI · VC++ 2010
The lack of intellisense has been the biggest show-stopper for using VC++ 2010 for any managed or mixed mode programming. Many people could not make the upgrade from VC++ 2008 for this very reason. Fortunately, the latest version of Visual Assist X from Whole Tomato Software provides fully functional intellisense for C++/CLI code! VA has always been a truly impressive add-in for VC++ users for a decade or so now, and with this update they make it obvious why they are still one of the most popular and most useful add-ins for Visual C++.
For anyone doing C++/CLI in VS 2010 and totally frustrated with the Notepad like editing experience, or for anyone holding off on upgrading to VS 2010, I strongly recommend Visual Assist X. I’ve been using it for a couple of weeks now and I have not noticed any performance issues with VS 2010 although I am on a very fast machine with heaps of memory. That said, there are several other improvements and refactoring features that Visual Assist X provides but I was primarily evaluating it for the C++/CLI intellisense.
Tags: C++/CLI · VC++ 2010
A month or two back, I had to write a launcher app that would allow/disallow the running of select installers based on various conditions and config files. Since this would run prior to any of the MSIs, and thus couldn’t assume the presence of any dependencies like .NET or the VC++ runtime, I wrote it as a statically linked MFC dialog app using VC++ 2008. All was well with initial testing. But during a final testing phase, the launcher failed to run on a fresh Windows 2000 box with an error about a missing gdiplus.dll.
Windows XP and later OSes like Vista and Windows 7 have GDI+ by default though you may need to apply patches for the latest updates on XP. Windows 2000 predates GDI+ and thus does not have GDI+ by default though a redistributable is available as a separate download. So that’s why it failed. But why is there a dependency at all? I had always assumed that a statically linked MFC app would run anywhere. The it struck me – the BCG stuff that they added to MFC starting VC++ 2008! That code used GDI+ and thus had a GDI+ dependency.
Obviously I couldn’t include the GDI+ redistributable for the launcher app and I didn’t want to remotely take the risk of manually copying along the GDI+ DLL (or any extra files it needs) what with the various updates out there. Eventually, I rewrote the app as a plain Win32 API dialog app (the app was kinda simple, so it didn’t take me too long). I know not many people support Windows 2000 today, but some of our clients are still on that OS, and so it was important for us to support it. Anyway, this is just something to keep in mind when using MFC to write a stub executable that should run anywhere.
Tags: C++ · VC++ Orcas · Win32/MFC
This is based on a recent thread on the MSDN forums. Someone had code that looked like this:
public class CustomObject<T>
{
Object _obj;
public Object Value
{
get
{
return _obj;
}
}
public static explicit operator CustomObject<T>(T obj)
{
return new CustomObject<T>() { _obj = obj };
}
}
class App
{
static void Main()
{
var tmp1 = (CustomObject<Object>)true; // from bool
var tmp2 = (CustomObject<Object>)12; // from int
// The following code throws a System.InvalidCastException
// Unable to cast object of type 'System.Object' to
// type 'CustomObject`1[System.Object]'
var tmp3 = (CustomObject<Object>)new Object(); // from object
}
}
The code compiles fine but the third conversion results in an InvalidCastException. And he was quite puzzled by it.
The answer is simple if you think about it, and the exception that’s thrown is a dead giveaway. When you have an object there, the compiler sees that as a downcast (casting from base to a more derived type). The reason is that CusObject<T> is derived from Object (implicitly) and so when the type being converted is Object, it will not call the explicit (or implicit) conversion operator, instead it will generate a castclass IL instruction which will obviously fail.
To make that more clear, forget the “generics” and take this example:
class Base
{
}
class Derived : Base
{
public static explicit operator Derived(Base b)
{
return new Derived();
}
}
That will not compile and you’ll see this:
Error 1 ‘Derived.explicit operator Derived(Base)’: user-defined conversions to or from a base class are not allowed
Of course when you use a generic parameter, the compiler cannot anticipate that you’d try doing this, and so it will let you declare the operator. And when you later have an ambiguous situation where you cast from base to derived, it will simply ignore the explicit operator and instead do a downcast.
In the MSDN thread, Louis.fr pointed out the appropriate section in the language spec that talks about this. For those interested it’s 10.10.3. Quoting below:
However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions.
In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored.
Tags: C#/.NET
There was a recent thread in the MSDN forums where someone was concerned about the behavior of Type.GetElementType. His example was similar to the following:
char [][,][] arr = new char[2][,][];
Console.WriteLine(arr.GetType().GetElementType());
The output he got was:
System.Char[][,]
This puzzled him no end because the element type is clearly char[,][]. He was wondering if this was a bug in GetElementType or even if it was a bug in the C# compiler (incorrectly parsing the array declaration).
Well there is no mystery here. Array types (or rather the runtime type for an array) have a ToString implementation that displays the array using a display format closely resembling the IL syntax for arrays, and not the C# syntax. This is very easily verified with the following code:
Console.WriteLine(typeof(char[][,][,,]));
The output here is:
System.Char[,,][,][]
This is a good idea since the C# array syntax is not universal among CLI languages anyway. Both VB.NET and C++/CLI use array syntax that’s different from this.
Tags: C#/.NET · CLR/.NET BCL
Pon Sayasith emailed me a couple of weeks ago saying he’d bought my C++/CLI book and then went on to ask me what I thought of a mixed mode static library that has a managed class and a native wrapper, that is then consumed by a native C++ app. My first reaction (which turned out to be wrong) was that this would not be possible. But I was basically over-thinking there.
My initial thought was that when you have a C++ project with say 1 cpp file compiled as /clr, you are still forced to change project settings to match those that support the /clr switch. But when you link with a lib with a /clr compiled cpp file, your native project settings are not changed. I wasn’t sure if that was alright to do and whether the compiler was unaware of the fact that the lib needs managed support.
But I did a quick test and apparently, once the compiler sees that a linker lib is mixed-mode, then it knows how to handle that and the main exe is itself generated as a .NET executable (with mscoree.dll called and all that). This actually has an advantage in that you don’t need to tweak project settings to support /clr compilation.
So if you want to avoid tweaking your main project’s settings just to enable /clr compilation on a couple of cpp files, then this approach is an excellent workaround for you. The static lib project will need to have its project settings tweaked for /clr compilation (even for selective cpp files), but your main project settings remain unchanged, and just as you want it.
Tags: C++/CLI
In an earlier FAQ entry, I talked about how you can show a previously closed Windows Forms form. Well the same holds true for a WPF window and for the same underlying reason. When you close a WPF window, the native window is closed and destroyed. So trying to re-show it will just throw an exception. The solution is to either hide/show windows as required, or to instantiate and show a new window whenever that’s needed.
Tags: FAQ · WPF (Avalon)
This question or a similar variation pops up in the forums once in a while. The core problem is that while null terminated char arrays can be marshaled to a System.String fairly easily, with other unmanaged arrays the marshaller cannot know for sure what size of array to create. The solution is to marshal the argument as an IntPtr and then in the calling code, the user can manually create a managed array and copy the data into it. This assumes that the unmanaged API has a size parameter (which it invariably does) that indicates the length of the unmanaged array. Here’s some example code that shows how this is done:
C++ code
typedef long (CALLBACK * READDATA)(unsigned char * data, int length);
extern "C" __declspec(dllexport) void __stdcall SomeFunc(READDATA rd)
{
unsigned char data[5] = {'a', 'b', 'c', 'd', 'e'};
rd(data, 5);
}
And here’s the calling code:
C# code
delegate int ReadDataDelegate(IntPtr data, int len);
[DllImport("SimpleLib.dll")]
static extern void SomeFunc(ReadDataDelegate d);
private static void CallNativeWithCallback()
{
ReadDataDelegate newCB = new ReadDataDelegate((data, len) =>
{
byte[] array = new byte[len];
Marshal.Copy(data, array, 0, len);
foreach (byte b in array)
{
Console.WriteLine((char)b);
}
Console.WriteLine();
return 0;
});
SomeFunc(newCB);
}
Tags: C#/.NET · C++/CLI · FAQ
This one comes up in the forums at least once a week. The scenario is where someone shows a form, closes it while retaining the handle, and then tries to show it again by calling Show or ShowDialog on the closed form. This throws an exception that says the object has been disposed.
Here’s my standard answer. You cannot re-show a form once it’s closed because the underlying window is destroyed. You can hide the form and then re-show it as required thereby reusing the same form instance.
Alternatively, for modal dialogs, instantiate a local form variable and call ShowDialog on that. So each time it’s a new form. And this way you don’t need to keep the form in memory all the time, specially if it’s an options dialog that’s rarely brought up.
Tags: FAQ · Windows Forms
This article discusses an attached behavior that lets you handle the View Window’s Closed and Closing events via commands in the View-Model. It was inspired by Reed Copsey, Jr.’s Blend behavior which is up on the Expression Code Gallery. Reed’s behavior uses a neat technique that lets the View-Model handle the Closing/Closed events of the View in an MVVM friendly manner. Since his code was tied to the Expression DLLs I thought it would be a good idea to write a plain WPF version. While similar in concept to the Blend behavior, I’ve slightly deviated from how the concept is implemented and also in how it’s used. So this is not a direct 1-to-1 replacement though you should be able to get things working pretty much the same without too much effort.
Article Link
Tags: C#/.NET · WPF (Avalon)
The Exif Compare Utility is a WinDiff equivalent for image files that compares the Exif meta-data and displays the differences and similarities. The application is written using WPF and MVVM, and also makes use of my ExifReader library. In the article I briefly explain how to use the application and also discuss some of the interesting code implementation details.
Article Link:
Tags: C#/.NET · WPF (Avalon)
A few days back, I had blogged about an attached property I wrote to force instant binding in a WPF TextBox. Well it turns out I was having a “duh” moment there and this was pointed out to me very politely by Richard Deeming (a fellow CodeProject author). Turns out all I needed to do was to set the UpdateSourceTrigger on the Binding to PropertyChanged. In fact, this is the very example that’s on the MSDN documentation for UpdateSourceTrigger. Oh well, you live, you learn.
<TextBox Height="23"
Text="{Binding EnteredName, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Margin="144,64,0,0" Name="textBoxName"
VerticalAlignment="Top" Width="152" />
I have updated the old blog entry and pointed it to this entry.
Tags: WPF (Avalon)
The majority of MFC apps have always had an About… menu entry in the main window’s system menu, and this was primarily because the App Wizard generated code for that by default. I wanted to do something similar in a WPF application I’ve been working on, and I wanted to do it in an MVVM friendly manner. In this recent article published on The Code Project, I explain a neat way of doing it so that you can easily add menu items and attach command handlers to them while retaining the basic MVVM paradigm. The code supports command parameters as well as an UI enabling/disabling mechanism. The basic idea is to make it so that it should be very easy to add system menu-items and then bind commands to them without having to make major changes to code.
I was rather cynical about all the buzz around MVVM, but now that I’ve begun using it I can see why so many people are fascinated by the concept.
Tags: C#/.NET · WPF (Avalon)
Update – Apr 9, 2010
You do not need to do all this – instead you just need to set UpdateSourceTrigger on the Binding to PropertyChanged. I’ve made an updated blog entry on this here : Correction for the instant binding attached behavior for WPF TextBoxes
Original entry follows for posterity
The default behavior for a TextBox is to invoke data binding when the TextBox loses focus. Normally this is fine since people do have to hit an OK button somewhere, but there are times when you’d prefer that databinding occurs as you type into the TextBox. I wrote an attached property for the TextBox which invokes data binding as you type into it, and not when the text box goes out of focus. Here’s how you would use it.
<TextBox Height="23"
nsmvvm:TextInstantBindingBehavior.EnableInstantBinding="True"
Text="{Binding EnteredName}" HorizontalAlignment="Left"
Margin="144,64,0,0" Name="textBoxName"
VerticalAlignment="Top" Width="152" />
Here’s the code for the attached property.
public class TextInstantBindingBehavior
{
public static DependencyProperty EnableInstantBindingProperty =
DependencyProperty.RegisterAttached(
"EnableInstantBinding",
typeof(bool), typeof(TextInstantBindingBehavior),
new FrameworkPropertyMetadata(
new PropertyChangedCallback(EnableInstantBindingChanged)));
public static void SetEnableInstantBinding(TextBox target, bool value)
{
target.SetValue(
TextInstantBindingBehavior.EnableInstantBindingProperty, value);
}
public static bool GetEnableInstantBinding(TextBox target)
{
return (bool)target.GetValue(EnableInstantBindingProperty);
}
private static void EnableInstantBindingChanged(
DependencyObject target, DependencyPropertyChangedEventArgs e)
{
TextBox element = target as TextBox;
if (element != null)
{
if (e.NewValue != null && (bool)e.NewValue)
{
element.TextChanged += Element_TextChanged;
}
else
{
element.TextChanged -= Element_TextChanged;
}
}
}
static void Element_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
if (GetEnableInstantBinding(textBox))
{
BindingExpression bindingExpression =
textBox.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
}
}
}
Tags: C#/.NET · WPF (Avalon)
I needed an Exif reader class for a C# application I was working on, and though I found quite a few implementations available including a few on The Code Project, none of them fully suited my requirements. So I wrote my own class. The article describes the use and implementation of this class, and also includes a couple of demo projects, one using Windows Forms and the PropertyGrid, and another using WPF and the growingly popular MVVM architecture. The ExifReader project is 100% Style Cop compliant except for the IDE generated AssemblyInfo.cs and the PropertyTagId.cs file which I custom created from multiple sources including some GDI+ header files, as well as trial and error. I didn’t think it would be a great idea to try and apply Style Cop guidelines to either of those files. The class does not directly access Exif metadata from image files, and instead delegates that functionality to the Image class from System.Drawing. The public interface does not expose any System.Drawing types and so this library can be used from WPF without needing to reference System.Drawing.
Article link
This class tries to cover as many of the documented and undocumented Exif tags as I could test on, but if any of you encounter images that have Exif tags that are not recognized by my class, I request you to kindly contact me and send me the images. Once I have the images, I can attempt to support those tags too. Right now, if it encounters an undocumented tag it will still extract the tag value, but you won’t see any descriptive tag-name or any custom parsing/formatting that may be required.
All the code, including the demos, have been written and tested on VS 2010 RC and .NET 4.0. While I have not intentionally used any .NET 4.0/C# 4.0 only feature like say the dynamic keyword, I may have inadvertently written code that may not compile in .NET 3.5. But I don’t expect any of those to be hard to fix or change for most programmers, but if you run into trouble and can’t figure out what to do, please ping me via the article forum and I’ll help fix it. I suspect the most common compatibility issues would be with regard to IEnumerable<T> cast requirements in .NET 3.5, since it became a variant interface only in .NET 4.0.
Tags: C#/.NET · WPF (Avalon)
March 22nd, 2010 · 1 Comment
Recently I was working on an MVVM demo for an article where I wanted live filtering based on the contents of a text box. The problem with this is that the TextBox does not have a Command property, and thus I’d need to use code-behind to proxy its text changed event to the ViewModel. And I did not want to do that. This was purely whimsical behavior on my part since MVVM does not mandate this at all, though a lot of people do recommend it. Apparently, you can get around this by referencing an Expression Blend DLL, which has support for interaction triggers that you can forward to a command object, thereby avoiding any code-behind. I didn’t want to reference an Expression Blend DLL, not for a simple demo app anyway and so I was forced to work around it by adding an attached behavior to the TextBox that could take a command object. This was a bit of an over-kill of course, and in a real world app I’d simply do it in code-behind. Most likely something like :
ViewModel viewModel = this.DataContext as ViewModel;
. . .
private void TextChanged(. . .)
{
viewModel.SomeCommand(. . .) ;
}
That’d be in my view’s code-behind, and while some purists may not be too happy, I think it’s definitely simpler than referencing Expression Blend! Here’s what a typical Xaml code snippet using this behavior would be like:
<TextBox x:Name="searchTextBox" Width="165"
HorizontalAlignment="Left" Margin="3,0,0,0"
Text="{Binding SearchText}"
local:TextChangedBehavior.TextChanged="{Binding FilterCommand}" />
Instead of handling the TextChanged event, I handle it via the attached behavior and route it to the FilterCommand command object in the ViewModel. Here’s the code for the attached behavior:
internal class TextChangedBehavior
{
public static DependencyProperty TextChangedCommandProperty
= DependencyProperty.RegisterAttached(
"TextChanged",
typeof(ICommand),
typeof(TextChangedBehavior),
new FrameworkPropertyMetadata(
null,
new PropertyChangedCallback(
TextChangedBehavior.TextChangedChanged)));
public static void SetTextChanged(TextBox target, ICommand value)
{
target.SetValue(TextChangedBehavior.TextChangedCommandProperty,
value);
}
public static ICommand GetTextChanged(TextBox target)
{
return (ICommand)target.GetValue(TextChangedCommandProperty);
}
private static void TextChangedChanged(
DependencyObject target, DependencyPropertyChangedEventArgs e)
{
TextBox element = target as TextBox;
if (element != null)
{
if (e.NewValue != null)
{
element.TextChanged += Element_TextChanged;
}
else
{
element.TextChanged -= Element_TextChanged;
}
}
}
static void Element_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
BindingExpression bindingExpression = textBox.GetBindingExpression(
TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
ICommand command = GetTextChanged(textBox);
if (command.CanExecute(null))
{
command.Execute(null);
}
}
}
Nothing complicated there, just a basic attached behavior implementation.
Tags: C#/.NET · WPF (Avalon)
This one’s an obvious error really – but it’s still undocumented, and thus qualifies for a blog entry. I guess once __arglist was dropped from the standard documentation, all its associated error messages were dropped too.
Compiler Error CS0226
Error Message
An __arglist expression may only appear inside of a call or new expression
Example
The following sample generates CS0226
// error CS0226
__arglist("apples", "pears");
Expected usage
static void Foo(Object obj, __arglist)
{
Console.WriteLine("First arg : {0}", obj);
ArgIterator iterator = new ArgIterator(__arglist);
for (int i = iterator.GetRemainingCount(); i > 0; i--)
{
Console.WriteLine(TypedReference.ToObject(iterator.GetNextArg()));
}
}
. . .
Foo(x, __arglist("apples", "pears"));
Tags: C#/.NET · VC++ 2010
February 27th, 2010 · 1 Comment
There was a very interesting thread on Code Project’s C++/CLI forum last week. Here’s the link to the thread :
The gist of the post is that the C++/CLI compiler was not recognizing an overloaded unary operator defined in an interface while it could do so when the interface was changed to a base class.
Here’s some example code that reproduces the problem :
interface class Base
{
public:
static int operator * (Base^) { return 99; }
static int operator + (Base^) { return 99; }
};
ref class Derived : Base
{
};
static void Foo()
{
Derived^ d = gcnew Derived();
int x = *d;
int y = +d;
Both those lines will not compile. In the case of the * operator you get a confusing error because the compiler assumes you are trying to dereference the handle.
Error 1 error C2440: 'initializing' : cannot convert from
'Test::Derived' to 'int'
Error 2 error C2675: unary '+' : 'Test::Derived ^' does not define this
operator or a conversion to a type acceptable to the predefined
operator
Changing Base from an interface class to a ref class works fine. I believe this is a compiler bug because the ECMA language specification says this in section 25.2 Interface members:
“An interface definition can declare zero or more members. The members of an interface shall be static data members, instance or static functions, a static constructor, instance or static properties, instance or static events, operator functions, or nested types of any kind. An interface shall not contain instance data members, instance constructors, or a finalizer.”
The workaround right now is to do this :
int x = Base::operator *(d);
int y = Base::operator +(d);
Affected versions:
This bug exists in VS 2008 as well as in VS 2010 RC.
Tags: C++/CLI · VC++ 2010
A fellow CPian leppie had posted about an undocumented C# error he got (error CS0224) and asked if anyone could reproduce the error (back in Dec 2009). For fun I played with the compiler and managed to reproduce the error. That gave me the idea of blogging about this and other such undocumented errors. Of course the moment I blog about it, it ceases to become undocumented by definition. Also by undocumented, I mean undocumented on MSDN. It may have been blogged on or written about elsewhere, though I do intend to use Google and Bing to try and make sure it’s not a commonly known error. I start off with leppie’s error of CS0224.
As of today (Feb 26, 2010) VS 2008′s MSDN documentation jumps from CS0221 to CS0225, and VS 2010′s MSDN documentation jumps from CS0201 to CS0229. I don’t know why VS 2010 decided to drop those extra error codes, but then this is the RC documentation, and maybe the RTM version will add back those that were documented in VS2008. Anyway enough with the talk and on to the error.
Compiler Error CS0224
Error Message
A method with vararg cannot be generic, be in a generic type, or have a parameter array
Example
The following sample generates CS0224
// error CS0224
static void Foo<T>(T t, __arglist) { }
Correct way
You could rewrite the method in one of the following two ways to get the equivalent functionality :
static void Foo<T>(T t, params object[] args) { }
or
static void Foo(Object obj, __arglist) { }
Tags: C#/.NET
In my last blog entry I had shown a WWS native client connecting to a WCF service. In this one I’ll talk about how the WCF service can be converted into an equivalent WWS service. Connecting clients (whether WWS, WCF, or other) would continue to behave the same. I am going to use the same c/h files that wsutil generated from the wsdl file. There is a function signature generated for us to match the service contract methods. In our case there’s just one – WSHttpBinding_IStringService_ReverseCallback. So the first thing is to add a method that matches this signature, and this will reverse a string just like the WCF service (except we write it in C or C++).
HRESULT CALLBACK Reverse(
__in const WS_OPERATION_CONTEXT* context,
__in WCHAR* s,
__out WCHAR** reverse,
__in_opt const WS_ASYNC_CONTEXT* asyncContext,
__in_opt WS_ERROR* error)
{
WS_HEAP* heap = NULL;
HRESULT hr = WsGetOperationContextProperty(
context,
WS_OPERATION_CONTEXT_PROPERTY_HEAP,
&heap,
sizeof(heap),
error);
if (FAILED(hr))
{
return hr;
}
hr = WsAlloc(
heap,
sizeof(WCHAR) * (wcslen(s) + 1),
(void**)reverse,
error);
if (FAILED(hr))
{
return hr;
}
wcscpy(*reverse, s);
wcsrev(*reverse);
return ERROR_SUCCESS;
}
I first use WsGetOperationContextProperty to get the heap and then use WsAlloc to allocate memory for the reversed string on this heap. We do not ever allocate memory using standard memory allocation mechanisms unless it’s memory we will have the option to delete/free when we are done using it. Instead we use the WWS heap which frees us from worrying about memory leaks – the memory will be released when the heap is reset or freed.
Now let’s get to creating the service. Again, I will not show the error handling code (to save space) but every HRESULT return value must be checked for success before proceeding further. The first thing is to create the error and heap objects just as we did when writing the WWS client.
WS_ERROR* error = NULL;
HRESULT hr = WsCreateError( NULL, 0, &error);
if (FAILED(hr))
{
// ...
}
WS_HEAP* heap = NULL;
hr = WsCreateHeap( 100000, 0, NULL, 0, &heap, error);
if (FAILED(hr))
{
// ...
}
The next step is to create a service endpoint.
WSHttpBinding_IStringServiceFunctionTable functions = { Reverse };
WS_STRING url = WS_STRING_VALUE(L"http://localhost:8000/StringService");
WS_HTTP_BINDING_TEMPLATE templateValue = {};
WS_SERVICE_ENDPOINT* serviceEndpoint;
hr = WSHttpBinding_IStringService_CreateServiceEndpoint(&templateValue,
&url, &functions, NULL, NULL, 0,
heap, &serviceEndpoint, error);
if (FAILED(hr))
{
// ...
}
Notice how we use the generated WSHttpBinding_IStringServiceFunctionTable to specify the list of functions (just one in our case). Now I use the proxy WSHttpBinding_IStringService_CreateServiceEndpoint to create the end point (and it internally calls WsCreateServiceEndpointFromTemplate). I have used default values for other arguments, but there is a lot of custom configuration that can be done. We’ll now create the service host:
WS_SERVICE_HOST* host;
const WS_SERVICE_ENDPOINT* serviceEndpoints[1];
serviceEndpoints[0]= serviceEndpoint;
hr = WsCreateServiceHost( serviceEndpoints, 1,
NULL, 0, &host, error);
if (FAILED(hr))
{
}
We only have one endpoint, but the service host can host multiple endpoints. While I have called WsCreateServiceHost with default arguments (basically passing NULL) it’s possible to set various service properties at this point. The last step is to open the service host.
hr = WsOpenServiceHost(host, NULL, error);
The above code will open the service and start listening on each of the endpoints (just one in our example). For a test console app, use a _getch() so the app won’t exit. Once the app’s done, close and free the service host :
WsCloseServiceHost(host, NULL, error);
WsFreeServiceHost(host);
And also free the heap/error objects :
if (heap)
{
WsFreeHeap(heap);
}
if (error)
{
WsFreeError(error);
}
You can now run the service and the WWS client from the last blog entry will connect to it and invoke the reverse function successfully. You can even write a simple WCF client and it’ll connect to this just as if this was a WCF service. I do agree that all these proxies, having to create/free structures, handling HRESULTs etc. may seem a tad foreign if you are coming from a pure C# or VB.NET world. But if you are not put off by C++, and keeping your service or client code native is important to you, then WWS sure seems to be a great way to do it. I do intend to research into WWS a little more in detail, so expect more blog entries and an article or two on the topic in future.
Tags: C++ · WWS API