Friday, September 7, 2007
Steve Shaughnessy on Blackfish SQL and dbExpress4.
Wednesday, September 5, 2007
Just in case...RAD Studio 2007
While RAD Studio 2007 includes many updates for the Delphi and C++Builder 2007 personalities, the main addition is the Delphi for .NET personality. This includes the new language feature, parameterized types (generics). This allows you to both create and consume generic data types as supported by the latest .NET 2.0 framework. And updated version of VCL for .NET is included with all the same level of support for Windows Vista. You can also create ASP.NET web applications.
Ok.. the commercial is over... :-)
Wednesday, August 29, 2007
Driven to Distraction...
Earlier in the summer I broke down and bought my own XBox 360. I rationalized the purchase by telling myself that I'll work with some of the other Delphi team members and get Delphi for .NET working with the managed XNA framework... yeah.. yeah.. that's the ticket. The truth of the matter is that Forza Motorsports 2 was my real motivation. I bought the system and the MS Force Feedback Wheel. After playing for about a week... I determined that you can't just use the wheel just by sitting it on your lap.... I needed a racing chair! I researched online all the various different chairs and they all were either too expensive (nevermind the fact that my wife was already rolling her eyes at me...) or just cheap looking. So I decided to build my own. I went to Summit Racing and purchased a cheap racing seat and adjustable sliding tracks. Then headed down to the closest Home Depot and purchased some 2x3, 2x2s and some 5/8” (16mm) plywood. Here's what I built over a weekend:
This is after the first day I had the base completed and the seat mounted. The base consists of a plywood sheet with 2x2s framing underneath that provide rigidity and raise the bolt heads that attach the seat rails off the floor.
It took a bit of planning and thinking to get the wheel and pedal mounts the way I wanted. I knew I wanted the height of the wheel mount to be adjustable and to be able to swing out of the way to easily entering and exiting the seat. So I created a simple moveable parallelogram mounting table. I mounted the pedals onto an angled platform between the “legs” of the wheel mount table. Here's the final result:
To limit the movement of the table and to allow for adjustment, I just simply placed extra blocks of wood on the bottom. The uprights will wedge against them and rest there with with its own weight to hold it in place. Since these photos were taken, I've revised the mounting of the wheel by eliminating the need for the provided clamp (which is somewhat weak) and screwing two blocks of wood to the top of the mounting table that overlap the “wings” that stick out on the sides of the wheel housing.
Now, I've never really been what one would call a “hard-core” gamer as I've always preferred to take the game apart and figure out how it works rather than actually playing the game :-). However, with this setup, and the big screen TV I must say that the racing game experience is on a whole different level. So when we went to Las Vegas earlier this past summer we went to the NASCAR Cafe that has an excellent linear accelerated roller coaster called Speed-The Ride and the Cyber Speedway. The problem is that when we first started visiting Las Vegas regularly about 7 years ago, I remember being totally blown away at the level of “realism” that the motion controlled cars provided... that was late '90s technology. I must now say that short of having a true motion-controlled experience, the graphics and detail of the racing in Forza 2 makes the Cyber Speedway feel like playing the old Pong video game.
So if you've got an XBox 360, my gamertag is “kylix rd” I've yet to sign-up for a full Xbox Live “gold” membership, so once I do maybe we can race :-).
Ok, so it hasn't been all vacations, and fun and games... there actually has been some things going on here at CodeGear. If you haven't noticed there's been a lot of information regarding Highlander, the full release of what is was once the Borland Developer Studio now called RAD Studio 2007. The betablogging program is in full swing and there is a lot of excellent information flowing about this new release. I suppose for the folks that view the language as a critical piece of the product, you'll be pleased to know that we'll finally be releasing full support for generics on the Delphi for .NET side. The next RAD Studio release, codenamed Tiburón will include support for generics on Win32 as well as full support for Unicode throughout VCL, the RTL top to bottom, left to right. Nick has an excellent post with links to the various betabloggers out there. If you know of others that Nick hasn't mentioned, make sure you contact him with a link and he'll update his blog entry.
While not specifcally Delphi related, it is CodeGear related. We've just announced the availability of JGear. JGear allows you to purchase some of the excellent JBuilder specific “mojo” and integrate it into an existing Eclipse or Eclipse-derived 3.2 installation! You no longer have be a JBuilder customer to get access to the features that make JBuilder the most powerful IDE in the Java space. Of course if you want the best experience, you should consider moving to the new Eclipse derived JBuilder.
Over the coming months, I'll try and get back into the swing of more regular blogging by helping to chronicle our move to Unicode with VCL and the IDE itself. I plan on beginning to highlight some of the little gotchas and things to watch for now so you can be best prepared for the switch when Tiburón is released next year. As we encounter these things and solve them, there will be things you can do today to your code to make sure the transition is as smooth as possible. Details will follow throughout the coming months.
Another item of note is that supposedly we're due to move to some new mo-betta blogging software sometime soon... I'm hoping that it will go smoothly, but just in case there are a few burps, I hope you will be understanding while the dust settles.
Thursday, July 19, 2007
WiinRemote
It's a Delphi application that allows you to use the new Nintendo Wii Remote with Windows. This is currently making the rounds across the hardware/gadget/homebrew blogosphere. It's great to see Delphi used for such a high-profile utility.
Wednesday, June 27, 2007
MS notices the roadmap...
Rob Relyea on the WPF/XAML team at MS just sent me this link to his blog. It's good to get some attention from those folks about our roadmap and strategy. While MS is certainly a competitor at one level, they are also huge partner at many other levels. Rob was just asking for some clarification on our roadmap, so I sent some of it along to him. Before you bother asking, there are still many aspects of our plans that must continue to remain undisclosed, so I won't go into the substance of my response. Rob is one of our many “friends on the inside” at MS :-).
Thursday, June 14, 2007
Connecting an event to a Active Script function
First off we'll add a button to the form and change the caption to “Hook Event.” We'll use the TEdit that's already there to get the name of the Active Script function to hook up when the Hook Even button is pressed. Here'e what the form looks like now:
Double-click the Hook Event button and then add this code:
procedure TForm3.Button2Click(Sender: TObject);
var
Info: EXCEPINFO;
State: TOleEnum;
Code: WideString;
Disp: IDispatch;
Id: Cardinal;
Name: WideString;
PropInfo: PPropInfo;
begin
Code := Memo1.Text;
OleCheck(FScript.GetScriptState(State));
if State = SCRIPTSTATE_CONNECTED then
OleCheck(FScript.SetScriptState(SCRIPTSTATE_DISCONNECTED));
OleCheck(FParse.ParseScriptText(PWideChar(Code), nil, nil, nil, 0, 0,
SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISPERSISTENT, nil, Info));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_CONNECTED));
OleCheck(FScript.GetScriptDispatch(nil, Disp));
Name := Edit1.Text;
OleCheck(Disp.GetIDsOfNames(GUID_NULL, @Name, 1, GetThreadLocale, @Id));
if FClickMethod.Code <> nil then
begin
ReleaseMethodPointer(FClickMethod);
FillChar(FClickMethod, SizeOf(FClickMethod), 0);
end;
PropInfo := GetPropInfo(CheckBox1, 'OnClick', [tkMethod]);
FClickMethod := CreateMethodPointer(Disp, Id, PropInfo.PropType^);
SetMethodProp(CheckBox1, 'OnClick', FClickMethod);
end;
So the difference we have from the last method is that we don't want the script state to be disconnected at the end, so we first get the script state. If its connected, then disconnect it. Just like the last installment, we'll parse the script text, and set the state to connected. If there are any statements outside a function block, they'll execute immediately but we want to leave the script in the connected state. So the next thing is to get an IDispatch for the whole script. That is the GetScriptDispatch call. This is where we'll use the TEdit control on the form. The text contains the name of the Active Script function to look up in the GetIDsOfNames call. If the function is found, this function will return its “ID.” Once we have that, the rest of the code is all about creating a special TMethod* “stub” that is then assigned to the OnClick of the CheckBox1 component. Remember I mentioned the CreateMethodPointer() function in ObjComAuto? This is where that little tidbit of magic is used. Once we have a TMethod we can assign that to the OnClick of CheckBox1 using the SetMethodProp function. We have to do it this way to get around the type incompatibility of the TNotifyEvent and TMethod. That's all there is to wiring up the event. Let's see this puppy in action!
Run the application and paste the following code into Memo1:
function CheckBox1Click(sender) {
if (sender.Checked == 1) {
Application.MainForm.Caption = "CheckBox1 is checked"
} else {
Application.MainForm.Caption = "CheckBox1 is NOT checked"
}
}
Then enter “CheckBox1Click” into Edit1 and press the “Hook Event” button. As long as you got no error messages, you should be able to click CheckBox1 on and off and the caption of the form should change to reflect the checked state of the checkbox!
Again, the source code for this demo is in CodeCentral here: http://cc.codegear.com/item/24667
*TMethod - This is a data structure that reflects the actual in-memory representation of a method pointer. It simply consists of the address of the actual method to call and a reference to the specific instance of the class on which the method is defined.
Wednesday, June 13, 2007
Adding Active Scripting to your Delphi Win32 application
Native Object Pascal language support for reference counted interfaces is only the start. There is a whole host of other things that Delphi provides in both the RTL and through a very little known technique of enabling extended meta-data on all public methods and properties of a class. Steve Trefethen has been hinting at a lot of this kind of support in his articles about creating a native compiled Win32 web “mash-up” with Google Maps. In nothing more than a couple of hours, I had a simple demo up and running that was running Java Script from within a Delphi application which would then access and manipulate various controls on a form. Let's get started.
First, we're going to examine the units from the Delphi RTL that are pertinent to making this happen. Looking in sourcewin32rtlcommon, you'll find an interesting unit, ObjAuto.pas. Take a moment to peruse this units. Looking in ObjAuto.pas there is some... how should I say this... very “interesting” code. I won't go into the gory details here since that would be a whole series of articles. Basically what this code does is allow a method of an object to be dynamically invoked through an IDispatch Invoke method call. This code also forms a lot of the basis for the implementation of Win32 SOAP servers and clients. The IDispatch angle is very important since that is how any Active Script code accesses stuff “on the outside.” There is another unit, ObjComAuto.dcu, for which we don't supply source (I'll work on rectifying that oversight), which contains the TObjectDispatch class and a couple of helper global functions.
Between ObjAuto.pas and ObjComAuto.pas nearly 80% of what is needed to make this all work is already there. All we need to do now is to provide a little bit of glue code to wire some of this stuff up. First up is to implement the IActiveScriptSite and IActiveScriptSiteWindow interfaces. You can find the declaration of these interfaces in AscrLib.pas down in sourcewin32websnap. You can read the documentation about them here.
First is IActiveScriptSite:
IActiveScriptSite = interface(IUnknown)
['{DB01A1E3-A42B-11CF-8F20-00805F2CD064}']
function GetLCID(out plcid: LongWord): HResult; stdcall;
function GetItemInfo(pstrName: PWideChar;
dwReturnMask: LongWord;
out ppiunkItem: IUnknown;
out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString(out pbstrVersion: WideString): HResult; stdcall;
function OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange(ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError(const pscripterror: IActiveScriptError): HResult; stdcall;
function OnEnterScript: HResult; stdcall;
function OnLeaveScript: HResult; stdcall;
end;
Then there is IActiveScriptSiteWindow:
IActiveScriptSiteWindow = interface(IUnknown)
['{D10F6761-83E9-11CF-8F20-00805F2CD064}']
function GetWindow(out phwnd: wireHWND): HResult; stdcall;
function EnableModeless(fEnable: Integer): HResult; stdcall;
end;
These are the only two interfaces you need to implement for the running Active Script to communicate back to the hosting application. Let's get started on the actual demostration app. Create a new VCL Win32 application and drop a TMemo control and a TButton. Then drop a few other controls down on the form because these will be the ones manipulated by the Active Script (although you can manipulate all the controls on the form). Here's what mine looked like:
Now switch to the source view and add AscrLib to the uses clause and add IActiveScriptSite and IActiveScriptSiteWindow to the form declaration as interfaces to implement.
TForm3 = class(TForm, IActiveScriptSite, IActiveScriptSiteWindow)Then “implement” them:
TForm3 = class(TForm, IActiveScriptSite, IActiveScriptSiteWindow)
Memo1: TMemo;
Button1: TButton;
RadioButton1: TRadioButton;
RadioButton2: TRadioButton;
ListBox1: TListBox;
ComboBox1: TComboBox;
Edit1: TEdit;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
private
{ Private declarations }
{ IActiveScriptSite }
function GetLCID(out plcid: LongWord): HResult; stdcall;
function GetItemInfo(pstrName: PWideChar;
dwReturnMask: LongWord;
out ppiunkItem: IUnknown;
out ppti: IUnknown): HResult; stdcall;
function GetDocVersionString(out pbstrVersion: WideString): HResult; stdcall;
function OnScriptTerminate(var pvarResult: OleVariant;
var pexcepinfo: EXCEPINFO): HResult; stdcall;
function OnStateChange(ssScriptState: tagSCRIPTSTATE): HResult; stdcall;
function OnScriptError(const pscripterror: IActiveScriptError): HResult; stdcall;
function OnEnterScript: HResult; stdcall;
function OnLeaveScript: HResult; stdcall;
{ IActiveScriptSiteWindow }
function GetWindow(out phwnd: HWND): HResult; stdcall;
function EnableModeless(fEnable: Integer): HResult; stdcall;
public
{ Public declarations }
end;
Most of these functions we don't care at this point, so just put Result := S_OK; in the body of them. The methods GetItemInfo, GetWindow, and OnScriptError are the ones we're going to look at right now. Here's GetItemInfo:
function TForm3.GetItemInfo(pstrName: PWideChar; dwReturnMask: LongWord; out ppiunkItem,
ppti: IInterface): HResult;
begin
Result := S_FALSE;
if SameText('Application', pstrName) then
begin
if dwReturnMask and SCRIPTINFO_IUNKNOWN <> 0 then
begin
ppiunkItem := TAutoObjectDispatch.Create(TApplicationWrapper.Connect(Application)) as IInterface;
Result := S_OK;
Exit;
end;
if dwReturnMask and SCRIPTINFO_ITYPEINFO <> 0 then
begin
Result := TYPE_E_ELEMENTNOTFOUND;
Exit;
end;
end;
end;
This is really all that is needed in order for the Active Script to get access to the application's context. We'll discuss TAutoObjectDispatch and TApplicationWrapper in a moment. So what happens in the script when it refers to “Application“ that is an unknown identifier so the script engine calls out to the script site and asks for this object. In this case this will be the VCL application object, so we have to create a wrapper object and around that a dispatch object that provides the necessary IDispatch based late-binding for the script runtime. From here the real magic starts.
Now lets tell the Active Scripting engine to run a script. To do this we first need to create a CoClass that implements the IActiveScript interface. Through some judicious spelunking with Google, I was able to find the JavaScript and VBScript CLSID GUIDs.
const
CLSID_VBScript: TGUID = '{b54f3741-5b07-11cf-a4b0-00aa004a55e8}';
CLSID_JScript: TGUID = '{f414c260-6ac0-11cf-b6d1-00aa00bbbb58}';
Just put these at the top of the unit. Add two private fields to the form class of type IActiveScript and IActiveScriptParse. Add ComObj to the uses clause. Double click the form to create the OnCreate event and inject this code into it:
procedure TForm3.FormCreate(Sender: TObject);
begin
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
OleCheck(CoCreateInstance(CLSID_JScript, nil, CLSCTX_INPROC_SERVER, IID_IActiveScript, FScript));
OleCheck(FScript.SetScriptSite(Self as IActiveScriptSite));
OleCheck(FScript.AddNamedItem('Application', SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISSOURCE));
if Supports(FScript, IActiveScriptParse, FParse) then
OleCheck(FParse.InitNew);
end;
Note the AddNamedItem call. That adds the “Application” object to the namespace of the script. So now we're ready to execute some script. Double click the Button1 (the one next to the Memo1). Add the following code to the method body:
procedure TForm3.Button1Click(Sender: TObject);
var
Info: EXCEPINFO;
Code: WideString;
begin
Code := Memo1.Text;
OleCheck(FParse.ParseScriptText(PWideChar(Code), nil, nil, nil, 0, 0,
SCRIPTITEM_ISVISIBLE or SCRIPTITEM_ISPERSISTENT, nil, Info));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_CONNECTED));
OleCheck(FScript.SetScriptState(SCRIPTSTATE_DISCONNECTED));
end;
Now run the application and enter the following into the Memo:
function main() {
var Component
var MainForm = Application.MainForm
for (var i = 0; i < MainForm.ComponentCount; i++) {
Component = MainForm.GetComponent(i)
MainForm.ListBox1.Items.Add(Component.Name)
}
}
main()
Press Button1. The Listbox should now fill up with all the names of the components on the form. So what the blazes happened!?? Remember the TAutoObjectDispatch class and the TApplicationWrapper from the GetItemInfo function? TAutoObjectDispatch is a descendant of the TObjectDispatch class in the ObjComAuto.dcu unit. Here's the basic declaration of TObjectDispatch:
TObjectDispatch = class(TInterfacedObject, IDispatch)
protected
function GetObjectDispatch(Obj: TObject): TObjectDispatch; virtual;
function GetMethodInfo(const AName: ShortString; var AInstance: TObject): PMethodInfoHeader; virtual;
function GetPropInfo(const AName: string; var AInstance: TObject; var CompIndex: Integer): PPropInfo; virtual;
property Instance: TObject read FInstance;
public
{ IDispatch }
function GetIDsOfNames(const IID: TGUID; Names: Pointer;
NameCount: Integer; LocaleID: Integer; DispIDs: Pointer): HRESULT;
virtual; stdcall;
function GetTypeInfo(Index: Integer; LocaleID: Integer;
out TypeInfo): HRESULT; stdcall;
function GetTypeInfoCount(out Count: Integer): HRESULT; stdcall;
function Invoke(DispID: Integer; const IID: TGUID; LocaleID: Integer;
Flags: Word; var Params; VarResult: Pointer; ExcepInfo: Pointer;
ArgErr: Pointer): HRESULT; virtual; stdcall;
public
constructor Create(Instance: TObject; Owned: Boolean = True);
destructor Destroy; override;
end;
We're going to create a descendant, TAutoObjectDispatch, that overrides GetObjectDispatch, GetMethodInfo, and GetPropInfo. In GetObjectDispatch, we'll look up a registered “wrapper” class for the given object, create that wrapper, and then place that back into another instance of TAutoObjectDispatch. I've already done a lot of this in an Automation.pas unit that is included with this project. There are also a bunch of “wrapper” classes for various VCL objects. Remember where I mentioned extended meta-data on public methods? Well the wrappers are where this is handled. The base wrapper class is declared within the {$METHODINFO ON} directive which will tell the compiler to generate extra method RTTI on all public methods of this class and all descendants of that class. It is this extra information that the dynamic invocation code uses to call the functions.
When the scripting engine calls GetItemInfo asking for an interface to the “Application” item, an IDispatch is returned. Then it calls GetIDsOfNames asking for the “MainForm” property. It then calls Invoke to get another IDispatch representing the MainForm, and the process starts all over again. By using the object wrappers you can control with fine detail everything that you want the script to have access to. Keep in mind that all published properties are always available, however you can still control this access by overriding GetPropInfo on TObjectDispatch.
In the next installment, I'll show how you can wire an event directly up to a script function. In the mean-time, take a close look at CreateMethodPointer in ObjComAuto.int contained within the demo for a clue as to how this will be done.
Again, the CodeCentral demo can be found here: http://cc.codegear.com/item/24664
Friday, June 1, 2007
A look at "array of const" for fun and profit
So here we are wanting to have a nice string format function that allows you to specify any number of parameters both as constants and variables for maximum flexibility. Sure we could have just introduced a direct clone of printf and even followed the same syntax, but that just didn't seem to “fit” the whole idea of maximum type safety. See the problem with the printf function is that if you want to format a string with the text representation of the value of a byte followed by an integer, there was no information passed in that clearly indicated that 'x' param is a byte. It also forces you to specify the same parameter multiple times if you wanted to use it in more than one place. It was essentially a variable length array with elements of varying sizes without any information as to the overall length and size of each element! Gee... I wonder why the world is filled with so many buffer overrun errors? Anyway, I digress :-).
So the requirements were simple. We needed a language construct that would allow us to more-or-less declare a function to take a variable number of parameters. Since a function's parameter list is essentially a compiler generated array pushed onto the stack (or passed in CPU registers), why not just allow an array to be declared in place and passed to the function? We also wanted this array to be self-describing and type-safe. So when you declare a parameter as an “array of const” the compiler actually makes that into an open array parameter as an “array of TVarRec.” The declaration for TVarRec is as follows:
PVarRec = ^TVarRec;
TVarRec = record { do not pack this record; it is compiler-generated }
case Byte of
vtInteger: (VInteger: Integer; VType: Byte);
vtBoolean: (VBoolean: Boolean);
vtChar: (VChar: Char);
vtExtended: (VExtended: PExtended);
vtString: (VString: PShortString);
vtPointer: (VPointer: Pointer);
vtPChar: (VPChar: PChar);
vtObject: (VObject: TObject);
vtClass: (VClass: TClass);
vtWideChar: (VWideChar: WideChar);
vtPWideChar: (VPWideChar: PWideChar);
vtAnsiString: (VAnsiString: Pointer);
vtCurrency: (VCurrency: PCurrency);
vtVariant: (VVariant: PVariant);
vtInterface: (VInterface: Pointer);
vtWideString: (VWideString: Pointer);
vtInt64: (VInt64: PInt64);
end; It's just a variant record. If you also look closely, all the data fields are the same size. They all max out at pointer size (which has some interesting implications for 64bit, but that is a subject for another day). So if the data being passed in is > 4 bytes it is done as a pointer to this data. An array with elements of this type will be a constant element size array. Also, since “array of const” becomes an “array of TVarRec” it is an open array so the length of the array is also passed in. This satisfies one objection to the C-style “...” construct. The other objection is solved by the fact that the compiler will encode into the VType field a value representing the type of that element. Another side benefit of this is that you can now refer to the position of an element in addition to its value. This allows you to do interesting things like explicitly refer to the element you want to format in the string using the format specifier '%x:y' where x is the ordinal position of the element with 0 being the first element.
So how do you use this special array construct? If you declared your function or method with a parameter of type “array of const” in the body of the method you just treat that parameter as an “array of TVarRec” and use all the standard array indexing and range checking functions, like Low and High. Here's a simple function that just writes to the console the type and value of each element:
procedure PrintArrayOfConst(const Args: array of const);
var
I: Integer;
begin
for I := Low(Args) to High(Args) do
begin
Write('Arg[', I, ']:');
case Args[I].VType of
vtInteger: Writeln('Integer = ', Args[I].VInteger);
vtBoolean: Writeln('Boolean = ', BoolToStr(Args[I].VBoolean, True));
vtChar: Writeln('Char = ''', Args[I].VChar, '''');
vtExtended: Writeln('Extended = ', FloatToStr(Args[I].VExtended^));
vtString: Writeln('ShortString = ''', Args[I].VString^, '''');
vtPChar: Writeln('PChar = ''', Args[I].VPChar, '''');
vtAnsiString: Writeln('AnsiString = ''', string(Args[I].VAnsiString), '''');
vtWideChar: Writeln('WideChar = ''', Args[I].VWideChar, '''');
vtPWideChar: Writeln('PWideChar = ''', Args[I].VPWideChar, '''');
vtWideString: Writeln('WideString = ''', WideString(Args[I].VWideChar), '''');
vtInt64: Writeln('Int64 = ', Args[I].VInt64^);
vtCurrency: Writeln('Currency = ', CurrToStr(Args[I].VCurrency^));
else
Writeln('Unsupported');
end;
end;
end;
Of course it doesn't support all things, but it should give you an idea what you can do with this little known but widely used (you've used Format() right?) language feature. Here's a sample of how to call this function:
PrintArrayOfConst([1, 'c', 'this is a string', 12.5, True, PChar('This is a pchar'), Int64(123456)]);I had to do some typecasting in order to get the compiler to recognize some of the literals as specific type because it will automatically pick the most natural type to use. Of course if you pass in variables for the array elements, the type of that variable is preserved. So go have fun, and definately do try this at home.
Tuesday, May 22, 2007
Roadmaps! Git yer roadmaps, here!
Friday, May 18, 2007
Softening stance...
After thouroughly reading this article about How To Keep Hostile Jerks From Taking Over Your Online Community from the other day and having some time to really think about it, I'm beginning to rethink a few things. First of all I want CodeGear's online community presence to be as comfortable and inviting both for our loyal customers and “fanboys” ;-) in addition to making sure all the tire-kickers out there are not immediatly put off by a negative environment. Some would argue that “that's just the way it is and to grow a thicker skin.” But the old adage that you can catch more flys with honey than vinegar is as true in the online world as it is anywhere else.
I remember many years ago reading this book, All I Really Need To Know I Learned In Kindergarten. I think that many folks tend to forget a lot of these simple truths because they are somehow “enlightened” or “educated.” Posturing and “oneupmanship” dominates a lot of online communities. I specifically did not use the word “competitive” because what many folks view as competition is in fact nothing of the sort. It is actually elevating oneself at the expense of others. Recognition and prominance cannot be done by yourself. I remember an old story that my father used to tell to illustrate the point that it is perfectly ok to ask for help and that every now and then you need this help to solve problems and get yourself out of a bind.
The basic story talks about a little monkey that rejects everyone's offers for help and wisdom. He is repeatedly told to never go into a certain section of the jungle because of the pools of quicksand. The problem is that it is also the best location to get bananas. So in defiance to his peers and parents, he ventures into that area and promptly gets stuck. The other jungle animals rush to his aid and he just tells them to go away because he can get himself out. The problem is that his solution is grab his own whiskers and pull himself up. Even very young children immediately understand the folly of such a tactic. Yet little monkey keeps eshewing all offers for help and is, in return, abusive and rude to all those who offer help. Ultimately the monkey succumbs and the last thing seen of the monkey is two fists full of whiskers above the pool of quicksand.
What is the ultimate solution for creating an online environment that is provocative, inviting, fun, and most of all informative and helpful for developers of all levels? I don't know if I have all the answers, but ignoring those “hostile jerks” and allowing them to run amok isn't the answer either. Clearly this is not a simple solution because it is not a simple problem, right? In keeping with the whole notion of constructive, reasoned and productive discussion, what do you think? Do we throw up our hands in despair? Should a hard-line no-tolerance policy be put into place? I suspect it is something in between, no?


