Latest Posts
Na een lange tijd van afwezigheid, je kent het wel geen zin en geen tijd maar ook er geen tijd voor vrij willen maken.
Eigenlijk vind ik het toch belangrijk dat meer mede programmeurs of hoe ze zichzelf ook noemen. Kunnen genieten van de code die al is bedacht. Zodat het wiel niet opnieuw wordt uitgevonden, hooguit verbeterd.
Dus ik ga zo nu en dan weer schrijven en ik verwacht dan ook weer commentaar terug.
Het moet wel bi-directioneel zijn!!
Hans
Na enig puzzelen ben ik de volgende code gaan gebruiken.
Als enum de maanden van het jaar
public enum Maanden
{
januari,
februari,
maart,
april,
mei,
juni,
juli,
augustus,
september,
oktober,
november,
december
}
Het vullen van de DropDownList (ddl)
private void FillCombo()
{
Hashtable ht = GetEnumForBind(typeof(Utilities.Maanden));
ddl.DataSource = ht;
ddl.DataTextField = "value";
ddl.DataValueField = "key";
ddl.DataBind();
ddl.SelectedIndex = 0;
}
/// < summary >
/// Get the enumration
/// summary >
/// < param name="enumeration" >TypeOf( maanden )< /param >
/// a hashtable
private Hashtable GetEnumForBind(Type enumeration)
{
string[] names = Enum.GetNames(enumeration);
Array values = Enum.GetValues(enumeration);
Hashtable ht = new Hashtable();
for (int i = 0; i < names.Length; i++)
{
ht.Add(Convert.ToInt32(values.GetValue(i)).ToString(), names[i]);
}
return ht;
}
Even simpel als het is.
Bedenk wel dat er in een enumeration geen spatie mogen woden gebruikt.
Dus items zo als "alle werkdagen" is niet toegestaan.
Om iemand te citeren ;-)
Happy coding
Nu ben ik al een tijdje aan het programmeren in C# en asp.net ook in v2.0. Nu loop ik echter tegen een leuk probleem op.
Voor een vereniging maak ik een gastenboek. Je weet wel allerlei leuke en grappige bericht worden erin gezet. In PHP waar ik de eerste twee sites in maakte, was het geen probleem.
Maar nu is asp.net lijkt het wel een probleem te worden.
Ik heb een database waarin vele records zijn met deze berichten. Ik wil ze nu tonen. Hoe doe ik dat?
1) een gridview: niet echt geschikt voor een gastenboek zo naast elkaar
2) een formview: prima geschikt echter vijf berichten onder elkaar, dat lukte mij niet. Als iemand dat weet graag!
3) een detailsview: ook prima geschikt, het template is lekker makkelijk aan te passen, maar ook hier vijf berichten onder elkaar, graag ik wil het weten.
Ik hoor je zeggen een repeater daar gaat het wel mee. Maar een repeater is niet te pagen.
dan moet je een datalist nemen zeg je natuurlijk! Prima, maar vertel mij dan eerst even hoe ik pagen,
Dus al met al stuk voor stuk goede oplossingen, maar niet voor een gastenboek
Mocht ik het verkeerd hebben? Dat kan. maar vertel het mij dan, hoe het dan wel moet gebeuren.
Ik ben zeer nieuwsgierig (altijd al geweest)
Tenminste, ik kreeg gisteren het eerste exemplaar. Op websites als www.computerboek.nl, Donner, Paagman en Computer Collectief is ie te bestellen. En op de site van de uitgever, Pearson Education, natuurlijk.
Het boek behandelt C# versie 2.0 met behulp van de Visual Studio 2005 ontwikkelomgeving en, zoals de titel al aangeeft, gaat het om de basis. Nee, je vindt dus geen truukjes, diep verborgen in de donkere spelonken van het .NET Framework. Maar juist degenen die het leuk vinden om zelf programma's te maken en er naar eigen idee nog wat van moeten leren kunnen er veel plezier aan beleven. Met zo'n 270 pagina's is het ook best te behappen. Je kunt gelijk aan de slag, want bij het boek zit ook een CD met een exemplaar van Visual C# 2005 Express Editie. Ook vond ik het leuk om wat interviews er in te plaatsen. Bedankt Frans en Jeroen.
Het boek is onder meer te bestellen via VBCentral. Je vindt daar ook het boek van André Obelink dat gaat over, hoe kan het ook anders: Visual Basic 2005.
Het is het eerste boek dat ik geschreven heb.... nou ja. Niet helemaal... in een grijs verleden heb ik meegeschreven aan een boek dat een geheel ander onderwerp behandelt.
Als je het gekocht en gelezen hebt, laat me weten wat je er van vond.
(Andere bestel-websites zijn: www.computerboek.nl, Donner, Paagman en Computer Collectief is ie te bestellen. En op de site van de uitgever, Pearson Education, natuurlijk)
I just had to say it! Yes, I was frustrated.
Negotiate (through kerberos) on IIS sucks!
I love free speech, especially if something said is simply true (Joe Kaplan agreed with me)! Of course, this is not an excuse to be rude to MS employees :), but they never would improve things, if we don't ask for it right?
Why is Kerberos on IIS such a pain?
I need an Intranet site, running ASPX to utilize integrated security. That's nice. So, I don't have to maintain another user database (bleeh, not again). Secondly, for my AD solutions, I just needed full security based on AD, and (again) not my own security mechanism.
I hear you say: "What this fuzz about? It just works? Yes, it just works -as long as the credential on the current browser-IIS session- does not need to go any further than a page. For SQL Server, most developers, still would use standard security (and not pass-through authentication, which needs kerberos).
But for Exchange-, ADSI-, or Active Directory enabled pages, pages that -really do something- with Active Directory, IIS and Kerberos really need a professor (Joe Kaplan, but his fee is too high for me :) ) in Windows-kerberos-science.
Well, euh, it's easy, just 3 things to check!
there is
- AD (Active Directory) support (does AD trust IIS+host for delegation?)
- IIS - support (does IIS support Negotiate?)
- Browser support (do IE support Negotiate and integrated logon?)
And between the lines, you need to make sure (such as using setspn.exe) that it all is going to work. To get this done and configured, you'd better not be a third party intranet supplier!
Microsoft, please, fix this. I mean, make it more easy for us.
Let me just enumerate some knowledgebase articles that give some checklists. (See knowledge base articles below, note there are much more articles about this subject!)
For a script, yummy for you, you might not have found this script before, because I'm just good :). What it does, is checking (enabling) the IIS server delegation. Note, using it is for your own risk!
(ps: My setup scripts are preferable VBS because that just works without any version messup).
What it does is actually shown in the picture. It checks the middle option.
update at 14 sep 2006: Some companies, have disabled netbios features and computer browsing. So they solely have DNS and AD, but this makes IIS fail to use negotiate as well. It just seems that IIS has a lot of legacy network component dependencies.
' this checks this IIS server, to be trusted
' otherwise, Negotiate (Wdigest) through kerberos will -not- work!
' if you don't like all protocols to be trusted, you should manually correct this property
' for this IIS server.
Sub SetThisIISServerTrusted()
Dim nt, ds, ldapHost, info, accInfo, dnMe
Const ADS_NAME_INITTYPE_SERVER = 2, ADS_NAME_TYPE_NT4 = 3, _
ADS_NAME_TYPE_1779 = 1, ADS_UF_TRUSTED_FOR_DELEGATION = &H80000
Set nt = CreateObject("NameTranslate")
Set info = CreateObject("WinNTSystemInfo")
Set ds = GetObject("LDAP://rootDSE")
' the AD server that is serving us must be authorative enough
ldapHost = ds.Get("dnsHostName")
nt.Init ADS_NAME_INITTYPE_SERVER, ldapHost
' computer name is by definition the netbiosname plus a $
nt.Set ADS_NAME_TYPE_NT4, info.DomainName + "\" + info.ComputerName + "$"
dnMe = nt.Get(ADS_NAME_TYPE_1779)
Set ds = GetObject("LDAP://" + dnMe)
accInfo = ds.Get("userAccountControl")
accInfo = accInfo Or ADS_UF_TRUSTED_FOR_DELEGATION
ds.Put "userAccountControl", accInfo
On Error Resume Next
ds.SetInfo
If Err.Number > 0 Then
WScript.Echo " Error at SetThisIISServerTrusted:" + Err.Description + ":" + Err.Source + ":" + Hex(Err.Number)
End If
On Error Goto 0
Set ds = Nothing
End Sub
PRB: "Access Denied" Error Message When Using ServerXMLHTTP to Access an Authenticated Site
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.KB.v10.en/enu_kbmsxmlkb/msxmlkb/291008.htm
Enable negotiate on IE6
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.KB.v10.en/enu_kbie/ie/299838.htm
Troubleshoot IIS negiotiate 326985
http://www.microsoft.com/technet/prodtechnol/windowsserver2003/technologies/security/tkerbdel.mspx
Enable negotiate on IIS
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.KB.v10.en/enu_kbiis/iis/215383.htm
Enable negotiate using XMLHttp
ms-help://MS.MSDNQTR.v80.en/MS.MSDN.v80/MS.KB.v10.en/enu_kbmsxmlkb/msxmlkb/314404.htm
http://support.microsoft.com/kb/326985/
Sometimes, I am willing to disclose :) some secrets of real performance. Even in the .NET world, we can't avoid BSTR allocation, and in the unmanaged coding world, automation and COM will be at our path once more.
In my honest opinion, many, many programmers make or made the mistake, in the unmanaged world, of not reallocating resources, instead, they just destroy the resource, and allocate it again. MS tried to fix this 'bug' by caching allocations, as much as possible. I find this decision, to start caching allocations, I mean for BSTR allocation, not a good decision. This must be one of the reasons, that COM in a multitasked, MPS environment sometimes, simply cannot scale!
I'll explain why. In a single user environment, caching data for a thread is a good idea, since say MS Word, and scripts like in VBA and VBS, might reuse data/allocations. But as soon as our ASP/COM server environment starts to do this, and the code is reentrant, caching is useless, since threads that allocated data, might not be allowed to reuse zombie-data (if a caching-pattern is used) from another thread.
It could have been solved so easily! (Now, I might sound presumptuous to say that, I agree) How? Just don't cache but reallocate!
VB6 and automation clients for instance, uses the BSTR datatype all over the place, and ATL when used in a COM environement, does as well. If you look at the compiled code that programmers deliver, they never reallocate (only the runtime does sometimes). So for instance myString = "Hello" and myString = "bye" could have been compiled internally by:
myString = SysAllocString(L"Hello");
SysReAllocString(myString, L"bye");
And that's really all!
SysReAllocString(Len) internally uses CoTaskMemRealloc and that function tries to enlarge the memory allocation in-place, and this at its turn minimizes RAM-synchronization in MPS systems on the CPU.
So far, my theory. Am I just filling up your internet-html disk-cache and chit-chatting because I'm just idle for an hour? No.
Let's just try this out!
I've rewritten CComBSTR (from the ATL namespace) and you can find this in the platform SDK at \PlatSDK\Include\atl\atlbase.h (in case you have Visual Studio 2005, don't use this location, but use the most recent header files).
This silly little program does nothing but appending random (sort of) wide strings to a BSTR allocation. Let's rewrite CComBSTR and measure it!
I assume that you can get the headers right to get the program below compile and run.
int
_tmain()
{
HRESULT hr = S_OK;
CoInitialize(NULL);
{
CComBSTR appendPlay;
DWORD timer = GetTickCount();
// Ethan Winer, an Assembly coding specialist, once thought me that loops counting down to zero are faster, this still is the case!, just a silly fact.
for(int xy = 10000; xy > 0; xy--)
{
PWSTR zy = xy % 2 == 0 ? L"hiya" : L"bye";
appendPlay.Append(zy);
}
wprintf(L"speed %d\n", GetTickCount() - timer);
}
CoUninitialize();
}
Now run the code and on my AMD 3200+ system, this takes 578 time ticks. This is even with the OLE BSTR cache enabled! (When caching is disabled, this takes 520 time ticks).
Let's improve the Append part of CComBSTR (make sure you keep the original atlbase.h intact). In my case, I just redefined CComBSTR to CComBSTR2 and copy-pasted all of it and rewrote the slow parts.
The slow original code is using the 'delete' 'allocate' sequence.
// very slow original
HRESULT __stdcall Append(LPCOLESTR lpsz, int nLen) throw()
{
if(lpsz == NULL)
{
if(nLen != 0)
return E_INVALIDARG;
else
return S_OK;
}
int n1 = Length();
if (n1+nLen < n1)
return E_OUTOFMEMORY;
BSTR b;
b = ::SysAllocStringLen(NULL, n1+nLen);
if (b == NULL)
return E_OUTOFMEMORY;
if(m_str != NULL)
memcpy(b, m_str, n1*sizeof(OLECHAR));
memcpy(b+n1, lpsz, nLen*sizeof(OLECHAR));
b[n1+nLen] = NULL;
SysFreeString(m_str);
m_str = b;
return S_OK;
}
And here goes the improved code. It has the Automation runtime resize the BSTR while the string in most cases remains at the same memory address. This is how the original BSTR programmers have designed for performance, while nobody is utilizing it! But we instead, do use it, as you understand.
HRESULT __stdcall Append(LPCOLESTR lpsz, int nLen) throw()
{
if (lpsz == NULL || (m_str != NULL && nLen == 0))
return S_OK;
int n1 = Length();
HRESULT hr = SetLength(n1 + nLen);
if ( SUCCEEDED(hr) )
memcpy(m_str+n1, lpsz, nLen*sizeof(OLECHAR));
return hr;
}
We need to append the SetLength function, which is a static wrapper for SysReAllocStringLen(..)
// Cuts the length to specified but does not clear contents
HRESULT __stdcall SetLength(unsigned int length) throw()
{
return _SetLength(&m_str, length);
}
static HRESULT __stdcall _SetLength(BSTR * str, unsigned int length) throw()
{
return ::SysReAllocStringLen(str, NULL, length) == FALSE ? E_OUTOFMEMORY : S_OK;
}
I've included the full 99% compatible CComBSTR2 replacement for you, as a handy dowload so bother about that later. :)
Now, get me to the results please, how much faster would this code run now?
Yes, it takes a whopping 15 milliseconds! And figure that, against 578 milliseconds, which makes the improvement 3800%
Now you might understand why .NET had to be invented by MS :) they figured that the maximum scalability limit was hit on real MPS systems, and that COM never could perform the task of being scalable just because the BSTR sucks in performance! And now, you know that, I'm just now on the conspiracy path, and I'm lying :-).
Anyway, the conclusion is, that current COM clients, and applications and servers, could, if they would like to, improve a lot for free, by just removing the BSTR reallocation barrier and take advantage of the maximum 'unmanaged code' speed possible on a Windows (r) System.
The non-conspiracy theory is that caching was made in times, when Microsoft did not play a big role in server environments, and that computers were relativily slow. A conclusion would be that if MS would like to improve an old car (COM) for free, they'd just remove the caching and implement the idea that I've proven to be very good. This would be good for classic ASP pages as well that are still very popular on the internet.
Just a final question. Why bother? If you are an Automation developer, creating services that depend heavily on BSTRs?
The answer is, I bothered once a while ago, just because of some artistic feeling (good programmers are artists, not scientists :) ) that I could improve the enormous BSTR stress on my product here Isp Session Manager. Of course, the biggest part of such managed COM server (as COM+ was called in the past!) is doing talking to a DBMS. But after implementing the CComBSTR replacement, the performance on a MPS server, suddenly got very easy and the pages per second throughput went up and showed a flat line (that's the wet dream of each webmaster). Before, without using the CComBSTR replacement the throughtput was erratic, so this again proved my point, that not-reallocating BSTRs causes a huge demand on RAM synchronization and makes scalability limited because of wrong usage of the COM runtime.
Here, you got the CComBSTR whopper for your own downloads. Please do not forget to deploy it after download...
If you ever might want cute buttons, independently of your clients browser and theme, you might create buttons by using some dynamic GDI based code.
I found this solution, which costed just 2 hours programming not bad at all, and you might profit from it as well.
This code, automatically wraps text, all using .NET power. It even can be improved, to support hover as well. B.t.w. on Windows Vista XAML supports this but we’re still on the internet man!
So why not make our own buttons.
Paste code below, onto an empty ASPX page (for instance button.aspx), and call use it through the following arguments (other tag arguments omitted for simplicity).
<asp:ImageButton id=”myButton” ImageUrl=Button.aspx?t=Hello%20World&w=150&h=35” runat=”server”/>
Syntax below is based on C# 2.0
The output might look like this(appConfig must contain the buttoncolors)

public partial class button : System.Web.UI.Page
{
private static Color parsRgb(string rgbColor)
{
int red, green, blue;
red = int.Parse(rgbColor.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
green = int.Parse(rgbColor.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
blue = int.Parse(rgbColor.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
return Color.FromArgb(red, green, blue);
}
protected void Page_Load(object sender, EventArgs e)
{
System.Collections.Specialized.NameValueCollection req= Request.QueryString;
//get height
string h=req["h"];
//get width
string w = req["w"];
// get text
string t = req["t"];
if (t == null || t.Length == 0)
throw new HttpException("QueryString t not supplied");
int hashCode = t.GetHashCode();
string checkEtag = Request.Headers["ETag"];
if (checkEtag != null && checkEtag.Length != 0)
{
if (checkEtag == hashCode.ToString())
{
Response.StatusCode = (int)System.Net.HttpStatusCode.NotModified;
Response.StatusDescription = "Not modified";
Response.SuppressContent = true;
return;
}
}
req= System.Web.Configuration.WebConfigurationManager.AppSettings;
string colrRadiantStart = req["btnBgRadiantStart"];
string fontSize = req["btnFontSize"];
string FontName = req["btnFontName"];
string colrRadiantEnd = req["btnBgRadiantEnd"];
string FontColor = req["btnFontColor"];
System.Drawing.Color ocolorEnd, colorStart, colorFont ;
if (colrRadiantStart != null && colrRadiantStart.Length == 6)
colorStart = parsRgb(colrRadiantStart);
else
throw new HttpException("You must provide a valid btnBgColor hex rgb-code in appConfig");
if (colrRadiantEnd == null || colrRadiantEnd.Length != 6)
throw new HttpException("You must provide a valid btnBgColor hex rgb-code in appConfig");
else
ocolorEnd = parsRgb(colrRadiantEnd);
if (FontColor == null || FontColor.Length != 6)
colorFont = Color.Black; //default
else
colorFont = parsRgb(FontColor);
int iH = int.Parse(h);
int iW = int.Parse(w);
Bitmap oBmp1 = new Bitmap(iW, iH);
Graphics oGrp1 = Graphics.FromImage(oBmp1);
// seems not to have effect
oGrp1.CompositingQuality = CompositingQuality.HighQuality;
oGrp1.InterpolationMode = InterpolationMode.HighQualityBilinear;
LinearGradientBrush lgb = new LinearGradientBrush(new Rectangle(0, 0, iW, iH), colorStart, ocolorEnd, LinearGradientMode.Vertical);
oGrp1.FillRectangle(lgb, 0, 0, iW, iH);
System.Drawing.FontFamily fntFam = new FontFamily(FontName);
Font fnt = new Font(fntFam, float.Parse(fontSize));
StringFormat stringFormat = StringFormat.GenericDefault;
//we must set this value, other wise text will default to left.
stringFormat.Alignment = StringAlignment.Center;
SizeF szf = oGrp1.MeasureString(t, fnt, new SizeF(iW, iH), stringFormat);
//center
PointF FPoint = new PointF((iW - szf.Width) / 2, (iH - szf.Height) / 2);
oGrp1.DrawString(t, fnt, new SolidBrush(colorFont), new RectangleF(FPoint.X, FPoint.Y, szf.Width, szf.Height), stringFormat);
Response.ContentType = "image/jpeg";
Response.Cache.SetETag(hashCode.ToString());
//Response.Cache.SetLastModified(DateTime.Now);
Response.Cache.SetCacheability(HttpCacheability.Public);
oBmp1.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
Response.End();
}
There are several tricks but I've not seen this one so I would like to mention this trick as well.
The idea is that IIS can be managed through ADSI interfaces right? You would use (in vbscript)
Sest myIIS = GetObject("IIS:") and now you have the namespace. But if IIS is not installed, it would return 'invalid syntax'. Not really a good error.
A cleaner method would be
Set myIIS = CreateObject("IIS")
This way, you use the progid that the IIS namespace, normally is fetched by a url moniker and GetObject().
so complete code:
Dim myIIS, isIISInstalled
On Error Resume Next
Set myIIS = CreateObject("IIS")
'ActiveX component cannot create object: 'IIS'
isIISInstalled = Err.Number <> &H800A01AD
On Error GoTo 0
You can apply the concept above to -any- language, including javascript, VB6, C# and VB.NET.
Op de
dotNed bijeenkomst van deze maand werd de vraag gesteld op welke termijn we de eerste non-beta/non-CTP versie van
Atlas kunnen verwachten. In een
recente webcast van Scott Guthrie wordt uitgebreidt stilgestaan bij het verleden, heden en de toekomst van ASP.NET, waaronder Atlas.
Nou, niet echt. Recentelijk vertelde S. "Soma" Somasegar dat WinFx, tot nu toe de naam voor 'alles na .NET 2.0', hernoemd is naar .NET 3.0. De vraag was wat gebeurt er met .NET als WinFx gereed is. Nu, niet zo veel eigenlijk.

Het idee is dat .NET 3.0 (voorheen dus WinFx) gebaseerd is op .NET 2.0, maar nu met uitbreidingen zoals Windows Presentation Foundation (WPF) en Windows Communication Foundation (WCF). Is het daarmee duidelijk? Nou, blijkbaar moet men het wel extra uitleggen:
- De bestaande versie 2.0 C# en VB.NET compilers wordt gebruikt om .NET 3.0 programmacode te compileren;
- Heeft .NET 3.0 ondersteuning voor LINQ? Nee, ook niet, want LINQ is nog niet klaar wanneer Vista uitkomt. .NET 3.0 wordt meegeleverd met Vista.
- De directory voor .NET 3.0 is windir%\Microsoft.NET\Framework\3.0.
Wat ik niet helemaal begrijp is de reden dat men hier voor een geheel nieuw versienummer heeft gekozen. En dan niet eens 2.1, maar gelijk 3.0. Dat, terwijl de extra onderdelen (WPF, WCF, WF, etc.) slechts een uitbreiding op het bestaande 2.0 Framework zijn. Waarom hadden die extra assemblies niet gewoon in de 2.0-folder geplaatst kunnen worden?
Oh, en over LINQ gesproken, Dlinq bestaat niet meer.
If you have a website using ASP pages or asp.net pages, and you want to integrate user management with active directory, you’ll have a lot of extra technology that you need to make yourself known with.
What you mostly would do, is disable anonymous web access, and have users login, using credentials stored at Active Directory.
But unfortunately, IIS, Internet Information Server, on Windows 2003 and 2000, does map only the current logged on user through an NT4 domain format, which looks like ‘DOMAIN\johnd’ retrieved from Request.ServerVariables(“LOGON_USER”) or Request.LogonUserIdentity.Name, will equal exactly that name in NT 4 format. You can also logon to an IIS website using a user principal (in AD, this the attribute userPrincipalName like 'johnd@domain') but we need to be sure that the identity -always- can be handled, no matter what syntax is given by the user at logon.
So from there you need to translate the NT4 name to a distinguished Name (dn is like “cn=itsme,cn=users,dc=nwtraders,dc=msft”) that is suitable for Active Directory.
Now comes an often made ‘trick’ which is obviously slow (see for a listing, completely below)!
One could do a Directory Search on sAMAccountName=’johnd’ and get the active, logged on user and its active directory record. <- don't do this!
A very fast method would be as follows.
note: this is assuming that you are using the DotNet framework version 2.0 and c#. The very same effective code could be written for Vb.Net.
protected void Page_Load(object sender, EventArgs e)
{
// identical to Request.LogonUserIdentity but using a static method instead of a Page property
//WindowsIdentity
wi = System.Security.Principal.WindowsIdentity.GetCurrent();
WindowsIdentity wi = Request.LogonUserIdentity;
DirectoryEntry dir = new DirectoryEntry("LDAP://<SID=" + SidToHex(wi.User) + ">");
dir = new DirectoryEntry("LDAP://" + (string)dir.Properties["distinguishedName"][0], null, null,
AuthenticationTypes.Secure |
AuthenticationTypes.ReadonlyServer);
//Now you got ‘dir’ at your disposal and you can read the current users profile information (for instance)!
}
To get Request.LogonUserIdentity initialized after a browser logon to your aspx pages, with the correct user-logon-info,
configure web.Config as follows so that it contains at least the configuration seen below.
web.Config
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<system.web>
<compilation defaultLanguage="c#" debug="true">
<assemblies>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
<authentication mode="Windows" />
<identity impersonate="true"/>
</system.web>
</configuration>
/// <summary>
/// needed for Windows 2000 compatibility
/// Windows 2003 can bind using a S-1-xx-x-xx-xxx- format
/// </summary>
/// <param name="sid"></param>
/// <returns></returns>
private static string SidToHex(SecurityIdentifier sid)
{
int binLength = sid.BinaryLength;
byte[] bt = new byte[binLength];
sid.GetBinaryForm(bt, 0);
System.Text.StringBuilder retval = new System.Text.StringBuilder(binLength * 2, binLength * 2);
for (int cx = 0; cx < binLength; cx++)
retval.Append(bt[cx].ToString("X2"));
return retval.ToString();
}
Bad performing often used code, not to be used!
string ldapPath = "LDAP://" + userDomain;
DirectoryEntry rootEntry = new DirectoryEntry(ldapPath);
using (DirectorySearcher ds = new DirectorySearcher(rootEntry, "(samAccountName=" + userName + ")"))
{
SearchResult result = ds.FindOne();
if (result != null)
{
ResultPropertyValueCollection resultValues = result.Properties["displayName"];
if (resultValues.Count > 0)
{
Label1.Text = (string) resultValues[0];
}
else
{
Label1.Text = "No display name";
}
}
}
The C# sample at the IPN website, is a little bit outdated, since paypal currently supports international character sets (great!) so if a chinese customer would enter his name in Chinese for instance :), it still would be kept ok. So to not corrupt the characters, we have to stick the same format as paypal sends us. This would be simple if the page would be a web service form, it is not, so we have to wrap the POST request and do the encoding all yourselves.
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Text;
using System.Net;
using System.ComponentModel;
using System.Data;
using System.Web;
using CSession;
namespace pp
{
/// <summary>
/// Summary description for ipn.
/// </summary>
public partial class ipn : System.Web.UI.Page
{
private bool isTesting;
// this page runs as LCID 1033 english
// and is tested on codepage 65001. you must –enable- the codepage
// on your paypal preferences!
// note that the shopping basket, is based on ISP Session from
// http://www.nieropwebconsult.nl/asp_session_manager.htm
// it integrates with classic asp as well as .NET
protected void Page_Load(object sender, System.EventArgs e)
{
NameValueCollection rq = Request.Form;
//Response.BufferOutput = false;
string test_ipn = rq["test_ipn"];
isTesting = test_ipn != null && test_ipn == "1"? true: false;
// Create the request back
// instead of pp_url you could read https://www.paypal.com/cgi-bin/webscr
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(
System.Configuration.ConfigurationSettings.AppSettings["pp_url"]);
string version = rq["notify_version"]; // ' 2.1 currently
// Set values for the request back
req.Method = "POST";
string notifyCmd = "cmd=_notify-validate";
req.ContentType = "application/x-www-form-urlencoded";
// the charset will be utf-8 if set as preference on paypal!
Encoding enc = System.Text.Encoding.GetEncoding(rq["charset"]);
StreamWriter tw = new StreamWriter(new MemoryStream());
int keyCount = rq.Count;
// here goes our first command
tw.Write(notifyCmd);
//tricky part. If you do Request.Form.ToString() the & and the = is encoded as well!
for (int xx = 0; xx < keyCount; xx++)
{
tw.Write('&');
tw.Write(HttpUtility.UrlEncodeToBytes(rq.GetKey(xx), enc));
tw.Write('=');
tw.Write(HttpUtility.UrlEncodeToBytes(rq.Get(xx), enc));
}
int contLen = (int)tw.BaseStream.Position;
req.ContentLength = contLen;
// Write the request back IPN strings in binary form
// do not use a TextWriter since that would cause
// encoding on encoding which obviously will fail
byte[] temp = new byte[contLen];
tw.BaseStream.Position = 0;
tw.BaseStream.Read(temp, 0, contLen);
Stream stOut = req.GetRequestStream();
stOut.Write(temp, 0, contLen);
stOut.Flush();
// Do the request to PayPal and get the response
StreamReader stIn = new StreamReader(req.GetResponse().GetResponseStream(), enc);
string strResponse = stIn.ReadToEnd();
stIn.Close();
string Item_number, Payment_status, Txn_id, sessionId;
Item_number = rq["item_number"];
sessionId = rq["invoice"];
Txn_id = rq["txn_id"];
db dbl = new db();
dbl.sqlConn.Open();
DataSet1.tLicenseRow lic = dbl.dataSet11.tLicense.NewtLicenseRow();
lic.CtryISO2Code = rq["address_country_code"];
lic.email = rq["payer_email"];
//TODO: lic should be fullfilled
lic.sessionID = new Guid(sessionId).ToByteArray();
lic.transactionID = Guid.NewGuid();
lic.salesBlob = rq.ToString().Replace("&", "\r\n");
lic.saleDate = DateTime.Now;
lic.total_value = decimal.Parse(rq["mc_gross"]);
lic.tax_value = decimal.Parse(rq["tax"]);
lic.salesBlobType = 1;
lic.total_fee = decimal.Parse(rq["mc_fee"]);
Payment_status = rq["payment_status"];
lic.Payment_status = Payment_status;
lic.downloadComplete = 0;
// Create the IpnTransaction
basket bsk = null;
bool validSession = false;
if (sessionId != null && sessionId.Length == 32)
{
bsk = new basket(sessionId);
validSession = true;
}
// Confirm whether the IPN was VERIFIED or INVALID. If INVALID, just ignore the IPN
if (strResponse == "VERIFIED")
{
if (validSession)
{
bsk.Session["Payment_status"] = 1;
bsk.setPurchaseAuthorized(lic.transactionID);
msg.Text = "valid";
Response.AppendToLog("valid session");
}
else
{
Response.AppendToLog("invalid session");
msg.Text = "invalid";
}
}
else
{
if (validSession)
{
Response.AppendToLog("invalid transaction, valid session");
msg.Text = "invalid";
bsk.Session["payment_status"] = 0;
}
}
dbl.dataSet11.tLicense.AddtLicenseRow(lic);
dbl.tLicense.Update(dbl.dataSet11.tLicense);
dbl.sqlConn.Close();
dbl.Dispose();
if (bsk != null) bsk.Dispose();
}
}
}
Als je Visual Studio gewend bent, ben je ook gewend geraakt aan IntelliSense. Daarmee is het sneller ontwikkelen omdat je niet alle namespaces, klassen, methoden en properties uit je hoofd hoeft te kennen. En dan open je de SQL Query Analyzer... weg hulp. Maar, er is een oplossing. Red-gate software maakt het mogelijk om ook in de Query Analyzer IntelliSense op SQL-statements te hebben, inclusief database-objecten. Het programma SQL Prompt zorgt hiervoor.

Daarnaast kan je ook SQL code snippets invoeren zodat het maken van SQL scripts een stuk sneller gaat.
Tot 1 september 2006 kan je deze software gratis downloaden. Ook Enterprise Manager, SQL Server Management Studio, UltraEdit, EditPlus 2 en Visual Studio 2003/2005 worden ondersteund. In de download krijg je ook Dependency Tracker, maar dit is een 14-dagen evaluatieversie.
Eigenlijk ter herinnering voor mezelf: "After you install version 2.0 of the .NET Framework on a server that is running Microsoft Operations Manager 2005 with SP1, you can no longer discover computers in Active Directory".
Als je dus het .NET 2.0 Framework installeert op een machine met MOM 2005 (en Service Pack 1) zal deze geen nieuwe computers meer ophalen uit de Active Directory. En MOM-agents die nog op de Pending Actions pagina staan krijgen 'Unmanaged' als Desired Management Mode.
Gelukkig is er een fix.
If you have to deal with MIME you'll have to interpret date information.
Some programmers argue, that the DotNet framework, should support the RFc822 date format out of the box.
Others say, 'no, you need not, because RFC1123 has already been implemented which goes as "Sun, 04 Dec 2005 20:57:57 GMT" for instance'.
I do not agree with this completely. First because sometimes, you might want to be able to detect the timezone difference between clients. If you have a GMT notation, you have the correct worldtime in sync, but you won't see a difference.
Second, if the RFC or API recommends a RFC822 it is a error to assume that the runtime or their runtime, automatically will deal with rfc1123 as well (it is in many situations).
So here is a way to format your string into rfc822. I did not pack it into a handy class or something. Just use the idea. You could make it detect and accept rfc822 and rfc1123 as well!
// the (UTC) suffix is optional. You can leave it out.
DateTime dt = DateTime.Now;
string Rfc822DateTime = dt.ToString(@"ddd, dd MMM yyyy HH:mm:ss zzzz (\U\T\C)", System.Globalization.DateTimeFormatInfo.InvariantInfo);
// remove the time separator in the timezone because zzzz will produce +01:00 for instance
Rfc822DateTime = Rfc822DateTime.Remove(Rfc822DateTime.LastIndexOf(':'), 1);
// for the sake of completeness I want to state that formatting as an RFc1123 string is as simple as formatting using a predefined mask that is .ToString("r").
// and do not forget the ToUniversalTime() method or otherwise, the time string is still a local time!
string rfc1123DateTime = dt.ToUniversalTime().ToString("r");
// and convert the rfc822 formatted date back
if (Rfc822DateTime.EndsWith("(UTC)"))
Rfc822DateTime = Rfc822DateTime.Substring(0, Rfc822DateTime.Length - 6);
DateTime rfc822Parse = DateTime.ParseExact(Rfc822DateTime, @"ddd, dd MMM yyyy HH:mm:ss zzzz", System.Globalization.DateTimeFormatInfo.InvariantInfo);
// rfc822Parse equals e.g.: "Mon, 15 May 2006 08:31:40 GMT"
// rfc1123DateTime equals eg.: "Mon, 15 May 2006 10:31:40 +0200"
// converting from string to datetime also requires us to convert from UTC to Local manually
DateTime rfc1123Parse = DateTime.ParseExact(rfc1123DateTime, "r", System.Globalization.DateTimeFormatInfo.InvariantInfo).ToLocalTime();
if (rfc822Parse != rfc1123Parse)
throw new FormatException("rfc822Parse and rfc1123Parse should be equal!");
Wouldn't it be fun to create new active directory objects, while you even don't startup the MMC for active directory? No, don't be afraid that everybody including ‘nobody’ on your network -now- can do this
If the code below does not run on a domain controller, you first should install the adminpak.msi (or register dsadmin.dll and dsa.mcs) for on Windows 2003 that also can be installed on Windows XP.
And of course, Active Directory is -really- secure. If the calling code is not executed by someone with sufficient rights, it will fail.
So here we go and make yourself really -see- active directory!
(ps: I like writing unique code, as far as I can see, nobody did it yet, hey, don't forget to visit www.adccure.nl !)
static void Main()
{
IDsAdminCreateObject co = new DsAdminCreateObject() as IDsAdminCreateObject;
object nativedsObject = new DirectoryEntry("LDAP://cn=users,dc=yourdomain,dc=local").NativeObject;
co.Initialize(nativedsObject, null, "user");
object newObject = co.CreateModal(DsAdminCreateObject.GetDeskTopWindow());
}
[ComImport, Guid("53554A38-F902-11d2-82B9-00C04F68928B"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDsAdminCreateObject
{
/// <summary>
/// Need to initialize before popping up the new object wizard
/// </summary>
/// <param name="ADsContainerObj">initialized dir object Container object eg: cn=users,DC=domain,dc=local</param>
/// <param name="ADsCopySource">can be null, specifies original object if you want a copy!</param>
/// <param name="ClassName">contains "User", "group", "contact", "inetOrgPerson" etc</param>
void Initialize( [MarshalAs(UnmanagedType.IDispatch)] object ADsContainerObj,
[MarshalAs(UnmanagedType.IDispatch), Optional(), DefaultParameterValue(null)] object ADsCopySource,
[MarshalAs(UnmanagedType.LPWStr)] string ClassName);
/// <summary>
/// Returns native ActiveDirectory object
/// </summary>
/// <param name="hwndParent">handle to parent window, specify 0 (mostly)</param>
[return: MarshalAs(UnmanagedType.IDispatch)]
object CreateModal(IntPtr hwndParent);
}
/// <summary>
/// Have our CLSID_DsAdminCreateObject be imported by .NET
/// </summary>
[ComImport, Guid("E301A009-F901-11d2-82B9-00C04F68928B")]
public class DsAdminCreateObject
{
/// we just needed a pointer to a window, if you run this code within a Windows Form, you can fetch a handle to it and hand it over to CreateModal!
[DllImport("user32", EntryPoint = "GetDesktopWindow", ExactSpelling = true, SetLastError = false)]
public static extern IntPtr GetDeskTopWindow();
}
Op 2-5-2006 heeft het Duitse Alkacon een nieuwe release (6.2.1) van OpenCms beschikbaar gesteld voor download. Dit volledig op java gebaseerde open source content management systeem doet niet onder voor zijn commerciële concurrentie. Een groot gemis is wel goeie documentatie van de vaak weinig transparante werking van de configuratie items. Dat ervaarde ik wel tjidens de opzet van de haarbal website. Het lukte me aardig om een straight forward site-je op te tuigen op basis van de standaard bijgeleverde template. Echt leuk wordt het natuurlijk pas als je zelf templates kan gaan maken, maar daar heb ik me vooralsnog nog niet aan gewaagd. Het belangrijkste is nu dat er een officieel platform beschikbaar is met informatie over haarballen op haarbal.nl.
Tussen alle postings over het gratis blijven van de Express edities van Visual Studio 2005 is er misschien nog iets aan de aandacht ontsnapt. Het blijkt namelijk dat er nu ook een definitieve versie is van Microsoft SQL Server Management Studio Express. Lange tijd was er enkel een CTP versie (van november 2005) beschikbaar. Met deze gratis management studio kan je SQL Server Express databases beheren, maar ook databases die door de grotere broers (alle SQL Server 2005 edities) zijn gemaakt.
Met de komst van Visual Studio 2005 is de opzet van ASP.NET projecten flink veranderd. In navolging van wat we gezien hebben met ASP.NET Web Matrix leek het wel een goed idee om af te stappen van project-bestanden (.csproj en .vbproj) en voortaan de fysieke folderstructuur te laten bepalen welke bestanden onderdeel werden van het project. Alhoewel het zeker transparanter was, bleek het ook vervelende consequenties te hebben.
Als je bijvoorbeeld 5000 plaatjes in een subfolder van de site hebt staan, maken deze dus onderdeel uit van je website-project. Zodra je de site wil 'publishen' worden ook deze 5000 plaatjes gepublished, waarbij niet gecontroleerd werd of deze plaatjes al dan niet waren veranderd. Daar waren wel weer work-arounds voor, maar handig is anders. Ook de migratie van VS.NET 2003-sites naar VS.NET 2005 verliep niet al te best. Toen de feedback op deze 'feature' loskwam was het eigenlijk al te laat om er nog voor het uitbrengen van Visual Studio 2005 wat aan te doen.
Daarom toog men aan het werk om een oplossing te bedenken. Die kwam in de vorm van het Web Application Projects model. De eerste release candidate (RC1) is hiervan nu beschikbaar.
Meer informatie over de manier waarop deze plugin voor Visual Studio 2005 werkt, vind je op de volgende lokcaties:
WOW64 stands for Windows on Windows64. It emulates Win32 mode for programs that were compiled for Windows 32-bit mode. It _even_ will emulate SystemInfo API calls! So if you ask GetSystemInfo to supply info about the processor architecture, or if you query through API GetEnvironmentVariable(L"PROCESSOR_ARCHITECTURE"...) Wow64 will make your 32-bit feel comfortable by saying: "Yes, I am an Intel compatible x86 CPU!".
So I was puzzled, my SETUP needed to do some tasks and it had to simply detect wether or not we are on a x64 windows edition.
The solution seemed simple. Try it for yourselves.
Boot to Windows x64 (if not done so yet) and run file://C:\WINDOWS\SysWOW64\cmd.exe this is the good old 32-bit command prompt from the Windows XP time.
Let's examine the environment variables you might need. The output in my case would be...
ALLUSERSPROFILE=C:\Documents and Settings\All Users
APPDATA=C:\Documents and Settings\Administrator\Application Data
ClusterLog=C:\WINDOWS\Cluster\cluster.log
CommonProgramFiles=C:\Program Files (x86)\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
COMPUTERNAME=BGRULEZ
ComSpec=C:\WINDOWS\system32\cmd.exe
FP_NO_HOST_CHECK=NO
HOMEDRIVE=C:
HOMEPATH=\Documents and Settings\Administrator
LOGONSERVER=\\BGRULEZ
NUMBER_OF_PROCESSORS=1
OS=Windows_NT
Path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=AMD64
PROCESSOR_IDENTIFIER=AMD64 Family 15 Model 47 Stepping 0, AuthenticAMD
PROCESSOR_LEVEL=15
PROCESSOR_REVISION=2f00
ProgramFiles=C:\Program Files (x86)
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files
PROMPT=$P$G
SESSIONNAME=Console
SystemDrive=C:
SystemRoot=C:\WINDOWS
TEMP=C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp
TMP=C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp
USERDOMAIN=BGRULEZ
USERNAME=Administrator
USERPROFILE=C:\Documents and Settings\Administrator
VS80COMNTOOLS=C:\Develop\VS2005\Common7\Tools\
windir=C:\WINDOWS
Evidentially you must solve the 'detect my AMD64' challenge through reading a new variable, named 'PROCESSOR_ARCHITEW6432' and not PROCESSOR_ARCHITECTURE.
If the variable does not exist, you are obviously running in a native win32 environment.
Update july 26 2006: Somebody made me awake, and pointed to the Kernel32 function: IsWow64Process(..) (see msdn).
I had to do some maintenance on a Windows ® service and found that you can use an ATL service as a windows service very well without all the overhead.
There are some samples on the internet, but they stop where I wanted to continue. That is, run UI-less services for instance, a job that could be done through a task scheduled-job but better be done through a service?
b.t.w. for the ones interested in a minimal windows service and who feel a sick inclination J writing the smallest EXE possible, they should use a plain C implementation provided by Microsoft’s ® Platform SDK located at [Platform SDK folder]\Samples\WinBase\Service and they should not use MS VC 8 but 4 or so...
Note that the grey highlighted code, is my code, the normal colored code is left over of what the MS Visual Studio created for you through the wizard. In addition, I’ve snipped all trace & debugging code.
The Visual Studio 2005 wizard creates an EXE that is suitable both as COM service (DCOM) and as windows service. You can guess a lot of overhead and unneeded lines.
This service does not perform anything, keep in mind it is a template for a 'scheduled' task-service, a batch for instance. Your code comes within DoDBMSJob...
// Service_Template.h
void
CALLBACK TimerProc(PVOID pdata);
DWORD WINAPI mainJob(LPVOID lpThreadParameter) ;
// Service_Template.cpp : Implementation of WinMain
#include "stdafx.h"
#include "resource.h"
#include "Service_Template.h" //
#pragma warning(disable: 4482) // nonstandard extension used: 'enum CServiceStatus' used in qualified name
[v1_enum] //32 bit rulez
enum CServiceStatus
{
run = 0,
pauze=1,
stop = 2,
shutmedown=3
};
class CService_Module : public CAtlServiceModuleT< CService_Module, IDS_SERVICENAME >
{
private:
// this flag in milliseconds, tells us how often
// is checked for tasks
DWORD m_iPollTime;
HANDLE m_ThreadHandle; // eventually use CHandle
HANDLE m_hServerStopEvent;
bool connDone;
CServiceStatus m_ServiceStatus ;
bool stopNow;
public :
~CService_Module() throw()
{
if (m_ThreadHandle != NULL)
CloseHandle(m_ThreadHandle);
if (m_hServerStopEvent != NULL)
CloseHandle(m_hServerStopEvent);
}
CService_Module() throw(): m_iPollTime(10), m_ThreadHandle(NULL), m_hServerStopEvent(NULL),
stopNow(false)
{
m_ServiceStatus =CServiceStatus::run;
//m_dwTimeOut = 60000; // one minute
}
// we can’t skip this! Leave as is created by wizard
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_GARBAGE_COLLECT, "{44DE094D-9AE1-5A56-A1A7-A20F9EAE0F21}")
HRESULT InitializeSecurity() throw()
{
// we don’t need this here
return S_OK;
}
void OnPauze() throw()
{
m_ServiceStatus = CServiceStatus::pauze;
__super::OnPause();
if ( m_hServerStopEvent != NULL)
SetEvent(m_hServerStopEvent);
}
void OnStop() throw()
{
__super::OnStop();
m_ServiceStatus =CServiceStatus::stop;
if ( m_hServerStopEvent != NULL)
{
stopNow= true;
// be sure not to kill the thread & process
// before it nicely ends
// otherwise, our pointers will ‘Release()’ while
// the instance has been killed already, a common bug!
SetEvent(m_hServerStopEvent);
DWORD tc = GetTickCount();
while(m_hServerStopEvent != NULL || GetTickCount() - tc > 20000) // max 20 seconds wait
Sleep(10);
}
}
void OnContinue( ) throw( )
{
__super::OnContinue();
m_ServiceStatus =CServiceStatus::run;
if ( m_hServerStopEvent != NULL)
SetEvent(m_hServerStopEvent);
}
void OnShutDown() throw()
{
//__super::OnShutDown();
m_ServiceStatus =CServiceStatus::pauze;
if ( m_hServerStopEvent != NULL)
SetEvent(m_hServerStopEvent);
}
HRESULT RegisterAppId(bool bService = false) throw()
{
HRESULT hr = S_OK;
// we extend our service description!
// on W2k and higher, this is user friendly!
// do not forget to add a string ot the string table
// here ‘IDS_Description’
BOOL res = __super::RegisterAppId(bService);
if (bService)
{
//DebugBreak();
if (IsInstalled())
{
SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SERVICE_CHANGE_CONFIG);
SC_HANDLE hService = NULL;
if (hSCM == NULL)
hr = AtlHresultFromLastError();
else
{
hService = ::OpenServiceW(hSCM, m_szServiceName, SERVICE_CHANGE_CONFIG);
if (hService != NULL)
{
const int m_szServiceNameLen = 4096;
WCHAR m_szServiceDescription[m_szServiceDescriptionLen]={0};
LoadStringW(_AtlBaseModule.GetModuleInstance(),
IDS_Description, m_szServiceDescription, m_szServiceDescriptionLen);
SERVICE_DESCRIPTION sdBuf = {m_szServiceDescription};
res = ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &sdBuf);
::CloseServiceHandle(hService);
}
else
hr = AtlHresultFromLastError();
::CloseServiceHandle(hSCM);
}
}
}
return hr;
}
HRESULT PreMessageLoop(int nShowCmd) throw()
{
//problem how to set a timer, we must provide the proc
// a pointer to this
// we could hack it and use HWND instead?
#ifdef _DEBUG
DebugBreak();
#endif
HRESULT hr = __super::PreMessageLoop(nShowCmd);
// if we don't have any COM classes, RegisterClassObjects
// retunrs S_FALSE
// This Causes the process to terminate
// We don't want this, so we return S_OK in this case
if (hr == S_FALSE) hr = S_OK;
if (m_bService == TRUE && hr == S_OK)
m_ThreadHandle = CreateThread(NULL, 0, mainJob, this, 0, 0);
return hr;
}
void __stdcall set_CollectTime(INT iCollectTime) throw()
{
m_iPollTime = iCollectTime;
}
INT get_CollectTime() throw()
{
return m_iPollTime;
}
void __stdcall SetEventHandleForStop(HANDLE eventHandle) throw()
{
m_hServerStopEvent = eventHandle;
}
CServiceStatus GetServiceStatus() throw()
{
return this->m_ServiceStatus;
}
void CALLBACK TimerProc(PVOID pdata) throw()
{
#ifdef _DEBUG
DebugBreak();
#endif
HRESULT hr;
//your registry and initialization comes here
hr = DoDBMSJob(iKeepConnection, bstrConnBuff, dwVersion);
return;
}
// here could be your task
STDMETHODIMP DoDBMSJob(int iAction, PCWSTR bstrConnBuff, DWORD dwVersion) throw()
{
/* iAction 0 do work and exit
* iAction 1 do work, cache connection and exit
* iAction 2 release cached connection and exit
*/
// Your task and cleanup code comes here...
// cleanup code _COULD_ be done in the main class
// but do not forget, if you cleanup there, you might
// fall into the Thread Apartment trap!
// Some COM objects, are not free-threaded
// so if you clean them up when windows
// tells you to stop execution with the thread windows
// ‘gives’ to you, the instance might fail and crash
return hr;
}
};
CService_Module _AtlModule;
DWORD WINAPI mainJob(LPVOID lpThreadParameter) throw()
{
LARGE_INTEGER liDueTime;
//fetch the pointer to our main class
CService_Module* p = static_cast<CService_Module*>(lpThreadParameter);
DWORD dwError = ERROR_SUCCESS;
//optimize memory usage
// should automatically increase if needed
//::SetProcessWorkingSetSize(GetCurrentProcess(), 1280000, 2560000);
DWORD dwWait =0;
HANDLE hServerStopEvent = CreateEventW(
NULL, // no security attributes
TRUE, // manual reset event
FALSE, // not-signalled
NULL); // no name
p->SetEventHandleForStop(hServerStopEvent);
// array with 2 events we want to monitors
HANDLE hEvents[2] = {hServerStopEvent,
::CreateWaitableTimer(NULL, TRUE, L"ASP_Session_Collect") };
HRESULT hr =::CoInitializeEx(NULL, COINIT_DISABLE_OLE1DDE | COINIT_MULTITHREADED);
for (;;) //endless loop
{
INT iPollTime = p->get_CollectTime();
liDueTime.QuadPart=-iPollTime * 10000;
//this timer is not periodic and is recreated each at loop so a negatieve value means a 'relative time'.
if (::SetWaitableTimer(hEvents[1], &liDueTime,0, NULL, NULL, FALSE) == FALSE)
{
dwError = ::GetLastError();
//MessageBox(NULL, _T("CreateTimerFailed"), _T("Error"), MB_OK | MB_ICONERROR);
goto cleanup;
}
// if your Service is Apartment threaded
// use MsgWaitForMultipleObjectsEx!
dwWait = ::WaitForMultipleObjects( 2, hEvents, FALSE, INFINITE);
if (dwWait == WAIT_OBJECT_0+1)
p->TimerProc(NULL);// no need to suspend timer sinced this is recreated each time
else // it was not the timer but a service event
{
CServiceStatus status = p->GetServiceStatus();
if (status==CServiceStatus::stop || status == CServiceStatus::shutmedown )
{
break;
}
while (status == CServiceStatus::pauze)
{
status = p->GetServiceStatus();
Sleep(1000); //wait 1 second
}
if (status==CServiceStatus::stop || status == CServiceStatus::shutmedown )
{
break;
}
// not timer event - error occurred,
}
}
cleanup:
p->DoDBMSJob(2, NULL, 0);
CoUninitialize();
if (hEvents[1])
::CloseHandle(hEvents[1]);
if (hServerStopEvent != NULL)
{
CloseHandle(hServerStopEvent);
hServerStopEvent=NULL;
p->SetEventHandleForStop(hServerStopEvent);
}
return 0;
}
//
extern "C" int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR lpCmdLine, int nShowCmd) throw()
{
return _AtlModule.WinMain(nShowCmd);
}
// we would not be complete without listing stdafx.h this time, I’ve set compatibility with W2k, that would be sufficient for most services
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently,
// but are changed infrequently
#pragma once
#define STRICT
// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#define WINVER 0x0500 // Change this to the appropriate value to target other versions of Windows.
#define _ATL_ATTRIBUTES //allow db_command etc...
#define _WIN32_WINNT 0x0500 // Change this to the appropriate value to target other versions of Windows.
#define _WIN32_WINDOWS 0x0500 // Change this to the appropriate value to target Windows Me or later.
#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later.
#define _WIN32_IE 0x0550 // Change this to the appropriate value to target other versions of IE.
#endif
//explicit disable since our main service ONLY will serve as a link between windows/registration and our mainJob function
#define _ATL_NO_COM_SUPPORT
// IMPORTANT CHANGE THIS instead of _ATL_APARTMENT_THREADED
#define _ATL_FREE_THREADED
#include "resource.h"
#include <atlbase.h>
#include <atlcom.h>
#include <atldbcli.h>
using namespace ATL;
here comes a great ATL feature, support eventlog writing! Put these lines in the RGS file, that Visual Studio created for you. If you don’t do so, the event log will report this: “The description for Event ID ( 0 ) in Source ( CTemplateService) cannot be found. etc)
Add this text in CTemplateService.rgs
HKLM
{
NoRemove SYSTEM
{
NoRemove CurrentControlSet
{
NoRemove Services
{
NoRemove EventLog
{
NoRemove Application
{
'CTemplate Service' ß this name must match IDS_SERVICENAME!
{
val 'EventMessageFile' = s '%MODULE_RAW%'
val 'TypesSupported' = d 7
}
}
}
}
}
}
}
Step 2
Add one resource file (Visual Studio does not support this!) and give it the extension .mc
For instance message.mc
The contents will be:
LanguageNames=(Neutral=0x409:MSG00409)
MessageId=0x1
SymbolicName=MSG_ERROR
Language=English
Your Company: Template Service, returned the following error: %1
.
ß-- one empty line!
In visual studio, you click on the properties of this file and enter at the command line
mc message.mc
outputs:
Enter: message.h message.rc
In your stdafx.h you need to include “message.h” now.
Now you have included message.rc as a resource. A message file is a superior way to include all languages in one executable or dll while the active thread defines the actual languageid being displayed on UI. This trick existed already on NT 3.x but not many developers / translators use it for internationalization. As you see, NT –requires- you to have a message resource file just to support logging to the Event Log store. FYI! Windows ® (Vista) will soon support w3c-‘like’ logging as well! Soon we can say goodbye to the rather unpleasant and difficult to implement eventlog.
Installation
According to the ATL documentation, you can deploy your compiled service as follows:
Install it with: CTemplateService.exe –service
Deinstall it with: CTemplateService.exe -UnRegServer

Op de maandelijkse .NET usergroup bijeenkomst is door Frans Bouma en Maurice de Beijer strijd geleverd over de vraag welke ontwikkeltaal nu beter is: C# of VB.NET. Hoe de strijd verliep en wat de uitkomst ervan was vind je op de site van dotNed.
How easy can one port a product from Win32 to x64? It depends on whether or not you ignore what your mom told you: "don't listen to strangers..." <- ps: ignoring this, only applies to developing!
You can learn from other ones faults, right? So in the past, years ago, when the MSDN magazine and others told us to use INT_PTR instead of INT etc at certain code you know the drill, I was not to lazy, to modify my code. So I listened to strangers...
So, today, I took an old project, still running and shining, ISP Session, and wanted to offer x64 support. I can tell you this; If I had been only had been listing a little better to the wise guys!
Just kidding. In short, Everything works on x64 systems, Visual Studio 2005, it is a 32 bit process, but it debugs fluently a 64-bit process. (I attached to W3WP.exe for instance). It stopped at my breakpoints and shows source code, I stepped through etc. It was a good experience!
So, here for the MVP part, the good words for Microsoft!
Of course, you bet, my code did not 'just compile' and run. Not because of ignoring my 64-bit warnings that the Visual Studio compiler might have produced, but because of 'optimizing' some parts of my code in the past, that I should have done better.
Here they are...
1) If the MSDN tells you to allocate a CreateStreamOnHGlobal through a GlobalAlloc that must be using 'movable memory' please do so! Well, I found that the code ran faster when using fixed memory (it was at a case, where the memory stream did not have to grow)! But Win x64, does not like this, and punished me by presenting a GP.
2) If you think that BSTR work exactly the same as before and you don't have to modify code at that, you're right, but this was not applying to me! I was managing my own BSTR allocation replacements (again because of improved efficiency on IIS on this) and I found that a BSTR allocation is somewhat different. It is as follows on 64-bit systems:
BSTR memory layout: [4 bytes (not used)] [4 bytes (length prefix)], wchar_t[length], [\0], total length of allocation is aligned on 16 bytes.
The 4 unused bytes are rather mysterious since SysStringLen() still returns a UINT length (not a UINT_PTR). Maybe this is because of future plans, far, far away?
On 32-bit systems, the prefix is really 4 bytes, and not 8.
For those who are interested in the details:
download it: http://technolog.nl/eprogrammer/bstrnocache.zip
(In combination with my CComBSTR replacement, BSTR heap management performance is outstanding, without using any cache)
3) Some third party code, ZLIB had to be recompiled into a valid 64-bit lib. If you forget to do this, your DLL will link to the wrong 'guy'. B.t.w. zlib version 1.2.3 just recompiled perfectly (kudo's to Jean-loup Gailly and Mark Adler!). To tell the C++ compiler to link to the correct lib, I had to add some conditional code in stdafx.h. Maybe, there is a smarter way to do this. Report me if so...
#ifdef
WIN64
#pragma comment(lib, "zlib64.lib")
#else
#pragma comment(lib, "zlib.lib")
#endif
After making my product strict again, it works flawless on x64 systems (and faster!).
I have an AMD Athlon 64 system, and of course, my main preferred platform today is still Win32 on XP when I develop for server environments.
In the past, I always choose an Intel platform, because of driver madness and just the affinity that you feel with the OS that Intel has. But now, on my system, I never have problems. Great. But that's not my story; I just wanted a system that could do x64 as x32 as well. Unfortunately, I still cannot test IA64 systems.
Question: Is going 64-bit just hype? mmm, when I was the CEO of my CPU baking company, I'd told you, 'of course not!'.
When I was eprogrammer, I'd told you I agree with him :). Why? Because of hard numbers!
And if you don't love hard numbers, you're certainly not like me...
Most tests involve graphics. Well, that's important as well, but what if we skip graphics? Does a 64-bits environment really beat 32-bits even when we don't do 64bit math?
So here we go.
I have a testing system. An AMD Athlon64 3200+, 1 gigabyte RAM and the fastest memory that exists for it. Don't be jealous, you soon will get a better system from your mom!
Our test does the following. It opens a big file; it will encode it to a base64 string. This is a good test, since both integer math and memory allocations play a role.
The encoding is done through a tiny COM component that I quickly wrote for this purpose. It just uses the ATL framework, <atlenc.h> which has full support for base64 coding and decoding.
Then I have a vbscript tester. It has the following lines...
(it opens oembios.bin, I just choose this because, it had to be big, so take your pick to redo the test)
Dim obj, v, t
Set obj = Createobject("NWC.Decode")
t = Timer
obj.readfile "c:\windows\system32\oembios.bin" ’12.5 MB
WScript.Echo “FileRead: “ & Timer - t
t = Timer
obj.ToBase64 v
WScript.Echo “Encode: “ & timer -t
On XP, the 32bit OS, this takes a whopping 0.21 seconds.
FYI, on Windows 2000, it also takes a 0.21 seconds. You see, the OS is at -this- particular test, is not showing improvements over oleautomation & COM performance.
When I compile the CPP COM object, with all optimizations disabled, it would take 0.65 seconds. So you get an idea of the difference when a compiler gets smart.
On Windows Server x64 edition, the same script (it opens the same file), and the COM object compiled to x64 code, takes just 0.11 seconds!
Of course, a good performance test, would involve pure tests, so measuring a mix of operations, would garble our output.
So I improved the COM object, and it would use two memory buffers (one for decoding, the other for binary contents) and only resize them, if a bigger allocation was needed.
In addition, the function ToString() which returns a variant, became ‘byref’ method. If you do so, you can reuse and reallocate string space (not many oleautomation programmers are aware of this efficiency step).
This makes a difference, since our file was 12.5 MB in size. An encoded Base64 unicode string, would need 35MB RAM string storage, and 12.5 MB binary space plus a conversion buffer of 17 MB (because ATL assumes you use a non wide-string). It makes sense, not to destroy that memory heap space and not recreate it at each call.
So, the first time, we decode, we measure memory allocations and math, the second time, all strings and allocations would be -used-, not (re)allocated!
'Hard' numbers! (finally)
Here is a typical output of our script doing 3 times a base64 encoding big files of 12.5 MB in size. The first time (yellow), the heap cache is not effective, the second time (blue), I open a different file (not 10.5 MB) and the third time (yellow again), I reopen the first file again.
|
x64 environment |
win32 environment |
|
readfile1 (test1) (12.5MB) |
0.011 |
0.044 |
|
encode |
0.097 |
0.203 (! See remarks *) |
|
Final base64 string length |
35MB |
identical |
|
Readfile 2 (test2) (10.5MB) |
0,015 |
0.015 |
|
Encode |
0.063 |
0.125 |
|
Final base64 string length |
28MB |
identical |
|
Readfile 1 (test3) (12.5MB) |
0.015 |
0.016 |
|
Encode |
0.109 |
0.125 |
|
Final string length |
35 MB |
|
Update: Added WOW64 test. ie, a vbs in 32-bit mode and a 32-bit Com server in emulation mode.
Readfile 1: 0.016
Encode: 0.09
ReadFile 2: 0.015
Encode: 0.047
Readfile 1: 0.016
Encode: 0.09
On a intel 4 with HT and 2.7 GH speed, the numbers hardly differ with the results on Windows x64.
Differences measured
File I/O read performance: hardly any difference
Memory allocation: +/- 100% faster on x64
(non 64 bit) 32 bit Integer Math: +/- 25% faster on x64.
Conclusion:
The biggest difference in our silly simple test was how memory is dealt with. After al, the conclusion is fairly explainable. On the X64 system, disk I/O read performance is not measurable faster, since the real bottleneck, must be hardware here, not integer math.
Memory heap allocation speed: Here we see a 100% performance boost. Or is our test on the AMD 64 running with a 32-bit just 100% slower because of the 'AMD 32-bit CPU emulation mode'? I think that the test that also ran on an Intel P4 system, answers that question.
What about the simple integer math? It has slightly better performance on a x64 system, better because our MS C++ compiler uses us much as possible available 'spare-registers' while the x32 (w)Intel CPU design, only has 4 spare-registers (like EAX,EBX, ECX and EDX) J.
And to those guys, who blamed Wow64 mode to cause a 'very slow performance' in emulation mode, this test has debunked that accusation.
-
If you have 32-bit software that will be running on a x64 environment, it won't run slower (but on a IA64 it will!). That is an argument not to port the software is it? Of course, if you build good old COM servers, you might think about migrating, if you've not yet decided for migrating it to .NET (msil) since COM components cannot be emulated (ie a 64-bit process, cannot load a 32- bit COM-server inProc). I also guess, that Microsoft won't port Office so soon to x64 for these reasons.
-
* It seems that when you run a 32-bit OS on a AMD 64-bit processor, the execution and heap management (100% difference) is a lot slower as the tests show, than when you boot a 64-bit OS (windows x64 here).
-
On an Intel PIV with a comparable performance CPU-index the same performance was measured as on the 64-bit environment.
Our final conclusion comes to the following.
Let's first rephrase our question. Does a standard 32-bit software-package profit of being ported to 64-bit software?
It depends. If your software needs 64-bit calculations, as is the case with graphical software, yes!
If the software is just as the test doing some 32-bit math and some harddisk activity, it really makes no sense spending money on porting. Just make use of the perfect Wow64 design done by Microsoft!
An other performance chart shows that software execution is not especially faster because of a new smart set of CPU instructions available, but because of the 4GB addressable memory limit! See Active Directory benchmark here
For instance, an Active Directory benchmark, showed a whopping 10,473 percent performance improvement over searching a non-indexed attribute when there are 3,000,000 users! Why that much? Because Active Directory on 64-bit architecture, allows the database to be fully cached in memory. And there you see, when your bucks spent on porting 64-bit, earn back quickly!
Source code for the COM component (MS Visual C++ 8.0) is here...
http://technolog.nl/eprogrammer/base64dec.zip
Hier zijn enkele bestanden die ik gebruikt hebt voor de demo's tijdens de sessie over het upgraden van website authenticatie met ASP.NET 2.0 en IIS 6 op de DevDays 2006.
Zowel voor viewstate als voor forms authenticatie wordt gebruik gemaakt van een decryptionkey en validatiekey. Deze worden standaard automatisch ingevuld. Maar dat is onpraktisch als je twee servers hebt ingezet voor eenzelfde applicatie (web farm) of ASP.NET 2.0 en 1.1 applicaties naast elkaar wil laten werken met hetzelfde authenticatieticket. Genereer daarom validatie- en encryptiekeys voor Web.Config/Machine.Config met dit commmandline tooltje. Let er ook op in de Web.Config van de ASP.NET 2.0 site 3DES als versleutelingstechniek te gebruiken. Standaard is dit nl. AES voor ASP.NET 2.0 geworden.
Voor 3DES encryptie is een sleutel van 3 x 8 bytes nodig. Voor het maken van de gewenste 3DES sleutel start je genmk met de waarde 48 (twee hex karakters vertegenwoordigen een enkele byte-value). Een validatiekey moet minimaal 20 bytes zijn (dus: genmk 40).
Overige downloads zijn:
Voor de sessie 'Upgrade website authenticatie met de ASP.NET 2.0 Security provider infrastructure' tijdens de DevDays '06 op 7 en 8 maart zijn hier nog wat handige links.
De codevoorbeelden post ik later.

De February CTP van Windows Vista is gereed en kan gedownload worden. De CTP alleen ter beschikking gesteld aan mensen die nu al in een Windows Vista bèta programma zitten, MSDN abonnees en TechNet abonnees. Er staat een CTP gepland die nog voor de zomer beschikbaar komt die iedereen kan downloaden. Meer informatie te vinden bij Derk.
I've been doing MMC AD extensions, for a while. Learning, and using trial and error.
Before you ask, why I do this? I like to do things that not often is done! So, at my 10th implementation of a varation on 'some address book' or 'shopping basket' through ASPX or whatever technology for some customer (no offense, thanks to those customers for hiring me) I get bored.
So I choose a new product, that I soon will release.
There are some reaons why this is hard. MS, are you listening? :)
1) How to deal with testing and active directory?
Just a few years ago, it was nearly impossible for developers, to develop extensions for the Active Directory snapin where modifying the domain schema was involved.
You first had to opt for a OID for your company. That's not to hard. But then, you start testing your code. And you find you can't undo schema changes, so easily your changes so you start enumerating IDs and names for each test-run, your test environment becomes messed with unrelated stuff and objects that are 'no longer valid'.
Through virtual server 2003, SP1, I find myself a lot happier, because you just can undo a virtual disk modification, and your active directory schema is back to before your test. But still, a reboot is involved. Maybe some admin-guru, could tell me that restoring the active directory is not so difficult at all. Anyway, a reboot is involved there as well!
Microsoft, here speaks one of your customers (echo... :) )! Please have us edit/delete our own attributeSchema and Classschema objects. I'm sure this request has been done a lot of times, but no, for some reasons, this seems too difficult to implement?
So why do I spent so much lines about testing your snapins and schema changes on AD? Because, I lost many hours, and even a full day, because the schema got once out of sync. And to get all DC's in sync again, you'd go back to an old copy of the virtual windows image but I had to rebuild a lot of things before this image was up and running.
If Windows would allow us to do something like in SQL server, 'ad hoc' updates (including deletes) on system tables for advanced and skilled users, it would be a good thing.
2) The second problem, is how to develop good code while the docs lack. The MSDN sdk folks, wrote us a lot of samples, sure they work, but they all are MFC and some samples, even were prepared and never updated for us, during the time Windows 2000 was designed. So the samples, use pure C++, they mess with messages and hooks(sorry, no offense to anyone) and if each template would need it's own static hooking code, I would find myself helpless because my C++ power is not that great.
Personally, I choose not to develop using MFC code. I like ATL and WTL and ..., a lot on this subject has been written by others.
And where is a good example of the implementation of the CSnapInPropertyPageImpl class (atlsnapin.h) or IDsAdminNewObjExt?
And why does geticon return no icon when I validly reference an iconresource in my dll? There are more issues, but I won't bother you about them.
So I decided, to refine sample code integrate it in my dialogs, and clean up the samples to get a minimized working sample. The final result is really cute, and understandable. I will publish such a sample soon.
3) When you read the MSDN docs, you soon realize, that the docs contain lots of bugs and unclear statements. You have to find it all yourselves. Don't get confused by the MMC 1.2 and 2.0 docs, because you'll discover, that the snapin for Active Directory, really uses some other interfaces and not the fancy new stuff that the MMC 1.2 and higher use.
Microsoft soon will release the SDK for MMC 3.0. I've said this before, but this is just a managed implementation of the unmanaged one. A lot of Pinvoke plumbing would not be needed that you -would- need, if you would do the same using C# and the well known MMC managed open source API on sourceforge.net.
Still, I choose not to go for this solution which is beta 2 for now. Because that beta, assumes your are doing MMC 1.2 and higher, while the Active Directory snapin does depend on some other interfaces and ADSI apis. There you are busy writing and testing your own P-invokes and you also should tell your clients, to install the NET framework version 2. I wonder, I have no hard numbers, how much clients would be willing to do this.
4) The fourth challence, and this is a very subjective one, that is, how good are your C++ and windows UI skills? You need to know about internationalization, ADSI, Win32, NT Security, a lot of C++ dialog stuff, debugging technics and keep your code generic so it would run on all domains.
5) The fifth challence, is really a huge lack in the AD Snapin design as follows: As you can see, when you open the Active Directory snapin, all property sheets, are having a standard UI and tabs. If you develop your own extension, only a -few dialogs- are to your exposal, such as the DirectoryObject picker. But what about the security tab? 'Member of' and 'members' tabs? What about the 'Security' and 'Object' tab? Yes, by some non-published snapin IDs you can have some of them. But MS did not document this, so MS could break this feature in future releases. Be prepared, to build those tab all -again- yourselves!
Which language? C++ or C#?
If you look at the skill requirements a developer needs to cover, you'll conclude that you could use both languages for snapins for Active Directory, but that C++ still has more power to windows. And that C# is not really the -rad- tool you would expect to implement all fancy features.
Personally, I'm not a windows UI guy at all, so all the API's related to DCs and HWNDs were historically based on some Microsoft Visual Basic 6 API hacking :), so you'll understand, that's not too hot for a real C++ freak :)
At a conclusion, I can say that, after all, developing extensions for the AD is really not that difficult. It is only that wide spread on technologies, and software and documentation borders refrain you from quickly getting some result (before you give up).
If the Active Directory and AD Snapin team would follow the IIS path, (b.t.w I am an IIS MVP :) ), they will find a lot more 3rd party extensions making tools. And more businesses will go for Microsoft Active Directory instead of Sun, Novell etc. because of those nifty features and lots of snap ins they have at 'their fingertips'.
Why IIS? Because personally, I find IIS the most open server platform and best documented and supported on Windows there is.
Blijkbaar wel, volgens een onderzoek gepubliceerd op CNNMoney.com zijn op het terrein van technologie vooral .NET ontwikkelaars in trek:
"Two tech jobs in high demand these days are .NET (dot net) developers and quality assurance analysts.
Developers who are expert users of Microsoft's software programming language .NET can make between $75,000 and $85,000 a year in major cities when they're starting out. If they pursue a job at a company that seeks someone with a background in a given field (say, a firm looking for a .NET developer experienced in using software related to derivatives) they might snag a salary hike of 15 percent or more when they switch jobs."
Als je Visual Studio 2005 hebt geïnstalleerd, is hulp tijdens het codekloppen meestal wel prettig. Wist je dat het ook mogelijk is om zoekopdrachten in de Nederlandse taal uit te voeren? Standaard staat enkel Engels geselecteerd, omdat er geen speciale Nederlandse versie van Visual Studio is (en volgens velen is dat maar goed ook ;-) ). Maar codevoorbeelden met Nederlandse uitleg kan wel eens praktisch zijn. Om dit in te stellen ga je in het Help-venster, ook wel Microsoft Document Explorer, naar Tools -> Options. Het volgende scherm verschijnt:

Kies hier bij Online topic language: Dutch
Dit betekent dat zowel Engels als Nederlandse online content wordt doorzocht bij zoekopdrachten.
Om te zien welke sites specifiek worden opgenomen in de zoekopdracht, kies je in het Options scherm voor Online, zodat het volgende scherm te zien is:

Als je vanaf nu zoekt naar voorbeelden, kan je ook in het Nederlands zoeken.

Het toevoegen van codevoorbeelden is heel eenvoudig. Heb je zelf iets gemaakt, en wil je het delen met anderen of gewoon bewaren voor later, ga dan naar DevTips.NET en voeg je codeknipsel toe. Binnen enkele dagen kan iedereen het vinden in de Visual Studio 2005 Help.
TechEd zal zich voortaan alleen nog maar richten op ontwikkelaars en zal vanaf dit jaar in november plaatsvinden. Dit heeft Ruud de Jonge, manager van de Developer en Platform Group bij Microsoft Nederland, bevestigd. [Meer op:
WebWereld]
Billy Hollis heeft het voor ons op
een rijtje gezet.

In samenwerking met Oosterkamp training | consultancy organiseert
VBCentral op 23 januari 2006 hun eerste
VBevent. Dit event staat in het teken van de ‘Visual Studio 2005 Community Launch’. Namens VBcentral zal
André Obelink het spits afbijten met een sessie over Smart Clients. Daarna zal
Thomas Huijer van Oosterkamp training | consultancy een presentatie geven over de nieuwe mogelijkheden van ASP.NET 2.0. In beide presentaties zal de nadruk liggen op de praktische toepassing en zullen er veel demo’s en codevoorbeelden de revue passeren.
Kijk hier voor meer info en inschrijven. Deelname is gratis.
Windows is a powerful platform, at least, if you know which APIs are at your service :)
For instance, function FindMimeFromData (exported from UrlMon.dll) was very helpfull in telling me that the user is opening the right type of file based on the content-type. I did not test if this is full proof for all conten types, but I just needed it for determing the type of image (jpeg/png/gif/wmf/emf/tif/ etc) and that works! You can't fool this function, by modifying the extension of the file, from .jpg to .gif for instance!
I got this TIP from a hardcore ATL programmer Alex Fedotov. The Russians are the best programmers :). And in fact, you could easily use this function in C# as well.
Spasiba! (it seems that .Text developers did not utilize nvarchar (unicode-16) for text its database storage so I can't use russian characters)
ps: If you wonder if I copied this from pinvoke.net, no, it's the other way around :)
[DllImport("urlmon.dll", EntryPoint="FindMimeFromData", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
static extern int FindMimeFromData(IntPtr pBC,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] byte[] pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
[MarshalAs(UnmanagedType.LPWStr)]
out string ppwzMimeOut,
int dwReserved);
/// <summary>
/// Ensures that file exists and retrieves the content type
/// </summary>
/// <param name="file"></param>
/// <returns>Returns for instance "images/jpeg" </returns>
public static string getMimeFromFile(string file)
{
if (!System.IO.File.Exists(file))
throw new FileNotFoundException(file + " not found");
int MaxContent = (int)new FileInfo(file).Length;
if (MaxContent > 4096) MaxContent = 4096;
FileStream fs = File.OpenRead(file);
byte[] buf = new byte[MaxContent];
fs.Read(buf, 0, MaxContent);
fs.Close();
string mime;
//note: the CLR frees the data automatically returned in ppwzMimeOut
int result = FindMimeFromData(IntPtr.Zero, file, buf, MaxContent, null, 0, out mime, 0);
if (result != 0)
throw Marshal.GetExceptionForHR(result);
return mime;
}
Note that the MSDN does not tell us, how to free the returned data pointed by ppwzMimeOut. I simply assume it to be a CoTaskAlloc-ed string and if it were not true, we have no problem unless you try to run this on a winblows 9.x environment. If someone knows facts about this please let me know.
Here it comes in C++.
///
<summary>
/// accepts an existing path to a file and reads some bytes (max 4096) to determine the content type
///</summary>
///<returns>"images/jpeg" for instance</returns>
STDMETHODIMP GetContentType(PCWSTR file, BSTR* contentType) throw()
{
AtlTrace(L"GetContentType %s\n", file);
CAtlFile fl;
ULONGLONG maxContent ;
HRESULT hr = fl.Create(file, FILE_READ_DATA, FILE_SHARE_READ, OPEN_EXISTING);
if (hr != S_OK) return hr;
hr = fl.GetSize(maxContent);
DWORD maxData = maxContent > 4096 ? 4096 : (DWORD)maxContent;
CTempBuffer<BYTE, 4096, CComAllocator> data(maxData);
PWSTR mime;
if (data != NULL)
{
hr = fl.Read(data, maxData, maxData);
fl.Close();
// have urlmon do the task
hr = FindMimeFromData(NULL, file, data, maxData, NULL, 0, &mime, 0);
}
else
return E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
*contentType = CComBSTR(mime).Detach();
CoTaskMemFree(mime);
}
return hr;
}